📄 compobj.c
字号:
/*
* COMPOBJ library
*
* Copyright 1995 Martin von Loewis
* Copyright 1998 Justin Bradford
* Copyright 1999 Francis Beaudet
* Copyright 1999 Sylvain St-Germain
* Copyright 2002 Marcus Meissner
* Copyright 2004 Mike Hearn
* Copyright 2005-2006 Robert Shearman (for CodeWeavers)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* Note
* 1. COINIT_MULTITHREADED is 0; it is the lack of COINIT_APARTMENTTHREADED
* Therefore do not test against COINIT_MULTITHREADED
*
* TODO list: (items bunched together depend on each other)
*
* - 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 endpoint names for
* clients and servers to meet up
*
* - Make all ole interface marshaling use NDR to be wire compatible with
* native DCOM
*
*/
#include "config.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#define COBJMACROS
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winreg.h"
#include "winuser.h"
#include "objbase.h"
#include "ole2.h"
#include "ole2ver.h"
#include "compobj_private.h"
#include "wine/unicode.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
HINSTANCE OLE32_hInstance = 0; /* FIXME: make static ... */
#define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
/****************************************************************************
* This section defines variables internal to the COM module.
*
* TODO: Most of these things will have to be made thread-safe.
*/
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, 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 */
static CRITICAL_SECTION csApartment;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
0, 0, &csApartment,
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": csApartment") }
};
static CRITICAL_SECTION csApartment = { &critsect_debug, -1, 0, 0, 0, 0 };
struct registered_psclsid
{
struct list entry;
IID iid;
CLSID clsid;
};
/*
* This lock count counts the number of times CoInitialize is called. It is
* decreased every time CoUninitialize is called. When it hits 0, the COM
* libraries are freed
*/
static LONG s_COMLockCount = 0;
/* Reference count used by CoAddRefServerProcess/CoReleaseServerProcess */
static LONG s_COMServerProcessReferences = 0;
/*
* This linked list contains the list of registered class objects. These
* are mostly used to register the factories for out-of-proc servers of OLE
* objects.
*
* TODO: Make this data structure aware of inter-process communication. This
* means that parts of this will be exported to the Wine Server.
*/
typedef struct tagRegisteredClass
{
struct list entry;
CLSID classIdentifier;
OXID apartment_id;
LPUNKNOWN classObject;
DWORD runContext;
DWORD connectFlags;
DWORD dwCookie;
LPSTREAM pMarshaledData; /* FIXME: only really need to store OXID and IPID */
void *RpcRegistration;
} RegisteredClass;
static struct list RegisteredClassList = LIST_INIT(RegisteredClassList);
static CRITICAL_SECTION csRegisteredClassList;
static CRITICAL_SECTION_DEBUG class_cs_debug =
{
0, 0, &csRegisteredClassList,
{ &class_cs_debug.ProcessLocksList, &class_cs_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": csRegisteredClassList") }
};
static CRITICAL_SECTION csRegisteredClassList = { &class_cs_debug, -1, 0, 0, 0, 0 };
/*****************************************************************************
* This section contains OpenDllList definitions
*
* The OpenDllList contains only handles of dll loaded by CoGetClassObject or
* other functions that do LoadLibrary _without_ giving back a HMODULE.
* Without this list these handles would never be freed.
*
* FIXME: a DLL that says OK when asked for unloading is unloaded in the
* next unload-call but not before 600 sec.
*/
typedef HRESULT (CALLBACK *DllGetClassObjectFunc)(REFCLSID clsid, REFIID iid, LPVOID *ppv);
typedef HRESULT (WINAPI *DllCanUnloadNowFunc)(void);
typedef struct tagOpenDll
{
LONG refs;
LPWSTR library_name;
HANDLE library;
DllGetClassObjectFunc DllGetClassObject;
DllCanUnloadNowFunc DllCanUnloadNow;
struct list entry;
} OpenDll;
static struct list openDllList = LIST_INIT(openDllList);
static CRITICAL_SECTION csOpenDllList;
static CRITICAL_SECTION_DEBUG dll_cs_debug =
{
0, 0, &csOpenDllList,
{ &dll_cs_debug.ProcessLocksList, &dll_cs_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": csOpenDllList") }
};
static CRITICAL_SECTION csOpenDllList = { &dll_cs_debug, -1, 0, 0, 0, 0 };
struct apartment_loaded_dll
{
struct list entry;
OpenDll *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,
REFCLSID rclsid, REFIID riid, void **ppv);
static void apartment_freeunusedlibraries(struct apartment *apt);
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);
static DWORD COM_RegReadPath(HKEY hkeyroot, const WCHAR *keyname, const WCHAR *valuename, WCHAR * dst, DWORD dstlen);
static void COMPOBJ_InitProcess( void )
{
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 (ie 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
* COM_AptWndProc 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 = OLE32_hInstance;
wclass.lpszClassName = wszAptWinClass;
RegisterClassW(&wclass);
}
static void COMPOBJ_UninitProcess( void )
{
UnregisterClassW(wszAptWinClass, OLE32_hInstance);
}
static void COM_TlsDestroy(void)
{
struct oletls *info = NtCurrentTeb()->ReservedForOle;
if (info)
{
if (info->apt) apartment_release(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.
*/
/* 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)
{
APARTMENT *apt;
TRACE("creating new apartment, model=%d\n", model);
apt = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*apt));
apt->tid = GetCurrentThreadId();
list_init(&apt->proxies);
list_init(&apt->stubmgrs);
list_init(&apt->psclsids);
list_init(&apt->loaded_dlls);
apt->ipidc = 0;
apt->refs = 1;
apt->remunk_exported = FALSE;
apt->oidc = 1;
InitializeCriticalSection(&apt->cs);
DEBUG_SET_CRITSEC_NAME(&apt->cs, "apartment");
apt->multi_threaded = !(model & COINIT_APARTMENTTHREADED);
if (apt->multi_threaded)
{
/* FIXME: should be randomly generated by in an RPC call to rpcss */
apt->oxid = ((OXID)GetCurrentProcessId() << 32) | 0xcafe;
}
else
{
/* FIXME: should be randomly generated by in an RPC call to rpcss */
apt->oxid = ((OXID)GetCurrentProcessId() << 32) | GetCurrentThreadId();
}
TRACE("Created apartment on OXID %s\n", wine_dbgstr_longlong(apt->oxid));
list_add_head(&apts, &apt->entry);
return apt;
}
/* 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 *apartment_get_or_create(DWORD model)
{
APARTMENT *apt = COM_CurrentApt();
if (!apt)
{
if (model & COINIT_APARTMENTTHREADED)
{
EnterCriticalSection(&csApartment);
apt = apartment_construct(model);
if (!MainApartment)
{
MainApartment = apt;
apt->main = TRUE;
TRACE("Created main-threaded apartment with OXID %s\n", wine_dbgstr_longlong(apt->oxid));
}
LeaveCriticalSection(&csApartment);
}
else
{
EnterCriticalSection(&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));
apartment_addref(MTA);
}
else
MTA = apartment_construct(model);
apt = MTA;
LeaveCriticalSection(&csApartment);
}
COM_CurrentInfo()->apt = apt;
}
return apt;
}
static inline BOOL apartment_is_model(const APARTMENT *apt, DWORD model)
{
return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
}
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;
}
DWORD apartment_release(struct apartment *apt)
{
DWORD ret;
EnterCriticalSection(&csApartment);
ret = InterlockedDecrement(&apt->refs);
TRACE("%s: after = %d\n", wine_dbgstr_longlong(apt->oxid), ret);
/* destruction stuff that needs to happen under csApartment CS */
if (ret == 0)
{
if (apt == MTA) MTA = NULL;
else if (apt == MainApartment) MainApartment = NULL;
list_remove(&apt->entry);
}
LeaveCriticalSection(&csApartment);
if (ret == 0)
{
struct list *cursor, *cursor2;
TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid));
/* Release the references to the registered class objects */
COM_RevokeAllClasses(apt);
/* no locking is needed for this apartment, because no other thread
* can access it at this point */
apartment_disconnectproxies(apt);
if (apt->win) DestroyWindow(apt->win);
if (apt->host_apt_tid) PostThreadMessageW(apt->host_apt_tid, WM_QUIT, 0, 0);
LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->stubmgrs)
{
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);
}
LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->psclsids)
{
struct registered_psclsid *registered_psclsid =
LIST_ENTRY(cursor, struct registered_psclsid, entry);
list_remove(®istered_psclsid->entry);
HeapFree(GetProcessHeap(), 0, registered_psclsid);
}
/* 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);
/* free as many unused libraries as possible... */
apartment_freeunusedlibraries(apt);
/* ... and free the memory for the apartment loaded dll entry and
* release the dll list reference without freeing the library for the
* rest */
while ((cursor = list_head(&apt->loaded_dlls)))
{
struct apartment_loaded_dll *apartment_loaded_dll = LIST_ENTRY(cursor, struct apartment_loaded_dll, entry);
COMPOBJ_DllList_ReleaseRef(apartment_loaded_dll->dll, FALSE);
list_remove(cursor);
HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
}
DEBUG_CLEAR_CRITSEC_NAME(&apt->cs);
DeleteCriticalSection(&apt->cs);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -