Hi Rob,

I've attached the patch I was working on at WineConf. The test cases
pass now but I haven't checked it against InstallShield yet: can you
look it over?

Sorry about the extraneous changes.

thanks -mike
Index: dlls/ole32/compobj_private.h
===================================================================
RCS file: /home/wine/wine/dlls/ole32/compobj_private.h,v
retrieving revision 1.49
diff -u -r1.49 compobj_private.h
--- dlls/ole32/compobj_private.h	17 Mar 2005 10:26:20 -0000	1.49
+++ dlls/ole32/compobj_private.h	5 May 2005 19:43:41 -0000
@@ -39,6 +39,8 @@
 #include "winternl.h"
 
 struct apartment;
+
+/* FIXME: remove this typedef */
 typedef struct apartment APARTMENT;
 
 /* Thread-safety Annotation Legend:
@@ -71,6 +73,7 @@
     IID               iid;        /* RO */
     IPID              ipid;       /* RO */
     IUnknown         *iface;      /* RO */
+    MSHLFLAGS         flags;      /* so we can enforce process-local marshalling rules (RO) */
 };
 
 
@@ -82,64 +85,76 @@
     CRITICAL_SECTION  lock;
     APARTMENT        *apt;        /* owning apt (RO) */
 
+    /* stub managers have two refcounts: one is for memory management
+     * purposes (the internal count) and the other is to track proxy
+     * refs (the external count)
+     */
     ULONG             extrefs;    /* number of 'external' references (CS lock) */
     ULONG             refs;       /* internal reference count (CS apt->cs) */
+    
     OID               oid;        /* apartment-scoped unique identifier (RO) */
     IUnknown         *object;     /* the object we are managing the stub for (RO) */
     ULONG             next_ipid;  /* currently unused (LOCK) */
