3208 lines
90 KiB
C
3208 lines
90 KiB
C
/*
|
|
* Copyright 2005 Juan Lang
|
|
* 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
|
|
*/
|
|
|
|
#define COBJMACROS
|
|
#define NONAMELESSUNION
|
|
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#define USE_COM_CONTEXT_DEF
|
|
#include "objbase.h"
|
|
#include "ctxtcall.h"
|
|
#include "oleauto.h"
|
|
#include "dde.h"
|
|
#include "winternl.h"
|
|
|
|
#include "combase_private.h"
|
|
|
|
#include "wine/debug.h"
|
|
#include "wine/heap.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(ole);
|
|
|
|
HINSTANCE hProxyDll;
|
|
|
|
/* Ole32 exports */
|
|
extern void WINAPI DestroyRunningObjectTable(void);
|
|
extern HRESULT WINAPI Ole32DllGetClassObject(REFCLSID rclsid, REFIID riid, void **obj);
|
|
|
|
/*
|
|
* 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 com_lockcount;
|
|
|
|
static LONG com_server_process_refcount;
|
|
|
|
struct comclassredirect_data
|
|
{
|
|
ULONG size;
|
|
ULONG flags;
|
|
DWORD model;
|
|
GUID clsid;
|
|
GUID alias;
|
|
GUID clsid2;
|
|
GUID tlbid;
|
|
ULONG name_len;
|
|
ULONG name_offset;
|
|
ULONG progid_len;
|
|
ULONG progid_offset;
|
|
ULONG clrdata_len;
|
|
ULONG clrdata_offset;
|
|
DWORD miscstatus;
|
|
DWORD miscstatuscontent;
|
|
DWORD miscstatusthumbnail;
|
|
DWORD miscstatusicon;
|
|
DWORD miscstatusdocprint;
|
|
};
|
|
|
|
struct ifacepsredirect_data
|
|
{
|
|
ULONG size;
|
|
DWORD mask;
|
|
GUID iid;
|
|
ULONG nummethods;
|
|
GUID tlbid;
|
|
GUID base;
|
|
ULONG name_len;
|
|
ULONG name_offset;
|
|
};
|
|
|
|
struct progidredirect_data
|
|
{
|
|
ULONG size;
|
|
DWORD reserved;
|
|
ULONG clsid_offset;
|
|
};
|
|
|
|
struct init_spy
|
|
{
|
|
struct list entry;
|
|
IInitializeSpy *spy;
|
|
unsigned int id;
|
|
};
|
|
|
|
struct registered_ps
|
|
{
|
|
struct list entry;
|
|
IID iid;
|
|
CLSID clsid;
|
|
};
|
|
|
|
static struct list registered_proxystubs = LIST_INIT(registered_proxystubs);
|
|
|
|
static CRITICAL_SECTION cs_registered_ps;
|
|
static CRITICAL_SECTION_DEBUG psclsid_cs_debug =
|
|
{
|
|
0, 0, &cs_registered_ps,
|
|
{ &psclsid_cs_debug.ProcessLocksList, &psclsid_cs_debug.ProcessLocksList },
|
|
0, 0, { (DWORD_PTR)(__FILE__ ": cs_registered_psclsid_list") }
|
|
};
|
|
static CRITICAL_SECTION cs_registered_ps = { &psclsid_cs_debug, -1, 0, 0, 0, 0 };
|
|
|
|
struct registered_class
|
|
{
|
|
struct list entry;
|
|
CLSID clsid;
|
|
OXID apartment_id;
|
|
IUnknown *object;
|
|
DWORD clscontext;
|
|
DWORD flags;
|
|
unsigned int cookie;
|
|
unsigned int rpcss_cookie;
|
|
};
|
|
|
|
static struct list registered_classes = LIST_INIT(registered_classes);
|
|
|
|
static CRITICAL_SECTION registered_classes_cs;
|
|
static CRITICAL_SECTION_DEBUG registered_classes_cs_debug =
|
|
{
|
|
0, 0, ®istered_classes_cs,
|
|
{ ®istered_classes_cs_debug.ProcessLocksList, ®istered_classes_cs_debug.ProcessLocksList },
|
|
0, 0, { (DWORD_PTR)(__FILE__ ": registered_classes_cs") }
|
|
};
|
|
static CRITICAL_SECTION registered_classes_cs = { ®istered_classes_cs_debug, -1, 0, 0, 0, 0 };
|
|
|
|
IUnknown * com_get_registered_class_object(const struct apartment *apt, REFCLSID rclsid, DWORD clscontext)
|
|
{
|
|
struct registered_class *cur;
|
|
IUnknown *object = NULL;
|
|
|
|
EnterCriticalSection(®istered_classes_cs);
|
|
|
|
LIST_FOR_EACH_ENTRY(cur, ®istered_classes, struct registered_class, entry)
|
|
{
|
|
if ((apt->oxid == cur->apartment_id) &&
|
|
(clscontext & cur->clscontext) &&
|
|
IsEqualGUID(&cur->clsid, rclsid))
|
|
{
|
|
object = cur->object;
|
|
IUnknown_AddRef(cur->object);
|
|
break;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(®istered_classes_cs);
|
|
|
|
return object;
|
|
}
|
|
|
|
static struct init_spy *get_spy_entry(struct tlsdata *tlsdata, unsigned int id)
|
|
{
|
|
struct init_spy *spy;
|
|
|
|
LIST_FOR_EACH_ENTRY(spy, &tlsdata->spies, struct init_spy, entry)
|
|
{
|
|
if (id == spy->id && spy->spy)
|
|
return spy;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static NTSTATUS create_key(HKEY *retkey, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr)
|
|
{
|
|
NTSTATUS status = NtCreateKey((HANDLE *)retkey, access, attr, 0, NULL, 0, NULL);
|
|
|
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
HANDLE subkey, root = attr->RootDirectory;
|
|
WCHAR *buffer = attr->ObjectName->Buffer;
|
|
DWORD attrs, pos = 0, i = 0, len = attr->ObjectName->Length / sizeof(WCHAR);
|
|
UNICODE_STRING str;
|
|
|
|
while (i < len && buffer[i] != '\\') i++;
|
|
if (i == len) return status;
|
|
|
|
attrs = attr->Attributes;
|
|
attr->ObjectName = &str;
|
|
|
|
while (i < len)
|
|
{
|
|
str.Buffer = buffer + pos;
|
|
str.Length = (i - pos) * sizeof(WCHAR);
|
|
status = NtCreateKey(&subkey, access, attr, 0, NULL, 0, NULL);
|
|
if (attr->RootDirectory != root) NtClose(attr->RootDirectory);
|
|
if (status) return status;
|
|
attr->RootDirectory = subkey;
|
|
while (i < len && buffer[i] == '\\') i++;
|
|
pos = i;
|
|
while (i < len && buffer[i] != '\\') i++;
|
|
}
|
|
str.Buffer = buffer + pos;
|
|
str.Length = (i - pos) * sizeof(WCHAR);
|
|
attr->Attributes = attrs;
|
|
status = NtCreateKey((HANDLE *)retkey, access, attr, 0, NULL, 0, NULL);
|
|
if (attr->RootDirectory != root) NtClose(attr->RootDirectory);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static HKEY classes_root_hkey;
|
|
|
|
static HKEY create_classes_root_hkey(DWORD access)
|
|
{
|
|
HKEY hkey, ret = 0;
|
|
OBJECT_ATTRIBUTES attr;
|
|
UNICODE_STRING name;
|
|
|
|
attr.Length = sizeof(attr);
|
|
attr.RootDirectory = 0;
|
|
attr.ObjectName = &name;
|
|
attr.Attributes = 0;
|
|
attr.SecurityDescriptor = NULL;
|
|
attr.SecurityQualityOfService = NULL;
|
|
RtlInitUnicodeString(&name, L"\\Registry\\Machine\\Software\\Classes");
|
|
|
|
if (create_key( &hkey, access, &attr )) return 0;
|
|
TRACE( "%s -> %p\n", debugstr_w(attr.ObjectName->Buffer), hkey );
|
|
|
|
if (!(access & KEY_WOW64_64KEY))
|
|
{
|
|
if (!(ret = InterlockedCompareExchangePointer( (void **)&classes_root_hkey, hkey, 0 )))
|
|
ret = hkey;
|
|
else
|
|
NtClose( hkey ); /* somebody beat us to it */
|
|
}
|
|
else
|
|
ret = hkey;
|
|
return ret;
|
|
}
|
|
|
|
static HKEY get_classes_root_hkey(HKEY hkey, REGSAM access);
|
|
|
|
static LSTATUS create_classes_key(HKEY hkey, const WCHAR *name, REGSAM access, HKEY *retkey)
|
|
{
|
|
OBJECT_ATTRIBUTES attr;
|
|
UNICODE_STRING nameW;
|
|
|
|
if (!(hkey = get_classes_root_hkey(hkey, access)))
|
|
return ERROR_INVALID_HANDLE;
|
|
|
|
attr.Length = sizeof(attr);
|
|
attr.RootDirectory = hkey;
|
|
attr.ObjectName = &nameW;
|
|
attr.Attributes = 0;
|
|
attr.SecurityDescriptor = NULL;
|
|
attr.SecurityQualityOfService = NULL;
|
|
RtlInitUnicodeString( &nameW, name );
|
|
|
|
return RtlNtStatusToDosError(create_key(retkey, access, &attr));
|
|
}
|
|
|
|
static HKEY get_classes_root_hkey(HKEY hkey, REGSAM access)
|
|
{
|
|
HKEY ret = hkey;
|
|
const BOOL is_win64 = sizeof(void*) > sizeof(int);
|
|
const BOOL force_wow32 = is_win64 && (access & KEY_WOW64_32KEY);
|
|
|
|
if (hkey == HKEY_CLASSES_ROOT &&
|
|
((access & KEY_WOW64_64KEY) || !(ret = classes_root_hkey)))
|
|
ret = create_classes_root_hkey(MAXIMUM_ALLOWED | (access & KEY_WOW64_64KEY));
|
|
if (force_wow32 && ret && ret == classes_root_hkey)
|
|
{
|
|
access &= ~KEY_WOW64_32KEY;
|
|
if (create_classes_key(classes_root_hkey, L"Wow6432Node", access, &hkey))
|
|
return 0;
|
|
ret = hkey;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static LSTATUS open_classes_key(HKEY hkey, const WCHAR *name, REGSAM access, HKEY *retkey)
|
|
{
|
|
OBJECT_ATTRIBUTES attr;
|
|
UNICODE_STRING nameW;
|
|
|
|
if (!(hkey = get_classes_root_hkey(hkey, access)))
|
|
return ERROR_INVALID_HANDLE;
|
|
|
|
attr.Length = sizeof(attr);
|
|
attr.RootDirectory = hkey;
|
|
attr.ObjectName = &nameW;
|
|
attr.Attributes = 0;
|
|
attr.SecurityDescriptor = NULL;
|
|
attr.SecurityQualityOfService = NULL;
|
|
RtlInitUnicodeString( &nameW, name );
|
|
|
|
return RtlNtStatusToDosError(NtOpenKey((HANDLE *)retkey, access, &attr));
|
|
}
|
|
|
|
HRESULT open_key_for_clsid(REFCLSID clsid, const WCHAR *keyname, REGSAM access, HKEY *subkey)
|
|
{
|
|
static const WCHAR clsidW[] = L"CLSID\\";
|
|
WCHAR path[CHARS_IN_GUID + ARRAY_SIZE(clsidW) - 1];
|
|
LONG res;
|
|
HKEY key;
|
|
|
|
lstrcpyW(path, clsidW);
|
|
StringFromGUID2(clsid, path + lstrlenW(clsidW), CHARS_IN_GUID);
|
|
res = open_classes_key(HKEY_CLASSES_ROOT, path, keyname ? KEY_READ : access, &key);
|
|
if (res == ERROR_FILE_NOT_FOUND)
|
|
return REGDB_E_CLASSNOTREG;
|
|
else if (res != ERROR_SUCCESS)
|
|
return REGDB_E_READREGDB;
|
|
|
|
if (!keyname)
|
|
{
|
|
*subkey = key;
|
|
return S_OK;
|
|
}
|
|
|
|
res = open_classes_key(key, keyname, access, subkey);
|
|
RegCloseKey(key);
|
|
if (res == ERROR_FILE_NOT_FOUND)
|
|
return REGDB_E_KEYMISSING;
|
|
else if (res != ERROR_SUCCESS)
|
|
return REGDB_E_READREGDB;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/* open HKCR\\AppId\\{string form of appid clsid} key */
|
|
HRESULT open_appidkey_from_clsid(REFCLSID clsid, REGSAM access, HKEY *subkey)
|
|
{
|
|
static const WCHAR appidkeyW[] = L"AppId\\";
|
|
DWORD res;
|
|
WCHAR buf[CHARS_IN_GUID];
|
|
WCHAR keyname[ARRAY_SIZE(appidkeyW) + CHARS_IN_GUID];
|
|
DWORD size;
|
|
HKEY hkey;
|
|
DWORD type;
|
|
HRESULT hr;
|
|
|
|
/* read the AppID value under the class's key */
|
|
hr = open_key_for_clsid(clsid, NULL, KEY_READ, &hkey);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
size = sizeof(buf);
|
|
res = RegQueryValueExW(hkey, L"AppId", NULL, &type, (LPBYTE)buf, &size);
|
|
RegCloseKey(hkey);
|
|
if (res == ERROR_FILE_NOT_FOUND)
|
|
return REGDB_E_KEYMISSING;
|
|
else if (res != ERROR_SUCCESS || type!=REG_SZ)
|
|
return REGDB_E_READREGDB;
|
|
|
|
lstrcpyW(keyname, appidkeyW);
|
|
lstrcatW(keyname, buf);
|
|
res = open_classes_key(HKEY_CLASSES_ROOT, keyname, access, subkey);
|
|
if (res == ERROR_FILE_NOT_FOUND)
|
|
return REGDB_E_KEYMISSING;
|
|
else if (res != ERROR_SUCCESS)
|
|
return REGDB_E_READREGDB;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* InternalIsProcessInitialized (combase.@)
|
|
*/
|
|
BOOL WINAPI InternalIsProcessInitialized(void)
|
|
{
|
|
struct apartment *apt;
|
|
|
|
if (!(apt = apartment_get_current_or_mta()))
|
|
return FALSE;
|
|
apartment_release(apt);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* InternalTlsAllocData (combase.@)
|
|
*/
|
|
HRESULT WINAPI InternalTlsAllocData(struct tlsdata **data)
|
|
{
|
|
if (!(*data = heap_alloc_zero(sizeof(**data))))
|
|
return E_OUTOFMEMORY;
|
|
|
|
list_init(&(*data)->spies);
|
|
NtCurrentTeb()->ReservedForOle = *data;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static void com_cleanup_tlsdata(void)
|
|
{
|
|
struct tlsdata *tlsdata = NtCurrentTeb()->ReservedForOle;
|
|
struct init_spy *cursor, *cursor2;
|
|
|
|
if (!tlsdata)
|
|
return;
|
|
|
|
if (tlsdata->apt)
|
|
apartment_release(tlsdata->apt);
|
|
if (tlsdata->errorinfo)
|
|
IErrorInfo_Release(tlsdata->errorinfo);
|
|
if (tlsdata->state)
|
|
IUnknown_Release(tlsdata->state);
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &tlsdata->spies, struct init_spy, entry)
|
|
{
|
|
list_remove(&cursor->entry);
|
|
if (cursor->spy)
|
|
IInitializeSpy_Release(cursor->spy);
|
|
heap_free(cursor);
|
|
}
|
|
|
|
if (tlsdata->context_token)
|
|
IObjContext_Release(tlsdata->context_token);
|
|
|
|
heap_free(tlsdata);
|
|
NtCurrentTeb()->ReservedForOle = NULL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* FreePropVariantArray (combase.@)
|
|
*/
|
|
HRESULT WINAPI FreePropVariantArray(ULONG count, PROPVARIANT *rgvars)
|
|
{
|
|
ULONG i;
|
|
|
|
TRACE("%u, %p.\n", count, rgvars);
|
|
|
|
if (!rgvars)
|
|
return E_INVALIDARG;
|
|
|
|
for (i = 0; i < count; ++i)
|
|
PropVariantClear(&rgvars[i]);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT propvar_validatetype(VARTYPE vt)
|
|
{
|
|
switch (vt)
|
|
{
|
|
case VT_EMPTY:
|
|
case VT_NULL:
|
|
case VT_I1:
|
|
case VT_I2:
|
|
case VT_I4:
|
|
case VT_I8:
|
|
case VT_R4:
|
|
case VT_R8:
|
|
case VT_CY:
|
|
case VT_DATE:
|
|
case VT_BSTR:
|
|
case VT_ERROR:
|
|
case VT_BOOL:
|
|
case VT_DECIMAL:
|
|
case VT_UI1:
|
|
case VT_UI2:
|
|
case VT_UI4:
|
|
case VT_UI8:
|
|
case VT_INT:
|
|
case VT_UINT:
|
|
case VT_LPSTR:
|
|
case VT_LPWSTR:
|
|
case VT_FILETIME:
|
|
case VT_BLOB:
|
|
case VT_DISPATCH:
|
|
case VT_UNKNOWN:
|
|
case VT_STREAM:
|
|
case VT_STORAGE:
|
|
case VT_STREAMED_OBJECT:
|
|
case VT_STORED_OBJECT:
|
|
case VT_BLOB_OBJECT:
|
|
case VT_CF:
|
|
case VT_CLSID:
|
|
case VT_I1|VT_VECTOR:
|
|
case VT_I2|VT_VECTOR:
|
|
case VT_I4|VT_VECTOR:
|
|
case VT_I8|VT_VECTOR:
|
|
case VT_R4|VT_VECTOR:
|
|
case VT_R8|VT_VECTOR:
|
|
case VT_CY|VT_VECTOR:
|
|
case VT_DATE|VT_VECTOR:
|
|
case VT_BSTR|VT_VECTOR:
|
|
case VT_ERROR|VT_VECTOR:
|
|
case VT_BOOL|VT_VECTOR:
|
|
case VT_VARIANT|VT_VECTOR:
|
|
case VT_UI1|VT_VECTOR:
|
|
case VT_UI2|VT_VECTOR:
|
|
case VT_UI4|VT_VECTOR:
|
|
case VT_UI8|VT_VECTOR:
|
|
case VT_LPSTR|VT_VECTOR:
|
|
case VT_LPWSTR|VT_VECTOR:
|
|
case VT_FILETIME|VT_VECTOR:
|
|
case VT_CF|VT_VECTOR:
|
|
case VT_CLSID|VT_VECTOR:
|
|
case VT_ARRAY|VT_I1:
|
|
case VT_ARRAY|VT_UI1:
|
|
case VT_ARRAY|VT_I2:
|
|
case VT_ARRAY|VT_UI2:
|
|
case VT_ARRAY|VT_I4:
|
|
case VT_ARRAY|VT_UI4:
|
|
case VT_ARRAY|VT_INT:
|
|
case VT_ARRAY|VT_UINT:
|
|
case VT_ARRAY|VT_R4:
|
|
case VT_ARRAY|VT_R8:
|
|
case VT_ARRAY|VT_CY:
|
|
case VT_ARRAY|VT_DATE:
|
|
case VT_ARRAY|VT_BSTR:
|
|
case VT_ARRAY|VT_BOOL:
|
|
case VT_ARRAY|VT_DECIMAL:
|
|
case VT_ARRAY|VT_DISPATCH:
|
|
case VT_ARRAY|VT_UNKNOWN:
|
|
case VT_ARRAY|VT_ERROR:
|
|
case VT_ARRAY|VT_VARIANT:
|
|
return S_OK;
|
|
}
|
|
WARN("Bad type %d\n", vt);
|
|
return STG_E_INVALIDPARAMETER;
|
|
}
|
|
|
|
static void propvar_free_cf_array(ULONG count, CLIPDATA *data)
|
|
{
|
|
ULONG i;
|
|
for (i = 0; i < count; ++i)
|
|
CoTaskMemFree(data[i].pClipData);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* PropVariantClear (combase.@)
|
|
*/
|
|
HRESULT WINAPI PropVariantClear(PROPVARIANT *pvar)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TRACE("%p.\n", pvar);
|
|
|
|
if (!pvar)
|
|
return S_OK;
|
|
|
|
hr = propvar_validatetype(pvar->vt);
|
|
if (FAILED(hr))
|
|
{
|
|
memset(pvar, 0, sizeof(*pvar));
|
|
return hr;
|
|
}
|
|
|
|
switch (pvar->vt)
|
|
{
|
|
case VT_EMPTY:
|
|
case VT_NULL:
|
|
case VT_I1:
|
|
case VT_I2:
|
|
case VT_I4:
|
|
case VT_I8:
|
|
case VT_R4:
|
|
case VT_R8:
|
|
case VT_CY:
|
|
case VT_DATE:
|
|
case VT_ERROR:
|
|
case VT_BOOL:
|
|
case VT_DECIMAL:
|
|
case VT_UI1:
|
|
case VT_UI2:
|
|
case VT_UI4:
|
|
case VT_UI8:
|
|
case VT_INT:
|
|
case VT_UINT:
|
|
case VT_FILETIME:
|
|
break;
|
|
case VT_DISPATCH:
|
|
case VT_UNKNOWN:
|
|
case VT_STREAM:
|
|
case VT_STREAMED_OBJECT:
|
|
case VT_STORAGE:
|
|
case VT_STORED_OBJECT:
|
|
if (pvar->u.pStream)
|
|
IStream_Release(pvar->u.pStream);
|
|
break;
|
|
case VT_CLSID:
|
|
case VT_LPSTR:
|
|
case VT_LPWSTR:
|
|
/* pick an arbitrary typed pointer - we don't care about the type
|
|
* as we are just freeing it */
|
|
CoTaskMemFree(pvar->u.puuid);
|
|
break;
|
|
case VT_BLOB:
|
|
case VT_BLOB_OBJECT:
|
|
CoTaskMemFree(pvar->u.blob.pBlobData);
|
|
break;
|
|
case VT_BSTR:
|
|
SysFreeString(pvar->u.bstrVal);
|
|
break;
|
|
case VT_CF:
|
|
if (pvar->u.pclipdata)
|
|
{
|
|
propvar_free_cf_array(1, pvar->u.pclipdata);
|
|
CoTaskMemFree(pvar->u.pclipdata);
|
|
}
|
|
break;
|
|
default:
|
|
if (pvar->vt & VT_VECTOR)
|
|
{
|
|
ULONG i;
|
|
|
|
switch (pvar->vt & ~VT_VECTOR)
|
|
{
|
|
case VT_VARIANT:
|
|
FreePropVariantArray(pvar->u.capropvar.cElems, pvar->u.capropvar.pElems);
|
|
break;
|
|
case VT_CF:
|
|
propvar_free_cf_array(pvar->u.caclipdata.cElems, pvar->u.caclipdata.pElems);
|
|
break;
|
|
case VT_BSTR:
|
|
for (i = 0; i < pvar->u.cabstr.cElems; i++)
|
|
SysFreeString(pvar->u.cabstr.pElems[i]);
|
|
break;
|
|
case VT_LPSTR:
|
|
for (i = 0; i < pvar->u.calpstr.cElems; i++)
|
|
CoTaskMemFree(pvar->u.calpstr.pElems[i]);
|
|
break;
|
|
case VT_LPWSTR:
|
|
for (i = 0; i < pvar->u.calpwstr.cElems; i++)
|
|
CoTaskMemFree(pvar->u.calpwstr.pElems[i]);
|
|
break;
|
|
}
|
|
if (pvar->vt & ~VT_VECTOR)
|
|
{
|
|
/* pick an arbitrary VT_VECTOR structure - they all have the same
|
|
* memory layout */
|
|
CoTaskMemFree(pvar->u.capropvar.pElems);
|
|
}
|
|
}
|
|
else if (pvar->vt & VT_ARRAY)
|
|
hr = SafeArrayDestroy(pvar->u.parray);
|
|
else
|
|
{
|
|
WARN("Invalid/unsupported type %d\n", pvar->vt);
|
|
hr = STG_E_INVALIDPARAMETER;
|
|
}
|
|
}
|
|
|
|
memset(pvar, 0, sizeof(*pvar));
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* PropVariantCopy (combase.@)
|
|
*/
|
|
HRESULT WINAPI PropVariantCopy(PROPVARIANT *pvarDest, const PROPVARIANT *pvarSrc)
|
|
{
|
|
ULONG len;
|
|
HRESULT hr;
|
|
|
|
TRACE("%p, %p vt %04x.\n", pvarDest, pvarSrc, pvarSrc->vt);
|
|
|
|
hr = propvar_validatetype(pvarSrc->vt);
|
|
if (FAILED(hr))
|
|
return DISP_E_BADVARTYPE;
|
|
|
|
/* this will deal with most cases */
|
|
*pvarDest = *pvarSrc;
|
|
|
|
switch (pvarSrc->vt)
|
|
{
|
|
case VT_EMPTY:
|
|
case VT_NULL:
|
|
case VT_I1:
|
|
case VT_UI1:
|
|
case VT_I2:
|
|
case VT_UI2:
|
|
case VT_BOOL:
|
|
case VT_DECIMAL:
|
|
case VT_I4:
|
|
case VT_UI4:
|
|
case VT_R4:
|
|
case VT_ERROR:
|
|
case VT_I8:
|
|
case VT_UI8:
|
|
case VT_INT:
|
|
case VT_UINT:
|
|
case VT_R8:
|
|
case VT_CY:
|
|
case VT_DATE:
|
|
case VT_FILETIME:
|
|
break;
|
|
case VT_DISPATCH:
|
|
case VT_UNKNOWN:
|
|
case VT_STREAM:
|
|
case VT_STREAMED_OBJECT:
|
|
case VT_STORAGE:
|
|
case VT_STORED_OBJECT:
|
|
if (pvarDest->u.pStream)
|
|
IStream_AddRef(pvarDest->u.pStream);
|
|
break;
|
|
case VT_CLSID:
|
|
pvarDest->u.puuid = CoTaskMemAlloc(sizeof(CLSID));
|
|
*pvarDest->u.puuid = *pvarSrc->u.puuid;
|
|
break;
|
|
case VT_LPSTR:
|
|
if (pvarSrc->u.pszVal)
|
|
{
|
|
len = strlen(pvarSrc->u.pszVal);
|
|
pvarDest->u.pszVal = CoTaskMemAlloc((len+1)*sizeof(CHAR));
|
|
CopyMemory(pvarDest->u.pszVal, pvarSrc->u.pszVal, (len+1)*sizeof(CHAR));
|
|
}
|
|
break;
|
|
case VT_LPWSTR:
|
|
if (pvarSrc->u.pwszVal)
|
|
{
|
|
len = lstrlenW(pvarSrc->u.pwszVal);
|
|
pvarDest->u.pwszVal = CoTaskMemAlloc((len+1)*sizeof(WCHAR));
|
|
CopyMemory(pvarDest->u.pwszVal, pvarSrc->u.pwszVal, (len+1)*sizeof(WCHAR));
|
|
}
|
|
break;
|
|
case VT_BLOB:
|
|
case VT_BLOB_OBJECT:
|
|
if (pvarSrc->u.blob.pBlobData)
|
|
{
|
|
len = pvarSrc->u.blob.cbSize;
|
|
pvarDest->u.blob.pBlobData = CoTaskMemAlloc(len);
|
|
CopyMemory(pvarDest->u.blob.pBlobData, pvarSrc->u.blob.pBlobData, len);
|
|
}
|
|
break;
|
|
case VT_BSTR:
|
|
pvarDest->u.bstrVal = SysAllocString(pvarSrc->u.bstrVal);
|
|
break;
|
|
case VT_CF:
|
|
if (pvarSrc->u.pclipdata)
|
|
{
|
|
len = pvarSrc->u.pclipdata->cbSize - sizeof(pvarSrc->u.pclipdata->ulClipFmt);
|
|
pvarDest->u.pclipdata = CoTaskMemAlloc(sizeof (CLIPDATA));
|
|
pvarDest->u.pclipdata->cbSize = pvarSrc->u.pclipdata->cbSize;
|
|
pvarDest->u.pclipdata->ulClipFmt = pvarSrc->u.pclipdata->ulClipFmt;
|
|
pvarDest->u.pclipdata->pClipData = CoTaskMemAlloc(len);
|
|
CopyMemory(pvarDest->u.pclipdata->pClipData, pvarSrc->u.pclipdata->pClipData, len);
|
|
}
|
|
break;
|
|
default:
|
|
if (pvarSrc->vt & VT_VECTOR)
|
|
{
|
|
int elemSize;
|
|
ULONG i;
|
|
|
|
switch (pvarSrc->vt & ~VT_VECTOR)
|
|
{
|
|
case VT_I1: elemSize = sizeof(pvarSrc->u.cVal); break;
|
|
case VT_UI1: elemSize = sizeof(pvarSrc->u.bVal); break;
|
|
case VT_I2: elemSize = sizeof(pvarSrc->u.iVal); break;
|
|
case VT_UI2: elemSize = sizeof(pvarSrc->u.uiVal); break;
|
|
case VT_BOOL: elemSize = sizeof(pvarSrc->u.boolVal); break;
|
|
case VT_I4: elemSize = sizeof(pvarSrc->u.lVal); break;
|
|
case VT_UI4: elemSize = sizeof(pvarSrc->u.ulVal); break;
|
|
case VT_R4: elemSize = sizeof(pvarSrc->u.fltVal); break;
|
|
case VT_R8: elemSize = sizeof(pvarSrc->u.dblVal); break;
|
|
case VT_ERROR: elemSize = sizeof(pvarSrc->u.scode); break;
|
|
case VT_I8: elemSize = sizeof(pvarSrc->u.hVal); break;
|
|
case VT_UI8: elemSize = sizeof(pvarSrc->u.uhVal); break;
|
|
case VT_CY: elemSize = sizeof(pvarSrc->u.cyVal); break;
|
|
case VT_DATE: elemSize = sizeof(pvarSrc->u.date); break;
|
|
case VT_FILETIME: elemSize = sizeof(pvarSrc->u.filetime); break;
|
|
case VT_CLSID: elemSize = sizeof(*pvarSrc->u.puuid); break;
|
|
case VT_CF: elemSize = sizeof(*pvarSrc->u.pclipdata); break;
|
|
case VT_BSTR: elemSize = sizeof(pvarSrc->u.bstrVal); break;
|
|
case VT_LPSTR: elemSize = sizeof(pvarSrc->u.pszVal); break;
|
|
case VT_LPWSTR: elemSize = sizeof(pvarSrc->u.pwszVal); break;
|
|
case VT_VARIANT: elemSize = sizeof(*pvarSrc->u.pvarVal); break;
|
|
|
|
default:
|
|
FIXME("Invalid element type: %ul\n", pvarSrc->vt & ~VT_VECTOR);
|
|
return E_INVALIDARG;
|
|
}
|
|
len = pvarSrc->u.capropvar.cElems;
|
|
pvarDest->u.capropvar.pElems = len ? CoTaskMemAlloc(len * elemSize) : NULL;
|
|
if (pvarSrc->vt == (VT_VECTOR | VT_VARIANT))
|
|
{
|
|
for (i = 0; i < len; i++)
|
|
PropVariantCopy(&pvarDest->u.capropvar.pElems[i], &pvarSrc->u.capropvar.pElems[i]);
|
|
}
|
|
else if (pvarSrc->vt == (VT_VECTOR | VT_CF))
|
|
{
|
|
FIXME("Copy clipformats\n");
|
|
}
|
|
else if (pvarSrc->vt == (VT_VECTOR | VT_BSTR))
|
|
{
|
|
for (i = 0; i < len; i++)
|
|
pvarDest->u.cabstr.pElems[i] = SysAllocString(pvarSrc->u.cabstr.pElems[i]);
|
|
}
|
|
else if (pvarSrc->vt == (VT_VECTOR | VT_LPSTR))
|
|
{
|
|
size_t strLen;
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
strLen = lstrlenA(pvarSrc->u.calpstr.pElems[i]) + 1;
|
|
pvarDest->u.calpstr.pElems[i] = CoTaskMemAlloc(strLen);
|
|
memcpy(pvarDest->u.calpstr.pElems[i],
|
|
pvarSrc->u.calpstr.pElems[i], strLen);
|
|
}
|
|
}
|
|
else if (pvarSrc->vt == (VT_VECTOR | VT_LPWSTR))
|
|
{
|
|
size_t strLen;
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
strLen = (lstrlenW(pvarSrc->u.calpwstr.pElems[i]) + 1) *
|
|
sizeof(WCHAR);
|
|
pvarDest->u.calpstr.pElems[i] = CoTaskMemAlloc(strLen);
|
|
memcpy(pvarDest->u.calpstr.pElems[i],
|
|
pvarSrc->u.calpstr.pElems[i], strLen);
|
|
}
|
|
}
|
|
else
|
|
CopyMemory(pvarDest->u.capropvar.pElems, pvarSrc->u.capropvar.pElems, len * elemSize);
|
|
}
|
|
else if (pvarSrc->vt & VT_ARRAY)
|
|
{
|
|
pvarDest->u.uhVal.QuadPart = 0;
|
|
return SafeArrayCopy(pvarSrc->u.parray, &pvarDest->u.parray);
|
|
}
|
|
else
|
|
WARN("Invalid/unsupported type %d\n", pvarSrc->vt);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoFileTimeNow (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoFileTimeNow(FILETIME *filetime)
|
|
{
|
|
GetSystemTimeAsFileTime(filetime);
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoCreateGuid (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoCreateGuid(GUID *guid)
|
|
{
|
|
RPC_STATUS status;
|
|
|
|
if (!guid) return E_INVALIDARG;
|
|
|
|
status = UuidCreate(guid);
|
|
if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY) return S_OK;
|
|
return HRESULT_FROM_WIN32(status);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoQueryProxyBlanket (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoQueryProxyBlanket(IUnknown *proxy, DWORD *authn_service,
|
|
DWORD *authz_service, OLECHAR **servername, DWORD *authn_level,
|
|
DWORD *imp_level, void **auth_info, DWORD *capabilities)
|
|
{
|
|
IClientSecurity *client_security;
|
|
HRESULT hr;
|
|
|
|
TRACE("%p, %p, %p, %p, %p, %p, %p, %p.\n", proxy, authn_service, authz_service, servername, authn_level, imp_level,
|
|
auth_info, capabilities);
|
|
|
|
hr = IUnknown_QueryInterface(proxy, &IID_IClientSecurity, (void **)&client_security);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IClientSecurity_QueryBlanket(client_security, proxy, authn_service, authz_service, servername,
|
|
authn_level, imp_level, auth_info, capabilities);
|
|
IClientSecurity_Release(client_security);
|
|
}
|
|
|
|
if (FAILED(hr)) ERR("-- failed with %#x.\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoSetProxyBlanket (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoSetProxyBlanket(IUnknown *proxy, DWORD authn_service, DWORD authz_service,
|
|
OLECHAR *servername, DWORD authn_level, DWORD imp_level, void *auth_info, DWORD capabilities)
|
|
{
|
|
IClientSecurity *client_security;
|
|
HRESULT hr;
|
|
|
|
TRACE("%p, %u, %u, %p, %u, %u, %p, %#x.\n", proxy, authn_service, authz_service, servername,
|
|
authn_level, imp_level, auth_info, capabilities);
|
|
|
|
hr = IUnknown_QueryInterface(proxy, &IID_IClientSecurity, (void **)&client_security);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IClientSecurity_SetBlanket(client_security, proxy, authn_service, authz_service, servername, authn_level,
|
|
imp_level, auth_info, capabilities);
|
|
IClientSecurity_Release(client_security);
|
|
}
|
|
|
|
if (FAILED(hr)) ERR("-- failed with %#x.\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoCopyProxy (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoCopyProxy(IUnknown *proxy, IUnknown **proxy_copy)
|
|
{
|
|
IClientSecurity *client_security;
|
|
HRESULT hr;
|
|
|
|
TRACE("%p, %p.\n", proxy, proxy_copy);
|
|
|
|
hr = IUnknown_QueryInterface(proxy, &IID_IClientSecurity, (void **)&client_security);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IClientSecurity_CopyProxy(client_security, proxy, proxy_copy);
|
|
IClientSecurity_Release(client_security);
|
|
}
|
|
|
|
if (FAILED(hr)) ERR("-- failed with %#x.\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoQueryClientBlanket (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoQueryClientBlanket(DWORD *authn_service, DWORD *authz_service, OLECHAR **servername,
|
|
DWORD *authn_level, DWORD *imp_level, RPC_AUTHZ_HANDLE *privs, DWORD *capabilities)
|
|
{
|
|
IServerSecurity *server_security;
|
|
HRESULT hr;
|
|
|
|
TRACE("%p, %p, %p, %p, %p, %p, %p.\n", authn_service, authz_service, servername, authn_level, imp_level,
|
|
privs, capabilities);
|
|
|
|
hr = CoGetCallContext(&IID_IServerSecurity, (void **)&server_security);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IServerSecurity_QueryBlanket(server_security, authn_service, authz_service, servername, authn_level,
|
|
imp_level, privs, capabilities);
|
|
IServerSecurity_Release(server_security);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoImpersonateClient (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoImpersonateClient(void)
|
|
{
|
|
IServerSecurity *server_security;
|
|
HRESULT hr;
|
|
|
|
TRACE("\n");
|
|
|
|
hr = CoGetCallContext(&IID_IServerSecurity, (void **)&server_security);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IServerSecurity_ImpersonateClient(server_security);
|
|
IServerSecurity_Release(server_security);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoRevertToSelf (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoRevertToSelf(void)
|
|
{
|
|
IServerSecurity *server_security;
|
|
HRESULT hr;
|
|
|
|
TRACE("\n");
|
|
|
|
hr = CoGetCallContext(&IID_IServerSecurity, (void **)&server_security);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IServerSecurity_RevertToSelf(server_security);
|
|
IServerSecurity_Release(server_security);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoInitializeSecurity (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoInitializeSecurity(PSECURITY_DESCRIPTOR sd, LONG cAuthSvc,
|
|
SOLE_AUTHENTICATION_SERVICE *asAuthSvc, void *reserved1, DWORD authn_level,
|
|
DWORD imp_level, void *reserved2, DWORD capabilities, void *reserved3)
|
|
{
|
|
FIXME("%p, %d, %p, %p, %d, %d, %p, %d, %p stub\n", sd, cAuthSvc, asAuthSvc, reserved1, authn_level,
|
|
imp_level, reserved2, capabilities, reserved3);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoGetObjectContext (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoGetObjectContext(REFIID riid, void **ppv)
|
|
{
|
|
IObjContext *context;
|
|
HRESULT hr;
|
|
|
|
TRACE("%s, %p.\n", debugstr_guid(riid), ppv);
|
|
|
|
*ppv = NULL;
|
|
hr = CoGetContextToken((ULONG_PTR *)&context);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
return IObjContext_QueryInterface(context, riid, ppv);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoGetDefaultContext (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoGetDefaultContext(APTTYPE type, REFIID riid, void **obj)
|
|
{
|
|
FIXME("%d, %s, %p stub\n", type, debugstr_guid(riid), obj);
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoGetCallState (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoGetCallState(int arg1, ULONG *arg2)
|
|
{
|
|
FIXME("%d, %p.\n", arg1, arg2);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoGetActivationState (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoGetActivationState(GUID guid, DWORD arg2, DWORD *arg3)
|
|
{
|
|
FIXME("%s, %x, %p.\n", debugstr_guid(&guid), arg2, arg3);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoGetTreatAsClass (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoGetTreatAsClass(REFCLSID clsidOld, CLSID *clsidNew)
|
|
{
|
|
WCHAR buffW[CHARS_IN_GUID];
|
|
LONG len = sizeof(buffW);
|
|
HRESULT hr = S_OK;
|
|
HKEY hkey = NULL;
|
|
|
|
TRACE("%s, %p.\n", debugstr_guid(clsidOld), clsidNew);
|
|
|
|
if (!clsidOld || !clsidNew)
|
|
return E_INVALIDARG;
|
|
|
|
*clsidNew = *clsidOld;
|
|
|
|
hr = open_key_for_clsid(clsidOld, L"TreatAs", KEY_READ, &hkey);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = S_FALSE;
|
|
goto done;
|
|
}
|
|
|
|
if (RegQueryValueW(hkey, NULL, buffW, &len))
|
|
{
|
|
hr = S_FALSE;
|
|
goto done;
|
|
}
|
|
|
|
hr = CLSIDFromString(buffW, clsidNew);
|
|
if (FAILED(hr))
|
|
ERR("Failed to get CLSID from string %s, hr %#x.\n", debugstr_w(buffW), hr);
|
|
done:
|
|
if (hkey) RegCloseKey(hkey);
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* ProgIDFromCLSID (combase.@)
|
|
*/
|
|
HRESULT WINAPI DECLSPEC_HOTPATCH ProgIDFromCLSID(REFCLSID clsid, LPOLESTR *progid)
|
|
{
|
|
ACTCTX_SECTION_KEYED_DATA data;
|
|
LONG progidlen = 0;
|
|
HKEY hkey;
|
|
HRESULT hr;
|
|
|
|
if (!progid)
|
|
return E_INVALIDARG;
|
|
|
|
*progid = NULL;
|
|
|
|
data.cbSize = sizeof(data);
|
|
if (FindActCtxSectionGuid(0, NULL, ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION,
|
|
clsid, &data))
|
|
{
|
|
struct comclassredirect_data *comclass = (struct comclassredirect_data *)data.lpData;
|
|
if (comclass->progid_len)
|
|
{
|
|
WCHAR *ptrW;
|
|
|
|
*progid = CoTaskMemAlloc(comclass->progid_len + sizeof(WCHAR));
|
|
if (!*progid) return E_OUTOFMEMORY;
|
|
|
|
ptrW = (WCHAR *)((BYTE *)comclass + comclass->progid_offset);
|
|
memcpy(*progid, ptrW, comclass->progid_len + sizeof(WCHAR));
|
|
return S_OK;
|
|
}
|
|
else
|
|
return REGDB_E_CLASSNOTREG;
|
|
}
|
|
|
|
hr = open_key_for_clsid(clsid, L"ProgID", KEY_READ, &hkey);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (RegQueryValueW(hkey, NULL, NULL, &progidlen))
|
|
hr = REGDB_E_CLASSNOTREG;
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
*progid = CoTaskMemAlloc(progidlen * sizeof(WCHAR));
|
|
if (*progid)
|
|
{
|
|
if (RegQueryValueW(hkey, NULL, *progid, &progidlen))
|
|
{
|
|
hr = REGDB_E_CLASSNOTREG;
|
|
CoTaskMemFree(*progid);
|
|
*progid = NULL;
|
|
}
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
|
return hr;
|
|
}
|
|
|
|
static inline BOOL is_valid_hex(WCHAR c)
|
|
{
|
|
if (!(((c >= '0') && (c <= '9')) ||
|
|
((c >= 'a') && (c <= 'f')) ||
|
|
((c >= 'A') && (c <= 'F'))))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static const BYTE guid_conv_table[256] =
|
|
{
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 */
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x30 */
|
|
0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 */
|
|
0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf /* 0x60 */
|
|
};
|
|
|
|
static BOOL guid_from_string(LPCWSTR s, GUID *id)
|
|
{
|
|
int i;
|
|
|
|
if (!s || s[0] != '{')
|
|
{
|
|
memset(id, 0, sizeof(*id));
|
|
if (!s) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
TRACE("%s -> %p\n", debugstr_w(s), id);
|
|
|
|
/* In form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */
|
|
|
|
id->Data1 = 0;
|
|
for (i = 1; i < 9; ++i)
|
|
{
|
|
if (!is_valid_hex(s[i])) return FALSE;
|
|
id->Data1 = (id->Data1 << 4) | guid_conv_table[s[i]];
|
|
}
|
|
if (s[9] != '-') return FALSE;
|
|
|
|
id->Data2 = 0;
|
|
for (i = 10; i < 14; ++i)
|
|
{
|
|
if (!is_valid_hex(s[i])) return FALSE;
|
|
id->Data2 = (id->Data2 << 4) | guid_conv_table[s[i]];
|
|
}
|
|
if (s[14] != '-') return FALSE;
|
|
|
|
id->Data3 = 0;
|
|
for (i = 15; i < 19; ++i)
|
|
{
|
|
if (!is_valid_hex(s[i])) return FALSE;
|
|
id->Data3 = (id->Data3 << 4) | guid_conv_table[s[i]];
|
|
}
|
|
if (s[19] != '-') return FALSE;
|
|
|
|
for (i = 20; i < 37; i += 2)
|
|
{
|
|
if (i == 24)
|
|
{
|
|
if (s[i] != '-') return FALSE;
|
|
i++;
|
|
}
|
|
if (!is_valid_hex(s[i]) || !is_valid_hex(s[i + 1])) return FALSE;
|
|
id->Data4[(i - 20) / 2] = guid_conv_table[s[i]] << 4 | guid_conv_table[s[i + 1]];
|
|
}
|
|
|
|
if (s[37] == '}' && s[38] == '\0')
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static HRESULT clsid_from_string_reg(LPCOLESTR progid, CLSID *clsid)
|
|
{
|
|
WCHAR buf2[CHARS_IN_GUID];
|
|
LONG buf2len = sizeof(buf2);
|
|
HKEY xhkey;
|
|
WCHAR *buf;
|
|
|
|
memset(clsid, 0, sizeof(*clsid));
|
|
buf = heap_alloc((lstrlenW(progid) + 8) * sizeof(WCHAR));
|
|
if (!buf) return E_OUTOFMEMORY;
|
|
|
|
lstrcpyW(buf, progid);
|
|
lstrcatW(buf, L"\\CLSID");
|
|
if (open_classes_key(HKEY_CLASSES_ROOT, buf, MAXIMUM_ALLOWED, &xhkey))
|
|
{
|
|
heap_free(buf);
|
|
WARN("couldn't open key for ProgID %s\n", debugstr_w(progid));
|
|
return CO_E_CLASSSTRING;
|
|
}
|
|
heap_free(buf);
|
|
|
|
if (RegQueryValueW(xhkey, NULL, buf2, &buf2len))
|
|
{
|
|
RegCloseKey(xhkey);
|
|
WARN("couldn't query clsid value for ProgID %s\n", debugstr_w(progid));
|
|
return CO_E_CLASSSTRING;
|
|
}
|
|
RegCloseKey(xhkey);
|
|
return guid_from_string(buf2, clsid) ? S_OK : CO_E_CLASSSTRING;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CLSIDFromProgID (combase.@)
|
|
*/
|
|
HRESULT WINAPI DECLSPEC_HOTPATCH CLSIDFromProgID(LPCOLESTR progid, CLSID *clsid)
|
|
{
|
|
ACTCTX_SECTION_KEYED_DATA data;
|
|
|
|
if (!progid || !clsid)
|
|
return E_INVALIDARG;
|
|
|
|
data.cbSize = sizeof(data);
|
|
if (FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_COM_PROGID_REDIRECTION,
|
|
progid, &data))
|
|
{
|
|
struct progidredirect_data *progiddata = (struct progidredirect_data *)data.lpData;
|
|
CLSID *alias = (CLSID *)((BYTE *)data.lpSectionBase + progiddata->clsid_offset);
|
|
*clsid = *alias;
|
|
return S_OK;
|
|
}
|
|
|
|
return clsid_from_string_reg(progid, clsid);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CLSIDFromProgIDEx (combase.@)
|
|
*/
|
|
HRESULT WINAPI CLSIDFromProgIDEx(LPCOLESTR progid, CLSID *clsid)
|
|
{
|
|
FIXME("%s, %p: semi-stub\n", debugstr_w(progid), clsid);
|
|
|
|
return CLSIDFromProgID(progid, clsid);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CLSIDFromString (combase.@)
|
|
*/
|
|
HRESULT WINAPI CLSIDFromString(LPCOLESTR str, LPCLSID clsid)
|
|
{
|
|
CLSID tmp_id;
|
|
HRESULT hr;
|
|
|
|
if (!clsid)
|
|
return E_INVALIDARG;
|
|
|
|
if (guid_from_string(str, clsid))
|
|
return S_OK;
|
|
|
|
/* It appears a ProgID is also valid */
|
|
hr = clsid_from_string_reg(str, &tmp_id);
|
|
if (SUCCEEDED(hr))
|
|
*clsid = tmp_id;
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* IIDFromString (combase.@)
|
|
*/
|
|
HRESULT WINAPI IIDFromString(LPCOLESTR str, IID *iid)
|
|
{
|
|
TRACE("%s, %p\n", debugstr_w(str), iid);
|
|
|
|
if (!str)
|
|
{
|
|
memset(iid, 0, sizeof(*iid));
|
|
return S_OK;
|
|
}
|
|
|
|
/* length mismatch is a special case */
|
|
if (lstrlenW(str) + 1 != CHARS_IN_GUID)
|
|
return E_INVALIDARG;
|
|
|
|
if (str[0] != '{')
|
|
return CO_E_IIDSTRING;
|
|
|
|
return guid_from_string(str, iid) ? S_OK : CO_E_IIDSTRING;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* StringFromCLSID (combase.@)
|
|
*/
|
|
HRESULT WINAPI StringFromCLSID(REFCLSID clsid, LPOLESTR *str)
|
|
{
|
|
if (!(*str = CoTaskMemAlloc(CHARS_IN_GUID * sizeof(WCHAR)))) return E_OUTOFMEMORY;
|
|
StringFromGUID2(clsid, *str, CHARS_IN_GUID);
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* StringFromGUID2 (combase.@)
|
|
*/
|
|
INT WINAPI StringFromGUID2(REFGUID guid, LPOLESTR str, INT cmax)
|
|
{
|
|
if (!guid || cmax < CHARS_IN_GUID) return 0;
|
|
swprintf(str, CHARS_IN_GUID, L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", guid->Data1,
|
|
guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
|
|
guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
|
|
return CHARS_IN_GUID;
|
|
}
|
|
|
|
static void init_multi_qi(DWORD count, MULTI_QI *mqi, HRESULT hr)
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
mqi[i].pItf = NULL;
|
|
mqi[i].hr = hr;
|
|
}
|
|
}
|
|
|
|
static HRESULT return_multi_qi(IUnknown *unk, DWORD count, MULTI_QI *mqi, BOOL include_unk)
|
|
{
|
|
ULONG index = 0, fetched = 0;
|
|
|
|
if (include_unk)
|
|
{
|
|
mqi[0].hr = S_OK;
|
|
mqi[0].pItf = unk;
|
|
index = fetched = 1;
|
|
}
|
|
|
|
for (; index < count; index++)
|
|
{
|
|
mqi[index].hr = IUnknown_QueryInterface(unk, mqi[index].pIID, (void **)&mqi[index].pItf);
|
|
if (mqi[index].hr == S_OK)
|
|
fetched++;
|
|
}
|
|
|
|
if (!include_unk)
|
|
IUnknown_Release(unk);
|
|
|
|
if (fetched == 0)
|
|
return E_NOINTERFACE;
|
|
|
|
return fetched == count ? S_OK : CO_S_NOTALLINTERFACES;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoGetInstanceFromFile (combase.@)
|
|
*/
|
|
HRESULT WINAPI DECLSPEC_HOTPATCH CoGetInstanceFromFile(COSERVERINFO *server_info, CLSID *rclsid,
|
|
IUnknown *outer, DWORD cls_context, DWORD grfmode, OLECHAR *filename, DWORD count,
|
|
MULTI_QI *results)
|
|
{
|
|
IPersistFile *pf = NULL;
|
|
IUnknown *obj = NULL;
|
|
CLSID clsid;
|
|
HRESULT hr;
|
|
|
|
if (!count || !results)
|
|
return E_INVALIDARG;
|
|
|
|
if (server_info)
|
|
FIXME("() non-NULL server_info not supported\n");
|
|
|
|
init_multi_qi(count, results, E_NOINTERFACE);
|
|
|
|
if (!rclsid)
|
|
{
|
|
hr = GetClassFile(filename, &clsid);
|
|
if (FAILED(hr))
|
|
{
|
|
ERR("Failed to get CLSID from a file.\n");
|
|
return hr;
|
|
}
|
|
|
|
rclsid = &clsid;
|
|
}
|
|
|
|
hr = CoCreateInstance(rclsid, outer, cls_context, &IID_IUnknown, (void **)&obj);
|
|
if (hr != S_OK)
|
|
{
|
|
init_multi_qi(count, results, hr);
|
|
return hr;
|
|
}
|
|
|
|
/* Init from file */
|
|
hr = IUnknown_QueryInterface(obj, &IID_IPersistFile, (void **)&pf);
|
|
if (FAILED(hr))
|
|
{
|
|
init_multi_qi(count, results, hr);
|
|
IUnknown_Release(obj);
|
|
return hr;
|
|
}
|
|
|
|
hr = IPersistFile_Load(pf, filename, grfmode);
|
|
IPersistFile_Release(pf);
|
|
if (SUCCEEDED(hr))
|
|
return return_multi_qi(obj, count, results, FALSE);
|
|
else
|
|
{
|
|
init_multi_qi(count, results, hr);
|
|
IUnknown_Release(obj);
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoGetInstanceFromIStorage (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoGetInstanceFromIStorage(COSERVERINFO *server_info, CLSID *rclsid,
|
|
IUnknown *outer, DWORD cls_context, IStorage *storage, DWORD count, MULTI_QI *results)
|
|
{
|
|
IPersistStorage *ps = NULL;
|
|
IUnknown *obj = NULL;
|
|
STATSTG stat;
|
|
HRESULT hr;
|
|
|
|
if (!count || !results || !storage)
|
|
return E_INVALIDARG;
|
|
|
|
if (server_info)
|
|
FIXME("() non-NULL server_info not supported\n");
|
|
|
|
init_multi_qi(count, results, E_NOINTERFACE);
|
|
|
|
if (!rclsid)
|
|
{
|
|
memset(&stat.clsid, 0, sizeof(stat.clsid));
|
|
hr = IStorage_Stat(storage, &stat, STATFLAG_NONAME);
|
|
if (FAILED(hr))
|
|
{
|
|
ERR("Failed to get CLSID from a storage.\n");
|
|
return hr;
|
|
}
|
|
|
|
rclsid = &stat.clsid;
|
|
}
|
|
|
|
hr = CoCreateInstance(rclsid, outer, cls_context, &IID_IUnknown, (void **)&obj);
|
|
if (hr != S_OK)
|
|
return hr;
|
|
|
|
/* Init from IStorage */
|
|
hr = IUnknown_QueryInterface(obj, &IID_IPersistStorage, (void **)&ps);
|
|
if (FAILED(hr))
|
|
ERR("failed to get IPersistStorage\n");
|
|
|
|
if (ps)
|
|
{
|
|
IPersistStorage_Load(ps, storage);
|
|
IPersistStorage_Release(ps);
|
|
}
|
|
|
|
return return_multi_qi(obj, count, results, FALSE);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoCreateInstance (combase.@)
|
|
*/
|
|
HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstance(REFCLSID rclsid, IUnknown *outer, DWORD cls_context,
|
|
REFIID riid, void **obj)
|
|
{
|
|
MULTI_QI multi_qi = { .pIID = riid };
|
|
HRESULT hr;
|
|
|
|
TRACE("%s, %p, %#x, %s, %p.\n", debugstr_guid(rclsid), outer, cls_context, debugstr_guid(riid), obj);
|
|
|
|
if (!obj)
|
|
return E_POINTER;
|
|
|
|
hr = CoCreateInstanceEx(rclsid, outer, cls_context, NULL, 1, &multi_qi);
|
|
*obj = multi_qi.pItf;
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoCreateInstanceFromApp (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoCreateInstanceFromApp(REFCLSID rclsid, IUnknown *outer, DWORD cls_context,
|
|
void *server_info, ULONG count, MULTI_QI *results)
|
|
{
|
|
TRACE("%s, %p, %#x, %p, %u, %p\n", debugstr_guid(rclsid), outer, cls_context, server_info,
|
|
count, results);
|
|
|
|
return CoCreateInstanceEx(rclsid, outer, cls_context | CLSCTX_APPCONTAINER, server_info,
|
|
count, results);
|
|
}
|
|
|
|
static HRESULT com_get_class_object(REFCLSID rclsid, DWORD clscontext,
|
|
COSERVERINFO *server_info, REFIID riid, void **obj)
|
|
{
|
|
struct class_reg_data clsreg = { 0 };
|
|
HRESULT hr = E_UNEXPECTED;
|
|
IUnknown *registered_obj;
|
|
struct apartment *apt;
|
|
|
|
if (!obj)
|
|
return E_INVALIDARG;
|
|
|
|
*obj = NULL;
|
|
|
|
if (!(apt = apartment_get_current_or_mta()))
|
|
{
|
|
ERR("apartment not initialised\n");
|
|
return CO_E_NOTINITIALIZED;
|
|
}
|
|
|
|
if (server_info)
|
|
FIXME("server_info name %s, authinfo %p\n", debugstr_w(server_info->pwszName), server_info->pAuthInfo);
|
|
|
|
if (clscontext & CLSCTX_INPROC_SERVER)
|
|
{
|
|
if (IsEqualCLSID(rclsid, &CLSID_InProcFreeMarshaler) ||
|
|
IsEqualCLSID(rclsid, &CLSID_GlobalOptions) ||
|
|
(!(clscontext & CLSCTX_APPCONTAINER) && IsEqualCLSID(rclsid, &CLSID_ManualResetEvent)) ||
|
|
IsEqualCLSID(rclsid, &CLSID_StdGlobalInterfaceTable))
|
|
{
|
|
apartment_release(apt);
|
|
return Ole32DllGetClassObject(rclsid, riid, obj);
|
|
}
|
|
}
|
|
|
|
if (clscontext & CLSCTX_INPROC)
|
|
{
|
|
ACTCTX_SECTION_KEYED_DATA data;
|
|
|
|
data.cbSize = sizeof(data);
|
|
/* search activation context first */
|
|
if (FindActCtxSectionGuid(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, NULL,
|
|
ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION, rclsid, &data))
|
|
{
|
|
struct comclassredirect_data *comclass = (struct comclassredirect_data *)data.lpData;
|
|
|
|
clsreg.u.actctx.module_name = (WCHAR *)((BYTE *)data.lpSectionBase + comclass->name_offset);
|
|
clsreg.u.actctx.hactctx = data.hActCtx;
|
|
clsreg.u.actctx.threading_model = comclass->model;
|
|
clsreg.origin = CLASS_REG_ACTCTX;
|
|
|
|
hr = apartment_get_inproc_class_object(apt, &clsreg, &comclass->clsid, riid,
|
|
!(clscontext & WINE_CLSCTX_DONT_HOST), obj);
|
|
ReleaseActCtx(data.hActCtx);
|
|
apartment_release(apt);
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* First, try and see if we can't match the class ID with one of the
|
|
* registered classes.
|
|
*/
|
|
if (!(clscontext & CLSCTX_APPCONTAINER) && (registered_obj = com_get_registered_class_object(apt, rclsid, clscontext)))
|
|
{
|
|
hr = IUnknown_QueryInterface(registered_obj, riid, obj);
|
|
IUnknown_Release(registered_obj);
|
|
apartment_release(apt);
|
|
return hr;
|
|
}
|
|
|
|
/* First try in-process server */
|
|
if (clscontext & CLSCTX_INPROC_SERVER)
|
|
{
|
|
HKEY hkey;
|
|
|
|
hr = open_key_for_clsid(rclsid, L"InprocServer32", KEY_READ, &hkey);
|
|
if (FAILED(hr))
|
|
{
|
|
if (hr == REGDB_E_CLASSNOTREG)
|
|
ERR("class %s not registered\n", debugstr_guid(rclsid));
|
|
else if (hr == REGDB_E_KEYMISSING)
|
|
{
|
|
WARN("class %s not registered as in-proc server\n", debugstr_guid(rclsid));
|
|
hr = REGDB_E_CLASSNOTREG;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
clsreg.u.hkey = hkey;
|
|
clsreg.origin = CLASS_REG_REGISTRY;
|
|
|
|
hr = apartment_get_inproc_class_object(apt, &clsreg, rclsid, riid, !(clscontext & WINE_CLSCTX_DONT_HOST), obj);
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
/* return if we got a class, otherwise fall through to one of the
|
|
* other types */
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
apartment_release(apt);
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
/* Next try in-process handler */
|
|
if (clscontext & CLSCTX_INPROC_HANDLER)
|
|
{
|
|
HKEY hkey;
|
|
|
|
hr = open_key_for_clsid(rclsid, L"InprocHandler32", KEY_READ, &hkey);
|
|
if (FAILED(hr))
|
|
{
|
|
if (hr == REGDB_E_CLASSNOTREG)
|
|
ERR("class %s not registered\n", debugstr_guid(rclsid));
|
|
else if (hr == REGDB_E_KEYMISSING)
|
|
{
|
|
WARN("class %s not registered in-proc handler\n", debugstr_guid(rclsid));
|
|
hr = REGDB_E_CLASSNOTREG;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
clsreg.u.hkey = hkey;
|
|
clsreg.origin = CLASS_REG_REGISTRY;
|
|
|
|
hr = apartment_get_inproc_class_object(apt, &clsreg, rclsid, riid, !(clscontext & WINE_CLSCTX_DONT_HOST), obj);
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
/* return if we got a class, otherwise fall through to one of the
|
|
* other types */
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
apartment_release(apt);
|
|
return hr;
|
|
}
|
|
}
|
|
apartment_release(apt);
|
|
|
|
/* Next try out of process */
|
|
if (clscontext & CLSCTX_LOCAL_SERVER)
|
|
{
|
|
hr = rpc_get_local_class_object(rclsid, riid, obj);
|
|
if (SUCCEEDED(hr))
|
|
return hr;
|
|
}
|
|
|
|
/* Finally try remote: this requires networked DCOM (a lot of work) */
|
|
if (clscontext & CLSCTX_REMOTE_SERVER)
|
|
{
|
|
FIXME ("CLSCTX_REMOTE_SERVER not supported\n");
|
|
hr = REGDB_E_CLASSNOTREG;
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
ERR("no class object %s could be created for context %#x\n", debugstr_guid(rclsid), clscontext);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoCreateInstanceEx (combase.@)
|
|
*/
|
|
HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstanceEx(REFCLSID rclsid, IUnknown *outer, DWORD cls_context,
|
|
COSERVERINFO *server_info, ULONG count, MULTI_QI *results)
|
|
{
|
|
IClassFactory *factory;
|
|
IUnknown *unk = NULL;
|
|
CLSID clsid;
|
|
HRESULT hr;
|
|
|
|
TRACE("%s, %p, %#x, %p, %u, %p\n", debugstr_guid(rclsid), outer, cls_context, server_info, count, results);
|
|
|
|
if (!count || !results)
|
|
return E_INVALIDARG;
|
|
|
|
if (server_info)
|
|
FIXME("Server info is not supported.\n");
|
|
|
|
init_multi_qi(count, results, E_NOINTERFACE);
|
|
|
|
clsid = *rclsid;
|
|
if (!(cls_context & CLSCTX_APPCONTAINER))
|
|
CoGetTreatAsClass(rclsid, &clsid);
|
|
|
|
if (FAILED(hr = com_get_class_object(&clsid, cls_context, NULL, &IID_IClassFactory, (void **)&factory)))
|
|
return hr;
|
|
|
|
hr = IClassFactory_CreateInstance(factory, outer, results[0].pIID, (void **)&unk);
|
|
IClassFactory_Release(factory);
|
|
if (FAILED(hr))
|
|
{
|
|
if (hr == CLASS_E_NOAGGREGATION && outer)
|
|
FIXME("Class %s does not support aggregation\n", debugstr_guid(&clsid));
|
|
else
|
|
FIXME("no instance created for interface %s of class %s, hr %#x.\n",
|
|
debugstr_guid(results[0].pIID), debugstr_guid(&clsid), hr);
|
|
return hr;
|
|
}
|
|
|
|
return return_multi_qi(unk, count, results, TRUE);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoGetClassObject (combase.@)
|
|
*/
|
|
HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject(REFCLSID rclsid, DWORD clscontext,
|
|
COSERVERINFO *server_info, REFIID riid, void **obj)
|
|
{
|
|
TRACE("%s, %#x, %s\n", debugstr_guid(rclsid), clscontext, debugstr_guid(riid));
|
|
|
|
return com_get_class_object(rclsid, clscontext, server_info, riid, obj);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoFreeUnusedLibraries (combase.@)
|
|
*/
|
|
void WINAPI DECLSPEC_HOTPATCH CoFreeUnusedLibraries(void)
|
|
{
|
|
CoFreeUnusedLibrariesEx(INFINITE, 0);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoGetCallContext (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoGetCallContext(REFIID riid, void **obj)
|
|
{
|
|
struct tlsdata *tlsdata;
|
|
HRESULT hr;
|
|
|
|
TRACE("%s, %p\n", debugstr_guid(riid), obj);
|
|
|
|
if (FAILED(hr = com_get_tlsdata(&tlsdata)))
|
|
return hr;
|
|
|
|
if (!tlsdata->call_state)
|
|
return RPC_E_CALL_COMPLETE;
|
|
|
|
return IUnknown_QueryInterface(tlsdata->call_state, riid, obj);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoSwitchCallContext (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoSwitchCallContext(IUnknown *context, IUnknown **old_context)
|
|
{
|
|
struct tlsdata *tlsdata;
|
|
HRESULT hr;
|
|
|
|
TRACE("%p, %p\n", context, old_context);
|
|
|
|
if (FAILED(hr = com_get_tlsdata(&tlsdata)))
|
|
return hr;
|
|
|
|
/* Reference counts are not touched. */
|
|
*old_context = tlsdata->call_state;
|
|
tlsdata->call_state = context;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoRegisterInitializeSpy (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoRegisterInitializeSpy(IInitializeSpy *spy, ULARGE_INTEGER *cookie)
|
|
{
|
|
struct tlsdata *tlsdata;
|
|
struct init_spy *entry;
|
|
unsigned int id;
|
|
HRESULT hr;
|
|
|
|
TRACE("%p, %p\n", spy, cookie);
|
|
|
|
if (!spy || !cookie)
|
|
return E_INVALIDARG;
|
|
|
|
if (FAILED(hr = com_get_tlsdata(&tlsdata)))
|
|
return hr;
|
|
|
|
hr = IInitializeSpy_QueryInterface(spy, &IID_IInitializeSpy, (void **)&spy);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
entry = heap_alloc(sizeof(*entry));
|
|
if (!entry)
|
|
{
|
|
IInitializeSpy_Release(spy);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
entry->spy = spy;
|
|
|
|
id = 0;
|
|
while (get_spy_entry(tlsdata, id) != NULL)
|
|
{
|
|
id++;
|
|
}
|
|
|
|
entry->id = id;
|
|
list_add_head(&tlsdata->spies, &entry->entry);
|
|
|
|
cookie->u.HighPart = GetCurrentThreadId();
|
|
cookie->u.LowPart = entry->id;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoRevokeInitializeSpy (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoRevokeInitializeSpy(ULARGE_INTEGER cookie)
|
|
{
|
|
struct tlsdata *tlsdata;
|
|
struct init_spy *spy;
|
|
HRESULT hr;
|
|
|
|
TRACE("%s\n", wine_dbgstr_longlong(cookie.QuadPart));
|
|
|
|
if (cookie.u.HighPart != GetCurrentThreadId())
|
|
return E_INVALIDARG;
|
|
|
|
if (FAILED(hr = com_get_tlsdata(&tlsdata)))
|
|
return hr;
|
|
|
|
if (!(spy = get_spy_entry(tlsdata, cookie.u.LowPart))) return E_INVALIDARG;
|
|
|
|
IInitializeSpy_Release(spy->spy);
|
|
spy->spy = NULL;
|
|
if (!tlsdata->spies_lock)
|
|
{
|
|
list_remove(&spy->entry);
|
|
heap_free(spy);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
static BOOL com_peek_message(struct apartment *apt, MSG *msg)
|
|
{
|
|
/* First try to retrieve messages for incoming COM calls to the apartment window */
|
|
return (apt->win && PeekMessageW(msg, apt->win, 0, 0, PM_REMOVE | PM_NOYIELD)) ||
|
|
/* Next retrieve other messages necessary for the app to remain responsive */
|
|
PeekMessageW(msg, NULL, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE | PM_NOYIELD) ||
|
|
PeekMessageW(msg, NULL, 0, 0, PM_QS_PAINT | PM_QS_SENDMESSAGE | PM_REMOVE | PM_NOYIELD);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoWaitForMultipleHandles (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoWaitForMultipleHandles(DWORD flags, DWORD timeout, ULONG handle_count, HANDLE *handles,
|
|
DWORD *index)
|
|
{
|
|
BOOL check_apc = !!(flags & COWAIT_ALERTABLE), post_quit = FALSE, message_loop;
|
|
DWORD start_time, wait_flags = 0;
|
|
struct tlsdata *tlsdata;
|
|
struct apartment *apt;
|
|
UINT exit_code;
|
|
HRESULT hr;
|
|
|
|
TRACE("%#x, %#x, %u, %p, %p\n", flags, timeout, handle_count, handles, index);
|
|
|
|
if (!index)
|
|
return E_INVALIDARG;
|
|
|
|
*index = 0;
|
|
|
|
if (!handles)
|
|
return E_INVALIDARG;
|
|
|
|
if (!handle_count)
|
|
return RPC_E_NO_SYNC;
|
|
|
|
if (FAILED(hr = com_get_tlsdata(&tlsdata)))
|
|
return hr;
|
|
|
|
apt = com_get_current_apt();
|
|
message_loop = apt && !apt->multi_threaded;
|
|
|
|
if (flags & COWAIT_WAITALL)
|
|
wait_flags |= MWMO_WAITALL;
|
|
if (flags & COWAIT_ALERTABLE)
|
|
wait_flags |= MWMO_ALERTABLE;
|
|
|
|
start_time = GetTickCount();
|
|
|
|
while (TRUE)
|
|
{
|
|
DWORD now = GetTickCount(), res;
|
|
|
|
if (now - start_time > timeout)
|
|
{
|
|
hr = RPC_S_CALLPENDING;
|
|
break;
|
|
}
|
|
|
|
if (message_loop)
|
|
{
|
|
TRACE("waiting for rpc completion or window message\n");
|
|
|
|
res = WAIT_TIMEOUT;
|
|
|
|
if (check_apc)
|
|
{
|
|
res = WaitForMultipleObjectsEx(handle_count, handles, !!(flags & COWAIT_WAITALL), 0, TRUE);
|
|
check_apc = FALSE;
|
|
}
|
|
|
|
if (res == WAIT_TIMEOUT)
|
|
res = MsgWaitForMultipleObjectsEx(handle_count, handles,
|
|
timeout == INFINITE ? INFINITE : start_time + timeout - now,
|
|
QS_SENDMESSAGE | QS_ALLPOSTMESSAGE | QS_PAINT, wait_flags);
|
|
|
|
if (res == WAIT_OBJECT_0 + handle_count) /* messages available */
|
|
{
|
|
int msg_count = 0;
|
|
MSG msg;
|
|
|
|
/* call message filter */
|
|
|
|
if (apt->filter)
|
|
{
|
|
PENDINGTYPE pendingtype = tlsdata->pending_call_count_server ? PENDINGTYPE_NESTED : PENDINGTYPE_TOPLEVEL;
|
|
DWORD be_handled = IMessageFilter_MessagePending(apt->filter, 0 /* FIXME */, now - start_time, pendingtype);
|
|
|
|
TRACE("IMessageFilter_MessagePending returned %d\n", be_handled);
|
|
|
|
switch (be_handled)
|
|
{
|
|
case PENDINGMSG_CANCELCALL:
|
|
WARN("call canceled\n");
|
|
hr = RPC_E_CALL_CANCELED;
|
|
break;
|
|
case PENDINGMSG_WAITNOPROCESS:
|
|
case PENDINGMSG_WAITDEFPROCESS:
|
|
default:
|
|
/* FIXME: MSDN is very vague about the difference
|
|
* between WAITNOPROCESS and WAITDEFPROCESS - there
|
|
* appears to be none, so it is possibly a left-over
|
|
* from the 16-bit world. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!apt->win)
|
|
{
|
|
/* If window is NULL on apartment, peek at messages so that it will not trigger
|
|
* MsgWaitForMultipleObjects next time. */
|
|
PeekMessageW(NULL, NULL, 0, 0, PM_QS_POSTMESSAGE | PM_NOREMOVE | PM_NOYIELD);
|
|
}
|
|
|
|
/* Some apps (e.g. Visio 2010) don't handle WM_PAINT properly and loop forever,
|
|
* so after processing 100 messages we go back to checking the wait handles */
|
|
while (msg_count++ < 100 && com_peek_message(apt, &msg))
|
|
{
|
|
if (msg.message == WM_QUIT)
|
|
{
|
|
TRACE("Received WM_QUIT message\n");
|
|
post_quit = TRUE;
|
|
exit_code = msg.wParam;
|
|
}
|
|
else
|
|
{
|
|
TRACE("Received message whilst waiting for RPC: 0x%04x\n", msg.message);
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE("Waiting for rpc completion\n");
|
|
|
|
res = WaitForMultipleObjectsEx(handle_count, handles, !!(flags & COWAIT_WAITALL),
|
|
(timeout == INFINITE) ? INFINITE : start_time + timeout - now, !!(flags & COWAIT_ALERTABLE));
|
|
}
|
|
|
|
switch (res)
|
|
{
|
|
case WAIT_TIMEOUT:
|
|
hr = RPC_S_CALLPENDING;
|
|
break;
|
|
case WAIT_FAILED:
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
default:
|
|
*index = res;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (post_quit) PostQuitMessage(exit_code);
|
|
|
|
TRACE("-- 0x%08x\n", hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoRegisterMessageFilter (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoRegisterMessageFilter(IMessageFilter *filter, IMessageFilter **ret_filter)
|
|
{
|
|
IMessageFilter *old_filter;
|
|
struct apartment *apt;
|
|
|
|
TRACE("%p, %p\n", filter, ret_filter);
|
|
|
|
apt = com_get_current_apt();
|
|
|
|
/* Can't set a message filter in a multi-threaded apartment */
|
|
if (!apt || apt->multi_threaded)
|
|
{
|
|
WARN("Can't set message filter in MTA or uninitialized apt\n");
|
|
return CO_E_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (filter)
|
|
IMessageFilter_AddRef(filter);
|
|
|
|
EnterCriticalSection(&apt->cs);
|
|
|
|
old_filter = apt->filter;
|
|
apt->filter = filter;
|
|
|
|
LeaveCriticalSection(&apt->cs);
|
|
|
|
if (ret_filter)
|
|
*ret_filter = old_filter;
|
|
else if (old_filter)
|
|
IMessageFilter_Release(old_filter);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static void com_revoke_all_ps_clsids(void)
|
|
{
|
|
struct registered_ps *cur, *cur2;
|
|
|
|
EnterCriticalSection(&cs_registered_ps);
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, ®istered_proxystubs, struct registered_ps, entry)
|
|
{
|
|
list_remove(&cur->entry);
|
|
heap_free(cur);
|
|
}
|
|
|
|
LeaveCriticalSection(&cs_registered_ps);
|
|
}
|
|
|
|
static HRESULT get_ps_clsid_from_registry(const WCHAR* path, REGSAM access, CLSID *pclsid)
|
|
{
|
|
WCHAR value[CHARS_IN_GUID];
|
|
HKEY hkey;
|
|
DWORD len;
|
|
|
|
access |= KEY_READ;
|
|
|
|
if (open_classes_key(HKEY_CLASSES_ROOT, path, access, &hkey))
|
|
return REGDB_E_IIDNOTREG;
|
|
|
|
len = sizeof(value);
|
|
if (ERROR_SUCCESS != RegQueryValueExW(hkey, NULL, NULL, NULL, (BYTE *)value, &len))
|
|
return REGDB_E_IIDNOTREG;
|
|
RegCloseKey(hkey);
|
|
|
|
if (CLSIDFromString(value, pclsid) != NOERROR)
|
|
return REGDB_E_IIDNOTREG;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CoGetPSClsid (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
|
|
{
|
|
static const WCHAR interfaceW[] = L"Interface\\";
|
|
static const WCHAR psW[] = L"\\ProxyStubClsid32";
|
|
WCHAR path[ARRAY_SIZE(interfaceW) - 1 + CHARS_IN_GUID - 1 + ARRAY_SIZE(psW)];
|
|
ACTCTX_SECTION_KEYED_DATA data;
|
|
struct registered_ps *cur;
|
|
REGSAM opposite = (sizeof(void*) > sizeof(int)) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY;
|
|
BOOL is_wow64;
|
|
HRESULT hr;
|
|
|
|
TRACE("%s, %p\n", debugstr_guid(riid), pclsid);
|
|
|
|
if (!InternalIsProcessInitialized())
|
|
{
|
|
ERR("apartment not initialised\n");
|
|
return CO_E_NOTINITIALIZED;
|
|
}
|
|
|
|
if (!pclsid)
|
|
return E_INVALIDARG;
|
|
|
|
EnterCriticalSection(&cs_registered_ps);
|
|
|
|
LIST_FOR_EACH_ENTRY(cur, ®istered_proxystubs, struct registered_ps, entry)
|
|
{
|
|
if (IsEqualIID(&cur->iid, riid))
|
|
{
|
|
*pclsid = cur->clsid;
|
|
LeaveCriticalSection(&cs_registered_ps);
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&cs_registered_ps);
|
|
|
|
data.cbSize = sizeof(data);
|
|
if (FindActCtxSectionGuid(0, NULL, ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION,
|
|
riid, &data))
|
|
{
|
|
struct ifacepsredirect_data *ifaceps = (struct ifacepsredirect_data *)data.lpData;
|
|
*pclsid = ifaceps->iid;
|
|
return S_OK;
|
|
}
|
|
|
|
/* Interface\\{string form of riid}\\ProxyStubClsid32 */
|
|
lstrcpyW(path, interfaceW);
|
|
StringFromGUID2(riid, path + ARRAY_SIZE(interfaceW) - 1, CHARS_IN_GUID);
|
|
lstrcpyW(path + ARRAY_SIZE(interfaceW) - 1 + CHARS_IN_GUID - 1, psW);
|
|
|
|
hr = get_ps_clsid_from_registry(path, 0, pclsid);
|
|
if (FAILED(hr) && (opposite == KEY_WOW64_32KEY || (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64)))
|
|
hr = get_ps_clsid_from_registry(path, opposite, pclsid);
|
|
|
|
if (hr == S_OK)
|
|
TRACE("() Returning CLSID %s\n", debugstr_guid(pclsid));
|
|
else
|
|
WARN("No PSFactoryBuffer object is registered for IID %s\n", debugstr_guid(riid));
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CoRegisterPSClsid (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoRegisterPSClsid(REFIID riid, REFCLSID rclsid)
|
|
{
|
|
struct registered_ps *cur;
|
|
|
|
TRACE("%s, %s\n", debugstr_guid(riid), debugstr_guid(rclsid));
|
|
|
|
if (!InternalIsProcessInitialized())
|
|
{
|
|
ERR("apartment not initialised\n");
|
|
return CO_E_NOTINITIALIZED;
|
|
}
|
|
|
|
EnterCriticalSection(&cs_registered_ps);
|
|
|
|
LIST_FOR_EACH_ENTRY(cur, ®istered_proxystubs, struct registered_ps, entry)
|
|
{
|
|
if (IsEqualIID(&cur->iid, riid))
|
|
{
|
|
cur->clsid = *rclsid;
|
|
LeaveCriticalSection(&cs_registered_ps);
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
cur = heap_alloc(sizeof(*cur));
|
|
if (!cur)
|
|
{
|
|
LeaveCriticalSection(&cs_registered_ps);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
cur->iid = *riid;
|
|
cur->clsid = *rclsid;
|
|
list_add_head(®istered_proxystubs, &cur->entry);
|
|
|
|
LeaveCriticalSection(&cs_registered_ps);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
struct thread_context
|
|
{
|
|
IComThreadingInfo IComThreadingInfo_iface;
|
|
IContextCallback IContextCallback_iface;
|
|
IObjContext IObjContext_iface;
|
|
LONG refcount;
|
|
};
|
|
|
|
static inline struct thread_context *impl_from_IComThreadingInfo(IComThreadingInfo *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct thread_context, IComThreadingInfo_iface);
|
|
}
|
|
|
|
static inline struct thread_context *impl_from_IContextCallback(IContextCallback *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct thread_context, IContextCallback_iface);
|
|
}
|
|
|
|
static inline struct thread_context *impl_from_IObjContext(IObjContext *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct thread_context, IObjContext_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI thread_context_info_QueryInterface(IComThreadingInfo *iface, REFIID riid, void **obj)
|
|
{
|
|
struct thread_context *context = impl_from_IComThreadingInfo(iface);
|
|
|
|
*obj = NULL;
|
|
|
|
if (IsEqualIID(riid, &IID_IComThreadingInfo) ||
|
|
IsEqualIID(riid, &IID_IUnknown))
|
|
{
|
|
*obj = &context->IComThreadingInfo_iface;
|
|
}
|
|
else if (IsEqualIID(riid, &IID_IContextCallback))
|
|
{
|
|
*obj = &context->IContextCallback_iface;
|
|
}
|
|
else if (IsEqualIID(riid, &IID_IObjContext))
|
|
{
|
|
*obj = &context->IObjContext_iface;
|
|
}
|
|
|
|
if (*obj)
|
|
{
|
|
IUnknown_AddRef((IUnknown *)*obj);
|
|
return S_OK;
|
|
}
|
|
|
|
FIXME("interface not implemented %s\n", debugstr_guid(riid));
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI thread_context_info_AddRef(IComThreadingInfo *iface)
|
|
{
|
|
struct thread_context *context = impl_from_IComThreadingInfo(iface);
|
|
return InterlockedIncrement(&context->refcount);
|
|
}
|
|
|
|
static ULONG WINAPI thread_context_info_Release(IComThreadingInfo *iface)
|
|
{
|
|
struct thread_context *context = impl_from_IComThreadingInfo(iface);
|
|
|
|
/* Context instance is initially created with CoGetContextToken() with refcount set to 0,
|
|
releasing context while refcount is at 0 destroys it. */
|
|
if (!context->refcount)
|
|
{
|
|
heap_free(context);
|
|
return 0;
|
|
}
|
|
|
|
return InterlockedDecrement(&context->refcount);
|
|
}
|
|
|
|
static HRESULT WINAPI thread_context_info_GetCurrentApartmentType(IComThreadingInfo *iface, APTTYPE *apttype)
|
|
{
|
|
APTTYPEQUALIFIER qualifier;
|
|
|
|
TRACE("%p\n", apttype);
|
|
|
|
return CoGetApartmentType(apttype, &qualifier);
|
|
}
|
|
|
|
static HRESULT WINAPI thread_context_info_GetCurrentThreadType(IComThreadingInfo *iface, THDTYPE *thdtype)
|
|
{
|
|
APTTYPEQUALIFIER qualifier;
|
|
APTTYPE apttype;
|
|
HRESULT hr;
|
|
|
|
hr = CoGetApartmentType(&apttype, &qualifier);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
TRACE("%p\n", thdtype);
|
|
|
|
switch (apttype)
|
|
{
|
|
case APTTYPE_STA:
|
|
case APTTYPE_MAINSTA:
|
|
*thdtype = THDTYPE_PROCESSMESSAGES;
|
|
break;
|
|
default:
|
|
*thdtype = THDTYPE_BLOCKMESSAGES;
|
|
break;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI thread_context_info_GetCurrentLogicalThreadId(IComThreadingInfo *iface, GUID *logical_thread_id)
|
|
{
|
|
TRACE("%p\n", logical_thread_id);
|
|
|
|
return CoGetCurrentLogicalThreadId(logical_thread_id);
|
|
}
|
|
|
|
static HRESULT WINAPI thread_context_info_SetCurrentLogicalThreadId(IComThreadingInfo *iface, REFGUID logical_thread_id)
|
|
{
|
|
FIXME("%s stub\n", debugstr_guid(logical_thread_id));
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static const IComThreadingInfoVtbl thread_context_info_vtbl =
|
|
{
|
|
thread_context_info_QueryInterface,
|
|
thread_context_info_AddRef,
|
|
thread_context_info_Release,
|
|
thread_context_info_GetCurrentApartmentType,
|
|
thread_context_info_GetCurrentThreadType,
|
|
thread_context_info_GetCurrentLogicalThreadId,
|
|
thread_context_info_SetCurrentLogicalThreadId
|
|
};
|
|
|
|
static HRESULT WINAPI thread_context_callback_QueryInterface(IContextCallback *iface, REFIID riid, void **obj)
|
|
{
|
|
struct thread_context *context = impl_from_IContextCallback(iface);
|
|
return IComThreadingInfo_QueryInterface(&context->IComThreadingInfo_iface, riid, obj);
|
|
}
|
|
|
|
static ULONG WINAPI thread_context_callback_AddRef(IContextCallback *iface)
|
|
{
|
|
struct thread_context *context = impl_from_IContextCallback(iface);
|
|
return IComThreadingInfo_AddRef(&context->IComThreadingInfo_iface);
|
|
}
|
|
|
|
static ULONG WINAPI thread_context_callback_Release(IContextCallback *iface)
|
|
{
|
|
struct thread_context *context = impl_from_IContextCallback(iface);
|
|
return IComThreadingInfo_Release(&context->IComThreadingInfo_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI thread_context_callback_ContextCallback(IContextCallback *iface,
|
|
PFNCONTEXTCALL callback, ComCallData *param, REFIID riid, int method, IUnknown *punk)
|
|
{
|
|
FIXME("%p, %p, %p, %s, %d, %p\n", iface, callback, param, debugstr_guid(riid), method, punk);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static const IContextCallbackVtbl thread_context_callback_vtbl =
|
|
{
|
|
thread_context_callback_QueryInterface,
|
|
thread_context_callback_AddRef,
|
|
thread_context_callback_Release,
|
|
thread_context_callback_ContextCallback
|
|
};
|
|
|
|
static HRESULT WINAPI thread_object_context_QueryInterface(IObjContext *iface, REFIID riid, void **obj)
|
|
{
|
|
struct thread_context *context = impl_from_IObjContext(iface);
|
|
return IComThreadingInfo_QueryInterface(&context->IComThreadingInfo_iface, riid, obj);
|
|
}
|
|
|
|
static ULONG WINAPI thread_object_context_AddRef(IObjContext *iface)
|
|
{
|
|
struct thread_context *context = impl_from_IObjContext(iface);
|
|
return IComThreadingInfo_AddRef(&context->IComThreadingInfo_iface);
|
|
}
|
|
|
|
static ULONG WINAPI thread_object_context_Release(IObjContext *iface)
|
|
{
|
|
struct thread_context *context = impl_from_IObjContext(iface);
|
|
return IComThreadingInfo_Release(&context->IComThreadingInfo_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI thread_object_context_SetProperty(IObjContext *iface, REFGUID propid, CPFLAGS flags, IUnknown *punk)
|
|
{
|
|
FIXME("%p, %s, %x, %p\n", iface, debugstr_guid(propid), flags, punk);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI thread_object_context_RemoveProperty(IObjContext *iface, REFGUID propid)
|
|
{
|
|
FIXME("%p, %s\n", iface, debugstr_guid(propid));
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI thread_object_context_GetProperty(IObjContext *iface, REFGUID propid, CPFLAGS *flags, IUnknown **punk)
|
|
{
|
|
FIXME("%p, %s, %p, %p\n", iface, debugstr_guid(propid), flags, punk);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI thread_object_context_EnumContextProps(IObjContext *iface, IEnumContextProps **props)
|
|
{
|
|
FIXME("%p, %p\n", iface, props);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static void WINAPI thread_object_context_Reserved1(IObjContext *iface)
|
|
{
|
|
FIXME("%p\n", iface);
|
|
}
|
|
|
|
static void WINAPI thread_object_context_Reserved2(IObjContext *iface)
|
|
{
|
|
FIXME("%p\n", iface);
|
|
}
|
|
|
|
static void WINAPI thread_object_context_Reserved3(IObjContext *iface)
|
|
{
|
|
FIXME("%p\n", iface);
|
|
}
|
|
|
|
static void WINAPI thread_object_context_Reserved4(IObjContext *iface)
|
|
{
|
|
FIXME("%p\n", iface);
|
|
}
|
|
|
|
static void WINAPI thread_object_context_Reserved5(IObjContext *iface)
|
|
{
|
|
FIXME("%p\n", iface);
|
|
}
|
|
|
|
static void WINAPI thread_object_context_Reserved6(IObjContext *iface)
|
|
{
|
|
FIXME("%p\n", iface);
|
|
}
|
|
|
|
static void WINAPI thread_object_context_Reserved7(IObjContext *iface)
|
|
{
|
|
FIXME("%p\n", iface);
|
|
}
|
|
|
|
static const IObjContextVtbl thread_object_context_vtbl =
|
|
{
|
|
thread_object_context_QueryInterface,
|
|
thread_object_context_AddRef,
|
|
thread_object_context_Release,
|
|
thread_object_context_SetProperty,
|
|
thread_object_context_RemoveProperty,
|
|
thread_object_context_GetProperty,
|
|
thread_object_context_EnumContextProps,
|
|
thread_object_context_Reserved1,
|
|
thread_object_context_Reserved2,
|
|
thread_object_context_Reserved3,
|
|
thread_object_context_Reserved4,
|
|
thread_object_context_Reserved5,
|
|
thread_object_context_Reserved6,
|
|
thread_object_context_Reserved7
|
|
};
|
|
|
|
/***********************************************************************
|
|
* CoGetContextToken (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoGetContextToken(ULONG_PTR *token)
|
|
{
|
|
struct tlsdata *tlsdata;
|
|
HRESULT hr;
|
|
|
|
TRACE("%p\n", token);
|
|
|
|
if (!InternalIsProcessInitialized())
|
|
{
|
|
ERR("apartment not initialised\n");
|
|
return CO_E_NOTINITIALIZED;
|
|
}
|
|
|
|
if (FAILED(hr = com_get_tlsdata(&tlsdata)))
|
|
return hr;
|
|
|
|
if (!token)
|
|
return E_POINTER;
|
|
|
|
if (!tlsdata->context_token)
|
|
{
|
|
struct thread_context *context;
|
|
|
|
context = heap_alloc_zero(sizeof(*context));
|
|
if (!context)
|
|
return E_OUTOFMEMORY;
|
|
|
|
context->IComThreadingInfo_iface.lpVtbl = &thread_context_info_vtbl;
|
|
context->IContextCallback_iface.lpVtbl = &thread_context_callback_vtbl;
|
|
context->IObjContext_iface.lpVtbl = &thread_object_context_vtbl;
|
|
/* Context token does not take a reference, it's always zero until the
|
|
interface is explicitly requested with CoGetObjectContext(). */
|
|
context->refcount = 0;
|
|
|
|
tlsdata->context_token = &context->IObjContext_iface;
|
|
}
|
|
|
|
*token = (ULONG_PTR)tlsdata->context_token;
|
|
TRACE("context_token %p\n", tlsdata->context_token);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoGetCurrentLogicalThreadId (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoGetCurrentLogicalThreadId(GUID *id)
|
|
{
|
|
struct tlsdata *tlsdata;
|
|
HRESULT hr;
|
|
|
|
if (!id)
|
|
return E_INVALIDARG;
|
|
|
|
if (FAILED(hr = com_get_tlsdata(&tlsdata)))
|
|
return hr;
|
|
|
|
if (IsEqualGUID(&tlsdata->causality_id, &GUID_NULL))
|
|
CoCreateGuid(&tlsdata->causality_id);
|
|
|
|
*id = tlsdata->causality_id;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoGetCurrentProcess (combase.@)
|
|
*/
|
|
DWORD WINAPI CoGetCurrentProcess(void)
|
|
{
|
|
struct tlsdata *tlsdata;
|
|
|
|
if (FAILED(com_get_tlsdata(&tlsdata)))
|
|
return 0;
|
|
|
|
if (!tlsdata->thread_seqid)
|
|
rpcss_get_next_seqid(&tlsdata->thread_seqid);
|
|
|
|
return tlsdata->thread_seqid;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoFreeUnusedLibrariesEx (combase.@)
|
|
*/
|
|
void WINAPI DECLSPEC_HOTPATCH CoFreeUnusedLibrariesEx(DWORD unload_delay, DWORD reserved)
|
|
{
|
|
struct apartment *apt = com_get_current_apt();
|
|
if (!apt)
|
|
{
|
|
ERR("apartment not initialised\n");
|
|
return;
|
|
}
|
|
|
|
apartment_freeunusedlibraries(apt, unload_delay);
|
|
}
|
|
|
|
/*
|
|
* When locked, don't modify list (unless we add a new head), so that it's
|
|
* safe to iterate it. Freeing of list entries is delayed and done on unlock.
|
|
*/
|
|
static inline void lock_init_spies(struct tlsdata *tlsdata)
|
|
{
|
|
tlsdata->spies_lock++;
|
|
}
|
|
|
|
static void unlock_init_spies(struct tlsdata *tlsdata)
|
|
{
|
|
struct init_spy *spy, *next;
|
|
|
|
if (--tlsdata->spies_lock) return;
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(spy, next, &tlsdata->spies, struct init_spy, entry)
|
|
{
|
|
if (spy->spy) continue;
|
|
list_remove(&spy->entry);
|
|
heap_free(spy);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoInitializeWOW (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoInitializeWOW(DWORD arg1, DWORD arg2)
|
|
{
|
|
FIXME("%#x, %#x\n", arg1, arg2);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoInitializeEx (combase.@)
|
|
*/
|
|
HRESULT WINAPI DECLSPEC_HOTPATCH CoInitializeEx(void *reserved, DWORD model)
|
|
{
|
|
struct tlsdata *tlsdata;
|
|
struct init_spy *cursor;
|
|
HRESULT hr;
|
|
|
|
TRACE("%p, %#x\n", reserved, model);
|
|
|
|
if (reserved)
|
|
WARN("Unexpected reserved argument %p\n", reserved);
|
|
|
|
if (FAILED(hr = com_get_tlsdata(&tlsdata)))
|
|
return hr;
|
|
|
|
if (InterlockedExchangeAdd(&com_lockcount, 1) == 0)
|
|
TRACE("Initializing the COM libraries\n");
|
|
|
|
lock_init_spies(tlsdata);
|
|
LIST_FOR_EACH_ENTRY(cursor, &tlsdata->spies, struct init_spy, entry)
|
|
{
|
|
if (cursor->spy) IInitializeSpy_PreInitialize(cursor->spy, model, tlsdata->inits);
|
|
}
|
|
unlock_init_spies(tlsdata);
|
|
|
|
hr = enter_apartment(tlsdata, model);
|
|
|
|
lock_init_spies(tlsdata);
|
|
LIST_FOR_EACH_ENTRY(cursor, &tlsdata->spies, struct init_spy, entry)
|
|
{
|
|
if (cursor->spy) hr = IInitializeSpy_PostInitialize(cursor->spy, hr, model, tlsdata->inits);
|
|
}
|
|
unlock_init_spies(tlsdata);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoUninitialize (combase.@)
|
|
*/
|
|
void WINAPI DECLSPEC_HOTPATCH CoUninitialize(void)
|
|
{
|
|
struct tlsdata *tlsdata;
|
|
struct init_spy *cursor, *next;
|
|
LONG lockcount;
|
|
|
|
TRACE("\n");
|
|
|
|
if (FAILED(com_get_tlsdata(&tlsdata)))
|
|
return;
|
|
|
|
lock_init_spies(tlsdata);
|
|
LIST_FOR_EACH_ENTRY_SAFE(cursor, next, &tlsdata->spies, struct init_spy, entry)
|
|
{
|
|
if (cursor->spy) IInitializeSpy_PreUninitialize(cursor->spy, tlsdata->inits);
|
|
}
|
|
unlock_init_spies(tlsdata);
|
|
|
|
/* sanity check */
|
|
if (!tlsdata->inits)
|
|
{
|
|
ERR("Mismatched CoUninitialize\n");
|
|
|
|
lock_init_spies(tlsdata);
|
|
LIST_FOR_EACH_ENTRY_SAFE(cursor, next, &tlsdata->spies, struct init_spy, entry)
|
|
{
|
|
if (cursor->spy) IInitializeSpy_PostUninitialize(cursor->spy, tlsdata->inits);
|
|
}
|
|
unlock_init_spies(tlsdata);
|
|
|
|
return;
|
|
}
|
|
|
|
leave_apartment(tlsdata);
|
|
|
|
/*
|
|
* Decrease the reference count.
|
|
* If we are back to 0 locks on the COM library, make sure we free
|
|
* all the associated data structures.
|
|
*/
|
|
lockcount = InterlockedExchangeAdd(&com_lockcount, -1);
|
|
if (lockcount == 1)
|
|
{
|
|
TRACE("Releasing the COM libraries\n");
|
|
|
|
com_revoke_all_ps_clsids();
|
|
DestroyRunningObjectTable();
|
|
}
|
|
else if (lockcount < 1)
|
|
{
|
|
ERR("Unbalanced lock count %d\n", lockcount);
|
|
InterlockedExchangeAdd(&com_lockcount, 1);
|
|
}
|
|
|
|
lock_init_spies(tlsdata);
|
|
LIST_FOR_EACH_ENTRY(cursor, &tlsdata->spies, struct init_spy, entry)
|
|
{
|
|
if (cursor->spy) IInitializeSpy_PostUninitialize(cursor->spy, tlsdata->inits);
|
|
}
|
|
unlock_init_spies(tlsdata);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoIncrementMTAUsage (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoIncrementMTAUsage(CO_MTA_USAGE_COOKIE *cookie)
|
|
{
|
|
TRACE("%p\n", cookie);
|
|
|
|
return apartment_increment_mta_usage(cookie);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoDecrementMTAUsage (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoDecrementMTAUsage(CO_MTA_USAGE_COOKIE cookie)
|
|
{
|
|
TRACE("%p\n", cookie);
|
|
|
|
apartment_decrement_mta_usage(cookie);
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoGetApartmentType (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoGetApartmentType(APTTYPE *type, APTTYPEQUALIFIER *qualifier)
|
|
{
|
|
struct tlsdata *tlsdata;
|
|
struct apartment *apt;
|
|
HRESULT hr;
|
|
|
|
TRACE("%p, %p\n", type, qualifier);
|
|
|
|
if (!type || !qualifier)
|
|
return E_INVALIDARG;
|
|
|
|
if (FAILED(hr = com_get_tlsdata(&tlsdata)))
|
|
return hr;
|
|
|
|
if (!tlsdata->apt)
|
|
*type = APTTYPE_CURRENT;
|
|
else if (tlsdata->apt->multi_threaded)
|
|
*type = APTTYPE_MTA;
|
|
else if (tlsdata->apt->main)
|
|
*type = APTTYPE_MAINSTA;
|
|
else
|
|
*type = APTTYPE_STA;
|
|
|
|
*qualifier = APTTYPEQUALIFIER_NONE;
|
|
|
|
if (!tlsdata->apt && (apt = apartment_get_mta()))
|
|
{
|
|
apartment_release(apt);
|
|
*type = APTTYPE_MTA;
|
|
*qualifier = APTTYPEQUALIFIER_IMPLICIT_MTA;
|
|
return S_OK;
|
|
}
|
|
|
|
return tlsdata->apt ? S_OK : CO_E_NOTINITIALIZED;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoRegisterClassObject (combase.@)
|
|
* BUGS
|
|
* MSDN claims that multiple interface registrations are legal, but we
|
|
* can't do that with our current implementation.
|
|
*/
|
|
HRESULT WINAPI CoRegisterClassObject(REFCLSID rclsid, IUnknown *object, DWORD clscontext,
|
|
DWORD flags, DWORD *cookie)
|
|
{
|
|
static LONG next_cookie;
|
|
|
|
struct registered_class *newclass;
|
|
IUnknown *found_object;
|
|
struct apartment *apt;
|
|
HRESULT hr = S_OK;
|
|
|
|
TRACE("%s, %p, %#x, %#x, %p\n", debugstr_guid(rclsid), object, clscontext, flags, cookie);
|
|
|
|
if (!cookie || !object)
|
|
return E_INVALIDARG;
|
|
|
|
if (!(apt = apartment_get_current_or_mta()))
|
|
{
|
|
ERR("COM was not initialized\n");
|
|
return CO_E_NOTINITIALIZED;
|
|
}
|
|
|
|
*cookie = 0;
|
|
|
|
/* REGCLS_MULTIPLEUSE implies registering as inproc server. This is what
|
|
* differentiates the flag from REGCLS_MULTI_SEPARATE. */
|
|
if (flags & REGCLS_MULTIPLEUSE)
|
|
clscontext |= CLSCTX_INPROC_SERVER;
|
|
|
|
/*
|
|
* First, check if the class is already registered.
|
|
* If it is, this should cause an error.
|
|
*/
|
|
if ((found_object = com_get_registered_class_object(apt, rclsid, clscontext)))
|
|
{
|
|
if (flags & REGCLS_MULTIPLEUSE)
|
|
{
|
|
if (clscontext & CLSCTX_LOCAL_SERVER)
|
|
hr = CoLockObjectExternal(found_object, TRUE, FALSE);
|
|
IUnknown_Release(found_object);
|
|
apartment_release(apt);
|
|
return hr;
|
|
}
|
|
|
|
IUnknown_Release(found_object);
|
|
ERR("object already registered for class %s\n", debugstr_guid(rclsid));
|
|
apartment_release(apt);
|
|
return CO_E_OBJISREG;
|
|
}
|
|
|
|
newclass = heap_alloc_zero(sizeof(*newclass));
|
|
if (!newclass)
|
|
{
|
|
apartment_release(apt);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
newclass->clsid = *rclsid;
|
|
newclass->apartment_id = apt->oxid;
|
|
newclass->clscontext = clscontext;
|
|
newclass->flags = flags;
|
|
|
|
if (!(newclass->cookie = InterlockedIncrement(&next_cookie)))
|
|
newclass->cookie = InterlockedIncrement(&next_cookie);
|
|
|
|
newclass->object = object;
|
|
IUnknown_AddRef(newclass->object);
|
|
|
|
EnterCriticalSection(®istered_classes_cs);
|
|
list_add_tail(®istered_classes, &newclass->entry);
|
|
LeaveCriticalSection(®istered_classes_cs);
|
|
|
|
*cookie = newclass->cookie;
|
|
|
|
if (clscontext & CLSCTX_LOCAL_SERVER)
|
|
{
|
|
IStream *marshal_stream;
|
|
|
|
hr = apartment_get_local_server_stream(apt, &marshal_stream);
|
|
if(FAILED(hr))
|
|
{
|
|
apartment_release(apt);
|
|
return hr;
|
|
}
|
|
|
|
hr = rpc_register_local_server(&newclass->clsid, marshal_stream, flags, &newclass->rpcss_cookie);
|
|
IStream_Release(marshal_stream);
|
|
}
|
|
|
|
apartment_release(apt);
|
|
return S_OK;
|
|
}
|
|
|
|
static void com_revoke_class_object(struct registered_class *entry)
|
|
{
|
|
list_remove(&entry->entry);
|
|
|
|
if (entry->clscontext & CLSCTX_LOCAL_SERVER)
|
|
rpc_revoke_local_server(entry->rpcss_cookie);
|
|
|
|
IUnknown_Release(entry->object);
|
|
heap_free(entry);
|
|
}
|
|
|
|
/* Cleans up rpcss registry */
|
|
static void com_revoke_local_servers(void)
|
|
{
|
|
struct registered_class *cur, *cur2;
|
|
|
|
EnterCriticalSection(®istered_classes_cs);
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, ®istered_classes, struct registered_class, entry)
|
|
{
|
|
if (cur->clscontext & CLSCTX_LOCAL_SERVER)
|
|
com_revoke_class_object(cur);
|
|
}
|
|
|
|
LeaveCriticalSection(®istered_classes_cs);
|
|
}
|
|
|
|
void apartment_revoke_all_classes(const struct apartment *apt)
|
|
{
|
|
struct registered_class *cur, *cur2;
|
|
|
|
EnterCriticalSection(®istered_classes_cs);
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, ®istered_classes, struct registered_class, entry)
|
|
{
|
|
if (cur->apartment_id == apt->oxid)
|
|
com_revoke_class_object(cur);
|
|
}
|
|
|
|
LeaveCriticalSection(®istered_classes_cs);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoRevokeClassObject (combase.@)
|
|
*/
|
|
HRESULT WINAPI DECLSPEC_HOTPATCH CoRevokeClassObject(DWORD cookie)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
struct registered_class *cur;
|
|
struct apartment *apt;
|
|
|
|
TRACE("%#x\n", cookie);
|
|
|
|
if (!(apt = apartment_get_current_or_mta()))
|
|
{
|
|
ERR("COM was not initialized\n");
|
|
return CO_E_NOTINITIALIZED;
|
|
}
|
|
|
|
EnterCriticalSection(®istered_classes_cs);
|
|
|
|
LIST_FOR_EACH_ENTRY(cur, ®istered_classes, struct registered_class, entry)
|
|
{
|
|
if (cur->cookie != cookie)
|
|
continue;
|
|
|
|
if (cur->apartment_id == apt->oxid)
|
|
{
|
|
com_revoke_class_object(cur);
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
ERR("called from wrong apartment, should be called from %s\n", wine_dbgstr_longlong(cur->apartment_id));
|
|
hr = RPC_E_WRONG_THREAD;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
LeaveCriticalSection(®istered_classes_cs);
|
|
apartment_release(apt);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoAddRefServerProcess (combase.@)
|
|
*/
|
|
ULONG WINAPI CoAddRefServerProcess(void)
|
|
{
|
|
ULONG refs;
|
|
|
|
TRACE("\n");
|
|
|
|
EnterCriticalSection(®istered_classes_cs);
|
|
refs = ++com_server_process_refcount;
|
|
LeaveCriticalSection(®istered_classes_cs);
|
|
|
|
TRACE("refs before: %d\n", refs - 1);
|
|
|
|
return refs;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoReleaseServerProcess [OLE32.@]
|
|
*/
|
|
ULONG WINAPI CoReleaseServerProcess(void)
|
|
{
|
|
ULONG refs;
|
|
|
|
TRACE("\n");
|
|
|
|
EnterCriticalSection(®istered_classes_cs);
|
|
|
|
refs = --com_server_process_refcount;
|
|
/* FIXME: suspend objects */
|
|
|
|
LeaveCriticalSection(®istered_classes_cs);
|
|
|
|
TRACE("refs after: %d\n", refs);
|
|
|
|
return refs;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoDisconnectObject (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoDisconnectObject(IUnknown *object, DWORD reserved)
|
|
{
|
|
struct stub_manager *manager;
|
|
struct apartment *apt;
|
|
IMarshal *marshal;
|
|
HRESULT hr;
|
|
|
|
TRACE("%p, %#x\n", object, reserved);
|
|
|
|
if (!object)
|
|
return E_INVALIDARG;
|
|
|
|
hr = IUnknown_QueryInterface(object, &IID_IMarshal, (void **)&marshal);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = IMarshal_DisconnectObject(marshal, reserved);
|
|
IMarshal_Release(marshal);
|
|
return hr;
|
|
}
|
|
|
|
if (!(apt = apartment_get_current_or_mta()))
|
|
{
|
|
ERR("apartment not initialised\n");
|
|
return CO_E_NOTINITIALIZED;
|
|
}
|
|
|
|
manager = get_stub_manager_from_object(apt, object, FALSE);
|
|
if (manager)
|
|
{
|
|
stub_manager_disconnect(manager);
|
|
/* Release stub manager twice, to remove the apartment reference. */
|
|
stub_manager_int_release(manager);
|
|
stub_manager_int_release(manager);
|
|
}
|
|
|
|
/* 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 */
|
|
|
|
apartment_release(apt);
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* CoLockObjectExternal (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoLockObjectExternal(IUnknown *object, BOOL lock, BOOL last_unlock_releases)
|
|
{
|
|
struct stub_manager *stubmgr;
|
|
struct apartment *apt;
|
|
|
|
TRACE("%p, %d, %d\n", object, lock, last_unlock_releases);
|
|
|
|
if (!(apt = apartment_get_current_or_mta()))
|
|
{
|
|
ERR("apartment not initialised\n");
|
|
return CO_E_NOTINITIALIZED;
|
|
}
|
|
|
|
stubmgr = get_stub_manager_from_object(apt, object, lock);
|
|
if (!stubmgr)
|
|
{
|
|
WARN("stub object not found %p\n", object);
|
|
/* 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 */
|
|
apartment_release(apt);
|
|
return S_OK;
|
|
}
|
|
|
|
if (lock)
|
|
stub_manager_ext_addref(stubmgr, 1, FALSE);
|
|
else
|
|
stub_manager_ext_release(stubmgr, 1, FALSE, last_unlock_releases);
|
|
|
|
stub_manager_int_release(stubmgr);
|
|
apartment_release(apt);
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoRegisterChannelHook (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoRegisterChannelHook(REFGUID guidExtension, IChannelHook *channel_hook)
|
|
{
|
|
TRACE("%s, %p\n", debugstr_guid(guidExtension), channel_hook);
|
|
|
|
return rpc_register_channel_hook(guidExtension, channel_hook);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoDisableCallCancellation (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoDisableCallCancellation(void *reserved)
|
|
{
|
|
FIXME("%p stub\n", reserved);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoEnableCallCancellation (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoEnableCallCancellation(void *reserved)
|
|
{
|
|
FIXME("%p stub\n", reserved);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoGetCallerTID (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoGetCallerTID(DWORD *tid)
|
|
{
|
|
FIXME("stub!\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoIsHandlerConnected (combase.@)
|
|
*/
|
|
BOOL WINAPI CoIsHandlerConnected(IUnknown *object)
|
|
{
|
|
FIXME("%p\n", object);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoSuspendClassObjects (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoSuspendClassObjects(void)
|
|
{
|
|
FIXME("\n");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoResumeClassObjects (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoResumeClassObjects(void)
|
|
{
|
|
FIXME("stub\n");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoRegisterSurrogate (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoRegisterSurrogate(ISurrogate *surrogate)
|
|
{
|
|
FIXME("%p stub\n", surrogate);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CoRegisterSurrogateEx (combase.@)
|
|
*/
|
|
HRESULT WINAPI CoRegisterSurrogateEx(REFGUID guid, void *reserved)
|
|
{
|
|
FIXME("%s, %p stub\n", debugstr_guid(guid), reserved);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* DllMain (combase.@)
|
|
*/
|
|
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD reason, LPVOID reserved)
|
|
{
|
|
TRACE("%p 0x%x %p\n", hinstDLL, reason, reserved);
|
|
|
|
switch (reason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
hProxyDll = hinstDLL;
|
|
break;
|
|
case DLL_PROCESS_DETACH:
|
|
com_revoke_local_servers();
|
|
if (reserved) break;
|
|
apartment_global_cleanup();
|
|
DeleteCriticalSection(®istered_classes_cs);
|
|
rpc_unregister_channel_hooks();
|
|
break;
|
|
case DLL_THREAD_DETACH:
|
|
com_cleanup_tlsdata();
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|