ole32: Reorder some compobj functions to avoid forward declarations.
This commit is contained in:
parent
2f989163bd
commit
f8afcdbb7c
|
@ -72,11 +72,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole);
|
|||
* This section defines variables internal to the COM module.
|
||||
*/
|
||||
|
||||
static HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSID rclsid,
|
||||
DWORD dwClsContext, LPUNKNOWN* ppUnk);
|
||||
static void COM_RevokeAllClasses(const struct apartment *apt);
|
||||
static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll, REFCLSID rclsid, REFIID riid, BOOL hostifnecessary, void **ppv);
|
||||
|
||||
static APARTMENT *MTA; /* protected by csApartment */
|
||||
static APARTMENT *MainApartment; /* the first STA apartment */
|
||||
static struct list apts = LIST_INIT( apts ); /* protected by csApartment */
|
||||
|
@ -183,62 +178,147 @@ struct apartment_loaded_dll
|
|||
|
||||
static const WCHAR wszAptWinClass[] = {'O','l','e','M','a','i','n','T','h','r','e','a','d','W','n','d','C','l','a','s','s',' ',
|
||||
'0','x','#','#','#','#','#','#','#','#',' ',0};
|
||||
static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
|
||||
BOOL apartment_threaded,
|
||||
REFCLSID rclsid, REFIID riid, void **ppv);
|
||||
static void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay);
|
||||
|
||||
static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret);
|
||||
static OpenDll *COMPOBJ_DllList_Get(LPCWSTR library_name);
|
||||
static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry, BOOL free_entry);
|
||||
/*****************************************************************************
|
||||
* This section contains OpenDllList implementation
|
||||
*/
|
||||
|
||||
static DWORD COM_RegReadPath(HKEY hkeyroot, const WCHAR *keyname, const WCHAR *valuename, WCHAR * dst, DWORD dstlen);
|
||||
|
||||
static void COMPOBJ_InitProcess( void )
|
||||
static OpenDll *COMPOBJ_DllList_Get(LPCWSTR library_name)
|
||||
{
|
||||
WNDCLASSW wclass;
|
||||
|
||||
/* Dispatching to the correct thread in an apartment is done through
|
||||
* window messages rather than RPC transports. When an interface is
|
||||
* marshalled into another apartment in the same process, a window of the
|
||||
* following class is created. The *caller* of CoMarshalInterface (i.e., the
|
||||
* application) is responsible for pumping the message loop in that thread.
|
||||
* The WM_USER messages which point to the RPCs are then dispatched to
|
||||
* apartment_wndproc by the user's code from the apartment in which the
|
||||
* interface was unmarshalled.
|
||||
*/
|
||||
memset(&wclass, 0, sizeof(wclass));
|
||||
wclass.lpfnWndProc = apartment_wndproc;
|
||||
wclass.hInstance = hProxyDll;
|
||||
wclass.lpszClassName = wszAptWinClass;
|
||||
RegisterClassW(&wclass);
|
||||
}
|
||||
|
||||
static void COMPOBJ_UninitProcess( void )
|
||||
{
|
||||
UnregisterClassW(wszAptWinClass, hProxyDll);
|
||||
}
|
||||
|
||||
static void COM_TlsDestroy(void)
|
||||
{
|
||||
struct oletls *info = NtCurrentTeb()->ReservedForOle;
|
||||
if (info)
|
||||
OpenDll *ptr;
|
||||
OpenDll *ret = NULL;
|
||||
EnterCriticalSection(&csOpenDllList);
|
||||
LIST_FOR_EACH_ENTRY(ptr, &openDllList, OpenDll, entry)
|
||||
{
|
||||
if (info->apt) apartment_release(info->apt);
|
||||
if (info->errorinfo) IErrorInfo_Release(info->errorinfo);
|
||||
if (info->state) IUnknown_Release(info->state);
|
||||
if (info->spy) IUnknown_Release(info->spy);
|
||||
if (info->context_token) IObjContext_Release(info->context_token);
|
||||
HeapFree(GetProcessHeap(), 0, info);
|
||||
NtCurrentTeb()->ReservedForOle = NULL;
|
||||
if (!strcmpiW(library_name, ptr->library_name) &&
|
||||
(InterlockedIncrement(&ptr->refs) != 1) /* entry is being destroy if == 1 */)
|
||||
{
|
||||
ret = ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&csOpenDllList);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* caller must ensure that library_name is not already in the open dll list */
|
||||
static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret)
|
||||
{
|
||||
OpenDll *entry;
|
||||
int len;
|
||||
HRESULT hr = S_OK;
|
||||
HANDLE hLibrary;
|
||||
DllCanUnloadNowFunc DllCanUnloadNow;
|
||||
DllGetClassObjectFunc DllGetClassObject;
|
||||
|
||||
TRACE("\n");
|
||||
|
||||
*ret = COMPOBJ_DllList_Get(library_name);
|
||||
if (*ret) return S_OK;
|
||||
|
||||
/* do this outside the csOpenDllList to avoid creating a lock dependency on
|
||||
* the loader lock */
|
||||
hLibrary = LoadLibraryExW(library_name, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||
if (!hLibrary)
|
||||
{
|
||||
ERR("couldn't load in-process dll %s\n", debugstr_w(library_name));
|
||||
/* failure: DLL could not be loaded */
|
||||
return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
|
||||
}
|
||||
|
||||
DllCanUnloadNow = (void *)GetProcAddress(hLibrary, "DllCanUnloadNow");
|
||||
/* Note: failing to find DllCanUnloadNow is not a failure */
|
||||
DllGetClassObject = (void *)GetProcAddress(hLibrary, "DllGetClassObject");
|
||||
if (!DllGetClassObject)
|
||||
{
|
||||
/* failure: the dll did not export DllGetClassObject */
|
||||
ERR("couldn't find function DllGetClassObject in %s\n", debugstr_w(library_name));
|
||||
FreeLibrary(hLibrary);
|
||||
return CO_E_DLLNOTFOUND;
|
||||
}
|
||||
|
||||
EnterCriticalSection( &csOpenDllList );
|
||||
|
||||
*ret = COMPOBJ_DllList_Get(library_name);
|
||||
if (*ret)
|
||||
{
|
||||
/* another caller to this function already added the dll while we
|
||||
* weren't in the critical section */
|
||||
FreeLibrary(hLibrary);
|
||||
}
|
||||
else
|
||||
{
|
||||
len = strlenW(library_name);
|
||||
entry = HeapAlloc(GetProcessHeap(),0, sizeof(OpenDll));
|
||||
if (entry)
|
||||
entry->library_name = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
|
||||
if (entry && entry->library_name)
|
||||
{
|
||||
memcpy(entry->library_name, library_name, (len + 1)*sizeof(WCHAR));
|
||||
entry->library = hLibrary;
|
||||
entry->refs = 1;
|
||||
entry->DllCanUnloadNow = DllCanUnloadNow;
|
||||
entry->DllGetClassObject = DllGetClassObject;
|
||||
list_add_tail(&openDllList, &entry->entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, entry);
|
||||
hr = E_OUTOFMEMORY;
|
||||
FreeLibrary(hLibrary);
|
||||
}
|
||||
*ret = entry;
|
||||
}
|
||||
|
||||
LeaveCriticalSection( &csOpenDllList );
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* pass FALSE for free_entry to release a reference without destroying the
|
||||
* entry if it reaches zero or TRUE otherwise */
|
||||
static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry, BOOL free_entry)
|
||||
{
|
||||
if (!InterlockedDecrement(&entry->refs) && free_entry)
|
||||
{
|
||||
EnterCriticalSection(&csOpenDllList);
|
||||
list_remove(&entry->entry);
|
||||
LeaveCriticalSection(&csOpenDllList);
|
||||
|
||||
TRACE("freeing %p\n", entry->library);
|
||||
FreeLibrary(entry->library);
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, entry->library_name);
|
||||
HeapFree(GetProcessHeap(), 0, entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* frees memory associated with active dll list */
|
||||
static void COMPOBJ_DllList_Free(void)
|
||||
{
|
||||
OpenDll *entry, *cursor2;
|
||||
EnterCriticalSection(&csOpenDllList);
|
||||
LIST_FOR_EACH_ENTRY_SAFE(entry, cursor2, &openDllList, OpenDll, entry)
|
||||
{
|
||||
list_remove(&entry->entry);
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, entry->library_name);
|
||||
HeapFree(GetProcessHeap(), 0, entry);
|
||||
}
|
||||
LeaveCriticalSection(&csOpenDllList);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Manage apartments.
|
||||
*/
|
||||
|
||||
DWORD apartment_addref(struct apartment *apt)
|
||||
{
|
||||
DWORD refs = InterlockedIncrement(&apt->refs);
|
||||
TRACE("%s: before = %d\n", wine_dbgstr_longlong(apt->oxid), refs - 1);
|
||||
return refs;
|
||||
}
|
||||
|
||||
/* allocates memory and fills in the necessary fields for a new apartment
|
||||
* object. must be called inside apartment cs */
|
||||
static APARTMENT *apartment_construct(DWORD model)
|
||||
|
@ -337,11 +417,146 @@ static inline BOOL apartment_is_model(const APARTMENT *apt, DWORD model)
|
|||
return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
|
||||
}
|
||||
|
||||
DWORD apartment_addref(struct apartment *apt)
|
||||
static void COM_RevokeRegisteredClassObject(RegisteredClass *curClass)
|
||||
{
|
||||
DWORD refs = InterlockedIncrement(&apt->refs);
|
||||
TRACE("%s: before = %d\n", wine_dbgstr_longlong(apt->oxid), refs - 1);
|
||||
return refs;
|
||||
list_remove(&curClass->entry);
|
||||
|
||||
if (curClass->runContext & CLSCTX_LOCAL_SERVER)
|
||||
RPC_StopLocalServer(curClass->RpcRegistration);
|
||||
|
||||
/*
|
||||
* Release the reference to the class object.
|
||||
*/
|
||||
IUnknown_Release(curClass->classObject);
|
||||
|
||||
if (curClass->pMarshaledData)
|
||||
{
|
||||
LARGE_INTEGER zero;
|
||||
memset(&zero, 0, sizeof(zero));
|
||||
IStream_Seek(curClass->pMarshaledData, zero, STREAM_SEEK_SET, NULL);
|
||||
CoReleaseMarshalData(curClass->pMarshaledData);
|
||||
IStream_Release(curClass->pMarshaledData);
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, curClass);
|
||||
}
|
||||
|
||||
static void COM_RevokeAllClasses(const struct apartment *apt)
|
||||
{
|
||||
RegisteredClass *curClass, *cursor;
|
||||
|
||||
EnterCriticalSection( &csRegisteredClassList );
|
||||
|
||||
LIST_FOR_EACH_ENTRY_SAFE(curClass, cursor, &RegisteredClassList, RegisteredClass, entry)
|
||||
{
|
||||
if (curClass->apartment_id == apt->oxid)
|
||||
COM_RevokeRegisteredClassObject(curClass);
|
||||
}
|
||||
|
||||
LeaveCriticalSection( &csRegisteredClassList );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* CoRevokeClassObject [OLE32.@]
|
||||
*
|
||||
* Removes a class object from the class registry.
|
||||
*
|
||||
* PARAMS
|
||||
* dwRegister [I] Cookie returned from CoRegisterClassObject().
|
||||
*
|
||||
* RETURNS
|
||||
* Success: S_OK.
|
||||
* Failure: HRESULT code.
|
||||
*
|
||||
* NOTES
|
||||
* Must be called from the same apartment that called CoRegisterClassObject(),
|
||||
* otherwise it will fail with RPC_E_WRONG_THREAD.
|
||||
*
|
||||
* SEE ALSO
|
||||
* CoRegisterClassObject
|
||||
*/
|
||||
HRESULT WINAPI CoRevokeClassObject(
|
||||
DWORD dwRegister)
|
||||
{
|
||||
HRESULT hr = E_INVALIDARG;
|
||||
RegisteredClass *curClass;
|
||||
APARTMENT *apt;
|
||||
|
||||
TRACE("(%08x)\n",dwRegister);
|
||||
|
||||
apt = COM_CurrentApt();
|
||||
if (!apt)
|
||||
{
|
||||
ERR("COM was not initialized\n");
|
||||
return CO_E_NOTINITIALIZED;
|
||||
}
|
||||
|
||||
EnterCriticalSection( &csRegisteredClassList );
|
||||
|
||||
LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
|
||||
{
|
||||
/*
|
||||
* Check if we have a match on the cookie.
|
||||
*/
|
||||
if (curClass->dwCookie == dwRegister)
|
||||
{
|
||||
if (curClass->apartment_id == apt->oxid)
|
||||
{
|
||||
COM_RevokeRegisteredClassObject(curClass);
|
||||
hr = S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("called from wrong apartment, should be called from %s\n",
|
||||
wine_dbgstr_longlong(curClass->apartment_id));
|
||||
hr = RPC_E_WRONG_THREAD;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection( &csRegisteredClassList );
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* frees unused libraries loaded by apartment_getclassobject by calling the
|
||||
* DLL's DllCanUnloadNow entry point */
|
||||
static void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay)
|
||||
{
|
||||
struct apartment_loaded_dll *entry, *next;
|
||||
EnterCriticalSection(&apt->cs);
|
||||
LIST_FOR_EACH_ENTRY_SAFE(entry, next, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
|
||||
{
|
||||
if (entry->dll->DllCanUnloadNow && (entry->dll->DllCanUnloadNow() == S_OK))
|
||||
{
|
||||
DWORD real_delay = delay;
|
||||
|
||||
if (real_delay == INFINITE)
|
||||
{
|
||||
/* DLLs that return multi-threaded objects aren't unloaded
|
||||
* straight away to cope for programs that have races between
|
||||
* last object destruction and threads in the DLLs that haven't
|
||||
* finished, despite DllCanUnloadNow returning S_OK */
|
||||
if (entry->multi_threaded)
|
||||
real_delay = 10 * 60 * 1000; /* 10 minutes */
|
||||
else
|
||||
real_delay = 0;
|
||||
}
|
||||
|
||||
if (!real_delay || (entry->unload_time && (entry->unload_time < GetTickCount())))
|
||||
{
|
||||
list_remove(&entry->entry);
|
||||
COMPOBJ_DllList_ReleaseRef(entry->dll, TRUE);
|
||||
HeapFree(GetProcessHeap(), 0, entry);
|
||||
}
|
||||
else
|
||||
entry->unload_time = GetTickCount() + real_delay;
|
||||
}
|
||||
else if (entry->unload_time)
|
||||
entry->unload_time = 0;
|
||||
}
|
||||
LeaveCriticalSection(&apt->cs);
|
||||
}
|
||||
|
||||
DWORD apartment_release(struct apartment *apt)
|
||||
|
@ -521,6 +736,106 @@ static APARTMENT *apartment_find_multi_threaded(void)
|
|||
return result;
|
||||
}
|
||||
|
||||
/* gets the specified class object by loading the appropriate DLL, if
|
||||
* necessary and calls the DllGetClassObject function for the DLL */
|
||||
static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
|
||||
BOOL apartment_threaded,
|
||||
REFCLSID rclsid, REFIID riid, void **ppv)
|
||||
{
|
||||
static const WCHAR wszOle32[] = {'o','l','e','3','2','.','d','l','l',0};
|
||||
HRESULT hr = S_OK;
|
||||
BOOL found = FALSE;
|
||||
struct apartment_loaded_dll *apartment_loaded_dll;
|
||||
|
||||
if (!strcmpiW(dllpath, wszOle32))
|
||||
{
|
||||
/* we don't need to control the lifetime of this dll, so use the local
|
||||
* implementation of DllGetClassObject directly */
|
||||
TRACE("calling ole32!DllGetClassObject\n");
|
||||
hr = DllGetClassObject(rclsid, riid, ppv);
|
||||
|
||||
if (hr != S_OK)
|
||||
ERR("DllGetClassObject returned error 0x%08x\n", hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&apt->cs);
|
||||
|
||||
LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
|
||||
if (!strcmpiW(dllpath, apartment_loaded_dll->dll->library_name))
|
||||
{
|
||||
TRACE("found %s already loaded\n", debugstr_w(dllpath));
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
apartment_loaded_dll = HeapAlloc(GetProcessHeap(), 0, sizeof(*apartment_loaded_dll));
|
||||
if (!apartment_loaded_dll)
|
||||
hr = E_OUTOFMEMORY;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
apartment_loaded_dll->unload_time = 0;
|
||||
apartment_loaded_dll->multi_threaded = FALSE;
|
||||
hr = COMPOBJ_DllList_Add( dllpath, &apartment_loaded_dll->dll );
|
||||
if (FAILED(hr))
|
||||
HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
TRACE("added new loaded dll %s\n", debugstr_w(dllpath));
|
||||
list_add_tail(&apt->loaded_dlls, &apartment_loaded_dll->entry);
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&apt->cs);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
/* one component being multi-threaded overrides any number of
|
||||
* apartment-threaded components */
|
||||
if (!apartment_threaded)
|
||||
apartment_loaded_dll->multi_threaded = TRUE;
|
||||
|
||||
TRACE("calling DllGetClassObject %p\n", apartment_loaded_dll->dll->DllGetClassObject);
|
||||
/* OK: get the ClassObject */
|
||||
hr = apartment_loaded_dll->dll->DllGetClassObject(rclsid, riid, ppv);
|
||||
|
||||
if (hr != S_OK)
|
||||
ERR("DllGetClassObject returned error 0x%08x\n", hr);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* COM_RegReadPath [internal]
|
||||
*
|
||||
* Reads a registry value and expands it when necessary
|
||||
*/
|
||||
static DWORD COM_RegReadPath(HKEY hkeyroot, const WCHAR *keyname, const WCHAR *valuename, WCHAR * dst, DWORD dstlen)
|
||||
{
|
||||
DWORD ret;
|
||||
HKEY key;
|
||||
DWORD keytype;
|
||||
WCHAR src[MAX_PATH];
|
||||
DWORD dwLength = dstlen * sizeof(WCHAR);
|
||||
|
||||
if((ret = RegOpenKeyExW(hkeyroot, keyname, 0, KEY_READ, &key)) == ERROR_SUCCESS) {
|
||||
if( (ret = RegQueryValueExW(key, NULL, NULL, &keytype, (LPBYTE)src, &dwLength)) == ERROR_SUCCESS ) {
|
||||
if (keytype == REG_EXPAND_SZ) {
|
||||
if (dstlen <= ExpandEnvironmentStringsW(src, dst, dstlen)) ret = ERROR_MORE_DATA;
|
||||
} else {
|
||||
lstrcpynW(dst, src, dstlen);
|
||||
}
|
||||
}
|
||||
RegCloseKey (key);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct host_object_params
|
||||
{
|
||||
HKEY hkeydll;
|
||||
|
@ -783,246 +1098,44 @@ void apartment_joinmta(void)
|
|||
COM_CurrentInfo()->apt = MTA;
|
||||
}
|
||||
|
||||
/* gets the specified class object by loading the appropriate DLL, if
|
||||
* necessary and calls the DllGetClassObject function for the DLL */
|
||||
static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
|
||||
BOOL apartment_threaded,
|
||||
REFCLSID rclsid, REFIID riid, void **ppv)
|
||||
static void COMPOBJ_InitProcess( void )
|
||||
{
|
||||
static const WCHAR wszOle32[] = {'o','l','e','3','2','.','d','l','l',0};
|
||||
HRESULT hr = S_OK;
|
||||
BOOL found = FALSE;
|
||||
struct apartment_loaded_dll *apartment_loaded_dll;
|
||||
WNDCLASSW wclass;
|
||||
|
||||
if (!strcmpiW(dllpath, wszOle32))
|
||||
{
|
||||
/* we don't need to control the lifetime of this dll, so use the local
|
||||
* implementation of DllGetClassObject directly */
|
||||
TRACE("calling ole32!DllGetClassObject\n");
|
||||
hr = DllGetClassObject(rclsid, riid, ppv);
|
||||
|
||||
if (hr != S_OK)
|
||||
ERR("DllGetClassObject returned error 0x%08x\n", hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&apt->cs);
|
||||
|
||||
LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
|
||||
if (!strcmpiW(dllpath, apartment_loaded_dll->dll->library_name))
|
||||
{
|
||||
TRACE("found %s already loaded\n", debugstr_w(dllpath));
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
apartment_loaded_dll = HeapAlloc(GetProcessHeap(), 0, sizeof(*apartment_loaded_dll));
|
||||
if (!apartment_loaded_dll)
|
||||
hr = E_OUTOFMEMORY;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
apartment_loaded_dll->unload_time = 0;
|
||||
apartment_loaded_dll->multi_threaded = FALSE;
|
||||
hr = COMPOBJ_DllList_Add( dllpath, &apartment_loaded_dll->dll );
|
||||
if (FAILED(hr))
|
||||
HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
TRACE("added new loaded dll %s\n", debugstr_w(dllpath));
|
||||
list_add_tail(&apt->loaded_dlls, &apartment_loaded_dll->entry);
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&apt->cs);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
/* one component being multi-threaded overrides any number of
|
||||
* apartment-threaded components */
|
||||
if (!apartment_threaded)
|
||||
apartment_loaded_dll->multi_threaded = TRUE;
|
||||
|
||||
TRACE("calling DllGetClassObject %p\n", apartment_loaded_dll->dll->DllGetClassObject);
|
||||
/* OK: get the ClassObject */
|
||||
hr = apartment_loaded_dll->dll->DllGetClassObject(rclsid, riid, ppv);
|
||||
|
||||
if (hr != S_OK)
|
||||
ERR("DllGetClassObject returned error 0x%08x\n", hr);
|
||||
}
|
||||
|
||||
return hr;
|
||||
/* Dispatching to the correct thread in an apartment is done through
|
||||
* window messages rather than RPC transports. When an interface is
|
||||
* marshalled into another apartment in the same process, a window of the
|
||||
* following class is created. The *caller* of CoMarshalInterface (i.e., the
|
||||
* application) is responsible for pumping the message loop in that thread.
|
||||
* The WM_USER messages which point to the RPCs are then dispatched to
|
||||
* apartment_wndproc by the user's code from the apartment in which the
|
||||
* interface was unmarshalled.
|
||||
*/
|
||||
memset(&wclass, 0, sizeof(wclass));
|
||||
wclass.lpfnWndProc = apartment_wndproc;
|
||||
wclass.hInstance = hProxyDll;
|
||||
wclass.lpszClassName = wszAptWinClass;
|
||||
RegisterClassW(&wclass);
|
||||
}
|
||||
|
||||
/* frees unused libraries loaded by apartment_getclassobject by calling the
|
||||
* DLL's DllCanUnloadNow entry point */
|
||||
static void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay)
|
||||
static void COMPOBJ_UninitProcess( void )
|
||||
{
|
||||
struct apartment_loaded_dll *entry, *next;
|
||||
EnterCriticalSection(&apt->cs);
|
||||
LIST_FOR_EACH_ENTRY_SAFE(entry, next, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
|
||||
{
|
||||
if (entry->dll->DllCanUnloadNow && (entry->dll->DllCanUnloadNow() == S_OK))
|
||||
{
|
||||
DWORD real_delay = delay;
|
||||
|
||||
if (real_delay == INFINITE)
|
||||
{
|
||||
/* DLLs that return multi-threaded objects aren't unloaded
|
||||
* straight away to cope for programs that have races between
|
||||
* last object destruction and threads in the DLLs that haven't
|
||||
* finished, despite DllCanUnloadNow returning S_OK */
|
||||
if (entry->multi_threaded)
|
||||
real_delay = 10 * 60 * 1000; /* 10 minutes */
|
||||
else
|
||||
real_delay = 0;
|
||||
}
|
||||
|
||||
if (!real_delay || (entry->unload_time && (entry->unload_time < GetTickCount())))
|
||||
{
|
||||
list_remove(&entry->entry);
|
||||
COMPOBJ_DllList_ReleaseRef(entry->dll, TRUE);
|
||||
HeapFree(GetProcessHeap(), 0, entry);
|
||||
}
|
||||
else
|
||||
entry->unload_time = GetTickCount() + real_delay;
|
||||
}
|
||||
else if (entry->unload_time)
|
||||
entry->unload_time = 0;
|
||||
}
|
||||
LeaveCriticalSection(&apt->cs);
|
||||
UnregisterClassW(wszAptWinClass, hProxyDll);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* This section contains OpenDllList implementation
|
||||
*/
|
||||
|
||||
/* caller must ensure that library_name is not already in the open dll list */
|
||||
static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret)
|
||||
static void COM_TlsDestroy(void)
|
||||
{
|
||||
OpenDll *entry;
|
||||
int len;
|
||||
HRESULT hr = S_OK;
|
||||
HANDLE hLibrary;
|
||||
DllCanUnloadNowFunc DllCanUnloadNow;
|
||||
DllGetClassObjectFunc DllGetClassObject;
|
||||
|
||||
TRACE("\n");
|
||||
|
||||
*ret = COMPOBJ_DllList_Get(library_name);
|
||||
if (*ret) return S_OK;
|
||||
|
||||
/* do this outside the csOpenDllList to avoid creating a lock dependency on
|
||||
* the loader lock */
|
||||
hLibrary = LoadLibraryExW(library_name, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||
if (!hLibrary)
|
||||
struct oletls *info = NtCurrentTeb()->ReservedForOle;
|
||||
if (info)
|
||||
{
|
||||
ERR("couldn't load in-process dll %s\n", debugstr_w(library_name));
|
||||
/* failure: DLL could not be loaded */
|
||||
return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
|
||||
if (info->apt) apartment_release(info->apt);
|
||||
if (info->errorinfo) IErrorInfo_Release(info->errorinfo);
|
||||
if (info->state) IUnknown_Release(info->state);
|
||||
if (info->spy) IUnknown_Release(info->spy);
|
||||
if (info->context_token) IObjContext_Release(info->context_token);
|
||||
HeapFree(GetProcessHeap(), 0, info);
|
||||
NtCurrentTeb()->ReservedForOle = NULL;
|
||||
}
|
||||
|
||||
DllCanUnloadNow = (void *)GetProcAddress(hLibrary, "DllCanUnloadNow");
|
||||
/* Note: failing to find DllCanUnloadNow is not a failure */
|
||||
DllGetClassObject = (void *)GetProcAddress(hLibrary, "DllGetClassObject");
|
||||
if (!DllGetClassObject)
|
||||
{
|
||||
/* failure: the dll did not export DllGetClassObject */
|
||||
ERR("couldn't find function DllGetClassObject in %s\n", debugstr_w(library_name));
|
||||
FreeLibrary(hLibrary);
|
||||
return CO_E_DLLNOTFOUND;
|
||||
}
|
||||
|
||||
EnterCriticalSection( &csOpenDllList );
|
||||
|
||||
*ret = COMPOBJ_DllList_Get(library_name);
|
||||
if (*ret)
|
||||
{
|
||||
/* another caller to this function already added the dll while we
|
||||
* weren't in the critical section */
|
||||
FreeLibrary(hLibrary);
|
||||
}
|
||||
else
|
||||
{
|
||||
len = strlenW(library_name);
|
||||
entry = HeapAlloc(GetProcessHeap(),0, sizeof(OpenDll));
|
||||
if (entry)
|
||||
entry->library_name = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
|
||||
if (entry && entry->library_name)
|
||||
{
|
||||
memcpy(entry->library_name, library_name, (len + 1)*sizeof(WCHAR));
|
||||
entry->library = hLibrary;
|
||||
entry->refs = 1;
|
||||
entry->DllCanUnloadNow = DllCanUnloadNow;
|
||||
entry->DllGetClassObject = DllGetClassObject;
|
||||
list_add_tail(&openDllList, &entry->entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, entry);
|
||||
hr = E_OUTOFMEMORY;
|
||||
FreeLibrary(hLibrary);
|
||||
}
|
||||
*ret = entry;
|
||||
}
|
||||
|
||||
LeaveCriticalSection( &csOpenDllList );
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static OpenDll *COMPOBJ_DllList_Get(LPCWSTR library_name)
|
||||
{
|
||||
OpenDll *ptr;
|
||||
OpenDll *ret = NULL;
|
||||
EnterCriticalSection(&csOpenDllList);
|
||||
LIST_FOR_EACH_ENTRY(ptr, &openDllList, OpenDll, entry)
|
||||
{
|
||||
if (!strcmpiW(library_name, ptr->library_name) &&
|
||||
(InterlockedIncrement(&ptr->refs) != 1) /* entry is being destroy if == 1 */)
|
||||
{
|
||||
ret = ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&csOpenDllList);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* pass FALSE for free_entry to release a reference without destroying the
|
||||
* entry if it reaches zero or TRUE otherwise */
|
||||
static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry, BOOL free_entry)
|
||||
{
|
||||
if (!InterlockedDecrement(&entry->refs) && free_entry)
|
||||
{
|
||||
EnterCriticalSection(&csOpenDllList);
|
||||
list_remove(&entry->entry);
|
||||
LeaveCriticalSection(&csOpenDllList);
|
||||
|
||||
TRACE("freeing %p\n", entry->library);
|
||||
FreeLibrary(entry->library);
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, entry->library_name);
|
||||
HeapFree(GetProcessHeap(), 0, entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* frees memory associated with active dll list */
|
||||
static void COMPOBJ_DllList_Free(void)
|
||||
{
|
||||
OpenDll *entry, *cursor2;
|
||||
EnterCriticalSection(&csOpenDllList);
|
||||
LIST_FOR_EACH_ENTRY_SAFE(entry, cursor2, &openDllList, OpenDll, entry)
|
||||
{
|
||||
list_remove(&entry->entry);
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, entry->library_name);
|
||||
HeapFree(GetProcessHeap(), 0, entry);
|
||||
}
|
||||
LeaveCriticalSection(&csOpenDllList);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
@ -2033,135 +2146,6 @@ HRESULT WINAPI CoRegisterClassObject(
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
static void COM_RevokeRegisteredClassObject(RegisteredClass *curClass)
|
||||
{
|
||||
list_remove(&curClass->entry);
|
||||
|
||||
if (curClass->runContext & CLSCTX_LOCAL_SERVER)
|
||||
RPC_StopLocalServer(curClass->RpcRegistration);
|
||||
|
||||
/*
|
||||
* Release the reference to the class object.
|
||||
*/
|
||||
IUnknown_Release(curClass->classObject);
|
||||
|
||||
if (curClass->pMarshaledData)
|
||||
{
|
||||
LARGE_INTEGER zero;
|
||||
memset(&zero, 0, sizeof(zero));
|
||||
IStream_Seek(curClass->pMarshaledData, zero, STREAM_SEEK_SET, NULL);
|
||||
CoReleaseMarshalData(curClass->pMarshaledData);
|
||||
IStream_Release(curClass->pMarshaledData);
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, curClass);
|
||||
}
|
||||
|
||||
static void COM_RevokeAllClasses(const struct apartment *apt)
|
||||
{
|
||||
RegisteredClass *curClass, *cursor;
|
||||
|
||||
EnterCriticalSection( &csRegisteredClassList );
|
||||
|
||||
LIST_FOR_EACH_ENTRY_SAFE(curClass, cursor, &RegisteredClassList, RegisteredClass, entry)
|
||||
{
|
||||
if (curClass->apartment_id == apt->oxid)
|
||||
COM_RevokeRegisteredClassObject(curClass);
|
||||
}
|
||||
|
||||
LeaveCriticalSection( &csRegisteredClassList );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* CoRevokeClassObject [OLE32.@]
|
||||
*
|
||||
* Removes a class object from the class registry.
|
||||
*
|
||||
* PARAMS
|
||||
* dwRegister [I] Cookie returned from CoRegisterClassObject().
|
||||
*
|
||||
* RETURNS
|
||||
* Success: S_OK.
|
||||
* Failure: HRESULT code.
|
||||
*
|
||||
* NOTES
|
||||
* Must be called from the same apartment that called CoRegisterClassObject(),
|
||||
* otherwise it will fail with RPC_E_WRONG_THREAD.
|
||||
*
|
||||
* SEE ALSO
|
||||
* CoRegisterClassObject
|
||||
*/
|
||||
HRESULT WINAPI CoRevokeClassObject(
|
||||
DWORD dwRegister)
|
||||
{
|
||||
HRESULT hr = E_INVALIDARG;
|
||||
RegisteredClass *curClass;
|
||||
APARTMENT *apt;
|
||||
|
||||
TRACE("(%08x)\n",dwRegister);
|
||||
|
||||
apt = COM_CurrentApt();
|
||||
if (!apt)
|
||||
{
|
||||
ERR("COM was not initialized\n");
|
||||
return CO_E_NOTINITIALIZED;
|
||||
}
|
||||
|
||||
EnterCriticalSection( &csRegisteredClassList );
|
||||
|
||||
LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
|
||||
{
|
||||
/*
|
||||
* Check if we have a match on the cookie.
|
||||
*/
|
||||
if (curClass->dwCookie == dwRegister)
|
||||
{
|
||||
if (curClass->apartment_id == apt->oxid)
|
||||
{
|
||||
COM_RevokeRegisteredClassObject(curClass);
|
||||
hr = S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("called from wrong apartment, should be called from %s\n",
|
||||
wine_dbgstr_longlong(curClass->apartment_id));
|
||||
hr = RPC_E_WRONG_THREAD;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection( &csRegisteredClassList );
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* COM_RegReadPath [internal]
|
||||
*
|
||||
* Reads a registry value and expands it when necessary
|
||||
*/
|
||||
static DWORD COM_RegReadPath(HKEY hkeyroot, const WCHAR *keyname, const WCHAR *valuename, WCHAR * dst, DWORD dstlen)
|
||||
{
|
||||
DWORD ret;
|
||||
HKEY key;
|
||||
DWORD keytype;
|
||||
WCHAR src[MAX_PATH];
|
||||
DWORD dwLength = dstlen * sizeof(WCHAR);
|
||||
|
||||
if((ret = RegOpenKeyExW(hkeyroot, keyname, 0, KEY_READ, &key)) == ERROR_SUCCESS) {
|
||||
if( (ret = RegQueryValueExW(key, NULL, NULL, &keytype, (LPBYTE)src, &dwLength)) == ERROR_SUCCESS ) {
|
||||
if (keytype == REG_EXPAND_SZ) {
|
||||
if (dstlen <= ExpandEnvironmentStringsW(src, dst, dstlen)) ret = ERROR_MORE_DATA;
|
||||
} else {
|
||||
lstrcpynW(dst, src, dstlen);
|
||||
}
|
||||
}
|
||||
RegCloseKey (key);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void get_threading_model(HKEY key, LPWSTR value, DWORD len)
|
||||
{
|
||||
static const WCHAR wszThreadingModel[] = {'T','h','r','e','a','d','i','n','g','M','o','d','e','l',0};
|
||||
|
|
Loading…
Reference in New Issue