-    STUB_STATE        state;      /* state machine (CS lock) */
+
+    /* We need to keep a count of the outstanding marshals, so we can enforce the
+     * marshalling rules (ie, you can only unmarshal normal marshals once). Note
+     * that these counts do NOT include unmarshalled interfaces, once a stream is
+     * unmarshalled and a proxy set up, this count is decremented.
+     */
+
+    ULONG             norm_refs;  /* refcount of normal marshals (CS lock) */
 };
 
 /* imported interface proxy */
 struct ifproxy
 {
-  struct list entry;       /* entry in proxy_manager list (CS parent->cs) */
-  struct proxy_manager *parent; /* owning proxy_manager (RO) */
-  LPVOID iface;            /* interface pointer (RO) */
-  IID iid;                 /* interface ID (RO) */
-  IPID ipid;               /* imported interface ID (RO) */
-  LPRPCPROXYBUFFER proxy;  /* interface proxy (RO) */
-  DWORD refs;              /* imported (public) references (MUTEX parent->remoting_mutex) */
-  IRpcChannelBuffer *chan; /* channel to object (CS parent->cs) */
+    struct list entry;       /* entry in proxy_manager list (CS parent->cs) */
+    struct proxy_manager *parent; /* owning proxy_manager (RO) */
+    LPVOID iface;            /* interface pointer (RO) */
+    IID iid;                 /* interface ID (RO) */
+    IPID ipid;               /* imported interface ID (RO) */
+    LPRPCPROXYBUFFER proxy;  /* interface proxy (RO) */
+    DWORD refs;              /* imported (public) references (MUTEX parent->remoting_mutex) */
+    IRpcChannelBuffer *chan; /* channel to object (CS parent->cs) */
 };
 
 /* imported object / proxy manager */
 struct proxy_manager
 {
-  const IMultiQIVtbl *lpVtbl;
-  struct apartment *parent; /* owning apartment (RO) */
-  struct list entry;        /* entry in apartment (CS parent->cs) */
-  OXID oxid;                /* object exported ID (RO) */
-  OID oid;                  /* object ID (RO) */
-  struct list interfaces;   /* imported interfaces (CS cs) */
-  DWORD refs;               /* proxy reference count (LOCK) */
-  CRITICAL_SECTION cs;      /* thread safety for this object and children */
-  ULONG sorflags;           /* STDOBJREF flags (RO) */
-  IRemUnknown *remunk;      /* proxy to IRemUnknown used for lifecycle management (CS cs) */
-  HANDLE remoting_mutex;    /* mutex used for synchronizing access to IRemUnknown */
+    const IMultiQIVtbl *lpVtbl;
+    struct apartment *parent; /* owning apartment (RO) */
+    struct list entry;        /* entry in apartment (CS parent->cs) */
+    OXID oxid;                /* object exported ID (RO) */
+    OID oid;                  /* object ID (RO) */
+    struct list interfaces;   /* imported interfaces (CS cs) */
+    DWORD refs;               /* proxy reference count (LOCK) */
+    CRITICAL_SECTION cs;      /* thread safety for this object and children */
+    ULONG sorflags;           /* STDOBJREF flags (RO) */
+    IRemUnknown *remunk;      /* proxy to IRemUnknown used for lifecycle management (CS cs) */
+    HANDLE remoting_mutex;    /* mutex used for synchronizing access to IRemUnknown */
 };
 
 /* this needs to become a COM object that implements IRemUnknown */
 struct apartment
 {
-  struct list entry;       
-
-  DWORD refs;              /* refcount of the apartment (LOCK) */
-  DWORD model;             /* threading model (RO) */
-  DWORD tid;               /* thread id (RO) */
-  HANDLE thread;           /* thread handle (RO) */
-  OXID oxid;               /* object exporter ID (RO) */
-  DWORD ipidc;             /* interface pointer ID counter, starts at 1 (LOCK) */
-  HWND win;                /* message window (RO) */
-  CRITICAL_SECTION cs;     /* thread safety */
-  LPMESSAGEFILTER filter;  /* message filter (CS cs) */
-  struct list proxies;     /* imported objects (CS cs) */
-  struct list stubmgrs;    /* stub managers for exported objects (CS cs) */
-  BOOL remunk_exported;    /* has the IRemUnknown interface for this apartment been created yet? (CS cs) */
-  LONG remoting_started;   /* has the RPC system been started for this apartment? (LOCK) */
+    struct list entry;       
 
-  /* FIXME: OID's should be given out by RPCSS */
-  OID oidc;                /* object ID counter, starts at 1, zero is invalid OID (CS cs) */
+    DWORD refs;              /* refcount of the apartment (LOCK) */
+    DWORD model;             /* threading model (RO) */
+    DWORD tid;               /* thread id (RO) */
+    HANDLE thread;           /* thread handle (RO) */
+    OXID oxid;               /* object exporter ID (RO) */
+    DWORD ipidc;             /* interface pointer ID counter, starts at 1 (LOCK) */
+    HWND win;                /* message window (RO) */
+    CRITICAL_SECTION cs;     /* thread safety */
+    LPMESSAGEFILTER filter;  /* message filter (CS cs) */
+    struct list proxies;     /* imported objects (CS cs) */
+    struct list stubmgrs;    /* stub managers for exported objects (CS cs) */
+    BOOL remunk_exported;    /* has the IRemUnknown interface for this apartment been created yet? (CS cs) */
+    LONG remoting_started;   /* has the RPC system been started for this apartment? (LOCK) */
+    
+    /* FIXME: OID's should be given out by RPCSS */
+    OID oidc;                /* object ID counter, starts at 1, zero is invalid OID (CS cs) */
 };
 
 /* this is what is stored in TEB->ReservedForOle */
@@ -148,7 +163,7 @@
     struct apartment *apt;
     IErrorInfo       *errorinfo;   /* see errorinfo.c */
     IUnknown         *state;       /* see CoSetState */
-    DWORD            inits;        /* number of times CoInitializeEx called */
+    DWORD             inits;       /* number of times CoInitializeEx called */
 };
 
 
@@ -169,15 +184,15 @@
 
 ULONG stub_manager_int_addref(struct stub_manager *This);
 ULONG stub_manager_int_release(struct stub_manager *This);
-struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object, MSHLFLAGS mshlflags);
+struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object);
 ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs);
 ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs);
-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 stub_manager *get_stub_manager(APARTMENT *apt, OID oid);
 struct stub_manager *get_stub_manager_from_object(APARTMENT *apt, void *object);
