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;