📄 compobj.c
字号:
if (!apt)
{
ERR("apartment not initialised\n");
return CO_E_NOTINITIALIZED;
}
EnterCriticalSection(&apt->cs);
LIST_FOR_EACH_ENTRY(registered_psclsid, &apt->psclsids, struct registered_psclsid, entry)
if (IsEqualIID(®istered_psclsid->iid, riid))
{
registered_psclsid->clsid = *rclsid;
LeaveCriticalSection(&apt->cs);
return S_OK;
}
registered_psclsid = HeapAlloc(GetProcessHeap(), 0, sizeof(struct registered_psclsid));
if (!registered_psclsid)
{
LeaveCriticalSection(&apt->cs);
return E_OUTOFMEMORY;
}
registered_psclsid->iid = *riid;
registered_psclsid->clsid = *rclsid;
list_add_head(&apt->psclsids, ®istered_psclsid->entry);
LeaveCriticalSection(&apt->cs);
return S_OK;
}
/***
* COM_GetRegisteredClassObject
*
* This internal method is used to scan the registered class list to
* find a class object.
*
* Params:
* rclsid Class ID of the class to find.
* dwClsContext Class context to match.
* ppv [out] returns a pointer to the class object. Complying
* to normal COM usage, this method will increase the
* reference count on this object.
*/
static HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSID rclsid,
DWORD dwClsContext, LPUNKNOWN* ppUnk)
{
HRESULT hr = S_FALSE;
RegisteredClass *curClass;
/*
* Sanity check
*/
assert(ppUnk!=0);
EnterCriticalSection( &csRegisteredClassList );
LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
{
/*
* Check if we have a match on the class ID and context.
*/
if ((apt->oxid == curClass->apartment_id) &&
(dwClsContext & curClass->runContext) &&
IsEqualGUID(&(curClass->classIdentifier), rclsid))
{
/*
* We have a match, return the pointer to the class object.
*/
*ppUnk = curClass->classObject;
IUnknown_AddRef(curClass->classObject);
hr = S_OK;
break;
}
}
LeaveCriticalSection( &csRegisteredClassList );
return hr;
}
/******************************************************************************
* CoRegisterClassObject [OLE32.@]
*
* Registers the class object for a given class ID. Servers housed in EXE
* 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,
* CO_E_OBJISREG if the object is already registered. We should not return this.
*
* SEE ALSO
* CoRevokeClassObject, CoGetClassObject
*
* NOTES
* In-process objects are only registered for the current apartment.
* CoGetClassObject() and CoCreateInstance() will not return objects registered
* in other apartments.
*
* BUGS
* MSDN claims that multiple interface registrations are legal, but we
* can't do that with our current implementation.
*/
HRESULT WINAPI CoRegisterClassObject(
REFCLSID rclsid,
LPUNKNOWN pUnk,
DWORD dwClsContext,
DWORD flags,
LPDWORD lpdwRegister)
{
RegisteredClass* newClass;
LPUNKNOWN foundObject;
HRESULT hr;
APARTMENT *apt;
TRACE("(%s,%p,0x%08x,0x%08x,%p)\n",
debugstr_guid(rclsid),pUnk,dwClsContext,flags,lpdwRegister);
if ( (lpdwRegister==0) || (pUnk==0) )
return E_INVALIDARG;
apt = COM_CurrentApt();
if (!apt)
{
ERR("COM was not initialized\n");
return CO_E_NOTINITIALIZED;
}
*lpdwRegister = 0;
/* REGCLS_MULTIPLEUSE implies registering as inproc server. This is what
* differentiates the flag from REGCLS_MULTI_SEPARATE. */
if (flags & REGCLS_MULTIPLEUSE)
dwClsContext |= CLSCTX_INPROC_SERVER;
/*
* First, check if the class is already registered.
* If it is, this should cause an error.
*/
hr = COM_GetRegisteredClassObject(apt, rclsid, dwClsContext, &foundObject);
if (hr == S_OK) {
if (flags & REGCLS_MULTIPLEUSE) {
if (dwClsContext & CLSCTX_LOCAL_SERVER)
hr = CoLockObjectExternal(foundObject, TRUE, FALSE);
IUnknown_Release(foundObject);
return hr;
}
IUnknown_Release(foundObject);
ERR("object already registered for class %s\n", debugstr_guid(rclsid));
return CO_E_OBJISREG;
}
newClass = HeapAlloc(GetProcessHeap(), 0, sizeof(RegisteredClass));
if ( newClass == NULL )
return E_OUTOFMEMORY;
newClass->classIdentifier = *rclsid;
newClass->apartment_id = apt->oxid;
newClass->runContext = dwClsContext;
newClass->connectFlags = flags;
newClass->pMarshaledData = NULL;
newClass->RpcRegistration = NULL;
/*
* Use the address of the chain node as the cookie since we are sure it's
* unique. FIXME: not on 64-bit platforms.
*/
newClass->dwCookie = (DWORD)newClass;
/*
* Since we're making a copy of the object pointer, we have to increase its
* reference count.
*/
newClass->classObject = pUnk;
IUnknown_AddRef(newClass->classObject);
EnterCriticalSection( &csRegisteredClassList );
list_add_tail(&RegisteredClassList, &newClass->entry);
LeaveCriticalSection( &csRegisteredClassList );
*lpdwRegister = newClass->dwCookie;
if (dwClsContext & CLSCTX_LOCAL_SERVER) {
hr = CreateStreamOnHGlobal(0, TRUE, &newClass->pMarshaledData);
if (hr) {
FIXME("Failed to create stream on hglobal, %x\n", hr);
return hr;
}
hr = CoMarshalInterface(newClass->pMarshaledData, &IID_IClassFactory,
newClass->classObject, MSHCTX_LOCAL, NULL,
MSHLFLAGS_TABLESTRONG);
if (hr) {
FIXME("CoMarshalInterface failed, %x!\n",hr);
return hr;
}
hr = RPC_StartLocalServer(&newClass->classIdentifier,
newClass->pMarshaledData,
flags & (REGCLS_MULTIPLEUSE|REGCLS_MULTI_SEPARATE),
&newClass->RpcRegistration);
}
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);
}
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};
DWORD keytype;
DWORD ret;
DWORD dwLength = len * sizeof(WCHAR);
ret = RegQueryValueExW(key, wszThreadingModel, NULL, &keytype, (LPBYTE)value, &dwLength);
if ((ret != ERROR_SUCCESS) || (keytype != REG_SZ))
value[0] = '\0';
}
static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll,
REFCLSID rclsid, REFIID riid, void **ppv)
{
static const WCHAR wszApartment[] = {'A','p','a','r','t','m','e','n','t',0};
static const WCHAR wszFree[] = {'F','r','e','e',0};
static const WCHAR wszBoth[] = {'B','o','t','h',0};
WCHAR dllpath[MAX_PATH+1];
WCHAR threading_model[10 /* strlenW(L"apartment")+1 */];
get_threading_model(hkeydll, threading_model, ARRAYSIZE(threading_model));
/* "Apartment" */
if (!strcmpiW(threading_model, wszApartment))
{
if (apt->multi_threaded)
return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, hkeydll, rclsid, riid, ppv);
}
/* "Free" */
else if (!strcmpiW(threading_model, wszFree))
{
if (!apt->multi_threaded)
return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, hkeydll, rclsid, riid, ppv);
}
/* everything except "Apartment", "Free" and "Both" */
else if (strcmpiW(threading_model, wszBoth))
{
/* everything else is main-threaded */
if (threading_model[0])
FIXME("unrecognised threading model %s for object %s, should be main-threaded?\n",
debugstr_w(threading_model), debugstr_guid(rclsid));
if (apt->multi_threaded || !apt->main)
return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, hkeydll, rclsid, riid, ppv);
}
if (COM_RegReadPath(hkeydll, NULL, NULL, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
{
/* failure: CLSID is not found in registry */
WARN("class %s not registered inproc\n", debugstr_guid(rclsid));
return REGDB_E_CLASSNOTREG;
}
return apartment_getclassobject(apt, dllpath, rclsid, riid, ppv);
}
/***********************************************************************
* CoGetClassObject [OLE32.@]
*
* Creates an object of the specified class.
*
* PARAMS
* rclsid [I] Class ID to create an instance of.
* dwClsContext [I] Flags to restrict the location of the created instance.
* pServerInfo [I] Optional. Details for connecting to a remote server.
* iid [I] The ID of the interface of the instance to return.
* ppv [O] On returns, contains a pointer to the specified interface of the object.
*
* RETURNS
* Success: S_OK
* Failure: HRESULT code.
*
* NOTES
* The dwClsContext parameter can be one or more of the following:
*| CLSCTX_INPROC_SERVER - Use an in
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -