[OLE #12] Implement the stub manager

Mike Hearn mh at codeweavers.com
Thu Dec 23 08:42:22 CST 2004


This makes quite a few tests pass, and has been tested against
InstallShield.

- Implement the COM stub manager, refactor the current stub code
- Begin implementing interface stubs

-------------- next part --------------
--- dlls/ole32/Makefile.in  (revision 7)
+++ dlls/ole32/Makefile.in  (local)
@@ -35,7 +35,9 @@ C_SRCS = \
 	rpc.c \
 	stg_bigblockfile.c \
 	stg_stream.c \
-	storage32.c
+	storage32.c \
+	stubmanager.c
+
 
 C_SRCS16 = \
 	memlockbytes16.c \
--- dlls/ole32/compobj.c  (revision 7)
+++ dlls/ole32/compobj.c  (local)
@@ -102,6 +102,7 @@ typedef struct tagRegisteredClass
   DWORD     connectFlags;
   DWORD     dwCookie;
   HANDLE    hThread; /* only for localserver */
+  APARTMENT *apt;    /* owning apartment */
   struct tagRegisteredClass* nextClass;
 } RegisteredClass;
 
@@ -226,6 +227,9 @@ APARTMENT* COM_CreateApartment(DWORD mod
     else
         apt = NtCurrentTeb()->ReservedForOle;
 
+    list_init(&apt->stubmgrs);
+    apt->oidc = 1;
+    
     apt->model = model;
     if (model & COINIT_APARTMENTTHREADED) {
       /* FIXME: how does windoze create OXIDs? */
@@ -269,17 +273,24 @@ static void COM_DestroyApartment(APARTME
 
 /* The given OXID must be local to this process: you cannot use apartment
    windows to send RPCs to other processes. This all needs to move to rpcrt4 */
-HWND COM_GetApartmentWin(OXID oxid)
+
+APARTMENT *COM_ApartmentFromOXID(OXID oxid)
 {
-    APARTMENT *apt;
-    HWND win = 0;
+    APARTMENT *apt = NULL;
 
     EnterCriticalSection(&csApartment);
     apt = apts;
     while (apt && apt->oxid != oxid) apt = apt->next;
-    if (apt) win = apt->win;
     LeaveCriticalSection(&csApartment);
-    return win;
+
+    return apt;
+}
+
+HWND COM_GetApartmentWin(OXID oxid)
+{
+    APARTMENT *apt = COM_ApartmentFromOXID(oxid);
+
+    return apt ? apt->win : NULL;
 }
 
 /* Currently inter-thread marshalling is not fully implemented, so this does nothing */
@@ -1107,6 +1118,13 @@ _LocalServerThread(LPVOID param) {
 
     TRACE("Starting classfactory server thread for %s.\n",debugstr_guid(&newClass->classIdentifier));
 
+    /* we need to enter the apartment of the thread which registered
+     * the class object to perform the next stage
+     */
+
+    assert( newClass->apt );
+    NtCurrentTeb()->ReservedForOle = newClass->apt;
+
     strcpy(pipefn,PIPEPREF);
     WINE_StringFromCLSID(&newClass->classIdentifier,pipefn+strlen(PIPEPREF));
 
@@ -1219,6 +1237,12 @@ HRESULT WINAPI CoRegisterClassObject(
   if ( (lpdwRegister==0) || (pUnk==0) )
     return E_INVALIDARG;
 
+  if (!COM_CurrentApt())
+  {
+      ERR("COM was not initialized\n");
+      return CO_E_NOTINITIALIZED;
+  }
+
   *lpdwRegister = 0;
 
   /*
@@ -1240,6 +1264,7 @@ HRESULT WINAPI CoRegisterClassObject(
   newClass->classIdentifier = *rclsid;
   newClass->runContext      = dwClsContext;
   newClass->connectFlags    = flags;
+  newClass->apt             = COM_CurrentApt();
   /*
    * Use the address of the chain node as the cookie since we are sure it's
    * unique.
@@ -1981,6 +2006,8 @@ HRESULT WINAPI CoLockObjectExternal(
     BOOL fLock,		/* [in] do lock */
     BOOL fLastUnlockReleases) /* [in] unlock all */
 {
+    TRACE("pUnk=%p, fLock=%s, fLastUnlockReleases=%s\n",
+          pUnk, fLock ? "TRUE" : "FALSE", fLastUnlockReleases ? "TRUE" : "FALSE");
 
 	if (fLock) {
             /*
--- dlls/ole32/compobj_private.h  (revision 7)
+++ dlls/ole32/compobj_private.h  (local)
@@ -5,6 +5,7 @@
  * Copyright 1999 Sylvain St-Germain
  * Copyright 2002 Marcus Meissner
  * Copyright 2003 Ove K?n, TransGaming Technologies
+ * Copyright 2004 Mike Hearn, CodeWeavers Inc
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -28,6 +29,8 @@
 
 #include <stdarg.h>
 
+#include <wine/list.h>
+
 #include "windef.h"
 #include "winbase.h"
 #include "wtypes.h"
@@ -97,15 +100,16 @@ typedef struct tagAPARTMENT {
   DWORD tid;               /* thread id */
   HANDLE thread;           /* thread handle */
   OXID oxid;               /* object exporter ID */
-  OID oidc;                /* object ID counter */
+  OID oidc;                /* object ID counter, starts at 1, zero is invalid OID */
   HWND win;                /* message window */
   CRITICAL_SECTION cs;     /* thread safety */
   LPMESSAGEFILTER filter;  /* message filter */
   XOBJECT *objs;           /* exported objects */
-  IOBJECT *proxies;        /* imported objects */
+  IOBJECT *proxies;        /* imported objects */  
   LPUNKNOWN state;         /* state object (see Co[Get,Set]State) */
   LPVOID ErrorInfo;        /* thread error info */
   DWORD listenertid;       /* id of apartment_listener_thread */
+  struct list stubmgrs;    /* stub managers for exported objects */
 } APARTMENT;
 
 extern APARTMENT MTA, *apts;
@@ -124,9 +128,9 @@ extern void* StdGlobalInterfaceTableInst
 
 /* Standard Marshalling definitions */
 typedef struct _wine_marshal_id {
-    OXID   oxid;
-    OID    oid;  /* unique value corresp. IUnknown of object */
-    IID    iid;
+    OXID    oxid;       /* id of apartment */
+    OID     oid;        /* id of stub manager */
+    IID     iid;        /* id of interface (NOT ifptr) */
 } wine_marshal_id;
 
 inline static BOOL
@@ -147,12 +151,46 @@ MARSHAL_Compare_Mids_NoInterface(wine_ma
     ;
 }
 
-HRESULT MARSHAL_Find_Stub_Buffer(wine_marshal_id *mid,IRpcStubBuffer **stub);
-void    MARSHAL_Invalidate_Stub_From_MID(wine_marshal_id *mid);
 HRESULT MARSHAL_Disconnect_Proxies(void);
-
 HRESULT MARSHAL_GetStandardMarshalCF(LPVOID *ppv);
 
+/* an interface stub */
+struct ifstub   
+{
+    struct list       entry;
+    IRpcStubBuffer   *stubbuffer;
+    IID               iid;         /* fixme: this should be an IPID not an IID */
+    IUnknown         *iface;
+
+    BOOL              table;
+};
+
+
+/* stub managers hold refs on the object and each interface stub */
+struct stub_manager
+{
+    struct list       entry;
+    struct list       ifstubs;
+    CRITICAL_SECTION  lock;
+    APARTMENT        *apt;        /* owning apt */
+
+    DWORD             refcount;   /* count of 'external' references */
+    OID               oid;        /* apartment-scoped unique identifier */
+    IUnknown         *object;     /* the object we are managing the stub for */
+    DWORD             next_ipid;  /* currently unused */
+};
+
+struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object);
+int stub_manager_ref(struct stub_manager *m, int refs);
+int stub_manager_unref(struct stub_manager *m, int refs);
+IRpcStubBuffer *stub_manager_iid_to_stubbuffer(struct stub_manager *m, IID *iid);
+struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, IUnknown *iptr, IID *iid, BOOL tablemarshal);
+struct stub_manager *get_stub_manager(OXID oxid, OID oid);
+struct stub_manager *get_stub_manager_from_object(OXID oxid, void *object);
+void stub_manager_delete_ifstub(struct stub_manager *m, IID *iid);   /* fixme: should be ipid */
+
+IRpcStubBuffer *mid_to_stubbuffer(wine_marshal_id *mid);
+
 void start_apartment_listener_thread(void);
 
 extern HRESULT PIPE_GetNewPipeBuf(wine_marshal_id *mid, IRpcChannelBuffer **pipebuf);
@@ -185,8 +223,9 @@ static inline APARTMENT* COM_CurrentApt(
 }
 
 /* compobj.c */
-APARTMENT* COM_CreateApartment(DWORD model);
+APARTMENT *COM_CreateApartment(DWORD model);
 HWND COM_GetApartmentWin(OXID oxid);
+APARTMENT *COM_ApartmentFromOXID(OXID oxid);
 
 #define ICOM_THIS_MULTI(impl,field,iface) impl* const This=(impl*)((char*)(iface) - offsetof(impl,field))
 
--- dlls/ole32/marshal.c  (revision 7)
+++ dlls/ole32/marshal.c  (local)
@@ -1,7 +1,9 @@
 /*
  *	Marshalling library
  *
- *  Copyright 2002  Marcus Meissner
+ * Copyright 2002 Marcus Meissner
+ * Copyright 2004 Mike Hearn, for CodeWeavers
+ * Copyright 2004 Rob Shearman, for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -55,6 +57,13 @@ extern const CLSID CLSID_DfMarshal;
  * 	Process Identifier, Object IUnknown ptr, IID
  *
  * Note that the IUnknown_QI(ob,xiid,&ppv) always returns the SAME ppv value!
+ *
+ * In Windows, a different triple is used: OXID (apt id), OID (stub
+ * manager id), IPID (interface ptr/stub id).
+ *
+ * OXIDs identify an apartment and are network scoped
+ * OIDs identify a stub manager and are apartment scoped
+ * IPIDs identify an interface stub and are apartment scoped
  */
 
 inline static HRESULT
@@ -77,85 +86,50 @@ typedef struct _mid2unknown {
     LPUNKNOWN		pUnk;
 } mid2unknown;
 
-typedef struct _mid2stub {
-    wine_marshal_id	mid;
-    IRpcStubBuffer	*stub;
-    LPUNKNOWN		pUnkServer;
-    BOOL               valid;
-} mid2stub;
-
-static mid2stub *stubs = NULL;
-static int nrofstubs = 0;
-
 static mid2unknown *proxies = NULL;
 static int nrofproxies = 0;
 
-void MARSHAL_Invalidate_Stub_From_MID(wine_marshal_id *mid) {
-    int i;
+IRpcStubBuffer *mid_to_stubbuffer(wine_marshal_id *mid)
+{
+    struct stub_manager *m;
 
-    for (i=0;i<nrofstubs;i++) {
-        if (!stubs[i].valid) continue;
-        
-	if (MARSHAL_Compare_Mids(mid,&(stubs[i].mid))) {
-            stubs[i].valid = FALSE;
-            IUnknown_Release(stubs[i].pUnkServer);
-	    return;
-	}
+    if (!(m = get_stub_manager(mid->oxid, mid->oid)))
+    {
+        WARN("unknown OID %s\n", wine_dbgstr_longlong(mid->oid));
+        return NULL;
     }
-    
-    return;
-}
 
-HRESULT
-MARSHAL_Find_Stub_Buffer(wine_marshal_id *mid,IRpcStubBuffer **stub) {
-    int i;
+    return stub_manager_iid_to_stubbuffer(m, &mid->iid);
+}
 
-    for (i=0;i<nrofstubs;i++) {
-       if (!stubs[i].valid) continue;
+/* fixme: this should return an IPID */
+/* creates a new stub manager and sets mid->oid when mid->oid == 0 */
+static HRESULT register_ifstub(wine_marshal_id *mid, IUnknown *obj, IRpcStubBuffer *stub, BOOL tablemarshal)
+{
+    struct stub_manager *manager = NULL;
+    struct ifstub       *ifstub;
 
-	if (MARSHAL_Compare_Mids(mid,&(stubs[i].mid))) {
-	    *stub = stubs[i].stub;
-	    IUnknown_AddRef((*stub));
-	    return S_OK;
-	}
+    /* mid->oid of zero means create a new stub manager */
+    
+    if (mid->oid && (manager = get_stub_manager(mid->oxid, mid->oid)))
+    {
+        TRACE("registering new ifstub on pre-existing manager\n");
     }
-    return E_FAIL;
-}
-
-static HRESULT
-MARSHAL_Find_Stub(wine_marshal_id *mid,LPUNKNOWN *pUnk) {
-    int i;
+    else
+    {
+        TRACE("constructing new stub manager\n");
+        
+        manager = new_stub_manager(COM_ApartmentFromOXID(mid->oxid), obj);
+        if (!manager) return E_OUTOFMEMORY;
 
-    for (i=0;i<nrofstubs;i++) {
-       if (!stubs[i].valid) continue;
+        if (!tablemarshal) stub_manager_ref(manager, 1);
 
-	if (MARSHAL_Compare_Mids(mid,&(stubs[i].mid))) {
-	    *pUnk = stubs[i].pUnkServer;
-	    IUnknown_AddRef((*pUnk));
-	    return S_OK;
-	}
+        mid->oid = manager->oid;
     }
-    return E_FAIL;
-}
 
-static HRESULT
-MARSHAL_Register_Stub(wine_marshal_id *mid,LPUNKNOWN pUnk,IRpcStubBuffer *stub) {
-    LPUNKNOWN	xPunk;
-    if (!MARSHAL_Find_Stub(mid,&xPunk)) {
-	FIXME("Already have entry for (%s/%s)!\n",wine_dbgstr_longlong(mid->oid),debugstr_guid(&(mid->iid)));
-	return S_OK;
-    }
-    if (nrofstubs)
-	stubs=HeapReAlloc(GetProcessHeap(),0,stubs,sizeof(stubs[0])*(nrofstubs+1));
-    else
-	stubs=HeapAlloc(GetProcessHeap(),0,sizeof(stubs[0]));
-    if (!stubs) return E_OUTOFMEMORY;
-    stubs[nrofstubs].stub = stub;
-    stubs[nrofstubs].pUnkServer = pUnk;
-    memcpy(&(stubs[nrofstubs].mid),mid,sizeof(*mid));
-    stubs[nrofstubs].valid = TRUE; /* set to false when released by ReleaseMarshalData */
-    nrofstubs++;
-    return S_OK;
+    ifstub = stub_manager_new_ifstub(manager, stub, obj, &mid->iid, tablemarshal);
+
+    return ifstub ? S_OK : E_OUTOFMEMORY;
 }
 
 HRESULT
@@ -256,54 +230,74 @@ StdMarshalImpl_MarshalInterface(
   LPMARSHAL iface, IStream *pStm,REFIID riid, void* pv, DWORD dwDestContext,
   void* pvDestContext, DWORD mshlflags
 ) {
-  wine_marshal_id	mid;
-  wine_marshal_data 	md;
-  IUnknown		*pUnk;
-  ULONG			res;
-  HRESULT		hres;
-  IRpcStubBuffer	*stub;
-  IPSFactoryBuffer	*psfacbuf;
-
+  wine_marshal_id       mid;
+  wine_marshal_data     md;
+  IUnknown             *pUnk;  
+  ULONG                 res;
+  HRESULT               hres;
+  IRpcStubBuffer       *stubbuffer;
+  IPSFactoryBuffer     *psfacbuf;
+  BOOL                  tablemarshal;
+  struct stub_manager  *manager;
+    
   TRACE("(...,%s,...)\n",debugstr_guid(riid));
 
   start_apartment_listener_thread(); /* just to be sure we have one running. */
 
-  IUnknown_QueryInterface((LPUNKNOWN)pv,&IID_IUnknown,(LPVOID*)&pUnk);
-  mid.oxid = COM_CurrentApt()->oxid;
-  mid.oid = (DWORD)pUnk; /* FIXME */
-  IUnknown_Release(pUnk);
-  memcpy(&mid.iid,riid,sizeof(mid.iid));
-  md.dwDestContext	= dwDestContext;
-  md.mshlflags		= mshlflags;
-  hres = IStream_Write(pStm,&mid,sizeof(mid),&res);
-  if (hres) return hres;
-  hres = IStream_Write(pStm,&md,sizeof(md),&res);
-  if (hres) return hres;
-
-  if (SUCCEEDED(MARSHAL_Find_Stub_Buffer(&mid,&stub))) {
-      /* Find_Stub_Buffer gives us a ref but we want to keep it, as if we'd created a new one */
-      TRACE("Found RpcStubBuffer %p\n", stub);
-      return S_OK;
-  }
   hres = get_facbuf_for_iid(riid,&psfacbuf);
   if (hres) return hres;
 
-  hres = IPSFactoryBuffer_CreateStub(psfacbuf,riid,pv,&stub);
+  hres = IPSFactoryBuffer_CreateStub(psfacbuf,riid,pv,&stubbuffer);
   IPSFactoryBuffer_Release(psfacbuf);
   if (hres) {
-    FIXME("Failed to create a stub for %s\n",debugstr_guid(riid));
+    FIXME("Failed to create an RpcStubBuffer from PSFactory for %s\n",debugstr_guid(riid));
     return hres;
   }
-  IUnknown_QueryInterface((LPUNKNOWN)pv,riid,(LPVOID*)&pUnk);
-  MARSHAL_Register_Stub(&mid,pUnk,stub);
+
+  tablemarshal = ((mshlflags & MSHLFLAGS_TABLESTRONG) || (mshlflags & MSHLFLAGS_TABLEWEAK));
+  if (tablemarshal) FIXME("table marshalling unimplemented\n");
+
+  /* now fill out the MID */
+  mid.oxid = COM_CurrentApt()->oxid;
+  memcpy(&mid.iid,riid,sizeof(mid.iid));
+ 
+  IUnknown_QueryInterface((LPUNKNOWN)pv, riid, (LPVOID*)&pUnk);
+ 
+  if ((manager = get_stub_manager_from_object(mid.oxid, pUnk)))
+  {
+      mid.oid = manager->oid;
+  }
+  else
+  {
+      mid.oid = 0;              /* will be set by register_ifstub */
+  }
+ 
+  hres = register_ifstub(&mid, pUnk, stubbuffer, tablemarshal);
+  
   IUnknown_Release(pUnk);
+  
+  if (hres)
+  {
+    FIXME("Failed to create ifstub, hres=0x%lx\n", hres);
+    return hres;
+  }
+
+  hres = IStream_Write(pStm,&mid,sizeof(mid),&res);
+  if (hres) return hres;
+
+  /* and then the marshal data */
+  md.dwDestContext      = dwDestContext;
+  md.mshlflags          = mshlflags;
+  hres = IStream_Write(pStm,&md,sizeof(md),&res);
+  if (hres) return hres;
+   
   return S_OK;
 }
 
 static HRESULT WINAPI
-StdMarshalImpl_UnmarshalInterface(
-  LPMARSHAL iface, IStream *pStm, REFIID riid, void **ppv
-) {
+StdMarshalImpl_UnmarshalInterface(LPMARSHAL iface, IStream *pStm, REFIID riid, void **ppv)
+{
+  struct stub_manager  *stubmgr;
   wine_marshal_id       mid;
   wine_marshal_data     md;
   ULONG			res;
@@ -311,31 +305,28 @@ StdMarshalImpl_UnmarshalInterface(
   IPSFactoryBuffer	*psfacbuf;
   IRpcProxyBuffer	*rpcproxy;
   IRpcChannelBuffer	*chanbuf;
-  int i;
 
   TRACE("(...,%s,....)\n",debugstr_guid(riid));
   hres = IStream_Read(pStm,&mid,sizeof(mid),&res);
   if (hres) return hres;
   hres = IStream_Read(pStm,&md,sizeof(md),&res);
   if (hres) return hres;
-  for (i=0; i < nrofstubs; i++)
+  
+  /* check if we're marshalling back to ourselves */
+  if ((stubmgr = get_stub_manager(mid.oxid, mid.oid)))
   {
-       if (!stubs[i].valid) continue;
-
-       if (MARSHAL_Compare_Mids(&mid, &(stubs[i].mid)))
-       {
-          IRpcStubBuffer       *stub = NULL;
-          stub = stubs[i].stub;
-          res = IRpcStubBuffer_Release(stub);
-          TRACE("Same apartment marshal for %s, returning original object\n",
-            debugstr_guid(riid));
-
-          stubs[i].valid = FALSE;
-          IUnknown_QueryInterface(stubs[i].pUnkServer, riid, ppv);
-          IUnknown_Release(stubs[i].pUnkServer); /* no longer need our reference */
-          return S_OK;
-       }
+      TRACE("Unmarshalling object marshalled in same apartment for iid %s, returning original object %p\n", debugstr_guid(riid), stubmgr->object);
+    
+      hres = IUnknown_QueryInterface(stubmgr->object, riid, ppv);
+      if ((md.mshlflags & MSHLFLAGS_TABLESTRONG) || (md.mshlflags & MSHLFLAGS_TABLEWEAK))
+          FIXME("table marshalling unimplemented\n");
+      
+      /* clean up the stubs */
+      stub_manager_delete_ifstub(stubmgr, &mid.iid);
+      stub_manager_unref(stubmgr, 1);
+      return hres;
   }
+  
   if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_NULL)) {
     /* should return proxy manager IUnknown object */
     FIXME("Special treatment required for IID of %s\n", debugstr_guid(riid));
@@ -370,32 +361,31 @@ StdMarshalImpl_UnmarshalInterface(
 
 static HRESULT WINAPI
 StdMarshalImpl_ReleaseMarshalData(LPMARSHAL iface, IStream *pStm) {
-  wine_marshal_id       mid;
-  ULONG                 res;
-  HRESULT               hres;
-  int                   i;
-
-  hres = IStream_Read(pStm,&mid,sizeof(mid),&res);
-  if (hres) return hres;
-
-  for (i=0; i < nrofstubs; i++)
-  {
-       if (!stubs[i].valid) continue;
+    wine_marshal_id      mid;
+    ULONG                res;
+    HRESULT              hres;
+    struct stub_manager *stubmgr;
 
-       if (MARSHAL_Compare_Mids(&mid, &(stubs[i].mid)))
-       {
-          IRpcStubBuffer       *stub = NULL;
-          stub = stubs[i].stub;
-          res = IRpcStubBuffer_Release(stub);
-          stubs[i].valid = FALSE;
-          TRACE("stub refcount of %p is %ld\n", stub, res);
+    TRACE("iface=%p, pStm=%p\n", iface, pStm);
+    
+    hres = IStream_Read(pStm,&mid,sizeof(mid),&res);
+    if (hres) return hres;
 
-          return S_OK;
-       }
-  }
+    if (!(stubmgr = get_stub_manager(mid.oxid, mid.oid)))
+    {
+        ERR("could not map MID to stub manager, oxid=%s, oid=%s\n",
+            wine_dbgstr_longlong(mid.oxid), wine_dbgstr_longlong(mid.oid));
+        return RPC_E_INVALID_OBJREF;
+    }
+    
+    /* currently, each marshal has its own interface stub.  this might
+     * not be correct. but, it means we don't need to refcount anything
+     * here. */
+    stub_manager_delete_ifstub(stubmgr, &mid.iid);
+    
+    stub_manager_unref(stubmgr, 1);
 
-  FIXME("Could not map MID to stub??\n");
-  return E_FAIL;
+    return S_OK;
 }
 
 static HRESULT WINAPI
--- dlls/ole32/rpc.c  (revision 7)
+++ dlls/ole32/rpc.c  (local)
@@ -1,5 +1,5 @@
 /*
- *	(Local) RPC Stuff aklsdfjaksldfj
+ *	(Local) RPC Stuff
  *
  *  Copyright 2002  Marcus Meissner
  *
@@ -334,16 +334,18 @@ PipeBuf_GetBuffer(
 
 static HRESULT
 COM_InvokeAndRpcSend(wine_rpc_request *req) {
-    IRpcStubBuffer	*stub;
+    IRpcStubBuffer     *stub;
     RPCOLEMESSAGE	msg;
     HRESULT		hres;
     DWORD		reqtype;
 
-    hres = MARSHAL_Find_Stub_Buffer(&(req->reqh.mid),&stub);
-    if (hres) {
+    if (!(stub = mid_to_stubbuffer(&(req->reqh.mid))))
+    {
 	ERR("Stub not found?\n");
-	return hres;
+	return E_FAIL;
     }
+
+    IUnknown_AddRef(stub);
     msg.Buffer		= req->Buffer;
     msg.iMethod		= req->reqh.iMethod;
     msg.cbBuffer	= req->reqh.cbBuffer;
@@ -661,8 +663,7 @@ COM_RpcReceive(wine_pipe *xpipe) {
 
     if (reqtype == REQTYPE_DISCONNECT) { /* only received by servers */
         wine_rpc_disconnect_header header;
-        IRpcStubBuffer *stub;
-        ULONG ret;
+        struct stub_manager *stubmgr;
 
         hres = read_pipe(xhPipe, &header, sizeof(header));
         if (hres) {
@@ -672,21 +673,15 @@ COM_RpcReceive(wine_pipe *xpipe) {
 
         TRACE("read disconnect header\n");
 
-        hres = MARSHAL_Find_Stub_Buffer(&header.mid, &stub);
-        if (hres) {
-            ERR("could not locate stub to disconnect, mid.oid=%s\n",
-                wine_dbgstr_longlong(header.mid.oid));
+        if (!(stubmgr = get_stub_manager(header.mid.oxid, header.mid.oid)))
+        {
+            ERR("could not locate stub to disconnect, mid.oid=%s\n", wine_dbgstr_longlong(header.mid.oid));
             goto end;
         }
 
-
-        /* release reference added by MARSHAL_Find_Stub_Buffer call */
-        IRpcStubBuffer_Release(stub);
-        /* release it for real */
-        ret = IRpcStubBuffer_Release(stub);
-        /* FIXME: race */
-        if (ret == 0)
-            MARSHAL_Invalidate_Stub_From_MID(&header.mid);
+        /* this should destroy the stub manager in the case of only one connection to it */
+        stub_manager_unref(stubmgr, 1);
+        
         goto end;
     } else if (reqtype == REQTYPE_REQUEST) {
 	wine_rpc_request	*xreq;
--- dlls/ole32/tests/marshal.c  (revision 7)
+++ dlls/ole32/tests/marshal.c  (local)
@@ -145,6 +145,7 @@ static DWORD CALLBACK host_object_proc(L
     {
         if (msg.hwnd == NULL && msg.message == RELEASEMARSHALDATA)
         {
+            trace("releasing marshal data\n");
             CoReleaseMarshalData(data->stream);
             SetEvent((HANDLE)msg.lParam);
         }
@@ -224,7 +225,7 @@ static void test_normal_marshal_and_rele
     ok_ole_success(hr, CoReleaseMarshalData);
     IStream_Release(pStream);
 
-    todo_wine { ok_no_locks(); }
+    ok_no_locks();
 }
 
 /* tests success case of a same-thread marshal and unmarshal */
@@ -385,7 +386,7 @@ static void test_tableweak_marshal_and_u
 
     IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
     hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2);
-    todo_wine { ok_ole_success(hr, CoUnmarshalInterface); }
+    ok_ole_success(hr, CoUnmarshalInterface);
 
     ok_more_than_one_lock();
 
@@ -395,7 +396,7 @@ static void test_tableweak_marshal_and_u
     /* this line is shows the difference between weak and strong table marshaling:
      *  weak has cLocks == 0
      *  strong has cLocks > 0 */
-    ok_no_locks();
+    todo_wine { ok_no_locks(); }
 
     end_host_object(tid, thread);
 }
@@ -426,7 +427,7 @@ static void test_tablestrong_marshal_and
 
     IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL);
     hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2);
-    todo_wine { ok_ole_success(hr, CoUnmarshalInterface); }
+    ok_ole_success(hr, CoUnmarshalInterface);
 
     ok_more_than_one_lock();
 
@@ -436,7 +437,7 @@ static void test_tablestrong_marshal_and
     /* this line is shows the difference between weak and strong table marshaling:
      *  weak has cLocks == 0
      *  strong has cLocks > 0 */
-    todo_wine { ok_more_than_one_lock(); }
+    ok_more_than_one_lock();
 
     /* release the remaining reference on the object by calling
      * CoReleaseMarshalData in the hosting thread */
@@ -444,7 +445,7 @@ static void test_tablestrong_marshal_and
     release_host_object(tid);
     IStream_Release(pStream);
 
-    ok_no_locks();
+    todo_wine { ok_no_locks(); }
 
     end_host_object(tid, thread);
 }
@@ -475,7 +476,7 @@ static void test_lock_object_external()
 
     CoLockObjectExternal((IUnknown*)&Test_ClassFactory, FALSE, TRUE);
 
-    todo_wine { ok_no_locks(); }
+    ok_no_locks();
 }
 
 /* tests disconnecting stubs */
--- dlls/ole32/stubmanager.c  (revision 7)
+++ dlls/ole32/stubmanager.c  (local)
@@ -0,0 +1,271 @@
+/*
+ * A stub manager is an object that controls interface stubs. It is
+ * identified by an OID (object identifier) and acts as the network
+ * identity of the object. There can be many stub managers in a
+ * process or apartment.
+ *
+ * Copyright 2002 Marcus Meissner
+ * Copyright 2004 Mike Hearn for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * 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
+ */
+
+#define COBJMACROS
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#include <assert.h>
+
+#include <wine/debug.h>
+#include <wine/list.h>
+
+WINE_DEFAULT_DEBUG_CHANNEL(ole);
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "objbase.h"
+#include "ole2.h"
+#include "ole2ver.h"
+#include "rpc.h"
+
+#include "compobj_private.h"
+
+struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object)
+{
+    struct stub_manager *sm;
+
+    assert( apt );
+    
+    sm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct stub_manager));
+    if (!sm) return NULL;
+
+    list_init(&sm->ifstubs);
+    InitializeCriticalSection(&sm->lock);
+    IUnknown_AddRef(object);
+    sm->object = object;
+    sm->apt    = apt;
+    EnterCriticalSection(&apt->cs);
+    sm->oid    = apt->oidc++;
+    LeaveCriticalSection(&apt->cs);
+    
+    /* yes, that's right, this starts at zero. that's zero EXTERNAL
+     * refs, ie 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->refcount = 0;
+
+    EnterCriticalSection(&apt->cs);
+    list_add_head(&apt->stubmgrs, &sm->entry);
+    LeaveCriticalSection(&apt->cs);
+
+    TRACE("Created new stub manager (oid=%s) at %p for object with IUnknown %p\n", wine_dbgstr_longlong(sm->oid), sm, object);
+    
+    return sm;
+}
+
+struct stub_manager *get_stub_manager_from_object(OXID oxid, void *object)
+{
+    struct stub_manager *result = NULL;
+    struct list         *cursor;
+    APARTMENT           *apt;
+
+    if (!(apt = COM_ApartmentFromOXID(oxid)))
+    {
+        WARN("Could not map OXID %s to apartment object\n", wine_dbgstr_longlong(oxid));
+        return NULL;
+    }
+
+    EnterCriticalSection(&apt->cs);
+    LIST_FOR_EACH( cursor, &apt->stubmgrs )
+    {
+        struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );
+
+        if (m->object == object)
+        {
+            result = m;
+            break;
+        }
+    }
+    LeaveCriticalSection(&apt->cs);
+
+    TRACE("found %p from object %p\n", result, object);
+
+    return result;    
+}
+
+struct stub_manager *get_stub_manager(OXID oxid, OID oid)
+{
+    struct stub_manager *result = NULL;
+    struct list         *cursor;
+    APARTMENT           *apt;
+
+    if (!(apt = COM_ApartmentFromOXID(oxid)))
+    {
+        WARN("Could not map OXID %s to apartment object\n", wine_dbgstr_longlong(oxid));
+        return NULL;
+    }
+    
+    EnterCriticalSection(&apt->cs);
+    
+    LIST_FOR_EACH( cursor, &apt->stubmgrs )
+    {
+        struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );
+
+        if (m->oid == oid)
+        {
+            result = m;
+            break;
+        }
+    }
+    
+    LeaveCriticalSection(&apt->cs);
+
+    TRACE("found %p from oid %s\n", result, wine_dbgstr_longlong(oid));
+
+    return result;
+}
+
+/* add some external references (ie from a client that demarshalled an ifptr) */
+int stub_manager_ref(struct stub_manager *m, int refs)
+{
+    int rc = InterlockedExchangeAdd(&m->refcount, refs) + refs;
+    TRACE("added %d refs to %p (oid %s), rc is now %d\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
+    return rc;
+}
+
+/* remove some external references */
+int stub_manager_unref(struct stub_manager *m, int refs)
+{
+    int rc = InterlockedExchangeAdd(&m->refcount, -refs) - refs;
+    
+    TRACE("removed %d refs from %p (oid %s), rc is now %d\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
+    
+    if (rc == 0)
+    {
+        TRACE("destroying %p (oid=%s)\n", m, wine_dbgstr_longlong(m->oid));
+
+        EnterCriticalSection(&m->apt->cs);
+        list_remove(&m->entry);
+        LeaveCriticalSection(&m->apt->cs);
+
+        /* table strong and normal marshals have a ref on us, so we
+         * can't die while they are outstanding unless the app does
+         * something weird like explicitly killing us (how?)
+         */
+
+        EnterCriticalSection(&m->lock);
+        if (!list_empty(&m->ifstubs))
+        {
+            ERR("PANIC: Stub manager is being destroyed with outstanding interface stubs\n");
+            assert( FALSE );
+        }
+
+        /* fixme: the lifecycle of table-weak marshals is not
+         * currently understood. results of testing against dcom98
+         * appear to contradict Essential COM   -m
+         */
+        
+        LeaveCriticalSection(&m->lock);        
+
+        IUnknown_Release(m->object);
+
+        HeapFree(GetProcessHeap(), 0, m);
+    }
+
+    return refs;
+}
+
+static struct ifstub *stub_manager_iid_to_ifstub(struct stub_manager *m, IID *iid)
+{
+    struct list    *cursor;
+    struct ifstub  *result = NULL;
+    
+    EnterCriticalSection(&m->lock);
+    LIST_FOR_EACH( cursor, &m->ifstubs )
+    {
+        struct ifstub *ifstub = LIST_ENTRY( cursor, struct ifstub, entry );
+
+        if (IsEqualIID(iid, &ifstub->iid))
+        {
+            result = ifstub;
+            break;
+        }
+    }
+    LeaveCriticalSection(&m->lock);
+
+    return result;
+}
+
+IRpcStubBuffer *stub_manager_iid_to_stubbuffer(struct stub_manager *m, IID *iid)
+{
+    struct ifstub *ifstub = stub_manager_iid_to_ifstub(m, iid);
+    
+    return ifstub ? ifstub->stubbuffer : NULL;
+}
+
+/* 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, IID *iid, BOOL tablemarshal)
+{
+    struct ifstub *stub;
+
+    TRACE("oid=%s, stubbuffer=%p, iptr=%p, iid=%s, tablemarshal=%s\n",
+          wine_dbgstr_longlong(m->oid), sb, iptr, debugstr_guid(iid), tablemarshal ? "TRUE" : "FALSE");
+
+    stub = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct ifstub));
+    if (!stub) return NULL;
+
+    stub->stubbuffer = sb;
+    IUnknown_AddRef(sb);
+
+    /* no need to ref this, same object as sb */
+    stub->iface = iptr;
+    stub->table = tablemarshal;
+    stub->iid = *iid;
+    
+    EnterCriticalSection(&m->lock);
+    list_add_head(&m->ifstubs, &stub->entry);
+    LeaveCriticalSection(&m->lock);
+
+    return stub;
+}
+
+/* fixme: should ifstubs be refcounted? iid should be ipid */
+void stub_manager_delete_ifstub(struct stub_manager *m, IID *iid)
+{
+    struct ifstub *ifstub;
+
+    TRACE("m=%p, m->oid=%s, iid=%s\n", m, wine_dbgstr_longlong(m->oid), debugstr_guid(iid));
+    
+    EnterCriticalSection(&m->lock);
+    
+    if ((ifstub = stub_manager_iid_to_ifstub(m, iid)))
+    {
+        list_remove(&ifstub->entry);
+        
+        IUnknown_Release(ifstub->stubbuffer);
+        IUnknown_Release(ifstub->iface);
+        
+        HeapFree(GetProcessHeap(), 0, ifstub);
+    }
+    else
+    {
+        WARN("could not map iid %s to ifstub\n", debugstr_guid(iid));
+    }
+    
+    LeaveCriticalSection(&m->lock);
+}


More information about the wine-patches mailing list