Commit 5475a2e6 authored by Mike Hearn's avatar Mike Hearn Committed by Alexandre Julliard

- Implement the COM stub manager, refactor the current stub code.

- Begin implementing interface stubs.
parent d900b539
......@@ -35,7 +35,8 @@ C_SRCS = \
rpc.c \
stg_bigblockfile.c \
stg_stream.c \
storage32.c
storage32.c \
stubmanager.c
C_SRCS16 = \
memlockbytes16.c \
......
......@@ -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 model)
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(APARTMENT *apt)
/* 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) {
/*
......
......@@ -5,6 +5,7 @@
* Copyright 1999 Sylvain St-Germain
* Copyright 2002 Marcus Meissner
* Copyright 2003 Ove Kven, 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* StdGlobalInterfaceTableInstance;
/* 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_marshal_id *mid1, wine_marshal_id *mid2) {
;
}
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(void)
}
/* 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))
......
......@@ -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;
......
/*
* 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 <stdarg.h>
#include "windef.h"
#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"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
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);
}
......@@ -145,6 +145,7 @@ static DWORD CALLBACK host_object_proc(LPVOID p)
{
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_release()
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_unmarshal_twice()
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_unmarshal_twice()
/* 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_unmarshal_twice()
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_unmarshal_twice()
/* 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_unmarshal_twice()
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 */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment