*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#include "winbase.h"
#include "winuser.h"
#include "objbase.h"
-#include "ole2.h"
-#include "ole2ver.h"
#include "rpc.h"
+
#include "wine/debug.h"
#include "compobj_private.h"
/* creates a new stub manager and adds it into the apartment. caller must
* release stub manager when it is no longer required. the apartment and
* external refs together take one implicit ref */
-struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object, MSHLFLAGS mshlflags)
+struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object)
{
struct stub_manager *sm;
if (!sm) return NULL;
list_init(&sm->ifstubs);
+
InitializeCriticalSection(&sm->lock);
+ DEBUG_SET_CRITSEC_NAME(&sm->lock, "stub_manager");
+
IUnknown_AddRef(object);
sm->object = object;
sm->apt = apt;
/* start off with 2 references because the stub is in the apartment
* and the caller will also hold a reference */
sm->refs = 2;
-
- /* yes, that's right, this starts at zero. that's zero EXTERNAL
- * refs, ie nobody has unmarshalled anything yet. we can't have
+ sm->weakrefs = 0;
+
+ sm->oxid_info.dwPid = GetCurrentProcessId();
+ sm->oxid_info.dwTid = GetCurrentThreadId();
+ /*
+ * FIXME: this is a hack for marshalling IRemUnknown. In real
+ * DCOM, the IPID of the IRemUnknown interface is generated like
+ * any other and passed to the OXID resolver which then returns it
+ * when queried. We don't have an OXID resolver yet so instead we
+ * use a magic IPID reserved for IRemUnknown.
+ */
+ sm->oxid_info.ipidRemUnknown.Data1 = 0xffffffff;
+ sm->oxid_info.ipidRemUnknown.Data2 = 0xffff;
+ sm->oxid_info.ipidRemUnknown.Data3 = 0xffff;
+ assert(sizeof(sm->oxid_info.ipidRemUnknown.Data4) == sizeof(apt->oxid));
+ memcpy(sm->oxid_info.ipidRemUnknown.Data4, &apt->oxid, sizeof(OXID));
+ sm->oxid_info.dwAuthnHint = RPC_C_AUTHN_LEVEL_NONE;
+ sm->oxid_info.psa = NULL /* FIXME */;
+
+ /* Yes, that's right, this starts at zero. that's zero EXTERNAL
+ * refs, i.e., nobody has unmarshalled anything yet. We can't have
* negative refs because the stub manager cannot be explicitly
* killed, it has to die by somebody unmarshalling then releasing
* the marshalled ifptr.
*/
sm->extrefs = 0;
- if (mshlflags & MSHLFLAGS_TABLESTRONG)
- sm->state = STUBSTATE_TABLE_STRONG;
- else if (mshlflags & MSHLFLAGS_TABLEWEAK)
- sm->state = STUBSTATE_TABLE_WEAK_UNMARSHALED;
- else
- sm->state = STUBSTATE_NORMAL_MARSHALED;
-
EnterCriticalSection(&apt->cs);
- sm->oid = apt->oidc++;
+ sm->oid = apt->oidc++;
list_add_head(&apt->stubmgrs, &sm->entry);
LeaveCriticalSection(&apt->cs);
return sm;
}
-/* m->apt->cs must be held on entry to this function */
+/* caller must remove stub manager from apartment prior to calling this function */
static void stub_manager_delete(struct stub_manager *m)
{
struct list *cursor;
TRACE("destroying %p (oid=%s)\n", m, wine_dbgstr_longlong(m->oid));
- list_remove(&m->entry);
-
/* release every ifstub */
while ((cursor = list_head(&m->ifstubs)))
{
stub_manager_delete_ifstub(m, ifstub);
}
+ CoTaskMemFree(m->oxid_info.psa);
IUnknown_Release(m->object);
+ DEBUG_CLEAR_CRITSEC_NAME(&m->lock);
DeleteCriticalSection(&m->lock);
HeapFree(GetProcessHeap(), 0, m);
refs = ++This->refs;
LeaveCriticalSection(&This->apt->cs);
- TRACE("before %ld\n", refs - 1);
+ TRACE("before %d\n", refs - 1);
return refs;
}
EnterCriticalSection(&apt->cs);
refs = --This->refs;
- TRACE("after %ld\n", refs);
+ TRACE("after %d\n", refs);
+ /* remove from apartment so no other thread can access it... */
if (!refs)
- stub_manager_delete(This);
+ list_remove(&This->entry);
+
LeaveCriticalSection(&apt->cs);
+ /* ... so now we can delete it without being inside the apartment critsec */
+ if (!refs)
+ stub_manager_delete(This);
+
return refs;
}
/* add some external references (ie from a client that unmarshaled an ifptr) */
-ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs)
+ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL tableweak)
{
ULONG rc;
EnterCriticalSection(&m->lock);
-
+
/* make sure we don't overflow extrefs */
refs = min(refs, (ULONG_MAX-1 - m->extrefs));
rc = (m->extrefs += refs);
+ if (tableweak)
+ rc += ++m->weakrefs;
+
LeaveCriticalSection(&m->lock);
- TRACE("added %lu refs to %p (oid %s), rc is now %lu\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
+ TRACE("added %u refs to %p (oid %s), rc is now %u\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
return rc;
}
/* remove some external references */
-ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs)
+ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs, BOOL tableweak, BOOL last_unlock_releases)
{
ULONG rc;
refs = min(refs, m->extrefs);
rc = (m->extrefs -= refs);
+ if (tableweak)
+ rc += --m->weakrefs;
+
LeaveCriticalSection(&m->lock);
- TRACE("removed %lu refs from %p (oid %s), rc is now %lu\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
+ TRACE("removed %u refs from %p (oid %s), rc is now %u\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
- if (rc == 0)
+ if (rc == 0 && last_unlock_releases)
stub_manager_int_release(m);
return rc;
return result;
}
+struct ifstub *stub_manager_find_ifstub(struct stub_manager *m, REFIID iid, MSHLFLAGS flags)
+{
+ struct ifstub *result = NULL;
+ struct ifstub *ifstub;
+
+ EnterCriticalSection(&m->lock);
+ LIST_FOR_EACH_ENTRY( ifstub, &m->ifstubs, struct ifstub, entry )
+ {
+ if (IsEqualIID(iid, &ifstub->iid) && (ifstub->flags == flags))
+ {
+ result = ifstub;
+ break;
+ }
+ }
+ LeaveCriticalSection(&m->lock);
+
+ return result;
+}
+
/* gets the stub manager associated with an ipid - caller must have
* a reference to the apartment while a reference to the stub manager is held.
* it must also call release on the stub manager when it is no longer needed */
{
/* FIXME: hack for IRemUnknown */
if (ipid->Data2 == 0xffff)
- *stub_apt = apartment_findfromoxid(*(OXID *)ipid->Data4, TRUE);
+ *stub_apt = apartment_findfromoxid(*(const OXID *)ipid->Data4, TRUE);
else
*stub_apt = apartment_findfromtid(ipid->Data2);
if (!*stub_apt)
{
- ERR("Couldn't find apartment corresponding to TID 0x%04x\n", ipid->Data2);
+ TRACE("Couldn't find apartment corresponding to TID 0x%04x\n", ipid->Data2);
return RPC_E_INVALID_OBJECT;
}
*stubmgr_ret = get_stub_manager_from_ipid(*stub_apt, ipid);
return S_OK;
}
-/* gets the apartment and IRpcStubBuffer from an object. the caller must
- * release the references to both objects */
-IRpcStubBuffer *ipid_to_apt_and_stubbuffer(const IPID *ipid, APARTMENT **stub_apt)
+/* gets the apartment, stub and channel of an object. the caller must
+ * release the references to all objects (except iface) if the function
+ * returned success, otherwise no references are returned. */
+HRESULT ipid_get_dispatch_params(const IPID *ipid, APARTMENT **stub_apt,
+ IRpcStubBuffer **stub, IRpcChannelBuffer **chan,
+ IID *iid, IUnknown **iface)
{
- IRpcStubBuffer *ret = NULL;
struct stub_manager *stubmgr;
struct ifstub *ifstub;
+ APARTMENT *apt;
HRESULT hr;
- *stub_apt = NULL;
-
- hr = ipid_to_stub_manager(ipid, stub_apt, &stubmgr);
- if (hr != S_OK) return NULL;
+ hr = ipid_to_stub_manager(ipid, &apt, &stubmgr);
+ if (hr != S_OK) return RPC_E_DISCONNECTED;
ifstub = stub_manager_ipid_to_ifstub(stubmgr, ipid);
if (ifstub)
- ret = ifstub->stubbuffer;
-
- if (ret) IRpcStubBuffer_AddRef(ret);
-
- stub_manager_int_release(stubmgr);
+ {
+ *stub = ifstub->stubbuffer;
+ IRpcStubBuffer_AddRef(*stub);
+ *chan = ifstub->chan;
+ IRpcChannelBuffer_AddRef(*chan);
+ *stub_apt = apt;
+ *iid = ifstub->iid;
+ *iface = ifstub->iface;
- return ret;
+ stub_manager_int_release(stubmgr);
+ return S_OK;
+ }
+ else
+ {
+ stub_manager_int_release(stubmgr);
+ apartment_release(apt);
+ return RPC_E_DISCONNECTED;
+ }
}
/* generates an ipid in the following format (similar to native version):
}
/* registers a new interface stub COM object with the stub manager and returns registration record */
-struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, IUnknown *iptr, REFIID iid)
+struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, IUnknown *iptr, REFIID iid, MSHLFLAGS flags)
{
struct ifstub *stub;
+ HRESULT hr;
TRACE("oid=%s, stubbuffer=%p, iptr=%p, iid=%s\n",
wine_dbgstr_longlong(m->oid), sb, iptr, debugstr_guid(iid));
stub = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct ifstub));
if (!stub) return NULL;
+ hr = RPC_CreateServerChannel(&stub->chan);
+ if (hr != S_OK)
+ {
+ HeapFree(GetProcessHeap(), 0, stub);
+ return NULL;
+ }
+
stub->stubbuffer = sb;
- IUnknown_AddRef(sb);
+ if (sb) IRpcStubBuffer_AddRef(sb);
- /* no need to ref this, same object as sb */
+ IUnknown_AddRef(iptr);
stub->iface = iptr;
-
+ stub->flags = flags;
stub->iid = *iid;
- /* FIXME: hack for IRemUnknown because we don't notify SCM of our IPID
- * yet, so we need to use a well-known one */
- if (IsEqualIID(iid, &IID_IRemUnknown))
- {
- stub->ipid.Data1 = 0xffffffff;
- stub->ipid.Data2 = 0xffff;
- stub->ipid.Data3 = 0xffff;
- assert(sizeof(stub->ipid.Data4) == sizeof(m->apt->oxid));
- memcpy(&stub->ipid.Data4, &m->apt->oxid, sizeof(OXID));
- }
+ /* FIXME: find a cleaner way of identifying that we are creating an ifstub
+ * for the remunknown interface */
+ if (flags & MSHLFLAGSP_REMUNKNOWN)
+ stub->ipid = m->oxid_info.ipidRemUnknown;
else
generate_ipid(m, &stub->ipid);
EnterCriticalSection(&m->lock);
list_add_head(&m->ifstubs, &stub->entry);
+ /* every normal marshal is counted so we don't allow more than we should */
+ if (flags & MSHLFLAGS_NORMAL) m->norm_refs++;
LeaveCriticalSection(&m->lock);
TRACE("ifstub %p created with ipid %s\n", stub, debugstr_guid(&stub->ipid));
list_remove(&ifstub->entry);
RPC_UnregisterInterface(&ifstub->iid);
-
- IUnknown_Release(ifstub->stubbuffer);
+
+ if (ifstub->stubbuffer) IUnknown_Release(ifstub->stubbuffer);
IUnknown_Release(ifstub->iface);
+ IRpcChannelBuffer_Release(ifstub->chan);
HeapFree(GetProcessHeap(), 0, ifstub);
}
/* returns TRUE if it is possible to unmarshal, FALSE otherwise. */
-BOOL stub_manager_notify_unmarshal(struct stub_manager *m)
+BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid)
{
- BOOL ret;
+ BOOL ret = TRUE;
+ struct ifstub *ifstub;
+
+ if (!(ifstub = stub_manager_ipid_to_ifstub(m, ipid)))
+ {
+ ERR("attempted unmarshal of unknown IPID %s\n", debugstr_guid(ipid));
+ return FALSE;
+ }
EnterCriticalSection(&m->lock);
- switch (m->state)
+ /* track normal marshals so we can enforce rules whilst in-process */
+ if (ifstub->flags & MSHLFLAGS_NORMAL)
{
- case STUBSTATE_TABLE_STRONG:
- case STUBSTATE_TABLE_WEAK_MARSHALED:
- /* no transition */
- ret = TRUE;
- break;
- case STUBSTATE_TABLE_WEAK_UNMARSHALED:
- m->state = STUBSTATE_TABLE_WEAK_MARSHALED;
- ret = TRUE;
- break;
- case STUBSTATE_NORMAL_MARSHALED:
- m->state = STUBSTATE_NORMAL_UNMARSHALED;
- ret = TRUE;
- break;
- default:
- WARN("object OID %s already unmarshaled\n",
- wine_dbgstr_longlong(m->oid));
- ret = FALSE;
- break;
+ if (m->norm_refs)
+ m->norm_refs--;
+ else
+ {
+ ERR("attempted invalid normal unmarshal, norm_refs is zero\n");
+ ret = FALSE;
+ }
}
LeaveCriticalSection(&m->lock);
return ret;
}
-void stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs)
+/* handles refcounting for CoReleaseMarshalData */
+void stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const IPID *ipid, BOOL tableweak)
{
- EnterCriticalSection(&m->lock);
-
- switch (m->state)
- {
- case STUBSTATE_NORMAL_MARSHALED:
- case STUBSTATE_NORMAL_UNMARSHALED: /* FIXME: check this */
- /* nothing to change */
- break;
- case STUBSTATE_TABLE_WEAK_UNMARSHALED:
- case STUBSTATE_TABLE_STRONG:
+ struct ifstub *ifstub;
+
+ if (!(ifstub = stub_manager_ipid_to_ifstub(m, ipid)))
+ return;
+
+ if (ifstub->flags & MSHLFLAGS_TABLEWEAK)
+ refs = 0;
+ else if (ifstub->flags & MSHLFLAGS_TABLESTRONG)
refs = 1;
- break;
- case STUBSTATE_TABLE_WEAK_MARSHALED:
- refs = 0; /* like native */
- break;
- }
-
- stub_manager_ext_release(m, refs);
- LeaveCriticalSection(&m->lock);
+ stub_manager_ext_release(m, refs, tableweak, TRUE);
}
/* is an ifstub table marshaled? */
-BOOL stub_manager_is_table_marshaled(struct stub_manager *m)
+BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid)
{
- BOOL ret;
-
- EnterCriticalSection(&m->lock);
- ret = ((m->state == STUBSTATE_TABLE_STRONG) ||
- (m->state == STUBSTATE_TABLE_WEAK_MARSHALED) ||
- (m->state == STUBSTATE_TABLE_WEAK_UNMARSHALED));
- LeaveCriticalSection(&m->lock);
-
- return ret;
+ struct ifstub *ifstub = stub_manager_ipid_to_ifstub(m, ipid);
+
+ assert( ifstub );
+
+ return ifstub->flags & (MSHLFLAGS_TABLESTRONG | MSHLFLAGS_TABLEWEAK);
}
* interacts with stub managers.
*/
-const IID IID_IRemUnknown = { 0x00000131, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} };
-
typedef struct rem_unknown
{
const IRemUnknownVtbl *lpVtbl;
- ULONG refs;
+ LONG refs;
} RemUnknown;
static const IRemUnknownVtbl RemUnknown_Vtbl;
refs = InterlockedIncrement(&This->refs);
- TRACE("%p before: %ld\n", iface, refs-1);
+ TRACE("%p before: %d\n", iface, refs-1);
return refs;
}
if (!refs)
HeapFree(GetProcessHeap(), 0, This);
- TRACE("%p after: %ld\n", iface, refs);
+ TRACE("%p after: %d\n", iface, refs);
return refs;
}
APARTMENT *apt;
struct stub_manager *stubmgr;
- TRACE("(%p)->(%s, %ld, %d, %p, %p)\n", iface, debugstr_guid(ripid), cRefs, cIids, iids, ppQIResults);
+ TRACE("(%p)->(%s, %d, %d, %p, %p)\n", iface, debugstr_guid(ripid), cRefs, cIids, iids, ppQIResults);
hr = ipid_to_stub_manager(ripid, &apt, &stubmgr);
if (hr != S_OK) return hr;
continue;
}
- stub_manager_ext_addref(stubmgr, InterfaceRefs[i].cPublicRefs);
+ stub_manager_ext_addref(stubmgr, InterfaceRefs[i].cPublicRefs, FALSE);
if (InterfaceRefs[i].cPrivateRefs)
FIXME("Adding %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs);
break;
}
- stub_manager_ext_release(stubmgr, InterfaceRefs[i].cPublicRefs);
+ stub_manager_ext_release(stubmgr, InterfaceRefs[i].cPublicRefs, FALSE, TRUE);
if (InterfaceRefs[i].cPrivateRefs)
FIXME("Releasing %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs);
};
/* starts the IRemUnknown listener for the current apartment */
-HRESULT start_apartment_remote_unknown()
+HRESULT start_apartment_remote_unknown(void)
{
IRemUnknown *pRemUnknown;
HRESULT hr = S_OK;
{
STDOBJREF stdobjref; /* dummy - not used */
/* register it with the stub manager */
- hr = marshal_object(apt, &stdobjref, &IID_IRemUnknown, (IUnknown *)pRemUnknown, MSHLFLAGS_NORMAL);
+ hr = marshal_object(apt, &stdobjref, &IID_IRemUnknown, (IUnknown *)pRemUnknown, MSHLFLAGS_NORMAL|MSHLFLAGSP_REMUNKNOWN);
/* release our reference to the object as the stub manager will manage the life cycle for us */
IRemUnknown_Release(pRemUnknown);
if (hr == S_OK)