-BOOL stub_manager_notify_unmarshal(struct stub_manager *m);
-BOOL stub_manager_is_table_marshaled(struct stub_manager *m);
-void stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs);
+BOOL stub_manager_notify_unmarshal(struct stub_manager *m, IPID *ipid);
+BOOL stub_manager_is_table_marshaled(struct stub_manager *m, IPID *ipid);
+void stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, IPID *ipid);
 HRESULT ipid_to_stub_manager(const IPID *ipid, APARTMENT **stub_apt, struct stub_manager **stubmgr_ret);
 IRpcStubBuffer *ipid_to_apt_and_stubbuffer(const IPID *ipid, APARTMENT **stub_apt);
 HRESULT start_apartment_remote_unknown(void);
Index: dlls/ole32/marshal.c
===================================================================
RCS file: /home/wine/wine/dlls/ole32/marshal.c,v
retrieving revision 1.72
diff -u -r1.72 marshal.c
--- dlls/ole32/marshal.c	17 Mar 2005 10:26:20 -0000	1.72
+++ dlls/ole32/marshal.c	5 May 2005 19:43:44 -0000
@@ -85,7 +85,7 @@
     return CoGetClassObject(&pxclsid,CLSCTX_INPROC_SERVER,NULL,&IID_IPSFactoryBuffer,(LPVOID*)facbuf);
 }
 
-/* creates a new stub manager */
+/* does exactly what it says on the tin */
 HRESULT marshal_object(APARTMENT *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *obj, MSHLFLAGS mshlflags)
 {
     struct stub_manager *manager;
@@ -119,15 +119,13 @@
     else
         stdobjref->flags = SORF_NULL;
 
-    /* FIXME: what happens if we register an interface twice with different
-     * marshaling flags? */
     if ((manager = get_stub_manager_from_object(apt, obj)))
         TRACE("registering new ifstub on pre-existing manager\n");
     else
     {
         TRACE("constructing new stub manager\n");
 
-        manager = new_stub_manager(apt, obj, mshlflags);
+        manager = new_stub_manager(apt, obj);
         if (!manager)
         {
             IRpcStubBuffer_Release(stub);
@@ -138,9 +136,10 @@
 
     tablemarshal = ((mshlflags & MSHLFLAGS_TABLESTRONG) || (mshlflags & MSHLFLAGS_TABLEWEAK));
 
-    ifstub = stub_manager_new_ifstub(manager, stub, obj, riid);
+    ifstub = stub_manager_new_ifstub(manager, stub, obj, riid, mshlflags);
     if (!ifstub)
     {
+        ERR("ifstub creation failed\n");
         IRpcStubBuffer_Release(stub);
         stub_manager_int_release(manager);
         /* FIXME: should we do another release to completely destroy the
@@ -520,8 +519,9 @@
     ifproxy->refs = cPublicRefs;
     ifproxy->proxy = NULL;
 
+    /* FIXME: we should take the binding strings and construct the channel in this function */
     assert(channel);
-    ifproxy->chan = channel; /* FIXME: we should take the binding strings and construct the channel in this function */
+    ifproxy->chan = channel; 
 
     /* the IUnknown interface is special because it does not have a
      * proxy associated with the ifproxy as we handle IUnknown ourselves */
@@ -939,8 +939,12 @@
     
       hres = IUnknown_QueryInterface(stubmgr->object, riid, ppv);
       
-      /* unref the ifstub. FIXME: only do this on success? */
-      if (!stub_manager_is_table_marshaled(stubmgr))
+      /* unref the ifstub */
+      if (hres)
+          WARN("QI failed with 0x%lx, possibly bogus refcounting here, "
+               "should we unref only on success?\n", hres);
+      
+      if (!stub_manager_is_table_marshaled(stubmgr, &stdobjref.ipid))
           stub_manager_ext_release(stubmgr, 1);
 
       stub_manager_int_release(stubmgr);
@@ -956,8 +960,11 @@
   {
       if ((stubmgr = get_stub_manager(stub_apt, stdobjref.oid)))
       {
-          if (!stub_manager_notify_unmarshal(stubmgr))
+          if (!stub_manager_notify_unmarshal(stubmgr, &stdobjref.ipid))
+          {
               hres = CO_E_OBJNOTCONNECTED;
+              WARN("stub manager refused unmarshal, returning CO_E_OBJNOTCONNECTED\n");
+          }
 
           stub_manager_int_release(stubmgr);
       }
@@ -1011,7 +1018,7 @@
         return RPC_E_INVALID_OBJREF;
     }
 
-    stub_manager_release_marshal_data(stubmgr, stdobjref.cPublicRefs);
+    stub_manager_release_marshal_data(stubmgr, stdobjref.cPublicRefs, &stdobjref.ipid);
 
     stub_manager_int_release(stubmgr);
     apartment_release(apt);
Index: dlls/ole32/rpc.c
===================================================================
RCS file: /home/wine/wine/dlls/ole32/rpc.c,v
retrieving revision 1.60
diff -u -r1.60 rpc.c
--- dlls/ole32/rpc.c	17 Mar 2005 10:26:20 -0000	1.60
+++ dlls/ole32/rpc.c	5 May 2005 19:43:46 -0000
@@ -230,6 +230,10 @@
      * any windows created by this thread will hang and RPCs that try
      * and re-enter this STA from an incoming server thread will
      * deadlock. InstallShield is an example of that.
+     *
+     * The RPC runtime doesn't seem to provide an async API, so we use
+     * the I_RpcSendReceive call instead. But this blocks, hence the
+     * separate thread.
      */
     params->handle = CreateThread(NULL, 0, rpc_sendreceive_thread, params, 0, &tid);
     if (!params->handle)
Index: dlls/ole32/stubmanager.c
===================================================================
RCS file: /home/wine/wine/dlls/ole32/stubmanager.c,v
retrieving revision 1.19
diff -u -r1.19 stubmanager.c
--- dlls/ole32/stubmanager.c	17 Mar 2005 10:26:20 -0000	1.19
+++ dlls/ole32/stubmanager.c	5 May 2005 19:43:49 -0000
@@ -6,7 +6,7 @@
  *
  * Copyright 2002 Marcus Meissner
  * Copyright 2004 Mike Hearn for CodeWeavers
- * Copyright 2004 Robert Shearman (for CodeWeavers)
+ * Copyright 2004 Robert 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
@@ -48,8 +48,9 @@
 
 /* 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)
+ * external refs together take one implicit ref.
+ */
+struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object)
 {
     struct stub_manager *sm;
 
@@ -78,16 +79,9 @@
      * 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);
 
@@ -398,7 +392,8 @@
 }
 
 /* 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;
 
@@ -413,11 +408,15 @@
 
     /* no need to ref this, same object as sb */
     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 */
+    /* 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.
+     */
     if (IsEqualIID(iid, &IID_IRemUnknown))
     {
         stub->ipid.Data1 = 0xffffffff;
@@ -431,6 +430,8 @@
 
     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));
@@ -453,32 +454,31 @@
 }
 
 /* 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, 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\n");
+        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);
@@ -486,42 +486,30 @@
     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, IPID *ipid)
 {
-    EnterCriticalSection(&m->lock);
+    struct ifstub *ifstub;
 
-    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:
-        refs = 1;
-        break;
-    case STUBSTATE_TABLE_WEAK_MARSHALED:
-        refs = 0; /* like native */
-        break;
-    }
+    if (!(ifstub = stub_manager_ipid_to_ifstub(m, ipid)))
+        return;
 
+    if (ifstub->flags & MSHLFLAGS_TABLEWEAK)
+        refs = 0;
+    else
+        refs = 1;
+        
     stub_manager_ext_release(m, refs);
-
-    LeaveCriticalSection(&m->lock);
 }
 
 /* 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, 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);
+    struct ifstub *ifstub = stub_manager_ipid_to_ifstub(m, ipid);
 
-    return ret;
+    assert( ifstub );
+    
+    return ifstub->flags & (MSHLFLAGS_TABLESTRONG | MSHLFLAGS_TABLEWEAK);
 }
 
 
@@ -607,6 +595,7 @@
 {
     HRESULT hr;
     USHORT i;
+
     USHORT successful_qis = 0;
     APARTMENT *apt;
     struct stub_manager *stubmgr;

Reply via email to