From f4477fcb16a5b181d3e85e41ed59156bde2a94d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9=20van=20Geldorp?= Date: Sun, 13 Feb 2005 20:52:16 +0000 Subject: [PATCH] Sync to Wine-20050211 Robert Shearman - Add documentation for most Co* functions. - Move several functions into different files to group them with similar functions. - Implement CoDisconnectObject. - Change CoLockObjectExternal so that it does the correct action now and eliminate a fair few lines of now redundant code. - Rename OLE32_Dll{Register,Unregister}Server to Dll{Register,Unregister}Server. - Move OLE automation interface registration to oleaut32. - Add IRemUnknown to list of interfaces to register. - Make proxy manager use IMultiQI instead of IInternalUnknown as tests show that IInternalUnknown isn't exposed. - Implement IMultiQI on top of IRemUnknown calls. - Silence some fixmes that occur during tests and don't give us any useful information. - Fix typo in class factory proxy that caused us to use the wrong offset into the CFProxy structure, causing us to not call the outer_unknown properly. - Use InterlockedIncrement for the ipid counter instead of a critical section (suggested by Mike Hearn). - Remove a line added by a bad merge. - Implement RemUnkStub_Disconnect. - Remove all of the RPC disconnect code. - Implement IRemUnknown. - Use IRemUnknown for life-cycle management instead of the current hacks. - Generate machine-local IPIDs. - Make pipes be uniquely identified only by their IPID. - Implement table marshaling. - The apartment reference should be held while the stub manager reference is held. - Fix same apartment-unmarshal detection. - Proxies/stubs for IRemUnknown methods, based on code written by Ove Ksven. - Initialize ppv pointer in ClientIdentity_QueryInterface to NULL as apps depend on this. - Don't release IRpcProxyBuffer on ifproxy destruction - the caller will do this for us. - Make find_proxy_manager add a reference to the proxy manager and make proxy_manager_construct return an object with a valid ref-count. - Remove stray not operator to fix a memory leak / crash in proxy_manager_destroy. - More debug messages, especially on errors. - Fix ref-count leak in the Class Factory proxy. - Add a test case for IClassFactory_CreateInstance. - Split up apartment creation so that the long code paths that don't need locking no longer have locking. - Add special cases for the threads that join apartments but can't increase the refcount of the apartment. - Free TLS storage on thread destruction (including releasing the apartment the thread is in, if any, and so making another test pass). - More tests. - Change return code of CoGetPSClsid to match test result. - Do a slight hack to make IRemUnknown proxies be added after the proxy that uses them to stop them being used after they are destroyed. - Fix multiple local server connections. - The apartment reference should be held while the stub manager reference is held. - Fix same apartment-unmarshal detection. - Don't use the pipe caching code because it doesn't work correctly at the moment. - Always write disconnect reply packet, even in failure cases. - Move object-to-stub-manager mapping rule to register_ifstub. - Pass the original IID to IMarshal_UnmarshalInterface and query for the requested interface. - Unmarshaling IID_NULL means use the IID the interface was originally marshaled with. - Add code for destroying the thread-local storage data, but don't use it yet. - Don't release apartment on changing modes because we didn't add a reference anywhere. - Quieten the RPC_E_DISCONNECTED error message as it is an expected return code. - Treat IID_NULL the same as IID_IUnknown. - Make tests compile on Win95 again. - Fix copy+paste error where the test failure should be from the CoUnmarshalInterface function. - Give IUnknown its own ifstub to fix ref-counting and ipid storage issues. - Add a new flag SORFP_NOLIFETIMEMGMT to tell the proxy manager not to call any IRemUnknown functions. - Move the low-level unmarshaling code into a new function, unmarshal_object, so that it can be easily reused for unmarshaling IRemUnknown. - Consolidate more stub creation details into register_ifstub. - Replace the current wine_marshal_id structure with STDOBJREF for the on-the-wire format. - Initialize clsid member to the marshaler clsid to fix custom marshaling. - Make proxy shutdown test succeed by releasing the channel on disconnect. - Remove wine_marshal_data: it is unneeded and there is no equivalent in STDOBJREF. - Remove obsolete structs, rearrange things to group the structs together and to group similar functions. - Document thread-safety of members of structs. - Document CoSetState & CoGetState. - Rewrite them to only retrieve TLS info once. - Remove trailing whitespace in COM_CurrentInfo. - Release the client security objects when no longer needed (reported by Mike McCormack). - Implement CoSetProxyBlanket, CoQueryProxyBlanket and CoCopyProxy. - Update todo list. - Destroy stubs on apartment shutdown. - Make MTA dynamically allocated so that proxies and other resources are freed at the proper time. - Changed/removed some incorrect comments regarding apartments. Mike Hearn - Various formatting/style changes. - Force context switch on chanbuf disconnect to avoid a race in the test suite. Mike Hearn Robert Shearman - Rework RPC dispatch layer to be simpler and not get confused by server/client duality. - Make threads shut down at the right time and not access freed memory after apartment destruction. - Rename stub_dispatch_thread to client_dispatch_thread. - Add some more tracing - Check return value of WaitNamedPipe. - Change named pipe timeouts to 0.5s, which should be enough for even the slowest machines. Christian Costa - CoInitialize(Ex) should return S_FALSE when COM is already initialized for the current thread. svn path=/trunk/; revision=13532 --- reactos/include/wine/list.h | 12 + reactos/include/wine/objidl.h | 84 ++ reactos/lib/ole32/compobj.c | 1213 ++++++++++++++++----------- reactos/lib/ole32/compobj_private.h | 202 +++-- reactos/lib/ole32/ifs.c | 69 +- reactos/lib/ole32/marshal.c | 725 ++++++++++++---- reactos/lib/ole32/ole2.c | 65 +- reactos/lib/ole32/ole2stubs.c | 9 - reactos/lib/ole32/ole32.spec | 10 +- reactos/lib/ole32/ole32_main.c | 5 +- reactos/lib/ole32/ole32_main.h | 1 + reactos/lib/ole32/oleproxy.c | 476 ++++++++++- reactos/lib/ole32/regsvr.c | 399 +-------- reactos/lib/ole32/rpc.c | 905 ++++++++++---------- reactos/lib/ole32/stubmanager.c | 508 +++++++++-- reactos/w32api/include/winerror.h | 1 + reactos/w32api/include/wtypes.h | 2 +- 17 files changed, 2976 insertions(+), 1710 deletions(-) diff --git a/reactos/include/wine/list.h b/reactos/include/wine/list.h index 92b84d8303b..2e9df7bf1b3 100644 --- a/reactos/include/wine/list.h +++ b/reactos/include/wine/list.h @@ -143,6 +143,18 @@ inline static void list_init( struct list *list ) #define LIST_FOR_EACH(cursor,list) \ for ((cursor) = (list)->next; (cursor) != (list); (cursor) = (cursor)->next) +/* iterate through the list, with safety against removal */ +#define LIST_FOR_EACH_SAFE(cursor, cursor2, list) \ + for ((cursor) = (list)->next, (cursor2) = (cursor)->next; \ + (cursor) != (list); \ + (cursor) = (cursor2), (cursor2) = (cursor)->next) + +/* iterate through the list using a list entry */ +#define LIST_FOR_EACH_ENTRY(elem, list, type, field) \ + for ((elem) = LIST_ENTRY((list)->next, type, field); \ + &(elem)->field != (list); \ + (elem) = LIST_ENTRY((elem)->field.next, type, field)) + /* macros for statically initialized lists */ #define LIST_INIT(list) { &(list), &(list) } diff --git a/reactos/include/wine/objidl.h b/reactos/include/wine/objidl.h index b9f9eecf0c1..449ccf12e7d 100644 --- a/reactos/include/wine/objidl.h +++ b/reactos/include/wine/objidl.h @@ -984,6 +984,17 @@ DECLARE_INTERFACE_(IClientSecurity,IUnknown) }; #undef INTERFACE +#ifdef COBJMACROS +/*** IUnknown methods ***/ +#define IClientSecurity_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IClientSecurity_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IClientSecurity_Release(p) (p)->lpVtbl->Release(p) +/*** IClientSecurity methods ***/ +#define IClientSecurity_QueryBlanket(p,a,b,c,d,e,f,g,h) (p)->lpVtbl->QueryBlanket(p,a,b,c,d,e,f,g,h) +#define IClientSecurity_SetBlanket(p,a,b,c,d,e,f,g,h) (p)->lpVtbl->SetBlanket(p,a,b,c,d,e,f,g,h) +#define IClientSecurity_CopyProxy(p,a,b) (p)->lpVtbl->CopyProxy(p,a,b) +#endif + EXTERN_C const IID IID_IServerSecurity; #define INTERFACE IServerSecurity DECLARE_INTERFACE_(IServerSecurity,IUnknown) @@ -2005,5 +2016,78 @@ struct IInternalUnknownVtbl { #endif /* __IInternalUnknown_INTERFACE_DEFINED__ */ +#ifndef __IMultiQI_FWD_DEFINED__ +#define __IMultiQI_FWD_DEFINED__ +typedef struct IMultiQI IMultiQI; +#endif + +typedef IMultiQI *LPMULTIQI; + +/***************************************************************************** + * IMultiQI interface + */ +#ifndef __IMultiQI_INTERFACE_DEFINED__ +#define __IMultiQI_INTERFACE_DEFINED__ + +#if defined(__cplusplus) && !defined(CINTERFACE) +struct IMultiQI : public IUnknown +{ + virtual HRESULT STDMETHODCALLTYPE QueryMultipleInterfaces( + ULONG cMQIs, + MULTI_QI* pMQIs) = 0; + +}; +#else +typedef struct IMultiQIVtbl IMultiQIVtbl; +struct IMultiQI { + const IMultiQIVtbl* lpVtbl; +}; +struct IMultiQIVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IMultiQI* This, + REFIID riid, + void** ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IMultiQI* This); + + ULONG (STDMETHODCALLTYPE *Release)( + IMultiQI* This); + + /*** IMultiQI methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryMultipleInterfaces)( + IMultiQI* This, + ULONG cMQIs, + MULTI_QI* pMQIs); + + END_INTERFACE +}; + +#ifdef COBJMACROS +/*** IUnknown methods ***/ +#define IMultiQI_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IMultiQI_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IMultiQI_Release(p) (p)->lpVtbl->Release(p) +/*** IMultiQI methods ***/ +#define IMultiQI_QueryMultipleInterfaces(p,a,b) (p)->lpVtbl->QueryMultipleInterfaces(p,a,b) +#endif + +#endif + +HRESULT CALLBACK IMultiQI_QueryMultipleInterfaces_Proxy( + IMultiQI* This, + ULONG cMQIs, + MULTI_QI* pMQIs); +void __RPC_STUB IMultiQI_QueryMultipleInterfaces_Stub( + struct IRpcStubBuffer* This, + struct IRpcChannelBuffer* pRpcChannelBuffer, + PRPC_MESSAGE pRpcMessage, + DWORD* pdwStubPhase); + +#endif /* __IMultiQI_INTERFACE_DEFINED__ */ + #endif /* __WINE_OBJIDL_H */ diff --git a/reactos/lib/ole32/compobj.c b/reactos/lib/ole32/compobj.c index 1ed832a8a12..a3da9bf4a38 100644 --- a/reactos/lib/ole32/compobj.c +++ b/reactos/lib/ole32/compobj.c @@ -28,37 +28,19 @@ * * TODO list: (items bunched together depend on each other) * - * - Switch wine_marshal_id to use IPIDs not IIDs - * - Once that's done, replace wine_marshal_id with STDOBJREF - * - * - Rewrite the CoLockObjectExternal code, it does totally the wrong - * thing currently (should be controlling the stub manager) - * - * - Make the MTA dynamically allocated and refcounted - * - Free the ReservedForOle data in DllMain(THREAD_DETACH) - * * - Implement the service control manager (in rpcss) to keep track * of registered class objects: ISCM::ServerRegisterClsid et al * - Implement the OXID resolver so we don't need magic pipe names for * clients and servers to meet up * - Flip our marshalling on top of the RPC runtime transport API, * so we no longer use named pipes to communicate - * - Rework threading so re-entrant calls don't need to be sent on - * the incoming pipe * - Implement RPC thread affinity (should fix InstallShield painting * problems) * - * - Implement IRemUnknown and marshalling for it, then use that for - * reffing/unreffing the stub manager from a proxy instead of our - * current hack of simply reffing the stub manager once when it's - * registered. - * - Implement table marshalling, then use it to let us do the final - * rework of the threading - * - * - Make our custom marshalling use NDR to be wire compatible with + * - Make all ole interface marshaling use NDR to be wire compatible with * native DCOM - * - * + * - Use & interpret ORPCTHIS & ORPCTHAT. + * */ #include "config.h" @@ -102,12 +84,11 @@ typedef LPCSTR LPCOLESTR16; static HRESULT COM_GetRegisteredClassObject(REFCLSID rclsid, DWORD dwClsContext, LPUNKNOWN* ppUnk); static void COM_RevokeAllClasses(void); -static void COM_ExternalLockFreeList(void); const CLSID CLSID_StdGlobalInterfaceTable = { 0x00000323, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} }; -APARTMENT MTA; -static struct list apts = LIST_INIT( apts ); +APARTMENT *MTA; /* protected by csApartment */ +static struct list apts = LIST_INIT( apts ); /* protected by csApartment */ static CRITICAL_SECTION csApartment; static CRITICAL_SECTION_DEBUG critsect_debug = @@ -213,89 +194,108 @@ void COMPOBJ_UninitProcess( void ) UnregisterClassA(aptWinClass, OLE32_hInstance); } +void COM_TlsDestroy() +{ + struct oletls *info = NtCurrentTeb()->ReservedForOle; + if (info) + { + if (info->apt) COM_ApartmentRelease(info->apt); + if (info->errorinfo) IErrorInfo_Release(info->errorinfo); + if (info->state) IUnknown_Release(info->state); + HeapFree(GetProcessHeap(), 0, info); + NtCurrentTeb()->ReservedForOle = NULL; + } +} + /****************************************************************************** * Manage apartments. */ - -/* The multi-threaded apartment (MTA) contains zero or more threads interacting - with free threaded (ie thread safe) COM objects. There is only ever one MTA - in a process - you can enter it by calling CoInitializeEx(COINIT_MULTITHREADED) - */ -static void COM_InitMTA(void) +/* allocates memory and fills in the necessary fields for a new apartment + * object */ +static APARTMENT *apartment_construct(DWORD model) { - /* OXIDs are object exporter IDs. Each apartment has an OXID, which is unique - within a network. That is, two different MTAs on different machines will have - different OXIDs. + APARTMENT *apt; - This method of generating an OXID is therefore wrong as it doesn't work across - a network, but for local RPC only it's OK. We can distinguish between MTAs and - STAs because STAs use the thread ID as well, and no thread can have an ID of zero. + TRACE("creating new apartment, model=%ld\n", model); - The algorithm Microsoft use is currently unknown. - */ - MTA.oxid = ((OXID)GetCurrentProcessId() << 32); - InitializeCriticalSection(&MTA.cs); + apt = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*apt)); + apt->tid = GetCurrentThreadId(); + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), &apt->thread, + THREAD_ALL_ACCESS, FALSE, 0); + + list_init(&apt->proxies); + list_init(&apt->stubmgrs); + apt->ipidc = 0; + apt->refs = 1; + apt->remunk_exported = FALSE; + apt->oidc = 1; + InitializeCriticalSection(&apt->cs); + + apt->model = model; + + if (model & COINIT_APARTMENTTHREADED) + { + /* FIXME: should be randomly generated by in an RPC call to rpcss */ + apt->oxid = ((OXID)GetCurrentProcessId() << 32) | GetCurrentThreadId(); + apt->win = CreateWindowA(aptWinClass, NULL, 0, + 0, 0, 0, 0, + 0, 0, OLE32_hInstance, NULL); + } + else + { + /* FIXME: should be randomly generated by in an RPC call to rpcss */ + apt->oxid = ((OXID)GetCurrentProcessId() << 32) | 0xcafe; + } + + apt->shutdown_event = CreateEventW(NULL, TRUE, FALSE, NULL); + + TRACE("Created apartment on OXID %s\n", wine_dbgstr_longlong(apt->oxid)); + + /* the locking here is not currently needed for the MTA case, but it + * doesn't hurt and makes the code simpler */ + EnterCriticalSection(&csApartment); + list_add_head(&apts, &apt->entry); + LeaveCriticalSection(&csApartment); + + return apt; } -static void COM_UninitMTA(void) -{ - DeleteCriticalSection(&MTA.cs); - MTA.oxid = 0; -} - - -/* creates an apartment structure which stores OLE thread-local - * information. Call with COINIT_UNINITIALIZED to create an apartment - * that will be initialized with a model later. Note: do not call - * with COINIT_UNINITIALIZED if the apartment has already been initialized - * with a different COINIT value */ -APARTMENT* COM_CreateApartment(DWORD model) +/* gets and existing apartment if one exists or otherwise creates an apartment + * structure which stores OLE apartment-local information and stores a pointer + * to it in the thread-local storage */ +static APARTMENT *get_or_create_apartment(DWORD model) { APARTMENT *apt = COM_CurrentApt(); if (!apt) { - if (!(model & COINIT_APARTMENTTHREADED)) /* See note 1 above */ - { - TRACE("thread 0x%lx is entering the multithreaded apartment\n", GetCurrentThreadId()); - COM_CurrentInfo()->apt = &MTA; - return COM_CurrentInfo()->apt; - } - - TRACE("creating new apartment, model=%ld\n", model); - - apt = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(APARTMENT)); - apt->tid = GetCurrentThreadId(); - DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), - GetCurrentProcess(), &apt->thread, - THREAD_ALL_ACCESS, FALSE, 0); - - list_init(&apt->proxies); - list_init(&apt->stubmgrs); - apt->oidc = 1; - apt->refs = 1; - InitializeCriticalSection(&apt->cs); - - apt->model = model; - - /* we don't ref the apartment as CoInitializeEx will do it for us */ - if (model & COINIT_APARTMENTTHREADED) { - /* FIXME: how does windoze create OXIDs? */ - apt->oxid = MTA.oxid | GetCurrentThreadId(); - TRACE("Created apartment on OXID %s\n", wine_dbgstr_longlong(apt->oxid)); - apt->win = CreateWindowA(aptWinClass, NULL, 0, - 0, 0, 0, 0, - 0, 0, OLE32_hInstance, NULL); + apt = apartment_construct(model); + COM_CurrentInfo()->apt = apt; } + else + { + EnterCriticalSection(&csApartment); - EnterCriticalSection(&csApartment); - list_add_head(&apts, &apt->entry); - LeaveCriticalSection(&csApartment); + /* The multi-threaded apartment (MTA) contains zero or more threads interacting + * with free threaded (ie thread safe) COM objects. There is only ever one MTA + * in a process */ + if (MTA) + { + TRACE("entering the multithreaded apartment %s\n", wine_dbgstr_longlong(MTA->oxid)); + COM_ApartmentAddRef(MTA); + } + else + MTA = apartment_construct(model); - COM_CurrentInfo()->apt = apt; + apt = MTA; + COM_CurrentInfo()->apt = apt; + + LeaveCriticalSection(&csApartment); + } } return apt; @@ -303,39 +303,61 @@ APARTMENT* COM_CreateApartment(DWORD model) DWORD COM_ApartmentAddRef(struct apartment *apt) { - return InterlockedIncrement(&apt->refs); + DWORD refs = InterlockedIncrement(&apt->refs); + TRACE("%s: before = %ld\n", wine_dbgstr_longlong(apt->oxid), refs - 1); + return refs; } DWORD COM_ApartmentRelease(struct apartment *apt) { DWORD ret; + EnterCriticalSection(&csApartment); + ret = InterlockedDecrement(&apt->refs); + TRACE("%s: after = %ld\n", wine_dbgstr_longlong(apt->oxid), ret); + /* destruction stuff that needs to happen under csApartment CS */ if (ret == 0) { - TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid)); - - EnterCriticalSection(&csApartment); + if (apt == MTA) MTA = NULL; list_remove(&apt->entry); - LeaveCriticalSection(&csApartment); + } + + LeaveCriticalSection(&csApartment); + + if (ret == 0) + { + struct list *cursor, *cursor2; + + TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid)); MARSHAL_Disconnect_Proxies(apt); if (apt->win) DestroyWindow(apt->win); - if (!list_empty(&apt->stubmgrs)) + LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->stubmgrs) { - FIXME("Destroy outstanding stubs\n"); + struct stub_manager *stubmgr = LIST_ENTRY(cursor, struct stub_manager, entry); + /* release the implicit reference given by the fact that the + * stub has external references (it must do since it is in the + * stub manager list in the apartment and all non-apartment users + * must have a ref on the apartment and so it cannot be destroyed). + */ + stub_manager_int_release(stubmgr); } + /* if this assert fires, then another thread took a reference to a + * stub manager without taking a reference to the containing + * apartment, which it must do. */ + assert(list_empty(&apt->stubmgrs)); + if (apt->filter) IUnknown_Release(apt->filter); - DeleteCriticalSection(&apt->cs); + SetEvent(apt->shutdown_event); + CloseHandle(apt->shutdown_event); CloseHandle(apt->thread); HeapFree(GetProcessHeap(), 0, apt); - - apt = NULL; } return ret; @@ -369,6 +391,30 @@ APARTMENT *COM_ApartmentFromOXID(OXID oxid, BOOL ref) return result; } +/* gets the apartment which has a given creator thread ID. The caller must + * release the reference from the apartment as soon as the apartment pointer + * is no longer required. */ +APARTMENT *COM_ApartmentFromTID(DWORD tid) +{ + APARTMENT *result = NULL; + struct list *cursor; + + EnterCriticalSection(&csApartment); + LIST_FOR_EACH( cursor, &apts ) + { + struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry ); + if (apt->tid == tid) + { + result = apt; + COM_ApartmentAddRef(result); + break; + } + } + LeaveCriticalSection(&csApartment); + + return result; +} + HWND COM_GetApartmentWin(OXID oxid, BOOL ref) { APARTMENT *apt; @@ -461,8 +507,12 @@ static void COMPOBJ_DllList_FreeUnused(int Timeout) } /****************************************************************************** - * CoBuildVersion [COMPOBJ.1] * CoBuildVersion [OLE32.@] + * CoBuildVersion [COMPOBJ.1] + * + * Gets the build version of the DLL. + * + * PARAMS * * RETURNS * Current build version, hiword is majornumber, loword is minornumber @@ -479,13 +529,17 @@ DWORD WINAPI CoBuildVersion(void) * Initializes the COM libraries by calling CoInitializeEx with * COINIT_APARTMENTTHREADED, ie it enters a STA thread. * + * PARAMS + * lpReserved [I] Pointer to IMalloc interface (obsolete, should be NULL). + * + * RETURNS + * Success: S_OK if not already initialized, S_FALSE otherwise. + * Failure: HRESULT code. + * * SEE ALSO * CoInitializeEx */ -HRESULT WINAPI CoInitialize( - LPVOID lpReserved /* [in] pointer to win32 malloc interface - (obsolete, should be NULL) */ -) +HRESULT WINAPI CoInitialize(LPVOID lpReserved) { /* * Just delegate to the newer method. @@ -496,13 +550,11 @@ HRESULT WINAPI CoInitialize( /****************************************************************************** * CoInitializeEx [OLE32.@] * - * Initializes the COM libraries. The behavior used to set the win32 - * IMalloc used for memory management is obsolete. If - * COINIT_APARTMENTTHREADED is specified this thread enters a new STA - * (single threaded apartment), otherwise COINIT_MULTITHREADED should - * be specified which indicates that the thread will enter the MTA. + * Initializes the COM libraries. * - * Currently STA threading is only partly implemented. + * PARAMS + * lpReserved [I] Pointer to IMalloc interface (obsolete, should be NULL). + * dwCoInit [I] One or more flags from the COINIT enumeration. See notes. * * RETURNS * S_OK if successful, @@ -510,13 +562,22 @@ HRESULT WINAPI CoInitialize( * RPC_E_CHANGED_MODE if a previous call to CoInitializeEx specified another * threading model. * + * NOTES + * + * The behavior used to set the IMalloc used for memory management is + * obsolete. + * The dwCoInit parameter must specify of of the following apartment + * threading models: + *| COINIT_APARTMENTTHREADED - A single-threaded apartment (STA). + *| COINIT_MULTITHREADED - A multi-threaded apartment (MTA). + * The parameter may also specify zero or more of the following flags: + *| COINIT_DISABLE_OLE1DDE - Don't use DDE for OLE1 support. + *| COINIT_SPEED_OVER_MEMORY - Trade memory for speed. + * * SEE ALSO * CoUninitialize */ -HRESULT WINAPI CoInitializeEx( - LPVOID lpReserved, /* [in] pointer to win32 malloc interface (obsolete, should be NULL) */ - DWORD dwCoInit /* [in] A value from COINIT specifies the threading model */ -) +HRESULT WINAPI CoInitializeEx(LPVOID lpReserved, DWORD dwCoInit) { HRESULT hr = S_OK; APARTMENT *apt; @@ -541,15 +602,13 @@ HRESULT WINAPI CoInitializeEx( */ TRACE("() - Initializing the COM libraries\n"); - COM_InitMTA(); - /* we may need to defer this until after apartment initialisation */ RunningObjectTableImpl_Initialize(); } if (!(apt = COM_CurrentInfo()->apt)) { - apt = COM_CreateApartment(dwCoInit); + apt = get_or_create_apartment(dwCoInit); if (!apt) return E_OUTOFMEMORY; } else if (dwCoInit != apt->model) @@ -557,9 +616,10 @@ HRESULT WINAPI CoInitializeEx( /* Changing the threading model after it's been set is illegal. If this warning is triggered by Wine code then we are probably using the wrong threading model to implement that API. */ ERR("Attempt to change threading model of this apartment from 0x%lx to 0x%lx\n", apt->model, dwCoInit); - COM_ApartmentRelease(apt); return RPC_E_CHANGED_MODE; } + else + hr = S_FALSE; COM_CurrentInfo()->inits++; @@ -594,15 +654,15 @@ void COM_FlushMessageQueue(void) /*********************************************************************** * CoUninitialize [OLE32.@] * - * This method will decrement the refcount on the COM libraries, - * potentially unloading them. The current thread leaves the apartment - * it's currently in. If not in an apartment, the routine does - * nothing. + * This method will decrement the refcount on the current apartment, freeing + * the resources associated with it if it is the last thread in the apartment. + * If the last apartment is freed, the function will additionally release + * any COM resources associated with the process. * - * If COM is to be shut down, any outstanding proxies are - * disconnected, all registered class objects are unregistered and the - * message queue for the thread is flushed (if native does - * this or not is unknown). + * PARAMS + * + * RETURNS + * Nothing. * * SEE ALSO * CoInitializeEx @@ -648,13 +708,8 @@ void WINAPI CoUninitialize(void) /* This will free the loaded COM Dlls */ CoFreeAllLibraries(); - /* This will free list of external references to COM objects */ - COM_ExternalLockFreeList(); - /* This ensures we deal with any pending RPCs */ COM_FlushMessageQueue(); - - COM_UninitMTA(); } else if (lCOMRefCnt<1) { ERR( "CoUninitialize() - not CoInitialized.\n" ); @@ -663,8 +718,8 @@ void WINAPI CoUninitialize(void) } /****************************************************************************** - * CoDisconnectObject [COMPOBJ.15] * CoDisconnectObject [OLE32.@] + * CoDisconnectObject [COMPOBJ.15] * * Disconnects all connections to this object from remote processes. Dispatches * pending RPCs while blocking new RPCs from occurring, and then calls @@ -672,25 +727,65 @@ void WINAPI CoUninitialize(void) * * Typically called when the object server is forced to shut down, for instance by * the user. + * + * PARAMS + * lpUnk [I] The object whose stub should be disconnected. + * reserved [I] Reserved. Should be set to 0. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. + * + * SEE ALSO + * CoMarshalInterface, CoReleaseMarshalData, CoLockObjectExternal */ HRESULT WINAPI CoDisconnectObject( LPUNKNOWN lpUnk, DWORD reserved ) { - FIXME("(%p, %lx): stub - probably harmless\n",lpUnk,reserved); + HRESULT hr; + IMarshal *marshal; + APARTMENT *apt; + + TRACE("(%p, 0x%08lx)\n", lpUnk, reserved); + + hr = IUnknown_QueryInterface(lpUnk, &IID_IMarshal, (void **)&marshal); + if (hr == S_OK) + { + hr = IMarshal_DisconnectObject(marshal, reserved); + IMarshal_Release(marshal); + return hr; + } + + apt = COM_CurrentApt(); + if (!apt) + return CO_E_NOTINITIALIZED; + + apartment_disconnect_object(apt, lpUnk); + + /* Note: native is pretty broken here because it just silently + * fails, without returning an appropriate error code if the object was + * not found, making apps think that the object was disconnected, when + * it actually wasn't */ + return S_OK; } /****************************************************************************** - * CoCreateGuid[OLE32.@] + * CoCreateGuid [OLE32.@] * * Simply forwards to UuidCreate in RPCRT4. * + * PARAMS + * pguid [O] Points to the GUID to initialize. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. + * * SEE ALSO * UuidCreate - * */ -HRESULT WINAPI CoCreateGuid( - GUID *pguid /* [out] points to the GUID to initialize */ -) { +HRESULT WINAPI CoCreateGuid(GUID *pguid) +{ return UuidCreate(pguid); } @@ -701,17 +796,24 @@ HRESULT WINAPI CoCreateGuid( * Converts a unique identifier from its string representation into * the GUID struct. * - * In Windows, if idstr is not a valid CLSID string then it gets - * treated as a ProgID. Wine currently doesn't do this. If idstr is - * NULL it's treated as an all-zero GUID. + * PARAMS + * idstr [I] The string representation of the GUID. + * id [O] GUID converted from the string. * * RETURNS * S_OK on success * CO_E_CLASSSTRING if idstr is not a valid CLSID + * + * BUGS + * + * In Windows, if idstr is not a valid CLSID string then it gets + * treated as a ProgID. Wine currently doesn't do this. If idstr is + * NULL it's treated as an all-zero GUID. + * + * SEE ALSO + * StringFromCLSID */ -HRESULT WINAPI __CLSIDFromStringA( - LPCSTR idstr, /* [in] string representation of guid */ - CLSID *id) /* [out] GUID converted from string */ +HRESULT WINAPI __CLSIDFromStringA(LPCSTR idstr, CLSID *id) { const BYTE *s = (const BYTE *) idstr; int i; @@ -771,9 +873,7 @@ HRESULT WINAPI __CLSIDFromStringA( /*****************************************************************************/ -HRESULT WINAPI CLSIDFromString( - LPOLESTR idstr, /* [in] string representation of GUID */ - CLSID *id ) /* [out] GUID represented by above string */ +HRESULT WINAPI CLSIDFromString(LPOLESTR idstr, CLSID *id ) { char xid[40]; HRESULT ret; @@ -831,14 +931,19 @@ HRESULT WINE_StringFromCLSID( * Converts a GUID into the respective string representation. * The target string is allocated using the OLE IMalloc. * + * PARAMS + * id [I] the GUID to be converted. + * idstr [O] A pointer to a to-be-allocated pointer pointing to the resulting string. + * * RETURNS * S_OK * E_FAIL + * + * SEE ALSO + * StringFromGUID2, CLSIDFromString */ -HRESULT WINAPI StringFromCLSID( - REFCLSID id, /* [in] the GUID to be converted */ - LPOLESTR *idstr /* [out] a pointer to a to-be-allocated pointer pointing to the resulting string */ -) { +HRESULT WINAPI StringFromCLSID(REFCLSID id, LPOLESTR *idstr) +{ char buf[80]; HRESULT ret; LPMALLOC mllc; @@ -856,19 +961,22 @@ HRESULT WINAPI StringFromCLSID( } /****************************************************************************** - * StringFromGUID2 [COMPOBJ.76] * StringFromGUID2 [OLE32.@] + * StringFromGUID2 [COMPOBJ.76] * * Modified version of StringFromCLSID that allows you to specify max * buffer size. * + * PARAMS + * id [I] GUID to convert to string. + * str [O] Buffer where the result will be stored. + * cmax [I] Size of the buffer in characters. + * * RETURNS - * The length of the resulting string, 0 if there was any problem. + * Success: The length of the resulting string in characters. + * Failure: 0. */ -INT WINAPI StringFromGUID2( - REFGUID id, /* [in] GUID to convert to string */ - LPOLESTR str, /* [out] Unicode buffer to hold result */ - INT cmax) +INT WINAPI StringFromGUID2(REFGUID id, LPOLESTR str, INT cmax) { char xguid[80]; @@ -880,18 +988,18 @@ INT WINAPI StringFromGUID2( /****************************************************************************** * ProgIDFromCLSID [OLE32.@] * - * Converts a class id into the respective Program ID. (By using a - * registry lookup) + * Converts a class id into the respective program ID. + * + * PARAMS + * clsid [I] Class ID, as found in registry. + * lplpszProgID [O] Associated ProgID. * * RETURNS * S_OK * E_OUTOFMEMORY * REGDB_E_CLASSNOTREG if the given clsid has no associated ProgID */ -HRESULT WINAPI ProgIDFromCLSID( - REFCLSID clsid, /* [in] class id as found in registry */ - LPOLESTR *lplpszProgID/* [out] associated Prog ID */ -) +HRESULT WINAPI ProgIDFromCLSID(REFCLSID clsid, LPOLESTR *lplpszProgID) { char strCLSID[50], *buf, *buf2; DWORD buf2len; @@ -959,18 +1067,20 @@ HRESULT WINAPI CLSIDFromProgID16( } /****************************************************************************** + * CLSIDFromProgID [OLE32.@] * CLSIDFromProgID [COMPOBJ.61] * - * Converts a program id into the respective GUID. (By using a - * registry lookup) + * Converts a program id into the respective GUID. + * + * PARAMS + * progid [I] Unicode program ID, as found in registry. + * riid [O] Associated CLSID. * * RETURNS - * S_OK - * CO_E_CLASSSTRING if the given ProgID cannot be found + * Success: S_OK + * Failure: CO_E_CLASSSTRING - the given ProgID cannot be found. */ -HRESULT WINAPI CLSIDFromProgID( - LPCOLESTR progid, /* [in] Unicode program id as found in registry */ - LPCLSID riid ) /* [out] associated CLSID */ +HRESULT WINAPI CLSIDFromProgID(LPCOLESTR progid, LPCLSID riid) { static const WCHAR clsidW[] = { '\\','C','L','S','I','D',0 }; char buf2[80]; @@ -1001,8 +1111,19 @@ HRESULT WINAPI CLSIDFromProgID( /***************************************************************************** * CoGetPSClsid [OLE32.@] * - * This function returns the CLSID of the proxy/stub factory that - * implements IPSFactoryBuffer for the specified interface. + * Retrieves the CLSID of the proxy/stub factory that implements + * IPSFactoryBuffer for the specified interface. + * + * PARAMS + * riid [I] Interface whose proxy/stub CLSID is to be returned. + * pclsid [O] Where to store returned proxy/stub CLSID. + * + * RETURNS + * S_OK + * E_OUTOFMEMORY + * REGDB_E_IIDNOTREG if no PSFactoryBuffer is associated with the IID, or it could not be parsed + * + * NOTES * * The standard marshaller activates the object with the CLSID * returned and uses the CreateProxy and CreateStub methods on its @@ -1014,17 +1135,15 @@ HRESULT WINAPI CLSIDFromProgID( * in the registry and any interface id registered by * CoRegisterPSClsid within the current process. * - * FIXME: We only search the registry, not ids registered with - * CoRegisterPSClsid. + * BUGS * - * RETURNS - * S_OK - * E_OUTOFMEMORY - * E_INVALIDARG if no PSFactoryBuffer is associated with the IID, or it could not be parsed + * We only search the registry, not ids registered with + * CoRegisterPSClsid. + * Also, native returns S_OK for interfaces with an key in HKCR\Interface, but + * without a ProxyStubClsid32 key and leaves garbage in pclsid. This should be + * considered a bug in native unless an application depends on this (unlikely). */ -HRESULT WINAPI CoGetPSClsid( - REFIID riid, /* [in] Interface whose proxy/stub CLSID is to be returned */ - CLSID *pclsid ) /* [out] Where to store returned proxy/stub CLSID */ +HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid) { char *buf, buf2[40]; DWORD buf2len; @@ -1038,9 +1157,7 @@ HRESULT WINAPI CoGetPSClsid( (length of iid string plus constant length of static text */ buf = HeapAlloc(GetProcessHeap(), 0, strlen(buf2)+27); if (buf == NULL) - { - return (E_OUTOFMEMORY); - } + return E_OUTOFMEMORY; /* Construct the registry key we want */ sprintf(buf,"Interface\\%s\\ProxyStubClsid32", buf2); @@ -1048,9 +1165,9 @@ HRESULT WINAPI CoGetPSClsid( /* Open the key.. */ if (RegOpenKeyA(HKEY_CLASSES_ROOT, buf, &xhkey)) { - WARN("No PSFactoryBuffer object is registered for this IID\n"); - HeapFree(GetProcessHeap(),0,buf); - return (E_INVALIDARG); + WARN("No PSFactoryBuffer object is registered for IID %s\n", debugstr_guid(riid)); + HeapFree(GetProcessHeap(),0,buf); + return REGDB_E_IIDNOTREG; } HeapFree(GetProcessHeap(),0,buf); @@ -1060,19 +1177,18 @@ HRESULT WINAPI CoGetPSClsid( buf2len = sizeof(buf2); if ( (RegQueryValueA(xhkey,NULL,buf2,&buf2len)) ) { - RegCloseKey(xhkey); - return E_INVALIDARG; + RegCloseKey(xhkey); + return REGDB_E_IIDNOTREG; } RegCloseKey(xhkey); /* We have the CLSid we want back from the registry as a string, so lets convert it into a CLSID structure */ - if ( (__CLSIDFromStringA(buf2,pclsid)) != NOERROR) { - return E_INVALIDARG; - } + if ( (__CLSIDFromStringA(buf2,pclsid)) != NOERROR) + return REGDB_E_IIDNOTREG; TRACE ("() Returning CLSID=%s\n", debugstr_guid(pclsid)); - return (S_OK); + return S_OK; } @@ -1080,7 +1196,15 @@ HRESULT WINAPI CoGetPSClsid( /*********************************************************************** * WriteClassStm (OLE32.@) * - * This function write a CLSID on stream + * Writes a CLSID to a stream. + * + * PARAMS + * pStm [I] Stream to write to. + * rclsid [I] CLSID to write. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. */ HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid) { @@ -1095,7 +1219,15 @@ HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid) /*********************************************************************** * ReadClassStm (OLE32.@) * - * This function read a CLSID from a stream + * Reads a CLSID from a stream. + * + * PARAMS + * pStm [I] Stream to read from. + * rclsid [O] CLSID to read. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. */ HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid) { @@ -1196,6 +1328,13 @@ end: * files use this method instead of exporting DllGetClassObject to allow * other code to connect to their objects. * + * PARAMS + * rclsid [I] CLSID of the object to register. + * pUnk [I] IUnknown of the object. + * dwClsContext [I] CLSCTX flags indicating the context in which to run the executable. + * flags [I] REGCLS flags indicating how connections are made. + * lpdwRegister [I] A unique cookie that can be passed to CoRevokeClassObject. + * * RETURNS * S_OK on success, * E_INVALIDARG if lpdwRegister or pUnk are NULL, @@ -1209,11 +1348,11 @@ end: * can't do that with our current implementation. */ HRESULT WINAPI CoRegisterClassObject( - REFCLSID rclsid, /* [in] CLSID of the object to register */ - LPUNKNOWN pUnk, /* [in] IUnknown of the object */ - DWORD dwClsContext, /* [in] CLSCTX flags indicating the context in which to run the executable */ - DWORD flags, /* [in] REGCLS flags indicating how connections are made */ - LPDWORD lpdwRegister) /* [out] A unique cookie that can be passed to CoRevokeClassObject */ + REFCLSID rclsid, + LPUNKNOWN pUnk, + DWORD dwClsContext, + DWORD flags, + LPDWORD lpdwRegister) { RegisteredClass* newClass; LPUNKNOWN foundObject; @@ -1303,9 +1442,17 @@ HRESULT WINAPI CoRegisterClassObject( /*********************************************************************** * CoRevokeClassObject [OLE32.@] * - * This method will remove a class object from the class registry + * Removes a class object from the class registry. * - * See the Windows documentation for more details. + * PARAMS + * dwRegister [I] Cookie returned from CoRegisterClassObject(). + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. + * + * SEE ALSO + * CoRegisterClassObject */ HRESULT WINAPI CoRevokeClassObject( DWORD dwRegister) @@ -1497,7 +1644,11 @@ HRESULT WINAPI CoGetClassObject( /*********************************************************************** * CoResumeClassObjects (OLE32.@) * - * Resumes classobjects registered with REGCLS suspended + * Resumes all class objects registered with REGCLS_SUSPENDED. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. */ HRESULT WINAPI CoResumeClassObjects(void) { @@ -1741,6 +1892,19 @@ HRESULT WINAPI CoCreateInstanceEx( /*********************************************************************** * CoLoadLibrary (OLE32.@) + * + * Loads a library. + * + * PARAMS + * lpszLibName [I] Path to library. + * bAutoFree [I] Whether the library should automatically be freed. + * + * RETURNS + * Success: Handle to loaded library. + * Failure: NULL. + * + * SEE ALSO + * CoFreeLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries */ HINSTANCE WINAPI CoLoadLibrary(LPOLESTR lpszLibName, BOOL bAutoFree) { @@ -1752,7 +1916,16 @@ HINSTANCE WINAPI CoLoadLibrary(LPOLESTR lpszLibName, BOOL bAutoFree) /*********************************************************************** * CoFreeLibrary [OLE32.@] * - * NOTES: don't believe the documentation + * Unloads a library from memory. + * + * PARAMS + * hLibrary [I] Handle to library to unload. + * + * RETURNS + * Nothing + * + * SEE ALSO + * CoLoadLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries */ void WINAPI CoFreeLibrary(HINSTANCE hLibrary) { @@ -1763,7 +1936,13 @@ void WINAPI CoFreeLibrary(HINSTANCE hLibrary) /*********************************************************************** * CoFreeAllLibraries [OLE32.@] * - * NOTES: don't believe the documentation + * Function for backwards compatibility only. Does nothing. + * + * RETURNS + * Nothing. + * + * SEE ALSO + * CoLoadLibrary, CoFreeLibrary, CoFreeUnusedLibraries */ void WINAPI CoFreeAllLibraries(void) { @@ -1772,33 +1951,43 @@ void WINAPI CoFreeAllLibraries(void) /*********************************************************************** - * CoFreeUnusedLibraries [COMPOBJ.17] * CoFreeUnusedLibraries [OLE32.@] + * CoFreeUnusedLibraries [COMPOBJ.17] * - * FIXME: Calls to CoFreeUnusedLibraries from any thread always route - * through the main apartment's thread to call DllCanUnloadNow + * Frees any unused libraries. Unused are identified as those that return + * S_OK from their DllCanUnloadNow function. + * + * RETURNS + * Nothing. + * + * SEE ALSO + * CoLoadLibrary, CoFreeAllLibraries, CoFreeLibrary */ void WINAPI CoFreeUnusedLibraries(void) { + /* FIXME: Calls to CoFreeUnusedLibraries from any thread always route + * through the main apartment's thread to call DllCanUnloadNow */ COMPOBJ_DllList_FreeUnused(0); } /*********************************************************************** - * CoFileTimeNow [COMPOBJ.82] * CoFileTimeNow [OLE32.@] + * CoFileTimeNow [COMPOBJ.82] + * + * Retrieves the current time in FILETIME format. + * + * PARAMS + * lpFileTime [O] The current time. * * RETURNS - * the current system time in lpFileTime + * S_OK. */ -HRESULT WINAPI CoFileTimeNow( FILETIME *lpFileTime ) /* [out] the current time */ +HRESULT WINAPI CoFileTimeNow( FILETIME *lpFileTime ) { GetSystemTimeAsFileTime( lpFileTime ); return S_OK; } -/*********************************************************************** - * CoLoadLibrary (OLE32.@) - */ static void COM_RevokeAllClasses() { EnterCriticalSection( &csRegisteredClassList ); @@ -1811,241 +2000,73 @@ static void COM_RevokeAllClasses() LeaveCriticalSection( &csRegisteredClassList ); } -/**************************************************************************** - * COM External Lock methods implementation - * - * This api provides a linked list to managed external references to - * COM objects. - * - * The public interface consists of three calls: - * COM_ExternalLockAddRef - * COM_ExternalLockRelease - * COM_ExternalLockFreeList - */ - -#define EL_END_OF_LIST 0 -#define EL_NOT_FOUND 0 - -/* - * Declaration of the static structure that manage the - * external lock to COM objects. - */ -typedef struct COM_ExternalLock COM_ExternalLock; -typedef struct COM_ExternalLockList COM_ExternalLockList; - -struct COM_ExternalLock -{ - IUnknown *pUnk; /* IUnknown referenced */ - ULONG uRefCount; /* external lock counter to IUnknown object*/ - COM_ExternalLock *next; /* Pointer to next element in list */ -}; - -struct COM_ExternalLockList -{ - COM_ExternalLock *head; /* head of list */ -}; - -/* - * Declaration and initialization of the static structure that manages - * the external lock to COM objects. - */ -static COM_ExternalLockList elList = { EL_END_OF_LIST }; - -/* - * Private methods used to managed the linked list - */ - - -static COM_ExternalLock* COM_ExternalLockLocate( - COM_ExternalLock *element, - IUnknown *pUnk); - -/**************************************************************************** - * Internal - Insert a new IUnknown* to the linked list - */ -static BOOL COM_ExternalLockInsert( - IUnknown *pUnk) -{ - COM_ExternalLock *newLock = NULL; - COM_ExternalLock *previousHead = NULL; - - /* - * Allocate space for the new storage object - */ - newLock = HeapAlloc(GetProcessHeap(), 0, sizeof(COM_ExternalLock)); - - if (newLock!=NULL) { - if ( elList.head == EL_END_OF_LIST ) { - elList.head = newLock; /* The list is empty */ - } else { - /* insert does it at the head */ - previousHead = elList.head; - elList.head = newLock; - } - - /* Set new list item data member */ - newLock->pUnk = pUnk; - newLock->uRefCount = 1; - newLock->next = previousHead; - - return TRUE; - } - return FALSE; -} - -/**************************************************************************** - * Internal - Method that removes an item from the linked list. - */ -static void COM_ExternalLockDelete( - COM_ExternalLock *itemList) -{ - COM_ExternalLock *current = elList.head; - - if ( current == itemList ) { - /* this section handles the deletion of the first node */ - elList.head = itemList->next; - HeapFree( GetProcessHeap(), 0, itemList); - } else { - do { - if ( current->next == itemList ){ /* We found the item to free */ - current->next = itemList->next; /* readjust the list pointers */ - HeapFree( GetProcessHeap(), 0, itemList); - break; - } - - /* Skip to the next item */ - current = current->next; - - } while ( current != EL_END_OF_LIST ); - } -} - -/**************************************************************************** - * Internal - Recursivity agent for IUnknownExternalLockList_Find - * - * NOTES: how long can the list be ?? (recursive!!!) - */ -static COM_ExternalLock* COM_ExternalLockLocate( COM_ExternalLock *element, IUnknown *pUnk) -{ - if ( element == EL_END_OF_LIST ) - return EL_NOT_FOUND; - else if ( element->pUnk == pUnk ) /* We found it */ - return element; - else /* Not the right guy, keep on looking */ - return COM_ExternalLockLocate( element->next, pUnk); -} - -/**************************************************************************** - * Public - Method that increments the count for a IUnknown* in the linked - * list. The item is inserted if not already in the list. - */ -static void COM_ExternalLockAddRef(IUnknown *pUnk) -{ - COM_ExternalLock *externalLock = COM_ExternalLockLocate(elList.head, pUnk); - - /* - * Add an external lock to the object. If it was already externally - * locked, just increase the reference count. If it was not. - * add the item to the list. - */ - if ( externalLock == EL_NOT_FOUND ) - COM_ExternalLockInsert(pUnk); - else - externalLock->uRefCount++; - - /* - * Add an internal lock to the object - */ - IUnknown_AddRef(pUnk); -} - -/**************************************************************************** - * Public - Method that decrements the count for a IUnknown* in the linked - * list. The item is removed from the list if its count end up at zero or if - * bRelAll is TRUE. - */ -static void COM_ExternalLockRelease( - IUnknown *pUnk, - BOOL bRelAll) -{ - COM_ExternalLock *externalLock = COM_ExternalLockLocate(elList.head, pUnk); - - if ( externalLock != EL_NOT_FOUND ) { - do { - externalLock->uRefCount--; /* release external locks */ - IUnknown_Release(pUnk); /* release local locks as well */ - - if ( bRelAll == FALSE ) break; /* perform single release */ - - } while ( externalLock->uRefCount > 0 ); - - if ( externalLock->uRefCount == 0 ) /* get rid of the list entry */ - COM_ExternalLockDelete(externalLock); - } -} -/**************************************************************************** - * Public - Method that frees the content of the list. - */ -static void COM_ExternalLockFreeList() -{ - COM_ExternalLock *head; - - head = elList.head; /* grab it by the head */ - while ( head != EL_END_OF_LIST ) { - COM_ExternalLockDelete(head); /* get rid of the head stuff */ - head = elList.head; /* get the new head... */ - } -} - -/**************************************************************************** - * Public - Method that dump the content of the list. - */ -void COM_ExternalLockDump() -{ - COM_ExternalLock *current = elList.head; - - DPRINTF("\nExternal lock list contains:\n"); - - while ( current != EL_END_OF_LIST ) { - DPRINTF( "\t%p with %lu references count.\n", current->pUnk, current->uRefCount); - - /* Skip to the next item */ - current = current->next; - } -} - /****************************************************************************** * CoLockObjectExternal [OLE32.@] + * + * Increments or decrements the external reference count of a stub object. + * + * PARAMS + * pUnk [I] Stub object. + * fLock [I] If TRUE then increments the external ref-count, + * otherwise decrements. + * fLastUnlockReleases [I] If TRUE then the last unlock has the effect of + * calling CoDisconnectObject. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. */ HRESULT WINAPI CoLockObjectExternal( - LPUNKNOWN pUnk, /* [in] object to be locked */ - BOOL fLock, /* [in] do lock */ - BOOL fLastUnlockReleases) /* [in] unlock all */ + LPUNKNOWN pUnk, + BOOL fLock, + BOOL fLastUnlockReleases) { + struct stub_manager *stubmgr; + struct apartment *apt; + TRACE("pUnk=%p, fLock=%s, fLastUnlockReleases=%s\n", pUnk, fLock ? "TRUE" : "FALSE", fLastUnlockReleases ? "TRUE" : "FALSE"); - if (fLock) { - /* - * Increment the external lock coutner, COM_ExternalLockAddRef also - * increment the object's internal lock counter. - */ - COM_ExternalLockAddRef( pUnk); - } else { - /* - * Decrement the external lock coutner, COM_ExternalLockRelease also - * decrement the object's internal lock counter. - */ - COM_ExternalLockRelease( pUnk, fLastUnlockReleases); - } + apt = COM_CurrentApt(); + if (!apt) return CO_E_NOTINITIALIZED; + + stubmgr = get_stub_manager_from_object(apt, pUnk); + + if (stubmgr) + { + if (fLock) + stub_manager_ext_addref(stubmgr, 1); + else + stub_manager_ext_release(stubmgr, 1); + + stub_manager_int_release(stubmgr); return S_OK; + } + else + { + WARN("stub object not found %p\n", pUnk); + /* Note: native is pretty broken here because it just silently + * fails, without returning an appropriate error code, making apps + * think that the object was disconnected, when it actually wasn't */ + return S_OK; + } } /*********************************************************************** * CoInitializeWOW (OLE32.@) + * + * WOW equivalent of CoInitialize? + * + * PARAMS + * x [I] Unknown. + * y [I] Unknown. + * + * RETURNS + * Unknown. */ -HRESULT WINAPI CoInitializeWOW(DWORD x,DWORD y) { +HRESULT WINAPI CoInitializeWOW(DWORD x,DWORD y) +{ FIXME("(0x%08lx,0x%08lx),stub!\n",x,y); return 0; } @@ -2053,40 +2074,69 @@ HRESULT WINAPI CoInitializeWOW(DWORD x,DWORD y) { /*********************************************************************** * CoGetState [OLE32.@] * - * NOTES: might be incomplete + * Retrieves the thread state object previously stored by CoSetState(). + * + * PARAMS + * ppv [I] Address where pointer to object will be stored. + * + * RETURNS + * Success: S_OK. + * Failure: E_OUTOFMEMORY. + * + * NOTES + * Crashes on all invalid ppv addresses, including NULL. + * If the function returns a non-NULL object then the caller must release its + * reference on the object when the object is no longer required. + * + * SEE ALSO + * CoSetState(). */ HRESULT WINAPI CoGetState(IUnknown ** ppv) { - HRESULT hr = E_FAIL; + struct oletls *info = COM_CurrentInfo(); + if (!info) return E_OUTOFMEMORY; *ppv = NULL; - if (COM_CurrentInfo()->state) + if (info->state) { - IUnknown_AddRef(COM_CurrentInfo()->state); - *ppv = COM_CurrentInfo()->state; - TRACE("apt->state=%p\n", COM_CurrentInfo()->state); - hr = S_OK; + IUnknown_AddRef(info->state); + *ppv = info->state; + TRACE("apt->state=%p\n", info->state); } - return hr; + return S_OK; } /*********************************************************************** * CoSetState [OLE32.@] * + * Sets the thread state object. + * + * PARAMS + * pv [I] Pointer to state object to be stored. + * + * NOTES + * The system keeps a reference on the object while the object stored. + * + * RETURNS + * Success: S_OK. + * Failure: E_OUTOFMEMORY. */ HRESULT WINAPI CoSetState(IUnknown * pv) { + struct oletls *info = COM_CurrentInfo(); + if (!info) return E_OUTOFMEMORY; + if (pv) IUnknown_AddRef(pv); - if (COM_CurrentInfo()->state) + if (info->state) { - TRACE("-- release %p now\n", COM_CurrentInfo()->state); - IUnknown_Release(COM_CurrentInfo()->state); + TRACE("-- release %p now\n", info->state); + IUnknown_Release(info->state); } - COM_CurrentInfo()->state = pv; + info->state = pv; return S_OK; } @@ -2124,47 +2174,21 @@ done: return res; } -/****************************************************************************** - * OleSetAutoConvert [OLE32.@] - */ -HRESULT WINAPI OleSetAutoConvert(REFCLSID clsidOld, REFCLSID clsidNew) -{ - HKEY hkey = 0; - char buf[200], szClsidNew[200]; - HRESULT res = S_OK; - - TRACE("(%s,%s)\n", debugstr_guid(clsidOld), debugstr_guid(clsidNew)); - sprintf(buf,"CLSID\\");WINE_StringFromCLSID(clsidOld,&buf[6]); - WINE_StringFromCLSID(clsidNew, szClsidNew); - if (RegOpenKeyA(HKEY_CLASSES_ROOT,buf,&hkey)) - { - res = REGDB_E_CLASSNOTREG; - goto done; - } - if (RegSetValueA(hkey, "AutoConvertTo", REG_SZ, szClsidNew, strlen(szClsidNew)+1)) - { - res = REGDB_E_WRITEREGDB; - goto done; - } - -done: - if (hkey) RegCloseKey(hkey); - return res; -} - -/****************************************************************************** - * OleDoAutoConvert [OLE32.@] - */ -HRESULT WINAPI OleDoAutoConvert(IStorage *pStg, LPCLSID pClsidNew) -{ - FIXME("(%p,%p) : stub\n",pStg,pClsidNew); - return E_NOTIMPL; -} - /****************************************************************************** * CoTreatAsClass [OLE32.@] * - * Sets TreatAs value of a class + * Sets the TreatAs value of a class. + * + * PARAMS + * clsidOld [I] Class to set TreatAs value on. + * clsidNew [I] The class the clsidOld should be treated as. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. + * + * SEE ALSO + * CoGetTreatAsClass */ HRESULT WINAPI CoTreatAsClass(REFCLSID clsidOld, REFCLSID clsidNew) { @@ -2214,7 +2238,18 @@ done: /****************************************************************************** * CoGetTreatAsClass [OLE32.@] * - * Reads the TreatAs value from a class. + * Gets the TreatAs value of a class. + * + * PARAMS + * clsidOld [I] Class to get the TreatAs value of. + * clsidNew [I] The class the clsidOld should be treated as. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. + * + * SEE ALSO + * CoSetTreatAsClass */ HRESULT WINAPI CoGetTreatAsClass(REFCLSID clsidOld, LPCLSID clsidNew) { @@ -2246,19 +2281,80 @@ done: } +/****************************************************************************** + * CoGetCurrentProcess [OLE32.@] + * CoGetCurrentProcess [COMPOBJ.34] + * + * Gets the current process ID. + * + * RETURNS + * The current process ID. + * + * NOTES + * Is DWORD really the correct return type for this function? + */ +DWORD WINAPI CoGetCurrentProcess(void) +{ + return GetCurrentProcessId(); +} + +/****************************************************************************** + * CoRegisterMessageFilter [OLE32.@] + * + * Registers a message filter. + * + * PARAMS + * lpMessageFilter [I] Pointer to interface. + * lplpMessageFilter [O] Indirect pointer to prior instance if non-NULL. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. + */ +HRESULT WINAPI CoRegisterMessageFilter( + LPMESSAGEFILTER lpMessageFilter, + LPMESSAGEFILTER *lplpMessageFilter) +{ + FIXME("stub\n"); + if (lplpMessageFilter) { + *lplpMessageFilter = NULL; + } + return S_OK; +} + +/*********************************************************************** + * CoIsOle1Class [OLE32.@] + * + * Determines whether the specified class an OLE v1 class. + * + * PARAMS + * clsid [I] Class to test. + * + * RETURNS + * TRUE if the class is an OLE v1 class, or FALSE otherwise. + */ +BOOL WINAPI CoIsOle1Class(REFCLSID clsid) +{ + FIXME("%s\n", debugstr_guid(clsid)); + return FALSE; +} + /*********************************************************************** * IsEqualGUID [OLE32.@] * * Compares two Unique Identifiers. * + * PARAMS + * rguid1 [I] The first GUID to compare. + * rguid2 [I] The other GUID to compare. + * * RETURNS * TRUE if equal */ #undef IsEqualGUID BOOL WINAPI IsEqualGUID( - REFGUID rguid1, /* [in] unique id 1 */ - REFGUID rguid2 /* [in] unique id 2 */ - ) + REFGUID rguid1, + REFGUID rguid2) { return !memcmp(rguid1,rguid2,sizeof(GUID)); } @@ -2280,6 +2376,13 @@ HRESULT WINAPI CoInitializeSecurity(PSECURITY_DESCRIPTOR pSecDesc, LONG cAuthSvc /*********************************************************************** * CoSuspendClassObjects [OLE32.@] + * + * Suspends all registered class objects to prevent further requests coming in + * for those objects. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. */ HRESULT WINAPI CoSuspendClassObjects(void) { @@ -2289,6 +2392,12 @@ HRESULT WINAPI CoSuspendClassObjects(void) /*********************************************************************** * CoAddRefServerProcess [OLE32.@] + * + * Helper function for incrementing the reference count of a local-server + * process. + * + * RETURNS + * New reference count. */ ULONG WINAPI CoAddRefServerProcess(void) { @@ -2298,9 +2407,139 @@ ULONG WINAPI CoAddRefServerProcess(void) /*********************************************************************** * CoReleaseServerProcess [OLE32.@] + * + * Helper function for decrementing the reference count of a local-server + * process. + * + * RETURNS + * New reference count. */ ULONG WINAPI CoReleaseServerProcess(void) { FIXME("\n"); return 1; } + +/*********************************************************************** + * CoQueryProxyBlanket [OLE32.@] + * + * Retrieves the security settings being used by a proxy. + * + * PARAMS + * pProxy [I] Pointer to the proxy object. + * pAuthnSvc [O] The type of authentication service. + * pAuthzSvc [O] The type of authorization service. + * ppServerPrincName [O] Optional. The server prinicple name. + * pAuthnLevel [O] The authentication level. + * pImpLevel [O] The impersonation level. + * ppAuthInfo [O] Information specific to the authorization/authentication service. + * pCapabilities [O] Flags affecting the security behaviour. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. + * + * SEE ALSO + * CoCopyProxy, CoSetProxyBlanket. + */ +HRESULT WINAPI CoQueryProxyBlanket(IUnknown *pProxy, DWORD *pAuthnSvc, + DWORD *pAuthzSvc, OLECHAR **ppServerPrincName, DWORD *pAuthnLevel, + DWORD *pImpLevel, void **ppAuthInfo, DWORD *pCapabilities) +{ + IClientSecurity *pCliSec; + HRESULT hr; + + TRACE("%p\n", pProxy); + + hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec); + if (SUCCEEDED(hr)) + { + hr = IClientSecurity_QueryBlanket(pCliSec, pProxy, pAuthnSvc, + pAuthzSvc, ppServerPrincName, + pAuthnLevel, pImpLevel, ppAuthInfo, + pCapabilities); + IClientSecurity_Release(pCliSec); + } + + if (FAILED(hr)) ERR("-- failed with 0x%08lx\n", hr); + return hr; +} + +/*********************************************************************** + * CoSetProxyBlanket [OLE32.@] + * + * Sets the security settings for a proxy. + * + * PARAMS + * pProxy [I] Pointer to the proxy object. + * AuthnSvc [I] The type of authentication service. + * AuthzSvc [I] The type of authorization service. + * pServerPrincName [I] The server prinicple name. + * AuthnLevel [I] The authentication level. + * ImpLevel [I] The impersonation level. + * pAuthInfo [I] Information specific to the authorization/authentication service. + * Capabilities [I] Flags affecting the security behaviour. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. + * + * SEE ALSO + * CoQueryProxyBlanket, CoCopyProxy. + */ +HRESULT WINAPI CoSetProxyBlanket(IUnknown *pProxy, DWORD AuthnSvc, + DWORD AuthzSvc, OLECHAR *pServerPrincName, DWORD AuthnLevel, + DWORD ImpLevel, void *pAuthInfo, DWORD Capabilities) +{ + IClientSecurity *pCliSec; + HRESULT hr; + + TRACE("%p\n", pProxy); + + hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec); + if (SUCCEEDED(hr)) + { + hr = IClientSecurity_SetBlanket(pCliSec, pProxy, AuthnSvc, + AuthzSvc, pServerPrincName, + AuthnLevel, ImpLevel, pAuthInfo, + Capabilities); + IClientSecurity_Release(pCliSec); + } + + if (FAILED(hr)) ERR("-- failed with 0x%08lx\n", hr); + return hr; +} + +/*********************************************************************** + * CoCopyProxy [OLE32.@] + * + * Copies a proxy. + * + * PARAMS + * pProxy [I] Pointer to the proxy object. + * ppCopy [O] Copy of the proxy. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. + * + * SEE ALSO + * CoQueryProxyBlanket, CoSetProxyBlanket. + */ +HRESULT WINAPI CoCopyProxy(IUnknown *pProxy, IUnknown **ppCopy) +{ + IClientSecurity *pCliSec; + HRESULT hr; + + TRACE("%p\n", pProxy); + + hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec); + if (SUCCEEDED(hr)) + { + hr = IClientSecurity_CopyProxy(pCliSec, pProxy, ppCopy); + IClientSecurity_Release(pCliSec); + } + + if (FAILED(hr)) ERR("-- failed with 0x%08lx\n", hr); + return hr; +} diff --git a/reactos/lib/ole32/compobj_private.h b/reactos/lib/ole32/compobj_private.h index e950eeaca33..056f1491d5f 100644 --- a/reactos/lib/ole32/compobj_private.h +++ b/reactos/lib/ole32/compobj_private.h @@ -5,7 +5,7 @@ * Copyright 1999 Sylvain St-Germain * Copyright 2002 Marcus Meissner * Copyright 2003 Ove Kåven, TransGaming Technologies - * Copyright 2004 Mike Hearn, CodeWeavers Inc + * Copyright 2004 Mike Hearn, Rob Shearman, CodeWeavers Inc * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -39,54 +39,78 @@ #include "winternl.h" struct apartment; +typedef struct apartment APARTMENT; + +/* Thread-safety Annotation Legend: + * + * RO - The value is read only. It never changes after creation, so no + * locking is required. + * LOCK - The value is written to only using Interlocked* functions. + * CS - The value is read or written to with a critical section held. + * The identifier following "CS" is the specific critical section that + * must be used. + */ + +typedef enum ifstub_state +{ + IFSTUB_STATE_NORMAL_MARSHALED, + IFSTUB_STATE_NORMAL_UNMARSHALED, + IFSTUB_STATE_TABLE_MARSHALED +} IFSTUB_STATE; + +/* an interface stub */ +struct ifstub +{ + struct list entry; /* entry in stub_manager->ifstubs list (CS stub_manager->lock) */ + IRpcStubBuffer *stubbuffer; /* RO */ + IID iid; /* RO */ + IPID ipid; /* RO */ + IUnknown *iface; /* RO */ + IFSTUB_STATE state; /* CS stub_manager->lock */ +}; -/* exported interface */ -typedef struct tagXIF { - struct tagXIF *next; - LPVOID iface; /* interface pointer */ - IID iid; /* interface ID */ - IPID ipid; /* exported interface ID */ - LPRPCSTUBBUFFER stub; /* interface stub */ - DWORD refs; /* external reference count */ - HRESULT hres; /* result of stub creation attempt */ -} XIF; +/* stub managers hold refs on the object and each interface stub */ +struct stub_manager +{ + struct list entry; /* entry in apartment stubmgr list (CS apt->cs) */ + struct list ifstubs; /* list of active ifstubs for the object (CS lock) */ + CRITICAL_SECTION lock; + APARTMENT *apt; /* owning apt (RO) */ -/* exported object */ -typedef struct tagXOBJECT { - IRpcStubBufferVtbl *lpVtbl; - struct apartment *parent; - struct tagXOBJECT *next; - LPUNKNOWN obj; /* object identity (IUnknown) */ - OID oid; /* object ID */ - DWORD ifc; /* interface ID counter */ - XIF *ifaces; /* exported interfaces */ - DWORD refs; /* external reference count */ -} XOBJECT; + ULONG extrefs; /* number of 'external' references (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) */ +}; /* imported interface proxy */ struct ifproxy { - struct list entry; - LPVOID iface; /* interface pointer */ - IID iid; /* interface ID */ - IPID ipid; /* imported interface ID */ - LPRPCPROXYBUFFER proxy; /* interface proxy */ - DWORD refs; /* imported (public) references */ + 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 (CS parent->cs) */ }; /* imported object / proxy manager */ struct proxy_manager { - const IInternalUnknownVtbl *lpVtbl; - struct apartment *parent; - struct list entry; - LPRPCCHANNELBUFFER chan; /* channel to object */ - OXID oxid; /* object exported ID */ - OID oid; /* object ID */ - struct list interfaces; /* imported interfaces */ - DWORD refs; /* proxy reference count */ - CRITICAL_SECTION cs; /* thread safety for this object and children */ + const IMultiQIVtbl *lpVtbl; + struct apartment *parent; /* owning apartment (RO) */ + struct list entry; /* entry in apartment (CS parent->cs) */ + LPRPCCHANNELBUFFER chan; /* channel to object (CS 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) */ }; /* this needs to become a COM object that implements IRemUnknown */ @@ -94,28 +118,42 @@ struct apartment { struct list entry; - DWORD refs; /* refcount of the apartment */ - DWORD model; /* threading model */ - DWORD tid; /* thread id */ - HANDLE thread; /* thread handle */ - OXID oxid; /* object exporter ID */ - OID oidc; /* object ID counter, starts at 1, zero is invalid OID */ - HWND win; /* message window */ + 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 */ - XOBJECT *objs; /* exported objects */ - struct list proxies; /* imported objects */ + 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) */ + + /* FIXME: These should all be removed long term as they leak information that should be encapsulated */ + OID oidc; /* object ID counter, starts at 1, zero is invalid OID (CS cs) */ DWORD listenertid; /* id of apartment_listener_thread */ - struct list stubmgrs; /* stub managers for exported objects */ + HANDLE shutdown_event; /* event used to tell the client_dispatch_thread to shut down */ }; -typedef struct apartment APARTMENT; +/* this is what is stored in TEB->ReservedForOle */ +struct oletls +{ + struct apartment *apt; + IErrorInfo *errorinfo; /* see errorinfo.c */ + IUnknown *state; /* see CoSetState */ + DWORD inits; /* number of times CoInitializeEx called */ +}; extern void* StdGlobalInterfaceTable_Construct(void); extern void StdGlobalInterfaceTable_Destroy(void* self); extern HRESULT StdGlobalInterfaceTable_GetFactory(LPVOID *ppv); +/* FIXME: these shouldn't be needed, except for 16-bit functions */ extern HRESULT WINE_StringFromCLSID(const CLSID *id,LPSTR idstr); +HRESULT WINAPI __CLSIDFromStringA(LPCSTR idstr, CLSID *id); + extern HRESULT create_marshalled_proxy(REFCLSID rclsid, REFIID iid, LPVOID *ppv); extern void* StdGlobalInterfaceTableInstance; @@ -139,52 +177,21 @@ MARSHAL_Compare_Mids(wine_marshal_id *mid1,wine_marshal_id *mid2) { HRESULT MARSHAL_Disconnect_Proxies(APARTMENT *apt); HRESULT MARSHAL_GetStandardMarshalCF(LPVOID *ppv); -/* Thread-safety Annotation Legend: - * - * RO - The value is read only. It never changes after creation, so no - * locking is required. - * LOCK - The value is written to only using Interlocked* functions. - * CS - The value is read or written to with a critical section held. - * The identifier following "CS" is the specific critical section that - * must be used. - */ - -/* an interface stub */ -struct ifstub -{ - struct list entry; /* entry in stub_manager->ifstubs list (CS stub_manager->lock) */ - IRpcStubBuffer *stubbuffer; /* RO */ - IID iid; /* RO */ - IPID ipid; /* RO */ - IUnknown *iface; /* RO */ - BOOL table; /* CS stub_manager->lock */ -}; - - -/* stub managers hold refs on the object and each interface stub */ -struct stub_manager -{ - struct list entry; /* entry in apartment stubmgr list (CS apt->cs) */ - struct list ifstubs; /* list of active ifstubs for the object (CS lock) */ - CRITICAL_SECTION lock; - APARTMENT *apt; /* owning apt (RO) */ - - ULONG extrefs; /* number of 'external' references (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) */ -}; - 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); ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs); ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs); -IRpcStubBuffer *stub_manager_ipid_to_stubbuffer(struct stub_manager *m, const IPID *iid); struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, IUnknown *iptr, REFIID 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); +struct stub_manager *get_stub_manager(APARTMENT *apt, OID oid); +struct stub_manager *get_stub_manager_from_object(APARTMENT *apt, void *object); +void apartment_disconnect_object(APARTMENT *apt, void *object); +BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid); +BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid); +HRESULT register_ifstub(APARTMENT *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *obj, MSHLFLAGS mshlflags); +HRESULT ipid_to_stub_manager(const IPID *ipid, APARTMENT **stub_apt, struct stub_manager **stubmgr_ret); +IRpcStubBuffer *ipid_to_stubbuffer(const IPID *ipid); +HRESULT start_apartment_remote_unknown(void); IRpcStubBuffer *mid_to_stubbuffer(wine_marshal_id *mid); @@ -202,23 +209,12 @@ HRESULT WINAPI RunningObjectTableImpl_UnInitialize(void); /* This function decomposes a String path to a String Table containing all the elements ("\" or "subDirectory" or "Directory" or "FileName") of the path */ int WINAPI FileMonikerImpl_DecomposePath(LPCOLESTR str, LPOLESTR** stringTable); -HRESULT WINAPI __CLSIDFromStringA(LPCSTR idstr, CLSID *id); - /* compobj.c */ -APARTMENT *COM_CreateApartment(DWORD model); APARTMENT *COM_ApartmentFromOXID(OXID oxid, BOOL ref); +APARTMENT *COM_ApartmentFromTID(DWORD tid); DWORD COM_ApartmentAddRef(struct apartment *apt); DWORD COM_ApartmentRelease(struct apartment *apt); -/* this is what is stored in TEB->ReservedForOle */ -struct oletls -{ - struct apartment *apt; - IErrorInfo *errorinfo; /* see errorinfo.c */ - IUnknown *state; /* see CoSetState */ - DWORD inits; /* number of times CoInitializeEx called */ -}; - /* * Per-thread values are stored in the TEB on offset 0xF80, * see http://www.microsoft.com/msj/1099/bugslayer/bugslayer1099.htm @@ -227,9 +223,9 @@ struct oletls /* will create if necessary */ static inline struct oletls *COM_CurrentInfo(void) { - if (!NtCurrentTeb()->ReservedForOle) + if (!NtCurrentTeb()->ReservedForOle) NtCurrentTeb()->ReservedForOle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct oletls)); - + return NtCurrentTeb()->ReservedForOle; } diff --git a/reactos/lib/ole32/ifs.c b/reactos/lib/ole32/ifs.c index 325f08444d1..da82e118c01 100644 --- a/reactos/lib/ole32/ifs.c +++ b/reactos/lib/ole32/ifs.c @@ -527,8 +527,15 @@ static IMallocSpyVtbl VT_IMallocSpy = /****************************************************************************** * CoGetMalloc [OLE32.@] * + * Retrieves the current IMalloc interface for the process. + * + * PARAMS + * dwMemContext [I] + * lpMalloc [O] Address where memory allocator object will be stored. + * * RETURNS - * The win32 IMalloc + * Success: S_OK. + * Failure: HRESULT code. */ HRESULT WINAPI CoGetMalloc(DWORD dwMemContext, LPMALLOC *lpMalloc) { @@ -538,15 +545,31 @@ HRESULT WINAPI CoGetMalloc(DWORD dwMemContext, LPMALLOC *lpMalloc) /*********************************************************************** * CoTaskMemAlloc [OLE32.@] + * + * Allocates memory using the current process memory allocator. + * + * PARAMS + * size [I] Size of the memory block to allocate. + * * RETURNS - * pointer to newly allocated block + * Success: Pointer to newly allocated memory block. + * Failure: NULL. */ LPVOID WINAPI CoTaskMemAlloc(ULONG size) { return IMalloc_Alloc((LPMALLOC)&Malloc32,size); } + /*********************************************************************** * CoTaskMemFree [OLE32.@] + * + * Frees memory allocated from the current process memory allocator. + * + * PARAMS + * ptr [I] Memory block to free. + * + * RETURNS + * Nothing. */ VOID WINAPI CoTaskMemFree(LPVOID ptr) { @@ -555,8 +578,16 @@ VOID WINAPI CoTaskMemFree(LPVOID ptr) /*********************************************************************** * CoTaskMemRealloc [OLE32.@] + * + * Allocates memory using the current process memory allocator. + * + * PARAMS + * pvOld [I] Pointer to old memory block. + * size [I] Size of the new memory block. + * * RETURNS - * pointer to newly allocated block + * Success: Pointer to newly allocated memory block. + * Failure: NULL. */ LPVOID WINAPI CoTaskMemRealloc(LPVOID pvOld, ULONG size) { @@ -566,6 +597,16 @@ LPVOID WINAPI CoTaskMemRealloc(LPVOID pvOld, ULONG size) /*********************************************************************** * CoRegisterMallocSpy [OLE32.@] * + * Registers an object that receives notifications on memory allocations and + * frees. + * + * PARAMS + * pMallocSpy [I] New spy object. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. + * * NOTES * if a mallocspy is already registered, we can't do it again since * only the spy knows, how to free a memory block @@ -597,6 +638,16 @@ HRESULT WINAPI CoRegisterMallocSpy(LPMALLOCSPY pMallocSpy) /*********************************************************************** * CoRevokeMallocSpy [OLE32.@] * + * Revokes a previousl registered object that receives notifications on memory + * allocations and frees. + * + * PARAMS + * pMallocSpy [I] New spy object. + * + * RETURNS + * Success: S_OK. + * Failure: HRESULT code. + * * NOTES * we can't revoke a malloc spy as long as memory blocks allocated with * the spy are active since only the spy knows how to free them @@ -629,12 +680,16 @@ HRESULT WINAPI CoRevokeMallocSpy(void) /****************************************************************************** * IsValidInterface [OLE32.@] * + * Determines whether a pointer is a valid interface. + * + * PARAMS + * punk [I] Interface to be tested. + * * RETURNS - * True, if the passed pointer is a valid interface + * TRUE, if the passed pointer is a valid interface, or FALSE otherwise. */ -BOOL WINAPI IsValidInterface( - LPUNKNOWN punk /* [in] interface to be tested */ -) { +BOOL WINAPI IsValidInterface(LPUNKNOWN punk) +{ return !( IsBadReadPtr(punk,4) || IsBadReadPtr(punk->lpVtbl,4) || diff --git a/reactos/lib/ole32/marshal.c b/reactos/lib/ole32/marshal.c index 2962d538255..2ae897ae9bf 100644 --- a/reactos/lib/ole32/marshal.c +++ b/reactos/lib/ole32/marshal.c @@ -49,6 +49,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole); extern const CLSID CLSID_DfMarshal; +/* number of refs given out for normal marshaling */ +#define NORMALEXTREFS 1 /* FIXME: this should be 5, but we have to wait for IRemUnknown support first */ + +/* private flag indicating that the caller does not want to notify the stub + * when the proxy disconnects or is destroyed */ +#define SORFP_NOLIFETIMEMGMT SORF_OXRES1 + +static HRESULT unmarshal_object(const STDOBJREF *stdobjref, APARTMENT *apt, REFIID riid, void **object); + /* Marshalling just passes a unique identifier to the remote client, * that makes it possible to find the passed interface again. * @@ -76,66 +85,75 @@ get_facbuf_for_iid(REFIID riid,IPSFactoryBuffer **facbuf) { return CoGetClassObject(&pxclsid,CLSCTX_INPROC_SERVER,NULL,&IID_IPSFactoryBuffer,(LPVOID*)facbuf); } -typedef struct _wine_marshal_data { - DWORD dwDestContext; - DWORD mshlflags; -} wine_marshal_data; - -IRpcStubBuffer *mid_to_stubbuffer(wine_marshal_id *mid) +/* creates a new stub manager */ +HRESULT register_ifstub(APARTMENT *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *obj, MSHLFLAGS mshlflags) { - IRpcStubBuffer *ret; - struct stub_manager *m; - - if (!(m = get_stub_manager(mid->oxid, mid->oid))) - { - WARN("unknown OID %s\n", wine_dbgstr_longlong(mid->oid)); - return NULL; - } - - ret = stub_manager_ipid_to_stubbuffer(m, &mid->ipid); - - stub_manager_int_release(m); - return ret; -} - -/* creates a new stub manager and sets mid->oid when mid->oid == 0 */ -static HRESULT register_ifstub(wine_marshal_id *mid, REFIID riid, IUnknown *obj, IRpcStubBuffer *stub, BOOL tablemarshal) -{ - struct stub_manager *manager = NULL; + struct stub_manager *manager; struct ifstub *ifstub; + BOOL tablemarshal; + IRpcStubBuffer *stub; + IPSFactoryBuffer *psfb; + HRESULT hr; - /* mid->oid of zero means create a new stub manager */ - - if (mid->oid && (manager = get_stub_manager(mid->oxid, mid->oid))) + hr = get_facbuf_for_iid(riid, &psfb); + if (hr != S_OK) { - TRACE("registering new ifstub on pre-existing manager\n"); + ERR("couldn't get IPSFactory buffer for interface %s\n", debugstr_guid(riid)); + return hr; } + + hr = IPSFactoryBuffer_CreateStub(psfb, riid, obj, &stub); + IPSFactoryBuffer_Release(psfb); + if (hr != S_OK) + { + ERR("Failed to create an IRpcStubBuffer from IPSFactory for %s\n", debugstr_guid(riid)); + return hr; + } + + if (mshlflags & MSHLFLAGS_NOPING) + stdobjref->flags = SORF_NOPING; + else + stdobjref->flags = SORF_NULL; + + stdobjref->oxid = apt->oxid; + + if ((manager = get_stub_manager_from_object(apt, obj))) + TRACE("registering new ifstub on pre-existing manager\n"); else { - struct apartment *apt; - TRACE("constructing new stub manager\n"); - apt = COM_ApartmentFromOXID(mid->oxid, TRUE); manager = new_stub_manager(apt, obj); - COM_ApartmentRelease(apt); - if (!manager) return E_OUTOFMEMORY; - - mid->oid = manager->oid; + if (!manager) + return E_OUTOFMEMORY; } + stdobjref->oid = manager->oid; + + tablemarshal = ((mshlflags & MSHLFLAGS_TABLESTRONG) || (mshlflags & MSHLFLAGS_TABLEWEAK)); ifstub = stub_manager_new_ifstub(manager, stub, obj, riid, tablemarshal); if (!ifstub) { + IRpcStubBuffer_Release(stub); stub_manager_int_release(manager); /* FIXME: should we do another release to completely destroy the * stub manager? */ return E_OUTOFMEMORY; } - if (!tablemarshal) stub_manager_ext_addref(manager, 1); + if (!tablemarshal) + { + stdobjref->cPublicRefs = NORMALEXTREFS; + stub_manager_ext_addref(manager, stdobjref->cPublicRefs); + } + else + { + stdobjref->cPublicRefs = 0; + if (mshlflags & MSHLFLAGS_TABLESTRONG) + stub_manager_ext_addref(manager, 1); + } - mid->ipid = ifstub->ipid; + stdobjref->ipid = ifstub->ipid; stub_manager_int_release(manager); return S_OK; @@ -145,47 +163,33 @@ static HRESULT register_ifstub(wine_marshal_id *mid, REFIID riid, IUnknown *obj, /* Client-side identity of the server object */ +static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnknown **remunk); static void proxy_manager_destroy(struct proxy_manager * This); static HRESULT proxy_manager_find_ifproxy(struct proxy_manager * This, REFIID riid, struct ifproxy ** ifproxy_found); +static HRESULT proxy_manager_query_local_interface(struct proxy_manager * This, REFIID riid, void ** ppv); -static HRESULT WINAPI ClientIdentity_QueryInterface(IInternalUnknown * iface, REFIID riid, void ** ppv) +static HRESULT WINAPI ClientIdentity_QueryInterface(IMultiQI * iface, REFIID riid, void ** ppv) { - struct proxy_manager * This = (struct proxy_manager *)iface; HRESULT hr; - struct ifproxy * ifproxy; + MULTI_QI mqi; TRACE("%s\n", debugstr_guid(riid)); - if (IsEqualIID(riid, &IID_IUnknown) || - IsEqualIID(riid, &IID_IInternalUnknown)) - { - *ppv = (void *)iface; - IInternalUnknown_AddRef(iface); - return S_OK; - } + mqi.pIID = riid; + hr = IMultiQI_QueryMultipleInterfaces(iface, 1, &mqi); + *ppv = (void *)mqi.pItf; - hr = proxy_manager_find_ifproxy(This, riid, &ifproxy); - if (hr == S_OK) - { - *ppv = ifproxy->iface; - IUnknown_AddRef((IUnknown *)*ppv); - return S_OK; - } - - FIXME("interface not found %s\n", debugstr_guid(riid)); - - /* FIXME: call IRemUnknown::RemQueryInterface */ - return E_NOINTERFACE; + return hr; } -static ULONG WINAPI ClientIdentity_AddRef(IInternalUnknown * iface) +static ULONG WINAPI ClientIdentity_AddRef(IMultiQI * iface) { struct proxy_manager * This = (struct proxy_manager *)iface; TRACE("%p - before %ld\n", iface, This->refs); return InterlockedIncrement(&This->refs); } -static ULONG WINAPI ClientIdentity_Release(IInternalUnknown * iface) +static ULONG WINAPI ClientIdentity_Release(IMultiQI * iface) { struct proxy_manager * This = (struct proxy_manager *)iface; ULONG refs = InterlockedDecrement(&This->refs); @@ -195,50 +199,205 @@ static ULONG WINAPI ClientIdentity_Release(IInternalUnknown * iface) return refs; } -static HRESULT WINAPI ClientIdentity_QueryInternalInterface(IInternalUnknown * iface, REFIID riid, void ** ppv) +static HRESULT WINAPI ClientIdentity_QueryMultipleInterfaces(IMultiQI *iface, ULONG cMQIs, MULTI_QI *pMQIs) { - FIXME("(%s, %p): stub!\n", debugstr_guid(riid), ppv); - return E_NOINTERFACE; + struct proxy_manager * This = (struct proxy_manager *)iface; + REMQIRESULT *qiresults = NULL; + ULONG nonlocal_mqis = 0; + ULONG i; + ULONG successful_mqis = 0; + IID *iids = HeapAlloc(GetProcessHeap(), 0, cMQIs * sizeof(*iids)); + /* mapping of RemQueryInterface index to QueryMultipleInterfaces index */ + ULONG *mapping = HeapAlloc(GetProcessHeap(), 0, cMQIs * sizeof(*mapping)); + + TRACE("cMQIs: %ld\n", cMQIs); + + /* try to get a local interface - this includes already active proxy + * interfaces and also interfaces exposed by the proxy manager */ + for (i = 0; i < cMQIs; i++) + { + TRACE("iid[%ld] = %s\n", i, debugstr_guid(pMQIs[i].pIID)); + pMQIs[i].hr = proxy_manager_query_local_interface(This, pMQIs[i].pIID, (void **)&pMQIs[i].pItf); + if (pMQIs[i].hr == S_OK) + successful_mqis++; + else + { + iids[nonlocal_mqis] = *pMQIs[i].pIID; + mapping[nonlocal_mqis] = i; + nonlocal_mqis++; + } + } + + TRACE("%ld interfaces not found locally\n", nonlocal_mqis); + + /* if we have more than one interface not found locally then we must try + * to query the remote object for it */ + if (nonlocal_mqis != 0) + { + IRemUnknown *remunk; + HRESULT hr; + IPID *ipid; + + /* get the ipid of the first entry */ + /* FIXME: should we implement ClientIdentity on the ifproxies instead + * of the proxy_manager so we use the correct ipid here? */ + ipid = &LIST_ENTRY(list_head(&This->interfaces), struct ifproxy, entry)->ipid; + + /* get IRemUnknown proxy so we can communicate with the remote object */ + hr = proxy_manager_get_remunknown(This, &remunk); + + if (hr == S_OK) + { + hr = IRemUnknown_RemQueryInterface(remunk, ipid, NORMALEXTREFS, + nonlocal_mqis, iids, &qiresults); + if (FAILED(hr)) + ERR("IRemUnknown_RemQueryInterface failed with error 0x%08lx\n", hr); + } + + /* IRemUnknown_RemQueryInterface can return S_FALSE if only some of + * the interfaces were returned */ + if (SUCCEEDED(hr)) + { + /* try to unmarshal each object returned to us */ + for (i = 0; i < nonlocal_mqis; i++) + { + ULONG index = mapping[i]; + HRESULT hrobj = qiresults[i].hResult; + if (hrobj == S_OK) + hrobj = unmarshal_object(&qiresults[i].std, This->parent, + pMQIs[index].pIID, + (void **)&pMQIs[index].pItf); + + if (hrobj == S_OK) + successful_mqis++; + else + ERR("Failed to get pointer to interface %s\n", debugstr_guid(pMQIs[index].pIID)); + pMQIs[index].hr = hrobj; + } + } + + /* free the memory allocated by the proxy */ + CoTaskMemFree(qiresults); + } + + TRACE("%ld/%ld successfully queried\n", successful_mqis, cMQIs); + + HeapFree(GetProcessHeap(), 0, iids); + HeapFree(GetProcessHeap(), 0, mapping); + + if (successful_mqis == cMQIs) + return S_OK; /* we got all requested interfaces */ + else if (successful_mqis == 0) + return E_NOINTERFACE; /* we didn't get any interfaces */ + else + return S_FALSE; /* we got some interfaces */ } -static const IInternalUnknownVtbl ClientIdentity_Vtbl = +static const IMultiQIVtbl ClientIdentity_Vtbl = { ClientIdentity_QueryInterface, ClientIdentity_AddRef, ClientIdentity_Release, - ClientIdentity_QueryInternalInterface + ClientIdentity_QueryMultipleInterfaces }; static HRESULT ifproxy_get_public_ref(struct ifproxy * This) { - /* FIXME: call IRemUnknown::RemAddRef if necessary */ - return S_OK; + HRESULT hr = S_OK; + /* FIXME: as this call could possibly be going over the network, we + * are going to spend a long time in this CS. We might want to replace + * this with a mutex */ + EnterCriticalSection(&This->parent->cs); + if (This->refs == 0) + { + IRemUnknown *remunk = NULL; + + TRACE("getting public ref for ifproxy %p\n", This); + + hr = proxy_manager_get_remunknown(This->parent, &remunk); + if (hr == S_OK) + { + HRESULT hrref; + REMINTERFACEREF rif; + rif.ipid = This->ipid; + rif.cPublicRefs = NORMALEXTREFS; + rif.cPrivateRefs = 0; + hr = IRemUnknown_RemAddRef(remunk, 1, &rif, &hrref); + if (hr == S_OK && hrref == S_OK) + This->refs += NORMALEXTREFS; + else + ERR("IRemUnknown_RemAddRef returned with 0x%08lx, hrref = 0x%08lx\n", hr, hrref); + } + } + LeaveCriticalSection(&This->parent->cs); + + return hr; } static HRESULT ifproxy_release_public_refs(struct ifproxy * This) { - /* FIXME: call IRemUnknown::RemRelease */ - return S_OK; + HRESULT hr = S_OK; + + /* FIXME: as this call could possibly be going over the network, we + * are going to spend a long time in this CS. We might want to replace + * this with a mutex */ + EnterCriticalSection(&This->parent->cs); + if (This->refs > 0) + { + IRemUnknown *remunk = NULL; + + TRACE("releasing %ld refs\n", This->refs); + + hr = proxy_manager_get_remunknown(This->parent, &remunk); + if (hr == S_OK) + { + REMINTERFACEREF rif; + rif.ipid = This->ipid; + rif.cPublicRefs = This->refs; + rif.cPrivateRefs = 0; + hr = IRemUnknown_RemRelease(remunk, 1, &rif); + if (hr == S_OK) + This->refs = 0; + else if (hr == RPC_E_DISCONNECTED) + WARN("couldn't release references because object was " + "disconnected: oxid = %s, oid = %s\n", + wine_dbgstr_longlong(This->parent->oxid), + wine_dbgstr_longlong(This->parent->oid)); + else + ERR("IRemUnknown_RemRelease failed with error 0x%08lx\n", hr); + } + } + LeaveCriticalSection(&This->parent->cs); + + return hr; } static void ifproxy_disconnect(struct ifproxy * This) { - IRpcProxyBuffer_Disconnect(This->proxy); + ifproxy_release_public_refs(This); + if (This->proxy) IRpcProxyBuffer_Disconnect(This->proxy); } static void ifproxy_destroy(struct ifproxy * This) { + TRACE("%p\n", This); + /* release public references to this object so that the stub can know * when to destroy itself */ ifproxy_release_public_refs(This); list_remove(&This->entry); - if (This->proxy) IRpcProxyBuffer_Release(This->proxy); + /* note: we don't call Release for This->proxy because its lifetime is + * controlled by the return value from ClientIdentity_Release, which this + * function is always called from */ + HeapFree(GetProcessHeap(), 0, This); } -static HRESULT proxy_manager_construct(APARTMENT * apt, OXID oxid, OID oid, IRpcChannelBuffer * channel, struct proxy_manager ** proxy_manager) +static HRESULT proxy_manager_construct( + APARTMENT * apt, ULONG sorflags, OXID oxid, OID oid, + IRpcChannelBuffer * channel, struct proxy_manager ** proxy_manager) { struct proxy_manager * This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); if (!This) return E_OUTOFMEMORY; @@ -257,19 +416,65 @@ static HRESULT proxy_manager_construct(APARTMENT * apt, OXID oxid, OID oid, IRpc This->oxid = oxid; This->oid = oid; - This->refs = 0; /* will be incremented on creation of first proxy */ + This->refs = 1; + /* the DCOM draft specification states that the SORF_NOPING flag is + * proxy manager specific, not ifproxy specific, so this implies that we + * should store the STDOBJREF flags in the proxy manager. */ + This->sorflags = sorflags; + + assert(channel); This->chan = channel; /* FIXME: we should take the binding strings and construct the channel in this function */ + /* we create the IRemUnknown proxy on demand */ + This->remunk = NULL; + EnterCriticalSection(&apt->cs); - list_add_head(&apt->proxies, &This->entry); + /* FIXME: we are dependent on the ordering in here to make sure a proxy's + * IRemUnknown proxy doesn't get destroyed before the regual proxy does + * because we need the IRemUnknown proxy during the destruction of the + * regular proxy. Ideally, we should maintain a separate list for the + * IRemUnknown proxies that need late destruction */ + list_add_tail(&apt->proxies, &This->entry); LeaveCriticalSection(&apt->cs); + TRACE("%p created for OXID %s, OID %s\n", This, + wine_dbgstr_longlong(oxid), wine_dbgstr_longlong(oid)); + *proxy_manager = This; return S_OK; } -static HRESULT proxy_manager_create_ifproxy(struct proxy_manager * This, IPID ipid, REFIID riid, ULONG cPublicRefs, struct ifproxy ** iif_out) +static HRESULT proxy_manager_query_local_interface(struct proxy_manager * This, REFIID riid, void ** ppv) +{ + HRESULT hr; + struct ifproxy * ifproxy; + + TRACE("%s\n", debugstr_guid(riid)); + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IMultiQI)) + { + *ppv = (void *)&This->lpVtbl; + IMultiQI_AddRef((IMultiQI *)&This->lpVtbl); + return S_OK; + } + + hr = proxy_manager_find_ifproxy(This, riid, &ifproxy); + if (hr == S_OK) + { + *ppv = ifproxy->iface; + IUnknown_AddRef((IUnknown *)*ppv); + return S_OK; + } + + *ppv = NULL; + return E_NOINTERFACE; +} + +static HRESULT proxy_manager_create_ifproxy( + struct proxy_manager * This, IPID ipid, REFIID riid, ULONG cPublicRefs, + struct ifproxy ** iif_out) { HRESULT hr; IPSFactoryBuffer * psfb; @@ -278,25 +483,42 @@ static HRESULT proxy_manager_create_ifproxy(struct proxy_manager * This, IPID ip list_init(&ifproxy->entry); + ifproxy->parent = This; ifproxy->ipid = ipid; ifproxy->iid = *riid; ifproxy->refs = cPublicRefs; ifproxy->proxy = NULL; - hr = get_facbuf_for_iid(riid, &psfb); - if (hr == S_OK) + /* the IUnknown interface is special because it does not have a + * proxy associated with the ifproxy as we handle IUnknown ourselves */ + if (IsEqualIID(riid, &IID_IUnknown)) { - /* important note: the outer unknown is set to the proxy manager. - * This ensures the COM identity rules are not violated, by having a - * one-to-one mapping of objects on the proxy side to objects on the - * stub side, no matter which interface you view the object through */ - hr = IPSFactoryBuffer_CreateProxy(psfb, (IUnknown *)&This->lpVtbl, riid, - &ifproxy->proxy, &ifproxy->iface); - IPSFactoryBuffer_Release(psfb); + ifproxy->iface = (void *)&This->lpVtbl; + hr = S_OK; } + else + { + hr = get_facbuf_for_iid(riid, &psfb); + if (hr == S_OK) + { + /* important note: the outer unknown is set to the proxy manager. + * This ensures the COM identity rules are not violated, by having a + * one-to-one mapping of objects on the proxy side to objects on the + * stub side, no matter which interface you view the object through */ + hr = IPSFactoryBuffer_CreateProxy(psfb, (IUnknown *)&This->lpVtbl, riid, + &ifproxy->proxy, &ifproxy->iface); + IPSFactoryBuffer_Release(psfb); + if (hr != S_OK) + ERR("Could not create proxy for interface %s, error 0x%08lx\n", + debugstr_guid(riid), hr); + } + else + ERR("Could not get IPSFactoryBuffer for interface %s, error 0x%08lx\n", + debugstr_guid(riid), hr); - if (hr == S_OK) - hr = IRpcProxyBuffer_Connect(ifproxy->proxy, This->chan); + if (hr == S_OK) + hr = IRpcProxyBuffer_Connect(ifproxy->proxy, This->chan); + } /* get at least one external reference to the object to keep it alive */ if (hr == S_OK) @@ -309,6 +531,8 @@ static HRESULT proxy_manager_create_ifproxy(struct proxy_manager * This, IPID ip LeaveCriticalSection(&This->cs); *iif_out = ifproxy; + TRACE("ifproxy %p created for IPID %s, interface %s with %lu public refs\n", + ifproxy, debugstr_guid(&ipid), debugstr_guid(riid), cPublicRefs); } else ifproxy_destroy(ifproxy); @@ -341,6 +565,9 @@ static void proxy_manager_disconnect(struct proxy_manager * This) { struct list * cursor; + TRACE("oxid = %s, oid = %s\n", wine_dbgstr_longlong(This->oxid), + wine_dbgstr_longlong(This->oid)); + EnterCriticalSection(&This->cs); LIST_FOR_EACH(cursor, &This->interfaces) @@ -352,13 +579,73 @@ static void proxy_manager_disconnect(struct proxy_manager * This) /* apartment is being destroyed so don't keep a pointer around to it */ This->parent = NULL; + /* FIXME: will this still be necessary if/when we use a real RPC + * channel? */ + IRpcChannelBuffer_Release(This->chan); + This->chan = NULL; + LeaveCriticalSection(&This->cs); } +static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnknown **remunk) +{ + HRESULT hr = S_OK; + + /* we don't want to try and unmarshal or use IRemUnknown if we don't want + * lifetime management */ + if (This->sorflags & SORFP_NOLIFETIMEMGMT) + return S_FALSE; + + EnterCriticalSection(&This->cs); + if (This->remunk) + /* already created - return existing object */ + *remunk = This->remunk; + else if (!This->parent) + /* disconnected - we can't create IRemUnknown */ + hr = S_FALSE; + else + { + STDOBJREF stdobjref; + /* Don't want IRemUnknown lifetime management as this is IRemUnknown! + * We also don't care about whether or not the stub is still alive */ + stdobjref.flags = SORFP_NOLIFETIMEMGMT | SORF_NOPING; + stdobjref.cPublicRefs = 1; + /* oxid of destination object */ + stdobjref.oxid = This->oxid; + /* FIXME: what should be used for the oid? The DCOM draft doesn't say */ + stdobjref.oid = (OID)-1; + /* FIXME: this is a hack around not having an OXID resolver yet - + * the OXID resolver should give us the IPID of the IRemUnknown + * interface */ + stdobjref.ipid.Data1 = 0xffffffff; + stdobjref.ipid.Data2 = 0xffff; + stdobjref.ipid.Data3 = 0xffff; + assert(sizeof(stdobjref.ipid.Data4) == sizeof(stdobjref.oxid)); + memcpy(&stdobjref.ipid.Data4, &stdobjref.oxid, sizeof(OXID)); + + /* do the unmarshal */ + hr = unmarshal_object(&stdobjref, This->parent, &IID_IRemUnknown, (void**)&This->remunk); + if (hr == S_OK) + *remunk = This->remunk; + } + LeaveCriticalSection(&This->cs); + + TRACE("got IRemUnknown* pointer %p, hr = 0x%08lx\n", *remunk, hr); + + return hr; +} + +/* destroys a proxy manager, freeing the memory it used. + * Note: this function should not be called from a list iteration in the + * apartment, due to the fact that it removes itself from the apartment and + * it could add a proxy to IRemUnknown into the apartment. */ static void proxy_manager_destroy(struct proxy_manager * This) { struct list * cursor; + TRACE("oxid = %s, oid = %s\n", wine_dbgstr_longlong(This->oxid), + wine_dbgstr_longlong(This->oid)); + if (This->parent) { EnterCriticalSection(&This->parent->cs); @@ -377,19 +664,23 @@ static void proxy_manager_destroy(struct proxy_manager * This) } /* destroy all of the interface proxies */ - while (!(cursor = list_head(&This->interfaces))) + while ((cursor = list_head(&This->interfaces))) { struct ifproxy * ifproxy = LIST_ENTRY(cursor, struct ifproxy, entry); ifproxy_destroy(ifproxy); } - IRpcChannelBuffer_Release(This->chan); + if (This->remunk) IRemUnknown_Release(This->remunk); + if (This->chan) IRpcChannelBuffer_Release(This->chan); DeleteCriticalSection(&This->cs); HeapFree(GetProcessHeap(), 0, This); } +/* finds the proxy manager corresponding to a given OXID and OID that has + * been unmarshaled in the specified apartment. The caller must release the + * reference to the proxy_manager when the object is no longer used. */ static BOOL find_proxy_manager(APARTMENT * apt, OXID oxid, OID oid, struct proxy_manager ** proxy_found) { BOOL found = FALSE; @@ -402,6 +693,7 @@ static BOOL find_proxy_manager(APARTMENT * apt, OXID oxid, OID oid, struct proxy if ((oxid == proxy->oxid) && (oid == proxy->oid)) { *proxy_found = proxy; + ClientIdentity_AddRef((IMultiQI *)&proxy->lpVtbl); found = TRUE; break; } @@ -475,10 +767,10 @@ StdMarshalImpl_GetUnmarshalClass( static HRESULT WINAPI StdMarshalImpl_GetMarshalSizeMax( LPMARSHAL iface, REFIID riid, void* pv, DWORD dwDestContext, - void* pvDestContext, DWORD mshlflags, DWORD* pSize -) { - *pSize = sizeof(wine_marshal_id)+sizeof(wine_marshal_data); - return S_OK; + void* pvDestContext, DWORD mshlflags, DWORD* pSize) +{ + *pSize = sizeof(STDOBJREF); + return S_OK; } static HRESULT WINAPI @@ -486,49 +778,32 @@ StdMarshalImpl_MarshalInterface( LPMARSHAL iface, IStream *pStm,REFIID riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags ) { - wine_marshal_id mid; - wine_marshal_data md; + STDOBJREF stdobjref; IUnknown *pUnk; ULONG res; HRESULT hres; - IRpcStubBuffer *stubbuffer; - IPSFactoryBuffer *psfacbuf; - BOOL tablemarshal; - struct stub_manager *manager; + APARTMENT *apt = COM_CurrentApt(); TRACE("(...,%s,...)\n",debugstr_guid(riid)); + if (!apt) + { + ERR("Apartment not initialized\n"); + return CO_E_NOTINITIALIZED; + } + start_apartment_listener_thread(); /* just to be sure we have one running. */ + start_apartment_remote_unknown(); - hres = get_facbuf_for_iid(riid,&psfacbuf); - if (hres) return hres; - - hres = IPSFactoryBuffer_CreateStub(psfacbuf,riid,pv,&stubbuffer); - IPSFactoryBuffer_Release(psfacbuf); - if (hres) { - FIXME("Failed to create an RpcStubBuffer from PSFactory for %s\n",debugstr_guid(riid)); - return hres; - } - - tablemarshal = ((mshlflags & MSHLFLAGS_TABLESTRONG) || (mshlflags & MSHLFLAGS_TABLEWEAK)); - if (tablemarshal) FIXME("table marshalling unimplemented\n"); - - /* now fill out the MID */ - mid.oxid = COM_CurrentApt()->oxid; - - IUnknown_QueryInterface((LPUNKNOWN)pv, riid, (LPVOID*)&pUnk); - - if ((manager = get_stub_manager_from_object(mid.oxid, pUnk))) + hres = IUnknown_QueryInterface((LPUNKNOWN)pv, riid, (LPVOID*)&pUnk); + if (hres != S_OK) { - mid.oid = manager->oid; - stub_manager_int_release(manager); + ERR("object doesn't expose interface %s, failing with error 0x%08lx\n", + debugstr_guid(riid), hres); + return E_NOINTERFACE; } - else - { - mid.oid = 0; /* will be set by register_ifstub */ - } - - hres = register_ifstub(&mid, riid, pUnk, stubbuffer, tablemarshal); + + hres = register_ifstub(apt, &stdobjref, riid, pUnk, mshlflags); IUnknown_Release(pUnk); @@ -538,101 +813,181 @@ StdMarshalImpl_MarshalInterface( return hres; } - hres = IStream_Write(pStm,&mid,sizeof(mid),&res); + hres = IStream_Write(pStm, &stdobjref, sizeof(stdobjref), &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; } +/* helper for StdMarshalImpl_UnmarshalInterface - does the unmarshaling with + * no questions asked about the rules surrounding same-apartment unmarshals + * and table marshaling */ +static HRESULT unmarshal_object(const STDOBJREF *stdobjref, APARTMENT *apt, REFIID riid, void **object) +{ + struct proxy_manager *proxy_manager = NULL; + HRESULT hr = S_OK; + + assert(apt); + + TRACE("stdobjref:\n\tflags = %04lx\n\tcPublicRefs = %ld\n\toxid = %s\n\toid = %s\n\tipid = %s\n", + stdobjref->flags, stdobjref->cPublicRefs, + wine_dbgstr_longlong(stdobjref->oxid), + wine_dbgstr_longlong(stdobjref->oid), + debugstr_guid(&stdobjref->ipid)); + + /* create an a new proxy manager if one doesn't already exist for the + * object */ + if (!find_proxy_manager(apt, stdobjref->oxid, stdobjref->oid, &proxy_manager)) + { + IRpcChannelBuffer *chanbuf; + wine_marshal_id mid; + + mid.oxid = stdobjref->oxid; + mid.oid = stdobjref->oid; + mid.ipid = stdobjref->ipid; + + hr = PIPE_GetNewPipeBuf(&mid,&chanbuf); + if (hr == S_OK) + hr = proxy_manager_construct(apt, stdobjref->flags, + stdobjref->oxid, stdobjref->oid, + chanbuf, &proxy_manager); + } + else + TRACE("proxy manager already created, using\n"); + + if (hr == S_OK) + { + struct ifproxy * ifproxy; + hr = proxy_manager_find_ifproxy(proxy_manager, riid, &ifproxy); + if (hr == E_NOINTERFACE) + hr = proxy_manager_create_ifproxy(proxy_manager, stdobjref->ipid, + riid, stdobjref->cPublicRefs, + &ifproxy); + + if (hr == S_OK) + { + /* FIXME: push this AddRef inside proxy_manager_find_ifproxy/create_ifproxy? */ + ClientIdentity_AddRef((IMultiQI*)&proxy_manager->lpVtbl); + *object = ifproxy->iface; + } + } + + /* release our reference to the proxy manager - the client/apartment + * will hold on to the remaining reference for us */ + if (proxy_manager) ClientIdentity_Release((IMultiQI*)&proxy_manager->lpVtbl); + + return hr; +} + static HRESULT WINAPI StdMarshalImpl_UnmarshalInterface(LPMARSHAL iface, IStream *pStm, REFIID riid, void **ppv) { struct stub_manager *stubmgr; - wine_marshal_id mid; - wine_marshal_data md; + STDOBJREF stdobjref; ULONG res; HRESULT hres; - IRpcChannelBuffer *chanbuf; - struct proxy_manager *proxy_manager; APARTMENT *apt = COM_CurrentApt(); + APARTMENT *stub_apt; TRACE("(...,%s,....)\n",debugstr_guid(riid)); - if (!apt) return CO_E_NOTINITIALIZED; + /* we need an apartment to unmarshal into */ + if (!apt) + { + ERR("Apartment not initialized\n"); + return CO_E_NOTINITIALIZED; + } - hres = IStream_Read(pStm,&mid,sizeof(mid),&res); - if (hres) return hres; - hres = IStream_Read(pStm,&md,sizeof(md),&res); + /* read STDOBJREF from wire */ + hres = IStream_Read(pStm, &stdobjref, sizeof(stdobjref), &res); if (hres) return hres; /* check if we're marshalling back to ourselves */ - /* FIXME: commented out until we can get the tests passing with it uncommented. */ - if (/*(apt->oxid == mid.oxid) &&*/ (stubmgr = get_stub_manager(mid.oxid, mid.oid))) + if ((apt->oxid == stdobjref.oxid) && (stubmgr = get_stub_manager(apt, stdobjref.oid))) { - TRACE("Unmarshalling object marshalled in same apartment for iid %s, returning original object %p\n", debugstr_guid(riid), stubmgr->object); + 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"); /* unref the ifstub. FIXME: only do this on success? */ - stub_manager_ext_release(stubmgr, 1); + if (!stub_manager_is_table_marshaled(stubmgr, &stdobjref.ipid)) + stub_manager_ext_release(stubmgr, 1); stub_manager_int_release(stubmgr); return hres; } - if (!find_proxy_manager(apt, mid.oxid, mid.oid, &proxy_manager)) + /* notify stub manager about unmarshal if process-local object. + * note: if the oxid is not found then we and native will quite happily + * ignore table marshaling and normal marshaling rules regarding number of + * unmarshals, etc, but if you abuse these rules then your proxy could end + * up returning RPC_E_DISCONNECTED. */ + if ((stub_apt = COM_ApartmentFromOXID(stdobjref.oxid, TRUE))) { - hres = PIPE_GetNewPipeBuf(&mid,&chanbuf); - if (hres == S_OK) - hres = proxy_manager_construct(apt, mid.oxid, mid.oid, chanbuf, &proxy_manager); + if ((stubmgr = get_stub_manager(stub_apt, stdobjref.oid))) + { + if (!stub_manager_notify_unmarshal(stubmgr, &stdobjref.ipid)) + hres = CO_E_OBJNOTCONNECTED; + + stub_manager_int_release(stubmgr); + } + else + { + WARN("Couldn't find object for OXID %s, OID %s, assuming disconnected\n", + wine_dbgstr_longlong(stdobjref.oxid), + wine_dbgstr_longlong(stdobjref.oid)); + hres = CO_E_OBJNOTCONNECTED; + } + + COM_ApartmentRelease(stub_apt); } + else + TRACE("Treating unmarshal from OXID %s as inter-process\n", + wine_dbgstr_longlong(stdobjref.oxid)); if (hres == S_OK) - { - struct ifproxy * ifproxy; - hres = proxy_manager_find_ifproxy(proxy_manager, riid, &ifproxy); - if (hres == S_OK) - IUnknown_AddRef((IUnknown *)ifproxy->iface); - else if (hres == E_NOINTERFACE) - hres = proxy_manager_create_ifproxy(proxy_manager, mid.ipid, riid, 1, &ifproxy); + hres = unmarshal_object(&stdobjref, apt, riid, ppv); - if (hres == S_OK) - *ppv = ifproxy->iface; /* AddRef'd above */ - } + if (hres) WARN("Failed with error 0x%08lx\n", hres); + else TRACE("Successfully created proxy %p\n", *ppv); return hres; } static HRESULT WINAPI StdMarshalImpl_ReleaseMarshalData(LPMARSHAL iface, IStream *pStm) { - wine_marshal_id mid; + STDOBJREF stdobjref; ULONG res; HRESULT hres; struct stub_manager *stubmgr; + APARTMENT *apt; TRACE("iface=%p, pStm=%p\n", iface, pStm); - hres = IStream_Read(pStm,&mid,sizeof(mid),&res); + hres = IStream_Read(pStm, &stdobjref, sizeof(stdobjref), &res); if (hres) return hres; - if (!(stubmgr = get_stub_manager(mid.oxid, mid.oid))) + if (!(apt = COM_ApartmentFromOXID(stdobjref.oxid, TRUE))) { - ERR("could not map MID to stub manager, oxid=%s, oid=%s\n", - wine_dbgstr_longlong(mid.oxid), wine_dbgstr_longlong(mid.oid)); + WARN("Could not map OXID %s to apartment object\n", + wine_dbgstr_longlong(stdobjref.oxid)); return RPC_E_INVALID_OBJREF; } - + + if (!(stubmgr = get_stub_manager(apt, stdobjref.oid))) + { + ERR("could not map MID to stub manager, oxid=%s, oid=%s\n", + wine_dbgstr_longlong(stdobjref.oxid), wine_dbgstr_longlong(stdobjref.oid)); + return RPC_E_INVALID_OBJREF; + } + + /* FIXME: don't release if table-weak and already unmarshaled an object */ + /* FIXME: this should also depend on stdobjref.cPublicRefs */ stub_manager_ext_release(stubmgr, 1); stub_manager_int_release(stubmgr); + COM_ApartmentRelease(apt); return S_OK; } @@ -744,7 +1099,7 @@ static HRESULT get_marshaler(REFIID riid, IUnknown *pUnk, DWORD dwDestContext, * The function leaves the stream pointer at the start of the data written * to the stream by the IMarshal* object. */ -static HRESULT get_unmarshaler_from_stream(IStream *stream, IMarshal **marshal) +static HRESULT get_unmarshaler_from_stream(IStream *stream, IMarshal **marshal, IID *iid) { HRESULT hr; ULONG res; @@ -765,6 +1120,8 @@ static HRESULT get_unmarshaler_from_stream(IStream *stream, IMarshal **marshal) return RPC_E_INVALID_OBJREF; } + if (iid) *iid = objref.iid; + /* FIXME: handler marshaling */ if (objref.flags & OBJREF_STANDARD) { @@ -978,6 +1335,7 @@ HRESULT WINAPI CoMarshalInterface(IStream *pStream, REFIID riid, IUnknown *pUnk, hr = E_UNEXPECTED; goto cleanup; } + objref.u_objref.u_custom.clsid = marshaler_clsid; objref.u_objref.u_custom.cbExtension = 0; objref.u_objref.u_custom.size = GlobalSize(hGlobal); /* write constant sized OR_CUSTOM data into stream */ @@ -1010,6 +1368,9 @@ cleanup: if (pMarshalStream && (objref.flags & OBJREF_CUSTOM)) IStream_Release(pMarshalStream); IMarshal_Release(pMarshal); + + TRACE("completed with hr 0x%08lx\n", hr); + return hr; } @@ -1035,21 +1396,39 @@ cleanup: */ HRESULT WINAPI CoUnmarshalInterface(IStream *pStream, REFIID riid, LPVOID *ppv) { - HRESULT hr; + HRESULT hr; LPMARSHAL pMarshal; + IID iid; + IUnknown *object; TRACE("(%p, %s, %p)\n", pStream, debugstr_guid(riid), ppv); - hr = get_unmarshaler_from_stream(pStream, &pMarshal); + hr = get_unmarshaler_from_stream(pStream, &pMarshal, &iid); if (hr != S_OK) return hr; /* call the helper object to do the actual unmarshaling */ - hr = IMarshal_UnmarshalInterface(pMarshal, pStream, riid, ppv); + hr = IMarshal_UnmarshalInterface(pMarshal, pStream, &iid, (LPVOID*)&object); if (hr) ERR("IMarshal::UnmarshalInterface failed, 0x%08lx\n", hr); + /* IID_NULL means use the interface ID of the marshaled object */ + if (!IsEqualIID(riid, &IID_NULL)) + iid = *riid; + + if (hr == S_OK) + { + hr = IUnknown_QueryInterface(object, &iid, ppv); + if (hr) + ERR("Couldn't query for interface %s, hr = 0x%08lx\n", + debugstr_guid(riid), hr); + IUnknown_Release(object); + } + IMarshal_Release(pMarshal); + + TRACE("completed with hr 0x%lx\n", hr); + return hr; } @@ -1083,7 +1462,7 @@ HRESULT WINAPI CoReleaseMarshalData(IStream *pStream) TRACE("(%p)\n", pStream); - hr = get_unmarshaler_from_stream(pStream, &pMarshal); + hr = get_unmarshaler_from_stream(pStream, &pMarshal, NULL); if (hr != S_OK) return hr; diff --git a/reactos/lib/ole32/ole2.c b/reactos/lib/ole32/ole2.c index 2a428123d62..cfa0b5aa8a5 100644 --- a/reactos/lib/ole32/ole2.c +++ b/reactos/lib/ole32/ole2.c @@ -48,6 +48,7 @@ #include "wine/wingdi16.h" #include "wine/winuser16.h" #include "ole32_main.h" +#include "compobj_private.h" #include "wine/debug.h" @@ -243,18 +244,6 @@ HRESULT WINAPI OleInitialize(LPVOID reserved) return hr; } -/****************************************************************************** - * CoGetCurrentProcess [COMPOBJ.34] - * CoGetCurrentProcess [OLE32.@] - * - * NOTES - * Is DWORD really the correct return type for this function? - */ -DWORD WINAPI CoGetCurrentProcess(void) -{ - return GetCurrentProcessId(); -} - /****************************************************************************** * OleUninitialize [OLE2.3] * OleUninitialize [OLE32.@] @@ -300,20 +289,6 @@ void WINAPI OleUninitialize(void) CoUninitialize(); } -/****************************************************************************** - * CoRegisterMessageFilter [OLE32.@] - */ -HRESULT WINAPI CoRegisterMessageFilter( - LPMESSAGEFILTER lpMessageFilter, /* [in] Pointer to interface */ - LPMESSAGEFILTER *lplpMessageFilter /* [out] Indirect pointer to prior instance if non-NULL */ -) { - FIXME("stub\n"); - if (lplpMessageFilter) { - *lplpMessageFilter = NULL; - } - return S_OK; -} - /****************************************************************************** * OleInitializeWOW [OLE32.@] */ @@ -2313,6 +2288,44 @@ HRESULT WINAPI OleCreate( return hres; } +/****************************************************************************** + * OleSetAutoConvert [OLE32.@] + */ +/* FIXME: convert to Unicode */ +HRESULT WINAPI OleSetAutoConvert(REFCLSID clsidOld, REFCLSID clsidNew) +{ + HKEY hkey = 0; + char buf[200], szClsidNew[200]; + HRESULT res = S_OK; + + TRACE("(%s,%s)\n", debugstr_guid(clsidOld), debugstr_guid(clsidNew)); + sprintf(buf,"CLSID\\");WINE_StringFromCLSID(clsidOld,&buf[6]); + WINE_StringFromCLSID(clsidNew, szClsidNew); + if (RegOpenKeyA(HKEY_CLASSES_ROOT,buf,&hkey)) + { + res = REGDB_E_CLASSNOTREG; + goto done; + } + if (RegSetValueA(hkey, "AutoConvertTo", REG_SZ, szClsidNew, strlen(szClsidNew)+1)) + { + res = REGDB_E_WRITEREGDB; + goto done; + } + +done: + if (hkey) RegCloseKey(hkey); + return res; +} + +/****************************************************************************** + * OleDoAutoConvert [OLE32.@] + */ +HRESULT WINAPI OleDoAutoConvert(IStorage *pStg, LPCLSID pClsidNew) +{ + FIXME("(%p,%p) : stub\n",pStg,pClsidNew); + return E_NOTIMPL; +} + /*********************************************************************** * OLE_FreeClipDataArray [internal] * diff --git a/reactos/lib/ole32/ole2stubs.c b/reactos/lib/ole32/ole2stubs.c index 2bcaabf689d..bb827871917 100644 --- a/reactos/lib/ole32/ole2stubs.c +++ b/reactos/lib/ole32/ole2stubs.c @@ -138,15 +138,6 @@ HRESULT WINAPI OleRegEnumFormatEtc ( return E_NOTIMPL; } -/*********************************************************************** - * CoIsOle1Class [OLE32.@] - */ -BOOL WINAPI CoIsOle1Class(REFCLSID clsid) -{ - FIXME("%s\n", debugstr_guid(clsid)); - return FALSE; -} - /*********************************************************************** * DllGetClassObject [OLE2.4] */ diff --git a/reactos/lib/ole32/ole32.spec b/reactos/lib/ole32/ole32.spec index ddc4827123c..3a8d3333260 100644 --- a/reactos/lib/ole32/ole32.spec +++ b/reactos/lib/ole32/ole32.spec @@ -7,7 +7,7 @@ @ stdcall CLSIDFromString(wstr ptr) @ stdcall CoAddRefServerProcess() @ stdcall CoBuildVersion() -@ stub CoCopyProxy #@ stdcall (ptr ptr) return 0,ERR_NOTIMPLEMENTED +@ stdcall CoCopyProxy(ptr ptr) @ stdcall CoCreateFreeThreadedMarshaler(ptr ptr) @ stdcall CoCreateGuid(ptr) @ stdcall CoCreateInstance(ptr ptr long ptr ptr) @@ -49,7 +49,7 @@ @ stdcall CoMarshalInterface(ptr ptr ptr long ptr long) @ stub CoQueryAuthenticationServices @ stub CoQueryClientBlanket -@ stub CoQueryProxyBlanket +@ stdcall CoQueryProxyBlanket(ptr ptr ptr ptr ptr ptr ptr ptr) @ stub CoQueryReleaseObject @ stub CoRegisterChannelHook @ stdcall CoRegisterClassObject(ptr ptr long long ptr) @@ -63,7 +63,7 @@ @ stub CoRevertToSelf #@ stdcall () return 0,ERR_NOTIMPLEMENTED @ stdcall CoRevokeClassObject(long) @ stdcall CoRevokeMallocSpy() -@ stub CoSetProxyBlanket #@ stdcall (ptr long long wstr long long ptr long) return 0,ERR_NOTIMPLEMENTED +@ stdcall CoSetProxyBlanket(ptr long long wstr long long ptr long) @ stdcall CoSetState(ptr) @ stub CoSwitchCallContext @ stdcall CoSuspendClassObjects() @@ -92,8 +92,8 @@ @ stdcall DllDebugObjectRPCHook(long ptr) @ stdcall -private DllGetClassObject (ptr ptr ptr) OLE32_DllGetClassObject @ stub DllGetClassObjectWOW -@ stdcall -private DllRegisterServer() OLE32_DllRegisterServer -@ stdcall -private DllUnregisterServer() OLE32_DllUnregisterServer +@ stdcall -private DllRegisterServer() +@ stdcall -private DllUnregisterServer() @ stdcall DoDragDrop(ptr ptr long ptr) @ stub EnableHookObject @ stdcall FreePropVariantArray(long ptr) diff --git a/reactos/lib/ole32/ole32_main.c b/reactos/lib/ole32/ole32_main.c index 1dfa6e7647f..a258d19a2b8 100644 --- a/reactos/lib/ole32/ole32_main.c +++ b/reactos/lib/ole32/ole32_main.c @@ -121,7 +121,6 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad) switch(fdwReason) { case DLL_PROCESS_ATTACH: - DisableThreadLibraryCalls(hinstDLL); OLE32_hInstance = hinstDLL; COMPOBJ_InitProcess(); if (TRACE_ON(ole)) CoRegisterMallocSpy((LPVOID)-1); @@ -132,6 +131,10 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad) COMPOBJ_UninitProcess(); OLE32_hInstance = 0; break; + + case DLL_THREAD_DETACH: + COM_TlsDestroy(); + break; } return TRUE; } diff --git a/reactos/lib/ole32/ole32_main.h b/reactos/lib/ole32/ole32_main.h index a3f5644ccdf..7ea3386052c 100644 --- a/reactos/lib/ole32/ole32_main.h +++ b/reactos/lib/ole32/ole32_main.h @@ -29,5 +29,6 @@ extern HINSTANCE OLE32_hInstance; void COMPOBJ_InitProcess( void ); void COMPOBJ_UninitProcess( void ); +void COM_TlsDestroy( void ); #endif /* __WINE_OLE32_MAIN_H */ diff --git a/reactos/lib/ole32/oleproxy.c b/reactos/lib/ole32/oleproxy.c index 5532988d533..951d1660dee 100644 --- a/reactos/lib/ole32/oleproxy.c +++ b/reactos/lib/ole32/oleproxy.c @@ -2,6 +2,7 @@ * OLE32 proxy/stub handler * * Copyright 2002 Marcus Meissner + * Copyright 2001 Ove Kåven, TransGaming Technologies * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -176,6 +177,7 @@ CFStub_Invoke( return hres; } hres = CoMarshalInterface(pStm,&iid,ppv,0,NULL,0); + IUnknown_Release((IUnknown*)ppv); if (hres) { FIXME("CoMarshalInterface failed, %lx!\n",hres); msg->cbBuffer = 0; @@ -266,8 +268,8 @@ CFStub_Construct(LPRPCSTUBBUFFER *ppv) { * the refcount. */ typedef struct _CFProxy { - IClassFactoryVtbl *lpvtbl_cf; - IRpcProxyBufferVtbl *lpvtbl_proxy; + const IClassFactoryVtbl *lpvtbl_cf; + const IRpcProxyBufferVtbl *lpvtbl_proxy; DWORD ref; IRpcChannelBuffer *chanbuf; @@ -318,7 +320,7 @@ static void WINAPI IRpcProxyBufferImpl_Disconnect(LPRPCPROXYBUFFER iface) { static HRESULT WINAPI CFProxy_QueryInterface(LPCLASSFACTORY iface,REFIID riid, LPVOID *ppv) { - ICOM_THIS_MULTI(CFProxy,lpvtbl_proxy,iface); + ICOM_THIS_MULTI(CFProxy,lpvtbl_cf,iface); if (This->outer_unknown) return IUnknown_QueryInterface(This->outer_unknown, riid, ppv); *ppv = NULL; if (IsEqualIID(&IID_IClassFactory,riid) || IsEqualIID(&IID_IUnknown,riid)) { @@ -444,13 +446,459 @@ CFProxy_Construct(IUnknown *pUnkOuter, LPVOID *ppv,LPVOID *ppProxy) { cf->lpvtbl_cf = &cfproxyvt; cf->lpvtbl_proxy = &pspbvtbl; - /* 1 reference for the proxy... */ + /* only one reference for the proxy buffer */ cf->ref = 1; cf->outer_unknown = pUnkOuter; *ppv = &(cf->lpvtbl_cf); *ppProxy = &(cf->lpvtbl_proxy); - /* ...and 1 for the object */ - IUnknown_AddRef((IUnknown *)*ppv); + return S_OK; +} + + +/********************* IRemUnknown Proxy/Stub ********************************/ + +typedef struct +{ + const IRpcStubBufferVtbl *lpVtbl; + ULONG refs; + IRemUnknown *iface; +} RemUnkStub; + +static HRESULT WINAPI RemUnkStub_QueryInterface(LPRPCSTUBBUFFER iface, + REFIID riid, + LPVOID *obj) +{ + RemUnkStub *This = (RemUnkStub *)iface; + TRACE("(%p)->QueryInterface(%s,%p)\n",This,debugstr_guid(riid),obj); + if (IsEqualGUID(&IID_IUnknown,riid) || + IsEqualGUID(&IID_IRpcStubBuffer,riid)) { + *obj = This; + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI RemUnkStub_AddRef(LPRPCSTUBBUFFER iface) +{ + RemUnkStub *This = (RemUnkStub *)iface; + TRACE("(%p)->AddRef()\n",This); + return InterlockedIncrement(&This->refs); +} + +static ULONG WINAPI RemUnkStub_Release(LPRPCSTUBBUFFER iface) +{ + RemUnkStub *This = (RemUnkStub *)iface; + ULONG refs; + TRACE("(%p)->Release()\n",This); + refs = InterlockedDecrement(&This->refs); + if (!refs) + HeapFree(GetProcessHeap(), 0, This); + return refs; +} + +static HRESULT WINAPI RemUnkStub_Connect(LPRPCSTUBBUFFER iface, + LPUNKNOWN lpUnkServer) +{ + RemUnkStub *This = (RemUnkStub *)iface; + TRACE("(%p)->Connect(%p)\n",This,lpUnkServer); + This->iface = (IRemUnknown*)lpUnkServer; + IRemUnknown_AddRef(This->iface); + return S_OK; +} + +static void WINAPI RemUnkStub_Disconnect(LPRPCSTUBBUFFER iface) +{ + RemUnkStub *This = (RemUnkStub *)iface; + TRACE("(%p)->Disconnect()\n",This); + IUnknown_Release(This->iface); + This->iface = NULL; +} + +static HRESULT WINAPI RemUnkStub_Invoke(LPRPCSTUBBUFFER iface, + PRPCOLEMESSAGE pMsg, + LPRPCCHANNELBUFFER pChannel) +{ + RemUnkStub *This = (RemUnkStub *)iface; + ULONG iMethod = pMsg->iMethod; + LPBYTE buf = pMsg->Buffer; + HRESULT hr = RPC_E_INVALIDMETHOD; + + TRACE("(%p)->Invoke(%p,%p) method %ld\n", This, pMsg, pChannel, iMethod); + switch (iMethod) + { + case 3: /* RemQueryInterface */ + { + IPID ipid; + ULONG cRefs; + USHORT cIids; + IID *iids; + REMQIRESULT *pQIResults = NULL; + + /* in */ + memcpy(&ipid, buf, sizeof(ipid)); + buf += sizeof(ipid); + memcpy(&cRefs, buf, sizeof(cRefs)); + buf += sizeof(cRefs); + memcpy(&cIids, buf, sizeof(cIids)); + buf += sizeof(cIids); + iids = (IID *)buf; + + hr = IRemUnknown_RemQueryInterface(This->iface, &ipid, cRefs, cIids, iids, &pQIResults); + + /* out */ + pMsg->cbBuffer = cIids * sizeof(REMQIRESULT); + if (pMsg->Buffer) + pMsg->Buffer = HeapReAlloc(GetProcessHeap(), 0, pMsg->Buffer, pMsg->cbBuffer); + else + pMsg->Buffer = HeapAlloc(GetProcessHeap(), 0, pMsg->cbBuffer); + buf = pMsg->Buffer; + /* FIXME: pQIResults is a unique pointer so pQIResults can be NULL! */ + memcpy(buf, pQIResults, cIids * sizeof(REMQIRESULT)); + + break; + } + case 4: /* RemAddRef */ + { + USHORT cIids; + REMINTERFACEREF *ir; + HRESULT *pResults; + + /* in */ + memcpy(&cIids, buf, sizeof(USHORT)); + buf += sizeof(USHORT); + ir = (REMINTERFACEREF*)buf; + pResults = CoTaskMemAlloc(cIids * sizeof(HRESULT)); + if (!pResults) return E_OUTOFMEMORY; + + hr = IRemUnknown_RemAddRef(This->iface, cIids, ir, pResults); + + /* out */ + pMsg->cbBuffer = cIids * sizeof(HRESULT); + if (pMsg->Buffer) + pMsg->Buffer = HeapReAlloc(GetProcessHeap(), 0, pMsg->Buffer, pMsg->cbBuffer); + else + pMsg->Buffer = HeapAlloc(GetProcessHeap(), 0, pMsg->cbBuffer); + buf = pMsg->Buffer; + memcpy(buf, pResults, cIids * sizeof(HRESULT)); + + CoTaskMemFree(pResults); + + break; + } + case 5: /* RemRelease */ + { + USHORT cIids; + REMINTERFACEREF *ir; + + /* in */ + memcpy(&cIids, buf, sizeof(USHORT)); + buf += sizeof(USHORT); + ir = (REMINTERFACEREF*)buf; + + hr = IRemUnknown_RemRelease(This->iface, cIids, ir); + + /* out */ + pMsg->cbBuffer = 0; + break; + } + } + return hr; +} + +static LPRPCSTUBBUFFER WINAPI RemUnkStub_IsIIDSupported(LPRPCSTUBBUFFER iface, + REFIID riid) +{ + RemUnkStub *This = (RemUnkStub *)iface; + TRACE("(%p)->IsIIDSupported(%s)\n", This, debugstr_guid(riid)); + return IsEqualGUID(&IID_IRemUnknown, riid) ? iface : NULL; +} + +static ULONG WINAPI RemUnkStub_CountRefs(LPRPCSTUBBUFFER iface) +{ + RemUnkStub *This = (RemUnkStub *)iface; + FIXME("(%p)->CountRefs()\n", This); + return 1; +} + +static HRESULT WINAPI RemUnkStub_DebugServerQueryInterface(LPRPCSTUBBUFFER iface, + LPVOID *ppv) +{ + RemUnkStub *This = (RemUnkStub *)iface; + FIXME("(%p)->DebugServerQueryInterface(%p)\n",This,ppv); + return E_NOINTERFACE; +} + +static void WINAPI RemUnkStub_DebugServerRelease(LPRPCSTUBBUFFER iface, + LPVOID pv) +{ + RemUnkStub *This = (RemUnkStub *)iface; + FIXME("(%p)->DebugServerRelease(%p)\n", This, pv); +} + +static const IRpcStubBufferVtbl RemUnkStub_VTable = +{ + RemUnkStub_QueryInterface, + RemUnkStub_AddRef, + RemUnkStub_Release, + RemUnkStub_Connect, + RemUnkStub_Disconnect, + RemUnkStub_Invoke, + RemUnkStub_IsIIDSupported, + RemUnkStub_CountRefs, + RemUnkStub_DebugServerQueryInterface, + RemUnkStub_DebugServerRelease +}; + +static HRESULT RemUnkStub_Construct(IRpcStubBuffer **ppStub) +{ + RemUnkStub *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + if (!This) return E_OUTOFMEMORY; + This->lpVtbl = &RemUnkStub_VTable; + This->refs = 0; + This->iface = NULL; + *ppStub = (IRpcStubBuffer*)This; + return S_OK; +} + + +typedef struct _RemUnkProxy { + const IRemUnknownVtbl *lpvtbl_remunk; + const IRpcProxyBufferVtbl *lpvtbl_proxy; + DWORD refs; + + IRpcChannelBuffer *chan; + IUnknown *outer_unknown; +} RemUnkProxy; + +static HRESULT WINAPI RemUnkProxy_QueryInterface(LPREMUNKNOWN iface, REFIID riid, void **ppv) +{ + RemUnkProxy *This = (RemUnkProxy *)iface; + if (This->outer_unknown) + return IUnknown_QueryInterface(This->outer_unknown, riid, ppv); + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IRemUnknown)) + { + IRemUnknown_AddRef(iface); + *ppv = (LPVOID)iface; + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI RemUnkProxy_AddRef(LPREMUNKNOWN iface) +{ + RemUnkProxy *This = (RemUnkProxy *)iface; + + TRACE("(%p)->AddRef()\n",This); + return InterlockedIncrement(&This->refs); +} + +static ULONG WINAPI RemUnkProxy_Release(LPREMUNKNOWN iface) +{ + RemUnkProxy *This = (RemUnkProxy *)iface; + ULONG refs; + + TRACE("(%p)->Release()\n",This); + if (This->outer_unknown) + refs = IUnknown_Release(This->outer_unknown); + else + refs = InterlockedDecrement(&This->refs); + + if (!refs) { + if (This->chan) IRpcChannelBuffer_Release(This->chan); + HeapFree(GetProcessHeap(),0,This); + } + return refs; +} + +static HRESULT WINAPI RemUnkProxy_RemQueryInterface(LPREMUNKNOWN iface, + REFIPID ripid, + ULONG cRefs, + USHORT cIids, + IID* iids, + REMQIRESULT** ppQIResults) +{ + RemUnkProxy *This = (RemUnkProxy *)iface; + RPCOLEMESSAGE msg; + HRESULT hr = S_OK; + ULONG status; + + TRACE("(%p)->(%s,%ld,%d,%p,%p)\n",This, + debugstr_guid(ripid),cRefs,cIids,iids,ppQIResults); + + *ppQIResults = NULL; + memset(&msg, 0, sizeof(msg)); + msg.iMethod = 3; + msg.cbBuffer = sizeof(IPID) + sizeof(ULONG) + + sizeof(USHORT) + cIids*sizeof(IID); + hr = IRpcChannelBuffer_GetBuffer(This->chan, &msg, &IID_IRemUnknown); + if (SUCCEEDED(hr)) { + LPBYTE buf = msg.Buffer; + memcpy(buf, ripid, sizeof(IPID)); + buf += sizeof(IPID); + memcpy(buf, &cRefs, sizeof(ULONG)); + buf += sizeof(ULONG); + memcpy(buf, &cIids, sizeof(USHORT)); + buf += sizeof(USHORT); + memcpy(buf, iids, cIids*sizeof(IID)); + + hr = IRpcChannelBuffer_SendReceive(This->chan, &msg, &status); + + if (SUCCEEDED(hr)) { + buf = msg.Buffer; + *ppQIResults = CoTaskMemAlloc(cIids*sizeof(REMQIRESULT)); + memcpy(*ppQIResults, buf, cIids*sizeof(REMQIRESULT)); + } + + IRpcChannelBuffer_FreeBuffer(This->chan, &msg); + } + + return hr; +} + +static HRESULT WINAPI RemUnkProxy_RemAddRef(LPREMUNKNOWN iface, + USHORT cInterfaceRefs, + REMINTERFACEREF* InterfaceRefs, + HRESULT* pResults) +{ + RemUnkProxy *This = (RemUnkProxy *)iface; + RPCOLEMESSAGE msg; + HRESULT hr = S_OK; + ULONG status; + + TRACE("(%p)->(%d,%p,%p)\n",This, + cInterfaceRefs,InterfaceRefs,pResults); + + memset(&msg, 0, sizeof(msg)); + msg.iMethod = 4; + msg.cbBuffer = sizeof(USHORT) + cInterfaceRefs*sizeof(REMINTERFACEREF); + hr = IRpcChannelBuffer_GetBuffer(This->chan, &msg, &IID_IRemUnknown); + if (SUCCEEDED(hr)) { + LPBYTE buf = msg.Buffer; + memcpy(buf, &cInterfaceRefs, sizeof(USHORT)); + buf += sizeof(USHORT); + memcpy(buf, InterfaceRefs, cInterfaceRefs*sizeof(REMINTERFACEREF)); + + hr = IRpcChannelBuffer_SendReceive(This->chan, &msg, &status); + + if (SUCCEEDED(hr)) { + buf = msg.Buffer; + memcpy(pResults, buf, cInterfaceRefs*sizeof(HRESULT)); + } + + IRpcChannelBuffer_FreeBuffer(This->chan, &msg); + } + + return hr; +} + +static HRESULT WINAPI RemUnkProxy_RemRelease(LPREMUNKNOWN iface, + USHORT cInterfaceRefs, + REMINTERFACEREF* InterfaceRefs) +{ + RemUnkProxy *This = (RemUnkProxy *)iface; + RPCOLEMESSAGE msg; + HRESULT hr = S_OK; + ULONG status; + + TRACE("(%p)->(%d,%p)\n",This, + cInterfaceRefs,InterfaceRefs); + + memset(&msg, 0, sizeof(msg)); + msg.iMethod = 5; + msg.cbBuffer = sizeof(USHORT) + cInterfaceRefs*sizeof(REMINTERFACEREF); + hr = IRpcChannelBuffer_GetBuffer(This->chan, &msg, &IID_IRemUnknown); + if (SUCCEEDED(hr)) { + LPBYTE buf = msg.Buffer; + memcpy(buf, &cInterfaceRefs, sizeof(USHORT)); + buf += sizeof(USHORT); + memcpy(buf, InterfaceRefs, cInterfaceRefs*sizeof(REMINTERFACEREF)); + + hr = IRpcChannelBuffer_SendReceive(This->chan, &msg, &status); + + IRpcChannelBuffer_FreeBuffer(This->chan, &msg); + } + + return hr; +} + +static const IRemUnknownVtbl RemUnkProxy_VTable = +{ + RemUnkProxy_QueryInterface, + RemUnkProxy_AddRef, + RemUnkProxy_Release, + RemUnkProxy_RemQueryInterface, + RemUnkProxy_RemAddRef, + RemUnkProxy_RemRelease +}; + + +static HRESULT WINAPI RURpcProxyBufferImpl_QueryInterface(LPRPCPROXYBUFFER iface,REFIID riid,LPVOID *ppv) { + *ppv = NULL; + if (IsEqualIID(riid,&IID_IRpcProxyBuffer)||IsEqualIID(riid,&IID_IUnknown)) { + IRpcProxyBuffer_AddRef(iface); + *ppv = (LPVOID)iface; + return S_OK; + } + FIXME("(%s), no interface.\n",debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI RURpcProxyBufferImpl_AddRef(LPRPCPROXYBUFFER iface) { + ICOM_THIS_MULTI(RemUnkProxy,lpvtbl_proxy,iface); + return InterlockedIncrement(&This->refs); +} + +static ULONG WINAPI RURpcProxyBufferImpl_Release(LPRPCPROXYBUFFER iface) { + ICOM_THIS_MULTI(RemUnkProxy,lpvtbl_proxy,iface); + ULONG ref = InterlockedDecrement(&This->refs); + + if (!ref) { + IRpcChannelBuffer_Release(This->chan);This->chan = NULL; + HeapFree(GetProcessHeap(),0,This); + } + return ref; +} + +static HRESULT WINAPI RURpcProxyBufferImpl_Connect(LPRPCPROXYBUFFER iface,IRpcChannelBuffer* pRpcChannelBuffer) { + ICOM_THIS_MULTI(RemUnkProxy,lpvtbl_proxy,iface); + + This->chan = pRpcChannelBuffer; + IRpcChannelBuffer_AddRef(This->chan); + return S_OK; +} +static void WINAPI RURpcProxyBufferImpl_Disconnect(LPRPCPROXYBUFFER iface) { + ICOM_THIS_MULTI(RemUnkProxy,lpvtbl_proxy,iface); + if (This->chan) { + IRpcChannelBuffer_Release(This->chan); + This->chan = NULL; + } +} + + +static const IRpcProxyBufferVtbl RURpcProxyBuffer_VTable = { + RURpcProxyBufferImpl_QueryInterface, + RURpcProxyBufferImpl_AddRef, + RURpcProxyBufferImpl_Release, + RURpcProxyBufferImpl_Connect, + RURpcProxyBufferImpl_Disconnect +}; + +static HRESULT +RemUnkProxy_Construct(IUnknown *pUnkOuter, LPVOID *ppv,LPVOID *ppProxy) { + RemUnkProxy *This; + + This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*This)); + if (!This) + return E_OUTOFMEMORY; + + This->lpvtbl_remunk = &RemUnkProxy_VTable; + This->lpvtbl_proxy = &RURpcProxyBuffer_VTable; + /* only one reference for the proxy buffer */ + This->refs = 1; + This->outer_unknown = pUnkOuter; + *ppv = &(This->lpvtbl_remunk); + *ppProxy = &(This->lpvtbl_proxy); return S_OK; } @@ -475,10 +923,10 @@ PSFacBuf_CreateProxy( LPPSFACTORYBUFFER iface, IUnknown* pUnkOuter, REFIID riid, IRpcProxyBuffer **ppProxy, LPVOID *ppv ) { - if (IsEqualIID(&IID_IClassFactory,riid) || - IsEqualIID(&IID_IUnknown,riid) - ) + if (IsEqualIID(&IID_IClassFactory,riid)) return CFProxy_Construct(pUnkOuter, ppv,(LPVOID*)ppProxy); + else if (IsEqualIID(&IID_IRemUnknown,riid)) + return RemUnkProxy_Construct(pUnkOuter, ppv,(LPVOID*)ppProxy); FIXME("proxying not implemented for (%s) yet!\n",debugstr_guid(riid)); return E_FAIL; } @@ -492,13 +940,17 @@ PSFacBuf_CreateStub( TRACE("(%s,%p,%p)\n",debugstr_guid(riid),pUnkServer,ppStub); - if (IsEqualIID(&IID_IClassFactory,riid) || - IsEqualIID(&IID_IUnknown,riid) - ) { + if (IsEqualIID(&IID_IClassFactory, riid) || + IsEqualIID(&IID_IUnknown, riid) /* FIXME: fixup stub manager and remove this*/) { hres = CFStub_Construct(ppStub); if (!hres) IRpcStubBuffer_Connect((*ppStub),pUnkServer); return hres; + } else if (IsEqualIID(&IID_IRemUnknown,riid)) { + hres = RemUnkStub_Construct(ppStub); + if (!hres) + IRpcStubBuffer_Connect((*ppStub),pUnkServer); + return hres; } FIXME("stubbing not implemented for (%s) yet!\n",debugstr_guid(riid)); return E_FAIL; diff --git a/reactos/lib/ole32/regsvr.c b/reactos/lib/ole32/regsvr.c index 5f6a699b3ec..c4083c9824d 100644 --- a/reactos/lib/ole32/regsvr.c +++ b/reactos/lib/ole32/regsvr.c @@ -28,7 +28,9 @@ #include "winuser.h" #include "winreg.h" #include "winerror.h" +#include "objbase.h" +#include "compobj_private.h" #include "ole2.h" #include "olectl.h" @@ -448,396 +450,27 @@ static struct regsvr_coclass const coclass_list[] = { * interface list */ -/* FIXME: perhaps the interfaces that are proxied by another dll - * should be registered in that dll? Or maybe the choice of proxy is - * arbitrary at this point? */ - -static GUID const CLSID_PSFactoryBuffer_ole2disp = { - 0x00020420, 0x0000, 0x0000, {0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46} }; - -static GUID const CLSID_PSFactoryBuffer_oleaut32 = { - 0xB196B286, 0xBAB4, 0x101A, {0xB6,0x9C,0x00,0xAA,0x00,0x34,0x1D,0x07} }; - -/* FIXME: these interfaces should be defined in ocidl.idl */ - -static IID const IID_IFontEventsDisp = { - 0x4EF6100A, 0xAF88, 0x11D0, {0x98,0x46,0x00,0xC0,0x4F,0xC2,0x99,0x93} }; - -static IID const IID_IProvideMultipleClassInfo = { - 0xA7ABA9C1, 0x8983, 0x11CF, {0x8F,0x20,0x00,0x80,0x5F,0x2C,0xD0,0x64} }; +#define INTERFACE_ENTRY(interface, base, clsid32, clsid16) { &IID_##interface, #interface, base, sizeof(interface##Vtbl)/sizeof(void*), clsid16, clsid32 } +#define STD_INTERFACE_ENTRY(interface) INTERFACE_ENTRY(interface, NULL, &CLSID_PSFactoryBuffer, NULL) static struct regsvr_interface const interface_list[] = { - { &IID_IUnknown, - "IUnknown", - NULL, - 3, - NULL, - &CLSID_PSFactoryBuffer - }, - { &IID_IClassFactory, - "IClassFactory", - NULL, - 5, - NULL, - &CLSID_PSFactoryBuffer - }, - { &IID_IStorage, - "IStorage", - NULL, - 18, - NULL, - &CLSID_PSFactoryBuffer - }, - { &IID_IStream, - "IStream", - NULL, - 14, - NULL, - &CLSID_PSFactoryBuffer - }, - { &IID_IPersistStorage, - "IPersistStorage", - &IID_IPersist, - 10, - NULL, - &CLSID_PSFactoryBuffer - }, - { &IID_IDataObject, - "IDataObject", - NULL, - 12, - NULL, - &CLSID_PSFactoryBuffer - }, - { &IID_IAdviseSink, - "IAdviseSink", - NULL, - 8, - NULL, - &CLSID_PSFactoryBuffer - }, - { &IID_IOleObject, - "IOleObject", - NULL, - 24, - NULL, - &CLSID_PSFactoryBuffer - }, - { &IID_IOleClientSite, - "IOleClientSite", - NULL, - 9, - NULL, - &CLSID_PSFactoryBuffer - }, - { &IID_IDispatch, - "IDispatch", - NULL, - 7, - &CLSID_PSFactoryBuffer_ole2disp, - &CLSID_PSFactoryBuffer_ole2disp - }, - { &IID_ITypeLib2, - "ITypeLib2", - NULL, - 16, - NULL, - &CLSID_PSFactoryBuffer_ole2disp - }, - { &IID_ITypeInfo2, - "ITypeInfo2", - NULL, - 32, - NULL, - &CLSID_PSFactoryBuffer_ole2disp - }, - { &IID_IPropertyPage2, - "IPropertyPage2", - NULL, - 15, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IErrorInfo, - "IErrorInfo", - NULL, - 8, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_ICreateErrorInfo, - "ICreateErrorInfo", - NULL, - 8, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IPersistPropertyBag2, - "IPersistPropertyBag2", - NULL, - 8, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IPropertyBag2, - "IPropertyBag2", - NULL, - 8, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IErrorLog, - "IErrorLog", - NULL, - 4, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IPerPropertyBrowsing, - "IPerPropertyBrowsing", - NULL, - 7, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IPersistPropertyBag, - "IPersistPropertyBag", - NULL, - 7, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IAdviseSinkEx, - "IAdviseSinkEx", - NULL, - 9, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IFontEventsDisp, - "IFontEventsDisp", - NULL, - 7, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IPropertyBag, - "IPropertyBag", - NULL, - 5, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IPointerInactive, - "IPointerInactive", - NULL, - 6, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_ISimpleFrameSite, - "ISimpleFrameSite", - NULL, - 5, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IPicture, - "IPicture", - NULL, - 17, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IPictureDisp, - "IPictureDisp", - NULL, - 7, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IPersistStreamInit, - "IPersistStreamInit", - NULL, - 9, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IOleUndoUnit, - "IOleUndoUnit", - NULL, - 7, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IPropertyNotifySink, - "IPropertyNotifySink", - NULL, - 5, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IOleInPlaceSiteEx, - "IOleInPlaceSiteEx", - NULL, - 18, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IOleParentUndoUnit, - "IOleParentUndoUnit", - NULL, - 12, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IProvideClassInfo2, - "IProvideClassInfo2", - NULL, - 5, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IProvideMultipleClassInfo, - "IProvideMultipleClassInfo", - NULL, - 7, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IProvideClassInfo, - "IProvideClassInfo", - NULL, - 4, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IConnectionPointContainer, - "IConnectionPointContainer", - NULL, - 5, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IEnumConnectionPoints, - "IEnumConnectionPoints", - NULL, - 7, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IConnectionPoint, - "IConnectionPoint", - NULL, - 8, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IEnumConnections, - "IEnumConnections", - NULL, - 7, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IOleControl, - "IOleControl", - NULL, - 7, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IOleControlSite, - "IOleControlSite", - NULL, - 10, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_ISpecifyPropertyPages, - "ISpecifyPropertyPages", - NULL, - 4, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IPropertyPageSite, - "IPropertyPageSite", - NULL, - 7, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IPropertyPage, - "IPropertyPage", - NULL, - 14, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IClassFactory2, - "IClassFactory2", - NULL, - 8, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IEnumOleUndoUnits, - "IEnumOleUndoUnits", - NULL, - 7, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IPersistMemory, - "IPersistMemory", - NULL, - 9, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IFont, - "IFont", - NULL, - 27, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IFontDisp, - "IFontDisp", - NULL, - 7, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IQuickActivate, - "IQuickActivate", - NULL, - 6, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IOleUndoManager, - "IOleUndoManager", - NULL, - 15, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, - { &IID_IObjectWithSite, - "IObjectWithSite", - NULL, - 5, - NULL, - &CLSID_PSFactoryBuffer_oleaut32 - }, + STD_INTERFACE_ENTRY(IUnknown), + STD_INTERFACE_ENTRY(IClassFactory), + STD_INTERFACE_ENTRY(IStorage), + STD_INTERFACE_ENTRY(IStream ), + STD_INTERFACE_ENTRY(IPersistStorage), + STD_INTERFACE_ENTRY(IDataObject), + STD_INTERFACE_ENTRY(IAdviseSink), + STD_INTERFACE_ENTRY(IOleObject), + STD_INTERFACE_ENTRY(IOleClientSite), + STD_INTERFACE_ENTRY(IRemUnknown), { NULL } /* list terminator */ }; /*********************************************************************** * DllRegisterServer (OLE32.@) */ -HRESULT WINAPI OLE32_DllRegisterServer() +HRESULT WINAPI DllRegisterServer() { HRESULT hr; @@ -852,7 +485,7 @@ HRESULT WINAPI OLE32_DllRegisterServer() /*********************************************************************** * DllUnregisterServer (OLE32.@) */ -HRESULT WINAPI OLE32_DllUnregisterServer() +HRESULT WINAPI DllUnregisterServer() { HRESULT hr; diff --git a/reactos/lib/ole32/rpc.c b/reactos/lib/ole32/rpc.c index 158016fab20..e7262045a32 100644 --- a/reactos/lib/ole32/rpc.c +++ b/reactos/lib/ole32/rpc.c @@ -1,7 +1,8 @@ /* * (Local) RPC Stuff * - * Copyright 2002 Marcus Meissner + * Copyright 2002 Marcus Meissner + * Copyright 2005 Mike Hearn, 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 @@ -52,26 +53,22 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole); #define OLESTUBMGR PIPEPREF"WINE_OLE_StubMgr" #define REQTYPE_REQUEST 0 -typedef struct _wine_rpc_request_header { +#define REQTYPE_RESPONSE 1 + +struct request_header +{ DWORD reqid; - wine_marshal_id mid; + IPID ipid; DWORD iMethod; DWORD cbBuffer; -} wine_rpc_request_header; +}; -#define REQTYPE_RESPONSE 1 -typedef struct _wine_rpc_response_header { +struct response_header +{ DWORD reqid; DWORD cbBuffer; DWORD retval; -} wine_rpc_response_header; - -/* used when shutting down a pipe, e.g. at the end of a process */ -#define REQTYPE_DISCONNECT 2 -typedef struct _wine_rpc_disconnect_header { - DWORD reqid; - wine_marshal_id mid; /* mid of stub to delete */ -} wine_rpc_disconnect_header; +}; #define REQSTATE_START 0 @@ -83,22 +80,25 @@ typedef struct _wine_rpc_disconnect_header { #define REQSTATE_RESP_GOT 6 #define REQSTATE_DONE 6 -typedef struct _wine_rpc_request { +struct rpc +{ int state; HANDLE hPipe; /* temp copy of handle */ - wine_rpc_request_header reqh; - wine_rpc_response_header resph; + struct request_header reqh; + struct response_header resph; LPBYTE Buffer; -} wine_rpc_request; +}; -static wine_rpc_request **reqs = NULL; +/* fixme: this should have a lock */ +static struct rpc **reqs = NULL; static int nrofreqs = 0; /* This pipe is _thread_ based, each thread which talks to a remote - * apartment (mid) has its own pipe. The same structure is used both + * apartment (oxid) has its own pipe. The same structure is used both * for outgoing and incoming RPCs. */ -typedef struct _wine_pipe { +struct pipe +{ wine_marshal_id mid; /* target mid */ DWORD tid; /* thread which owns this pipe */ HANDLE hPipe; @@ -108,60 +108,43 @@ typedef struct _wine_pipe { CRITICAL_SECTION crit; APARTMENT *apt; /* apartment of the marshalling thread for the stub dispatch case */ -} wine_pipe; - -#define MAX_WINE_PIPES 256 - -static wine_pipe pipes[MAX_WINE_PIPES]; -static int nrofpipes = 0; +}; typedef struct _PipeBuf { - IRpcChannelBufferVtbl *lpVtbl; - DWORD ref; + IRpcChannelBufferVtbl *lpVtbl; + DWORD ref; - wine_marshal_id mid; + wine_marshal_id mid; + HANDLE pipe; } PipeBuf; -static HRESULT WINAPI -read_pipe(HANDLE hf, LPVOID ptr, DWORD size) { + + +/* some helper functions */ + +static HRESULT WINAPI read_pipe(HANDLE hf, LPVOID ptr, DWORD size) +{ DWORD res; - if (!ReadFile(hf,ptr,size,&res,NULL)) { - FIXME("Failed to read from %p, le is %ld\n",hf,GetLastError()); - return E_FAIL; + + if (!ReadFile(hf,ptr,size,&res,NULL)) + { + ERR("Failed to read from %p, le is %ld\n",hf,GetLastError()); + return E_FAIL; } - if (res!=size) { - FIXME("Read only %ld of %ld bytes from %p.\n",res,size,hf); - return E_FAIL; + + if (res != size) + { + if (!res) + { + WARN("%p disconnected\n", hf); + return RPC_E_DISCONNECTED; + } + ERR("Read only %ld of %ld bytes from %p.\n",res,size,hf); + return E_FAIL; } return S_OK; } -static void -drs(LPCSTR where) { -#if 0 - static int nrofreaders = 0; - - int i, states[10]; - - memset(states,0,sizeof(states)); - for (i=nrofreqs;i--;) - states[reqs[i]->state]++; - FIXME("%lx/%s/%d: rq %d, w %d, rg %d, rsq %d, rsg %d, d %d\n", - GetCurrentProcessId(), - where, - nrofreaders, - states[REQSTATE_REQ_QUEUED], - states[REQSTATE_REQ_WAITING_FOR_REPLY], - states[REQSTATE_REQ_GOT], - states[REQSTATE_RESP_QUEUED], - states[REQSTATE_RESP_GOT], - states[REQSTATE_DONE] - ); -#endif - - return ; -} - static HRESULT WINAPI write_pipe(HANDLE hf, LPVOID ptr, DWORD size) { DWORD res; @@ -176,106 +159,77 @@ write_pipe(HANDLE hf, LPVOID ptr, DWORD size) { return S_OK; } -static DWORD WINAPI stub_dispatch_thread(LPVOID); - -static HRESULT -PIPE_RegisterPipe(wine_marshal_id *mid, HANDLE hPipe, BOOL startreader) { - int i; - char pipefn[100]; - - for (i=0;ioxid) - return S_OK; - if (nrofpipes + 1 >= MAX_WINE_PIPES) - { - FIXME("Out of pipes, please increase MAX_WINE_PIPES\n"); - return E_OUTOFMEMORY; - } - sprintf(pipefn,OLESTUBMGR"_%08lx%08lx",(DWORD)(mid->oxid >> 32),(DWORD)mid->oxid); - memcpy(&(pipes[nrofpipes].mid),mid,sizeof(*mid)); - pipes[nrofpipes].hPipe = hPipe; - pipes[nrofpipes].apt = COM_CurrentApt(); - assert( pipes[nrofpipes].apt ); - InitializeCriticalSection(&(pipes[nrofpipes].crit)); - nrofpipes++; - if (startreader) { - pipes[nrofpipes-1].hThread = CreateThread(NULL,0,stub_dispatch_thread,(LPVOID)(pipes+(nrofpipes-1)),0,&(pipes[nrofpipes-1].tid)); - } else { - pipes[nrofpipes-1].tid = GetCurrentThreadId(); - } - return S_OK; +static HANDLE dupe_handle(HANDLE h) +{ + HANDLE h2; + + if (!DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(), + &h2, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + ERR("could not duplicate handle: %ld\n", GetLastError()); + return INVALID_HANDLE_VALUE; + } + + return h2; } -static HANDLE -PIPE_FindByMID(wine_marshal_id *mid) { - int i; - for (i=0;ioxid) && - (GetCurrentThreadId()==pipes[i].tid) - ) - return pipes[i].hPipe; - return INVALID_HANDLE_VALUE; -} -static wine_pipe* -PIPE_GetFromMID(wine_marshal_id *mid) { - int i; - for (i=0;ioxid) && - (GetCurrentThreadId()==pipes[i].tid) - ) - return pipes+i; - } - return NULL; -} -static HRESULT -RPC_GetRequest(wine_rpc_request **req) { - static int reqid = 0xdeadbeef; + +static DWORD WINAPI client_dispatch_thread(LPVOID); + +/* FIXME: this all needs to be made thread safe */ +static HRESULT RPC_GetRequest(struct rpc **req) +{ + static int reqid = 0; int i; - for (i=0;istate == REQSTATE_DONE) { - reqs[i]->reqh.reqid = reqid++; - reqs[i]->resph.reqid = reqs[i]->reqh.reqid; - reqs[i]->hPipe = INVALID_HANDLE_VALUE; - *req = reqs[i]; - reqs[i]->state = REQSTATE_START; - return S_OK; - } + /* try to reuse */ + for (i = 0; i < nrofreqs; i++) + { + if (reqs[i]->state == REQSTATE_DONE) + { + TRACE("reusing reqs[%d]\n", i); + + reqs[i]->reqh.reqid = reqid++; + reqs[i]->resph.reqid = reqs[i]->reqh.reqid; + reqs[i]->hPipe = INVALID_HANDLE_VALUE; + reqs[i]->state = REQSTATE_START; + *req = reqs[i]; + return S_OK; + } } - /* create new */ + + TRACE("creating new struct rpc (request)\n"); + if (reqs) - reqs = (wine_rpc_request**)HeapReAlloc( + reqs = (struct rpc**)HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, reqs, - sizeof(wine_rpc_request*)*(nrofreqs+1) + sizeof(struct rpc*)*(nrofreqs+1) ); else - reqs = (wine_rpc_request**)HeapAlloc( + reqs = (struct rpc**)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, - sizeof(wine_rpc_request*) + sizeof(struct rpc*) ); - if (!reqs) - return E_OUTOFMEMORY; - reqs[nrofreqs] = (wine_rpc_request*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(wine_rpc_request)); + + if (!reqs) return E_OUTOFMEMORY; + + reqs[nrofreqs] = (struct rpc*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(struct rpc)); reqs[nrofreqs]->reqh.reqid = reqid++; reqs[nrofreqs]->resph.reqid = reqs[nrofreqs]->reqh.reqid; reqs[nrofreqs]->hPipe = INVALID_HANDLE_VALUE; - *req = reqs[nrofreqs]; reqs[nrofreqs]->state = REQSTATE_START; + *req = reqs[nrofreqs]; + nrofreqs++; + return S_OK; } -static void -RPC_FreeRequest(wine_rpc_request *req) { - req->state = REQSTATE_DONE; /* Just reuse slot. */ - return; -} - static HRESULT WINAPI PipeBuf_QueryInterface( LPRPCCHANNELBUFFER iface,REFIID riid,LPVOID *ppv @@ -299,33 +253,19 @@ static ULONG WINAPI PipeBuf_Release(LPRPCCHANNELBUFFER iface) { PipeBuf *This = (PipeBuf *)iface; ULONG ref; - wine_rpc_disconnect_header header; - HANDLE pipe; - DWORD reqtype = REQTYPE_DISCONNECT; ref = InterlockedDecrement(&This->ref); if (ref) return ref; - memcpy(&header.mid, &This->mid, sizeof(wine_marshal_id)); - - pipe = PIPE_FindByMID(&This->mid); - - write_pipe(pipe, &reqtype, sizeof(reqtype)); - write_pipe(pipe, &header, sizeof(wine_rpc_disconnect_header)); - - TRACE("written disconnect packet\n"); - + CloseHandle(This->pipe); HeapFree(GetProcessHeap(),0,This); return 0; } static HRESULT WINAPI -PipeBuf_GetBuffer( - LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,REFIID riid -) { - /*PipeBuf *This = (PipeBuf *)iface;*/ - +PipeBuf_GetBuffer(LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,REFIID riid) +{ TRACE("(%p,%s)\n",msg,debugstr_guid(riid)); /* probably reuses IID in real. */ if (msg->cbBuffer && (msg->Buffer == NULL)) @@ -334,17 +274,15 @@ PipeBuf_GetBuffer( } static HRESULT -COM_InvokeAndRpcSend(wine_rpc_request *req) { +COM_InvokeAndRpcSend(struct rpc *req) { IRpcStubBuffer *stub; RPCOLEMESSAGE msg; HRESULT hres; DWORD reqtype; - if (!(stub = mid_to_stubbuffer(&(req->reqh.mid)))) - { - ERR("Stub not found?\n"); - return E_FAIL; - } + if (!(stub = ipid_to_stubbuffer(&(req->reqh.ipid)))) + /* ipid_to_stubbuffer will already have logged the error */ + return RPC_E_DISCONNECTED; IUnknown_AddRef(stub); msg.Buffer = req->Buffer; @@ -364,25 +302,19 @@ COM_InvokeAndRpcSend(wine_rpc_request *req) { hres = write_pipe(req->hPipe,req->Buffer,req->resph.cbBuffer); if (hres) return hres; req->state = REQSTATE_DONE; - drs("invoke"); return S_OK; } -static HRESULT COM_RpcReceive(wine_pipe *xpipe); +static HRESULT process_incoming_rpc(HANDLE pipe); -static HRESULT -RPC_QueueRequestAndWait(wine_rpc_request *req) { - int i; - wine_rpc_request *xreq; - HRESULT hres; - DWORD reqtype; - wine_pipe *xpipe = PIPE_GetFromMID(&(req->reqh.mid)); +static HRESULT RPC_QueueRequestAndWait(struct rpc *req, HANDLE pipe) +{ + int i; + struct rpc *xreq; + HRESULT hres; + DWORD reqtype; - if (!xpipe) { - FIXME("no pipe found.\n"); - return E_POINTER; - } - req->hPipe = xpipe->hPipe; + req->hPipe = pipe; req->state = REQSTATE_REQ_WAITING_FOR_REPLY; reqtype = REQTYPE_REQUEST; hres = write_pipe(req->hPipe,&reqtype,sizeof(reqtype)); @@ -395,7 +327,7 @@ RPC_QueueRequestAndWait(wine_rpc_request *req) { /* This loop is about allowing re-entrancy. While waiting for the * response to one RPC we may receive a request starting another. */ while (!hres) { - hres = COM_RpcReceive(xpipe); + hres = process_incoming_rpc(pipe); if (hres) break; for (i=0;imid.oxid == COM_CurrentApt()->oxid) { ERR("Need to call directly!\n"); @@ -432,37 +361,44 @@ PipeBuf_SendReceive( if (hres) return hres; req->reqh.iMethod = msg->iMethod; req->reqh.cbBuffer = msg->cbBuffer; - memcpy(&(req->reqh.mid),&(This->mid),sizeof(This->mid)); + req->reqh.ipid = This->mid.ipid; req->Buffer = msg->Buffer; - hres = RPC_QueueRequestAndWait(req); - if (hres) { - RPC_FreeRequest(req); + TRACE(" -> rpc ->\n"); + hres = RPC_QueueRequestAndWait(req, This->pipe); + TRACE(" <- response <-\n"); + if (hres) + { + req->state = REQSTATE_DONE; return hres; } - msg->cbBuffer = req->resph.cbBuffer; - msg->Buffer = req->Buffer; - *status = req->resph.retval; - RPC_FreeRequest(req); + + msg->cbBuffer = req->resph.cbBuffer; + msg->Buffer = req->Buffer; + *status = req->resph.retval; + req->state = REQSTATE_DONE; + return S_OK; } static HRESULT WINAPI -PipeBuf_FreeBuffer(LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg) { - FIXME("(%p), stub!\n",msg); - return E_FAIL; +PipeBuf_FreeBuffer(LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg) +{ + TRACE("(%p)\n",msg); + HeapFree(GetProcessHeap(), 0, msg->Buffer); + return S_OK; } static HRESULT WINAPI -PipeBuf_GetDestCtx( - LPRPCCHANNELBUFFER iface,DWORD* pdwDestContext,void** ppvDestContext -) { +PipeBuf_GetDestCtx(LPRPCCHANNELBUFFER iface,DWORD* pdwDestContext,void** ppvDestContext) +{ FIXME("(%p,%p), stub!\n",pdwDestContext,ppvDestContext); return E_FAIL; } static HRESULT WINAPI -PipeBuf_IsConnected(LPRPCCHANNELBUFFER iface) { +PipeBuf_IsConnected(LPRPCCHANNELBUFFER iface) +{ FIXME("(), stub!\n"); return S_OK; } @@ -478,96 +414,108 @@ static IRpcChannelBufferVtbl pipebufvt = { PipeBuf_IsConnected }; -HRESULT -PIPE_GetNewPipeBuf(wine_marshal_id *mid, IRpcChannelBuffer **pipebuf) { - wine_marshal_id ourid; - DWORD res; - HANDLE hPipe; - HRESULT hres; - PipeBuf *pbuf; +/* returns a pipebuf for proxies */ +HRESULT PIPE_GetNewPipeBuf(wine_marshal_id *mid, IRpcChannelBuffer **pipebuf) +{ + wine_marshal_id ourid; + HANDLE handle; + PipeBuf *pbuf; + char pipefn[200]; - hPipe = PIPE_FindByMID(mid); - if (hPipe == INVALID_HANDLE_VALUE) { - char pipefn[200]; - sprintf(pipefn,OLESTUBMGR"_%08lx%08lx",(DWORD)(mid->oxid >> 32),(DWORD)mid->oxid); - hPipe = CreateFileA( - pipefn, - GENERIC_READ|GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - 0, - 0 - ); - if (hPipe == INVALID_HANDLE_VALUE) { - FIXME("Could not open named pipe %s, le is %lx\n",pipefn,GetLastError()); - return E_FAIL; - } - hres = PIPE_RegisterPipe(mid, hPipe, FALSE); - if (hres) return hres; - memset(&ourid,0,sizeof(ourid)); - ourid.oxid = COM_CurrentApt()->oxid; - if (!WriteFile(hPipe,&ourid,sizeof(ourid),&res,NULL)||(res!=sizeof(ourid))) { - ERR("Failed writing startup mid!\n"); - return E_FAIL; - } - } - pbuf = (PipeBuf*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(PipeBuf)); - pbuf->lpVtbl = &pipebufvt; - pbuf->ref = 1; - memcpy(&(pbuf->mid),mid,sizeof(*mid)); - *pipebuf = (IRpcChannelBuffer*)pbuf; - return S_OK; + /* connect to the apartment listener thread */ + sprintf(pipefn,OLESTUBMGR"_%08lx%08lx",(DWORD)(mid->oxid >> 32),(DWORD)mid->oxid); + + TRACE("proxy pipe: connecting to apartment listener thread: %s\n", pipefn); + + while (TRUE) + { + BOOL ret = WaitNamedPipeA(pipefn, NMPWAIT_USE_DEFAULT_WAIT); + if (!ret) + { + ERR("Could not open named pipe %s, error %ld\n", pipefn, GetLastError()); + return RPC_E_SERVER_DIED; + } + + handle = CreateFileA(pipefn, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, 0); + + if (handle == INVALID_HANDLE_VALUE) + { + if (GetLastError() == ERROR_PIPE_BUSY) continue; + + ERR("Could not open named pipe %s, error %ld\n", pipefn, GetLastError()); + return RPC_E_SERVER_DIED; + } + + break; + } + + memset(&ourid,0,sizeof(ourid)); + ourid.oxid = COM_CurrentApt()->oxid; + + TRACE("constructing new pipebuf for proxy\n"); + + pbuf = (PipeBuf*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(PipeBuf)); + pbuf->lpVtbl = &pipebufvt; + pbuf->ref = 1; + memcpy(&(pbuf->mid),mid,sizeof(*mid)); + pbuf->pipe = dupe_handle(handle); + + *pipebuf = (IRpcChannelBuffer*)pbuf; + + return S_OK; } static HRESULT -create_server(REFCLSID rclsid) { - static const WCHAR embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 }; - HKEY key; - char buf[200]; - HRESULT hres = E_UNEXPECTED; - char xclsid[80]; - WCHAR exe[MAX_PATH+1]; - DWORD exelen = sizeof(exe); - WCHAR command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)]; - STARTUPINFOW sinfo; - PROCESS_INFORMATION pinfo; +create_server(REFCLSID rclsid) +{ + static const WCHAR embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 }; + HKEY key; + char buf[200]; + HRESULT hres = E_UNEXPECTED; + char xclsid[80]; + WCHAR exe[MAX_PATH+1]; + DWORD exelen = sizeof(exe); + WCHAR command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)]; + STARTUPINFOW sinfo; + PROCESS_INFORMATION pinfo; - WINE_StringFromCLSID((LPCLSID)rclsid,xclsid); + WINE_StringFromCLSID((LPCLSID)rclsid,xclsid); - sprintf(buf,"CLSID\\%s\\LocalServer32",xclsid); - hres = RegOpenKeyExA(HKEY_CLASSES_ROOT, buf, 0, KEY_READ, &key); + sprintf(buf,"CLSID\\%s\\LocalServer32",xclsid); + hres = RegOpenKeyExA(HKEY_CLASSES_ROOT, buf, 0, KEY_READ, &key); - if (hres != ERROR_SUCCESS) { - WARN("CLSID %s not registered as LocalServer32\n", xclsid); - return REGDB_E_READREGDB; /* Probably */ - } + if (hres != ERROR_SUCCESS) { + WARN("CLSID %s not registered as LocalServer32\n", xclsid); + return REGDB_E_READREGDB; /* Probably */ + } - memset(exe,0,sizeof(exe)); - hres= RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)exe, &exelen); - RegCloseKey(key); - if (hres) { - WARN("No default value for LocalServer32 key\n"); - return REGDB_E_CLASSNOTREG; /* FIXME: check retval */ - } + memset(exe,0,sizeof(exe)); + hres= RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)exe, &exelen); + RegCloseKey(key); + if (hres) { + WARN("No default value for LocalServer32 key\n"); + return REGDB_E_CLASSNOTREG; /* FIXME: check retval */ + } - memset(&sinfo,0,sizeof(sinfo)); - sinfo.cb = sizeof(sinfo); + memset(&sinfo,0,sizeof(sinfo)); + sinfo.cb = sizeof(sinfo); - /* EXE servers are started with the -Embedding switch. MSDN also claims /Embedding is used, - 9x does -Embedding, perhaps an 9x/NT difference? */ + /* EXE servers are started with the -Embedding switch. MSDN also claims /Embedding is used, + * 9x does -Embedding, perhaps an 9x/NT difference? + */ - strcpyW(command, exe); - strcatW(command, embedding); + strcpyW(command, exe); + strcatW(command, embedding); - TRACE("activating local server '%s' for %s\n", debugstr_w(command), xclsid); + TRACE("activating local server '%s' for %s\n", debugstr_w(command), xclsid); - if (!CreateProcessW(exe, command, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) { - WARN("failed to run local server %s\n", debugstr_w(exe)); - return E_FAIL; - } + if (!CreateProcessW(exe, command, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) { + WARN("failed to run local server %s\n", debugstr_w(exe)); + return E_FAIL; + } - return S_OK; + return S_OK; } /* @@ -577,7 +525,7 @@ static DWORD start_local_service(LPCWSTR name, DWORD num, LPWSTR *params) { SC_HANDLE handle, hsvc; - DWORD r = ERROR_FUNCTION_FAILED; + DWORD r = ERROR_FUNCTION_FAILED; TRACE("Starting service %s %ld params\n", debugstr_w(name), num); @@ -591,7 +539,7 @@ start_local_service(LPCWSTR name, DWORD num, LPWSTR *params) r = ERROR_SUCCESS; else r = GetLastError(); - if (r==ERROR_SERVICE_ALREADY_RUNNING) + if (r == ERROR_SERVICE_ALREADY_RUNNING) r = ERROR_SUCCESS; CloseServiceHandle(hsvc); } @@ -619,10 +567,8 @@ create_local_service(REFCLSID rclsid) static const WCHAR szClsId[] = { 'C','L','S','I','D','\\',0 }; static const WCHAR szAppId[] = { 'A','p','p','I','d',0 }; static const WCHAR szAppIdKey[] = { 'A','p','p','I','d','\\',0 }; - static const WCHAR szLocalService[] = { - 'L','o','c','a','l','S','e','r','v','i','c','e',0 }; - static const WCHAR szServiceParams[] = { - 'S','e','r','v','i','c','e','P','a','r','a','m','s',0}; + static const WCHAR szLocalService[] = { 'L','o','c','a','l','S','e','r','v','i','c','e',0 }; + static const WCHAR szServiceParams[] = {'S','e','r','v','i','c','e','P','a','r','a','m','s',0}; HKEY hkey; LONG r; DWORD type, sz; @@ -680,145 +626,124 @@ create_local_service(REFCLSID rclsid) } /* http://msdn.microsoft.com/library/en-us/dnmsj99/html/com0199.asp, Figure 4 */ -HRESULT create_marshalled_proxy(REFCLSID rclsid, REFIID iid, LPVOID *ppv) { - HRESULT hres; - HANDLE hPipe; - char pipefn[200]; - DWORD res,bufferlen; - char marshalbuffer[200]; - IStream *pStm; - LARGE_INTEGER seekto; - ULARGE_INTEGER newpos; - int tries = 0; -#define MAXTRIES 10000 +HRESULT create_marshalled_proxy(REFCLSID rclsid, REFIID iid, LPVOID *ppv) +{ + HRESULT hres; + HANDLE hPipe; + char pipefn[200]; + DWORD res, bufferlen; + char marshalbuffer[200]; + IStream *pStm; + LARGE_INTEGER seekto; + ULARGE_INTEGER newpos; + int tries = 0; + + static const int MAXTRIES = 10000; - TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid)); + TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid)); - strcpy(pipefn,PIPEPREF); - WINE_StringFromCLSID(rclsid,pipefn+strlen(PIPEPREF)); + strcpy(pipefn,PIPEPREF); + WINE_StringFromCLSID(rclsid,pipefn+strlen(PIPEPREF)); - while (tries++=MAXTRIES) - return E_NOINTERFACE; - hres = CreateStreamOnHGlobal(0,TRUE,&pStm); - if (hres) return hres; - hres = IStream_Write(pStm,marshalbuffer,bufferlen,&res); - if (hres) goto out; - seekto.u.LowPart = 0;seekto.u.HighPart = 0; - hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos); - TRACE("unmarshalling classfactory\n"); - hres = CoUnmarshalInterface(pStm,&IID_IClassFactory,ppv); -out: - IStream_Release(pStm); - return hres; -} - - -static void WINAPI -PIPE_StartRequestThread(HANDLE xhPipe) { - wine_marshal_id remoteid; - HRESULT hres; - - hres = read_pipe(xhPipe,&remoteid,sizeof(remoteid)); - if (hres) { - ERR("Failed to read remote mid!\n"); - return; + WaitNamedPipeA( pipefn, NMPWAIT_WAIT_FOREVER ); + hPipe = CreateFileA(pipefn, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + if (hPipe == INVALID_HANDLE_VALUE) { + if (tries == 1) { + if ( (hres = create_server(rclsid)) && + (hres = create_local_service(rclsid)) ) + return hres; + Sleep(1000); + } else { + WARN("Connecting to %s, no response yet, retrying: le is %lx\n",pipefn,GetLastError()); + Sleep(1000); + } + continue; + } + bufferlen = 0; + if (!ReadFile(hPipe,marshalbuffer,sizeof(marshalbuffer),&bufferlen,NULL)) { + FIXME("Failed to read marshal id from classfactory of %s.\n",debugstr_guid(rclsid)); + Sleep(1000); + continue; + } + TRACE("read marshal id from pipe\n"); + CloseHandle(hPipe); + break; } - PIPE_RegisterPipe(&remoteid,xhPipe, TRUE); + + if (tries >= MAXTRIES) + return E_NOINTERFACE; + + hres = CreateStreamOnHGlobal(0,TRUE,&pStm); + if (hres) return hres; + hres = IStream_Write(pStm,marshalbuffer,bufferlen,&res); + if (hres) goto out; + seekto.u.LowPart = 0;seekto.u.HighPart = 0; + hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos); + + TRACE("unmarshalling classfactory\n"); + hres = CoUnmarshalInterface(pStm,&IID_IClassFactory,ppv); +out: + IStream_Release(pStm); + return hres; } -static HRESULT -COM_RpcReceive(wine_pipe *xpipe) { - DWORD reqtype; - HRESULT hres = S_OK; - HANDLE xhPipe = xpipe->hPipe; - /*FIXME("%lx %d reading reqtype\n",GetCurrentProcessId(),xhPipe);*/ - hres = read_pipe(xhPipe,&reqtype,sizeof(reqtype)); - if (hres) goto end; - EnterCriticalSection(&(xpipe->crit)); - /*FIXME("%lx got reqtype %ld\n",GetCurrentProcessId(),reqtype);*/ +/* this reads an RPC from the given pipe and places it in the global reqs array */ +static HRESULT process_incoming_rpc(HANDLE pipe) +{ + DWORD reqtype; + HRESULT hres = S_OK; - if (reqtype == REQTYPE_DISCONNECT) { /* only received by servers */ - wine_rpc_disconnect_header header; - struct stub_manager *stubmgr; + hres = read_pipe(pipe,&reqtype,sizeof(reqtype)); + if (hres) return hres; - hres = read_pipe(xhPipe, &header, sizeof(header)); - if (hres) { - ERR("could not read disconnect header\n"); - goto end; - } + /* only received by servers */ + if (reqtype == REQTYPE_REQUEST) + { + struct rpc *xreq; - TRACE("read disconnect header\n"); - - 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; - } - - stub_manager_ext_release(stubmgr, 1); - - stub_manager_int_release(stubmgr); - - goto end; - } else if (reqtype == REQTYPE_REQUEST) { - wine_rpc_request *xreq; RPC_GetRequest(&xreq); - xreq->hPipe = xhPipe; - hres = read_pipe(xhPipe,&(xreq->reqh),sizeof(xreq->reqh)); - if (hres) goto end; + + xreq->hPipe = pipe; + hres = read_pipe(pipe,&(xreq->reqh),sizeof(xreq->reqh)); + if (hres) + { + xreq->state = REQSTATE_DONE; + return hres; + } + xreq->resph.reqid = xreq->reqh.reqid; xreq->Buffer = HeapAlloc(GetProcessHeap(),0, xreq->reqh.cbBuffer); - hres = read_pipe(xhPipe,xreq->Buffer,xreq->reqh.cbBuffer); + hres = read_pipe(pipe,xreq->Buffer,xreq->reqh.cbBuffer); if (hres) goto end; + + TRACE("received RPC for IPID %s\n", debugstr_guid(&xreq->reqh.ipid)); + xreq->state = REQSTATE_REQ_GOT; goto end; - } else if (reqtype == REQTYPE_RESPONSE) { - wine_rpc_response_header resph; + } + else if (reqtype == REQTYPE_RESPONSE) + { + struct response_header resph; int i; - hres = read_pipe(xhPipe,&resph,sizeof(resph)); + hres = read_pipe(pipe,&resph,sizeof(resph)); if (hres) goto end; - for (i=nrofreqs;i--;) { - wine_rpc_request *xreq = reqs[i]; + + TRACE("read RPC response\n"); + + for (i = nrofreqs; i--;) + { + struct rpc *xreq = reqs[i]; + if (xreq->state != REQSTATE_REQ_WAITING_FOR_REPLY) continue; - if (xreq->reqh.reqid == resph.reqid) { + + if (xreq->reqh.reqid == resph.reqid) + { memcpy(&(xreq->resph),&resph,sizeof(resph)); if (xreq->Buffer) @@ -826,54 +751,86 @@ COM_RpcReceive(wine_pipe *xpipe) { else xreq->Buffer = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,xreq->resph.cbBuffer); - hres = read_pipe(xhPipe,xreq->Buffer,xreq->resph.cbBuffer); + hres = read_pipe(pipe,xreq->Buffer,xreq->resph.cbBuffer); if (hres) goto end; + + TRACE("received response for reqid 0x%lx\n", xreq->reqh.reqid); + xreq->state = REQSTATE_RESP_GOT; - /*PulseEvent(hRpcChanged);*/ goto end; } } - ERR("Did not find request for id %lx\n",resph.reqid); - hres = S_OK; + + ERR("protocol error: did not find request for id %lx\n",resph.reqid); + hres = E_FAIL; goto end; } - ERR("Unknown reqtype %ld\n",reqtype); + + ERR("protocol error: unknown reqtype %ld\n",reqtype); hres = E_FAIL; end: - LeaveCriticalSection(&(xpipe->crit)); return hres; } -/* This thread listens on the given pipe for requests to a particular stub manager */ -static DWORD WINAPI stub_dispatch_thread(LPVOID param) +struct stub_dispatch_params { - wine_pipe *xpipe = (wine_pipe*)param; - HANDLE xhPipe = xpipe->hPipe; - HRESULT hres = S_OK; + struct apartment *apt; + HANDLE pipe; +}; - TRACE("starting for apartment OXID %08lx%08lx\n", (DWORD)(xpipe->mid.oxid >> 32), (DWORD)(xpipe->mid.oxid)); - - /* join marshalling apartment. fixme: this stuff is all very wrong, threading needs to work like native */ - NtCurrentTeb()->ReservedForOle = xpipe->apt; +/* This thread listens on the given pipe for requests to any stub manager */ +static DWORD WINAPI client_dispatch_thread(LPVOID param) +{ + HANDLE pipe = ((struct stub_dispatch_params *)param)->pipe; + struct apartment *apt = ((struct stub_dispatch_params *)param)->apt; + HRESULT hres = S_OK; + HANDLE shutdown_event = dupe_handle(apt->shutdown_event); - while (!hres) { - int i; - - hres = COM_RpcReceive(xpipe); - if (hres) break; + HeapFree(GetProcessHeap(), 0, param); + + /* join marshalling apartment. fixme: this stuff is all very wrong, threading needs to work like native */ + COM_CurrentInfo()->apt = apt; - for (i=nrofreqs;i--;) { - wine_rpc_request *xreq = reqs[i]; - if ((xreq->state == REQSTATE_REQ_GOT) && (xreq->hPipe == xhPipe)) { - hres = COM_InvokeAndRpcSend(xreq); - if (!hres) break; - } - } + while (TRUE) + { + int i; + + TRACE("waiting for RPC on OXID: %08lx%08lx\n", (DWORD)(apt->oxid >> 32), (DWORD)(apt->oxid)); + + /* read a new request into the global array, block if no requests have been sent */ + hres = process_incoming_rpc(pipe); + if (hres) break; + + /* do you expect me to talk? */ + if (WaitForSingleObject(shutdown_event, 0) == WAIT_OBJECT_0) + { + /* no mr bond, i expect you to die! bwahaha */ + CloseHandle(shutdown_event); + break; + } + + TRACE("received RPC on OXID: %08lx%08lx\n", (DWORD)(apt->oxid >> 32), (DWORD)(apt->oxid)); + + /* now scan the array looking for the RPC just loaded */ + for (i=nrofreqs;i--;) + { + struct rpc *req = reqs[i]; + + if ((req->state == REQSTATE_REQ_GOT) && (req->hPipe == pipe)) + { + hres = COM_InvokeAndRpcSend(req); + if (!hres) break; + } + } } - /* fixme: this thread never quits naturally */ - WARN("exiting with hres %lx\n",hres); - CloseHandle(xhPipe); + TRACE("exiting with hres %lx\n",hres); + + /* leave marshalling apartment. fixme: this stuff is all very wrong, threading needs to work like native */ + COM_CurrentInfo()->apt = NULL; + + DisconnectNamedPipe(pipe); + CloseHandle(pipe); return 0; } @@ -892,29 +849,40 @@ struct apartment_listener_params */ static DWORD WINAPI apartment_listener_thread(LPVOID p) { - char pipefn[200]; - HANDLE listenPipe; + char pipefn[200]; + HANDLE listenPipe, thread_handle; + OVERLAPPED overlapped; + HANDLE wait[2]; + struct apartment_listener_params * params = (struct apartment_listener_params *)p; - APARTMENT *apt = params->apt; + struct apartment *apt = params->apt; HANDLE event = params->event; + HANDLE apt_shutdown_event = dupe_handle(apt->shutdown_event); + OXID this_oxid = apt->oxid; /* copy here so we can print it when we shut down */ HeapFree(GetProcessHeap(), 0, params); + overlapped.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL); + /* we must join the marshalling threads apartment. we already have a ref here */ - NtCurrentTeb()->ReservedForOle = apt; + COM_CurrentInfo()->apt = apt; sprintf(pipefn,OLESTUBMGR"_%08lx%08lx", (DWORD)(apt->oxid >> 32), (DWORD)(apt->oxid)); TRACE("Apartment listener thread starting on (%s)\n",pipefn); - while (1) { + while (TRUE) + { + struct stub_dispatch_params *params; + DWORD res; + listenPipe = CreateNamedPipeA( pipefn, - PIPE_ACCESS_DUPLEX, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 4096, 4096, - NMPWAIT_USE_DEFAULT_WAIT, + 500 /* 0.5 seconds */, NULL ); @@ -927,19 +895,56 @@ static DWORD WINAPI apartment_listener_thread(LPVOID p) if (listenPipe == INVALID_HANDLE_VALUE) { FIXME("pipe creation failed for %s, error %ld\n",pipefn,GetLastError()); - return 1; /* permanent failure, so quit stubmgr thread */ + break; /* permanent failure, so quit stubmgr thread */ } + TRACE("waiting for a client ...\n"); + /* an already connected pipe is not an error */ - if (!ConnectNamedPipe(listenPipe,NULL) && - (GetLastError() != ERROR_PIPE_CONNECTED)) { - ERR("Failure during ConnectNamedPipe %ld!\n",GetLastError()); - CloseHandle(listenPipe); - continue; + if (!ConnectNamedPipe(listenPipe, &overlapped)) + { + DWORD le = GetLastError(); + + if ((le != ERROR_IO_PENDING) && (le != ERROR_PIPE_CONNECTED)) + { + ERR("Failure during ConnectNamedPipe %ld!\n",GetLastError()); + CloseHandle(listenPipe); + continue; + } } - PIPE_StartRequestThread(listenPipe); + /* wait for action */ + wait[0] = apt_shutdown_event; + wait[1] = overlapped.hEvent; + res = WaitForMultipleObjectsEx(2, wait, FALSE, INFINITE, FALSE); + if (res == WAIT_OBJECT_0) break; + + ResetEvent(overlapped.hEvent); + + /* start the stub dispatch thread for this connection */ + TRACE("starting stub dispatch thread for OXID %08lx%08lx\n", (DWORD)(apt->oxid >> 32), (DWORD)(apt->oxid)); + params = HeapAlloc(GetProcessHeap(), 0, sizeof(struct stub_dispatch_params)); + if (!params) + { + ERR("out of memory, dropping this client\n"); + CloseHandle(listenPipe); + continue; + } + params->apt = apt; + params->pipe = listenPipe; + thread_handle = CreateThread(NULL, 0, &client_dispatch_thread, params, 0, NULL); + CloseHandle(thread_handle); } + + TRACE("shutting down: %s\n", wine_dbgstr_longlong(this_oxid)); + + /* we must leave the marshalling threads apartment. we don't have a ref here */ + COM_CurrentInfo()->apt = NULL; + + DisconnectNamedPipe(listenPipe); + CloseHandle(listenPipe); + CloseHandle(overlapped.hEvent); + CloseHandle(apt_shutdown_event); return 0; } @@ -1004,12 +1009,15 @@ static DWORD WINAPI local_server_thread(LPVOID param) HeapFree(GetProcessHeap(), 0, lsp); hPipe = CreateNamedPipeA( pipefn, PIPE_ACCESS_DUPLEX, - PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, - 4096, 4096, NMPWAIT_USE_DEFAULT_WAIT, NULL ); - if (hPipe == INVALID_HANDLE_VALUE) { + PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, + 4096, 4096, 500 /* 0.5 second timeout */, NULL ); + + if (hPipe == INVALID_HANDLE_VALUE) + { FIXME("pipe creation failed for %s, le is %ld\n",pipefn,GetLastError()); return 1; } + while (1) { if (!ConnectNamedPipe(hPipe,NULL)) { ERR("Failure during ConnectNamedPipe %ld, ABORT!\n",GetLastError()); @@ -1037,8 +1045,6 @@ static DWORD WINAPI local_server_thread(LPVOID param) return hres; } - IStream_Release(pStm); - WriteFile(hPipe,buffer,buflen,&res,NULL); FlushFileBuffers(hPipe); DisconnectNamedPipe(hPipe); @@ -1046,6 +1052,7 @@ static DWORD WINAPI local_server_thread(LPVOID param) TRACE("done marshalling IClassFactory\n"); } CloseHandle(hPipe); + IStream_Release(pStm); return 0; } diff --git a/reactos/lib/ole32/stubmanager.c b/reactos/lib/ole32/stubmanager.c index 54a42904788..24c77517b15 100644 --- a/reactos/lib/ole32/stubmanager.c +++ b/reactos/lib/ole32/stubmanager.c @@ -94,6 +94,7 @@ static void stub_manager_delete(struct stub_manager *m) list_remove(&m->entry); + /* release every ifstub */ while ((cursor = list_head(&m->ifstubs))) { struct ifstub *ifstub = LIST_ENTRY(cursor, struct ifstub, entry); @@ -107,19 +108,13 @@ static void stub_manager_delete(struct stub_manager *m) HeapFree(GetProcessHeap(), 0, m); } -/* gets the stub manager associated with an object - caller must call - * release on the stub manager when it is no longer needed */ -struct stub_manager *get_stub_manager_from_object(OXID oxid, void *object) +/* gets the stub manager associated with an object - 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 */ +struct stub_manager *get_stub_manager_from_object(APARTMENT *apt, void *object) { struct stub_manager *result = NULL; struct list *cursor; - APARTMENT *apt; - - if (!(apt = COM_ApartmentFromOXID(oxid, TRUE))) - { - 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 ) @@ -135,13 +130,69 @@ struct stub_manager *get_stub_manager_from_object(OXID oxid, void *object) } LeaveCriticalSection(&apt->cs); - COM_ApartmentRelease(apt); - - TRACE("found %p from object %p\n", result, object); + if (result) + TRACE("found %p for object %p\n", result, object); + else + TRACE("not found for object %p\n", object); return result; } +/* removes the apartment reference to an object, destroying it when no other + * threads have a reference to it */ +void apartment_disconnect_object(APARTMENT *apt, void *object) +{ + int found = FALSE; + struct stub_manager *stubmgr; + + EnterCriticalSection(&apt->cs); + LIST_FOR_EACH_ENTRY( stubmgr, &apt->stubmgrs, struct stub_manager, entry ) + { + if (stubmgr->object == object) + { + found = TRUE; + stub_manager_int_release(stubmgr); + break; + } + } + LeaveCriticalSection(&apt->cs); + + if (found) + TRACE("disconnect object %p\n", object); + else + WARN("couldn't find object %p\n", object); +} + +/* gets the stub manager associated with an object id - 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 */ +struct stub_manager *get_stub_manager(APARTMENT *apt, OID oid) +{ + struct stub_manager *result = NULL; + struct list *cursor; + + 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; + stub_manager_int_addref(result); + break; + } + } + LeaveCriticalSection(&apt->cs); + + if (result) + TRACE("found %p for oid %s\n", result, wine_dbgstr_longlong(oid)); + else + TRACE("not found for oid %s\n", wine_dbgstr_longlong(oid)); + + return result; +} + /* increments the internal refcount */ ULONG stub_manager_int_addref(struct stub_manager *This) { @@ -174,41 +225,6 @@ ULONG stub_manager_int_release(struct stub_manager *This) return refs; } -/* gets the stub manager associated with an object id - caller must call - * release on the stub manager when it is no longer needed */ -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, TRUE))) - { - 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; - stub_manager_int_addref(result); - break; - } - } - LeaveCriticalSection(&apt->cs); - - COM_ApartmentRelease(apt); - - TRACE("found %p from oid %s\n", result, wine_dbgstr_longlong(oid)); - - return result; -} - /* add some external references (ie from a client that unmarshaled an ifptr) */ ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs) { @@ -253,11 +269,101 @@ static struct ifstub *stub_manager_ipid_to_ifstub(struct stub_manager *m, const return result; } -IRpcStubBuffer *stub_manager_ipid_to_stubbuffer(struct stub_manager *m, const IPID *ipid) +/* 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 */ +static struct stub_manager *get_stub_manager_from_ipid(APARTMENT *apt, const IPID *ipid) { - struct ifstub *ifstub = stub_manager_ipid_to_ifstub(m, ipid); - - return ifstub ? ifstub->stubbuffer : NULL; + struct stub_manager *result = NULL; + struct list *cursor; + + EnterCriticalSection(&apt->cs); + LIST_FOR_EACH( cursor, &apt->stubmgrs ) + { + struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry ); + + if (stub_manager_ipid_to_ifstub(m, ipid)) + { + result = m; + stub_manager_int_addref(result); + break; + } + } + LeaveCriticalSection(&apt->cs); + + if (result) + TRACE("found %p for ipid %s\n", result, debugstr_guid(ipid)); + else + ERR("not found for ipid %s\n", debugstr_guid(ipid)); + + return result; +} + +HRESULT ipid_to_stub_manager(const IPID *ipid, APARTMENT **stub_apt, struct stub_manager **stubmgr_ret) +{ + /* FIXME: hack for IRemUnknown */ + if (ipid->Data2 == 0xffff) + *stub_apt = COM_ApartmentFromOXID(*(OXID *)ipid->Data4, TRUE); + else + *stub_apt = COM_ApartmentFromTID(ipid->Data2); + if (!*stub_apt) + { + ERR("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); + if (!*stubmgr_ret) + { + COM_ApartmentRelease(*stub_apt); + *stub_apt = NULL; + return RPC_E_INVALID_OBJECT; + } + return S_OK; +} + +IRpcStubBuffer *ipid_to_stubbuffer(const IPID *ipid) +{ + IRpcStubBuffer *ret = NULL; + APARTMENT *apt; + struct stub_manager *stubmgr; + struct ifstub *ifstub; + HRESULT hr; + + hr = ipid_to_stub_manager(ipid, &apt, &stubmgr); + if (hr != S_OK) return NULL; + + ifstub = stub_manager_ipid_to_ifstub(stubmgr, ipid); + if (ifstub) + ret = ifstub->stubbuffer; + + stub_manager_int_release(stubmgr); + + COM_ApartmentRelease(apt); + + return ret; +} + +/* generates an ipid in the following format (similar to native version): + * Data1 = apartment-local ipid counter + * Data2 = apartment creator thread ID + * Data3 = process ID + * Data4 = random value + */ +static inline HRESULT generate_ipid(struct stub_manager *m, IPID *ipid) +{ + HRESULT hr; + hr = UuidCreate(ipid); + if (FAILED(hr)) + { + ERR("couldn't create IPID for stub manager %p\n", m); + UuidCreateNil(ipid); + return hr; + } + + ipid->Data1 = InterlockedIncrement(&m->apt->ipidc); + ipid->Data2 = (USHORT)m->apt->tid; + ipid->Data3 = (USHORT)GetCurrentProcessId(); + return S_OK; } /* registers a new interface stub COM object with the stub manager and returns registration record */ @@ -276,14 +382,33 @@ struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *s /* no need to ref this, same object as sb */ stub->iface = iptr; - stub->table = tablemarshal; + + if (tablemarshal) + stub->state = IFSTUB_STATE_TABLE_MARSHALED; + else + stub->state = IFSTUB_STATE_NORMAL_MARSHALED; + stub->iid = *iid; - stub->ipid = *iid; /* FIXME: should be globally unique */ + + /* 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)); + } + else + generate_ipid(m, &stub->ipid); EnterCriticalSection(&m->lock); list_add_head(&m->ifstubs, &stub->entry); LeaveCriticalSection(&m->lock); + TRACE("ifstub %p created with ipid %s\n", stub, debugstr_guid(&stub->ipid)); + return stub; } @@ -298,3 +423,278 @@ static void stub_manager_delete_ifstub(struct stub_manager *m, struct ifstub *if HeapFree(GetProcessHeap(), 0, ifstub); } + +/* returns TRUE if it is possible to unmarshal, FALSE otherwise. */ +BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid) +{ + struct ifstub *ifstub; + BOOL ret; + + ifstub = stub_manager_ipid_to_ifstub(m, ipid); + if (!ifstub) + { + WARN("Can't find ifstub for OID %s, IPID %s\n", + wine_dbgstr_longlong(m->oid), wine_dbgstr_guid(ipid)); + return FALSE; + } + + EnterCriticalSection(&m->lock); + + switch (ifstub->state) + { + case IFSTUB_STATE_TABLE_MARSHALED: + ret = TRUE; + break; + case IFSTUB_STATE_NORMAL_MARSHALED: + ifstub->state = IFSTUB_STATE_NORMAL_UNMARSHALED; + ret = TRUE; + break; + default: + WARN("object OID %s, IPID %s already unmarshaled\n", + wine_dbgstr_longlong(m->oid), wine_dbgstr_guid(ipid)); + ret = FALSE; + break; + } + + LeaveCriticalSection(&m->lock); + + return ret; +} + +/* is an ifstub table marshaled? */ +BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid) +{ + struct ifstub *ifstub; + BOOL ret; + + ifstub = stub_manager_ipid_to_ifstub(m, ipid); + if (!ifstub) + { + WARN("Can't find ifstub for OID %s, IPID %s\n", + wine_dbgstr_longlong(m->oid), wine_dbgstr_guid(ipid)); + return FALSE; + } + + EnterCriticalSection(&m->lock); + ret = (ifstub->state == IFSTUB_STATE_TABLE_MARSHALED); + LeaveCriticalSection(&m->lock); + + return ret; +} + + +/***************************************************************************** + * + * IRemUnknown implementation + * + * + * Note: this object is not related to the lifetime of a stub_manager, but it + * 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; +} RemUnknown; + +static const IRemUnknownVtbl RemUnknown_Vtbl; + + +/* construct an IRemUnknown object with one outstanding reference */ +static HRESULT RemUnknown_Construct(IRemUnknown **ppRemUnknown) +{ + RemUnknown *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + + if (!This) return E_OUTOFMEMORY; + + This->lpVtbl = &RemUnknown_Vtbl; + This->refs = 1; + + *ppRemUnknown = (IRemUnknown *)This; + return S_OK; +} + +static HRESULT WINAPI RemUnknown_QueryInterface(IRemUnknown *iface, REFIID riid, void **ppv) +{ + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IRemUnknown)) + { + *ppv = (LPVOID)iface; + IRemUnknown_AddRef(iface); + return S_OK; + } + + FIXME("No interface for iid %s\n", debugstr_guid(riid)); + + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI RemUnknown_AddRef(IRemUnknown *iface) +{ + ULONG refs; + RemUnknown *This = (RemUnknown *)iface; + + refs = InterlockedIncrement(&This->refs); + + TRACE("%p before: %ld\n", iface, refs-1); + return refs; +} + +static ULONG WINAPI RemUnknown_Release(IRemUnknown *iface) +{ + ULONG refs; + RemUnknown *This = (RemUnknown *)iface; + + refs = InterlockedDecrement(&This->refs); + if (!refs) + HeapFree(GetProcessHeap(), 0, This); + + TRACE("%p after: %ld\n", iface, refs); + return refs; +} + +static HRESULT WINAPI RemUnknown_RemQueryInterface(IRemUnknown *iface, + REFIPID ripid, ULONG cRefs, USHORT cIids, IID *iids /* [size_is(cIids)] */, + REMQIRESULT **ppQIResults /* [size_is(,cIids)] */) +{ + HRESULT hr; + USHORT i; + USHORT successful_qis = 0; + APARTMENT *apt; + struct stub_manager *stubmgr; + + TRACE("(%p)->(%s, %ld, %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; + + *ppQIResults = CoTaskMemAlloc(sizeof(REMQIRESULT) * cIids); + + for (i = 0; i < cIids; i++) + { + HRESULT hrobj = register_ifstub(apt, &(*ppQIResults)[i].std, &iids[i], + stubmgr->object, MSHLFLAGS_NORMAL); + if (hrobj == S_OK) + successful_qis++; + (*ppQIResults)[i].hResult = hrobj; + } + + stub_manager_int_release(stubmgr); + COM_ApartmentRelease(apt); + + if (successful_qis == cIids) + return S_OK; /* we got all requested interfaces */ + else if (successful_qis == 0) + return E_NOINTERFACE; /* we didn't get any interfaces */ + else + return S_FALSE; /* we got some interfaces */ +} + +static HRESULT WINAPI RemUnknown_RemAddRef(IRemUnknown *iface, + USHORT cInterfaceRefs, + REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */, + HRESULT *pResults /* [size_is(cInterfaceRefs)] */) +{ + HRESULT hr = S_OK; + USHORT i; + + TRACE("(%p)->(%d, %p, %p)\n", iface, cInterfaceRefs, InterfaceRefs, pResults); + + for (i = 0; i < cInterfaceRefs; i++) + { + APARTMENT *apt; + struct stub_manager *stubmgr; + + pResults[i] = ipid_to_stub_manager(&InterfaceRefs[i].ipid, &apt, &stubmgr); + if (pResults[i] != S_OK) + { + hr = S_FALSE; + continue; + } + + stub_manager_ext_addref(stubmgr, InterfaceRefs[i].cPublicRefs); + if (InterfaceRefs[i].cPrivateRefs) + FIXME("Adding %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs); + + stub_manager_int_release(stubmgr); + COM_ApartmentRelease(apt); + } + + return hr; +} + +static HRESULT WINAPI RemUnknown_RemRelease(IRemUnknown *iface, + USHORT cInterfaceRefs, + REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */) +{ + HRESULT hr = S_OK; + USHORT i; + + TRACE("(%p)->(%d, %p)\n", iface, cInterfaceRefs, InterfaceRefs); + + for (i = 0; i < cInterfaceRefs; i++) + { + APARTMENT *apt; + struct stub_manager *stubmgr; + + hr = ipid_to_stub_manager(&InterfaceRefs[i].ipid, &apt, &stubmgr); + if (hr != S_OK) + { + hr = E_INVALIDARG; + /* FIXME: we should undo any changes already made in this function */ + break; + } + + stub_manager_ext_release(stubmgr, InterfaceRefs[i].cPublicRefs); + if (InterfaceRefs[i].cPrivateRefs) + FIXME("Releasing %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs); + + stub_manager_int_release(stubmgr); + COM_ApartmentRelease(apt); + } + + return hr; +} + +static const IRemUnknownVtbl RemUnknown_Vtbl = +{ + RemUnknown_QueryInterface, + RemUnknown_AddRef, + RemUnknown_Release, + RemUnknown_RemQueryInterface, + RemUnknown_RemAddRef, + RemUnknown_RemRelease +}; + +/* starts the IRemUnknown listener for the current apartment */ +HRESULT start_apartment_remote_unknown() +{ + IRemUnknown *pRemUnknown; + HRESULT hr = S_OK; + APARTMENT *apt = COM_CurrentApt(); + + EnterCriticalSection(&apt->cs); + if (!apt->remunk_exported) + { + /* create the IRemUnknown object */ + hr = RemUnknown_Construct(&pRemUnknown); + if (hr == S_OK) + { + STDOBJREF stdobjref; /* dummy - not used */ + /* register it with the stub manager */ + hr = register_ifstub(COM_CurrentApt(), &stdobjref, &IID_IRemUnknown, (IUnknown *)pRemUnknown, MSHLFLAGS_NORMAL); + /* release our reference to the object as the stub manager will manage the life cycle for us */ + IRemUnknown_Release(pRemUnknown); + if (hr == S_OK) + apt->remunk_exported = TRUE; + } + } + LeaveCriticalSection(&apt->cs); + return hr; +} diff --git a/reactos/w32api/include/winerror.h b/reactos/w32api/include/winerror.h index 0ff2573a11b..895f3d49b31 100644 --- a/reactos/w32api/include/winerror.h +++ b/reactos/w32api/include/winerror.h @@ -2198,6 +2198,7 @@ #define RPC_E_CANTCALLOUT_ININPUTSYNCCALL ((HRESULT)0x8001010DL) #define RPC_E_WRONG_THREAD ((HRESULT)0x8001010EL) #define RPC_E_THREAD_NOT_INIT ((HRESULT)0x8001010FL) +#define RPC_E_INVALID_OBJECT ((HRESULT)0x80010114L) #define RPC_E_INVALID_OBJREF ((HRESULT)0x8001011DL) #define RPC_E_UNEXPECTED ((HRESULT)0x8001FFFFL) diff --git a/reactos/w32api/include/wtypes.h b/reactos/w32api/include/wtypes.h index aa749e0209e..0beb1f66119 100644 --- a/reactos/w32api/include/wtypes.h +++ b/reactos/w32api/include/wtypes.h @@ -60,7 +60,7 @@ typedef enum tagCLSCTX { CLSCTX_INPROC_SERVER16=8,CLSCTX_REMOTE_SERVER=16 } CLSCTX; typedef enum tagMSHLFLAGS { - MSHLFLAGS_NORMAL,MSHLFLAGS_TABLESTRONG,MSHLFLAGS_TABLEWEAK + MSHLFLAGS_NORMAL,MSHLFLAGS_TABLESTRONG,MSHLFLAGS_TABLEWEAK,MSHLFLAGS_NOPING } MSHLFLAGS; typedef struct _FLAGGED_WORD_BLOB { unsigned long fFlags;