ole32: Support registration-free COM in CoGetClassObject().

This commit is contained in:
Nikolay Sivov 2013-09-27 08:47:15 +04:00 committed by Alexandre Julliard
parent 99ceb599bb
commit af93b53082
2 changed files with 213 additions and 46 deletions

View File

@ -90,6 +90,54 @@ static CRITICAL_SECTION_DEBUG critsect_debug =
};
static CRITICAL_SECTION csApartment = { &critsect_debug, -1, 0, 0, 0, 0 };
enum comclass_threadingmodel
{
ThreadingModel_Apartment = 1,
ThreadingModel_Free = 2,
ThreadingModel_No = 3,
ThreadingModel_Both = 4,
ThreadingModel_Neutral = 5
};
struct comclassredirect_data
{
ULONG size;
BYTE res;
BYTE miscmask;
BYTE res1[2];
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 class_reg_data
{
union
{
struct
{
struct comclassredirect_data *data;
void *section;
HANDLE hactctx;
} actctx;
HKEY hkey;
} u;
BOOL hkey;
};
struct registered_psclsid
{
struct list entry;
@ -1227,14 +1275,17 @@ static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
*
* Reads a registry value and expands it when necessary
*/
static DWORD COM_RegReadPath(HKEY hkeyroot, WCHAR * dst, DWORD dstlen)
static DWORD COM_RegReadPath(const struct class_reg_data *regdata, WCHAR *dst, DWORD dstlen)
{
DWORD ret;
if (regdata->hkey)
{
DWORD keytype;
WCHAR src[MAX_PATH];
DWORD dwLength = dstlen * sizeof(WCHAR);
if( (ret = RegQueryValueExW(hkeyroot, NULL, NULL, &keytype, (LPBYTE)src, &dwLength)) == ERROR_SUCCESS ) {
if( (ret = RegQueryValueExW(regdata->u.hkey, NULL, NULL, &keytype, (BYTE*)src, &dwLength)) == ERROR_SUCCESS ) {
if (keytype == REG_EXPAND_SZ) {
if (dstlen <= ExpandEnvironmentStringsW(src, dst, dstlen)) ret = ERROR_MORE_DATA;
} else {
@ -1253,10 +1304,23 @@ static DWORD COM_RegReadPath(HKEY hkeyroot, WCHAR * dst, DWORD dstlen)
}
return ret;
}
else
{
ULONG_PTR cookie;
WCHAR *nameW;
*dst = 0;
nameW = (WCHAR*)((BYTE*)regdata->u.actctx.section + regdata->u.actctx.data->name_offset);
ActivateActCtx(regdata->u.actctx.hactctx, &cookie);
ret = SearchPathW(NULL, nameW, NULL, dstlen, dst, NULL);
DeactivateActCtx(0, cookie);
return !*dst;
}
}
struct host_object_params
{
HKEY hkeydll;
struct class_reg_data regdata;
CLSID clsid; /* clsid of object to marshal */
IID iid; /* interface to marshal */
HANDLE event; /* event signalling when ready for multi-threaded case */
@ -1275,7 +1339,7 @@ static HRESULT apartment_hostobject(struct apartment *apt,
TRACE("clsid %s, iid %s\n", debugstr_guid(&params->clsid), debugstr_guid(&params->iid));
if (COM_RegReadPath(params->hkeydll, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
if (COM_RegReadPath(&params->regdata, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
{
/* failure: CLSID is not found in registry */
WARN("class %s not registered inproc\n", debugstr_guid(&params->clsid));
@ -1372,7 +1436,7 @@ static DWORD CALLBACK apartment_hostobject_thread(LPVOID p)
* caller of this function */
static HRESULT apartment_hostobject_in_hostapt(
struct apartment *apt, BOOL multi_threaded, BOOL main_apartment,
HKEY hkeydll, REFCLSID rclsid, REFIID riid, void **ppv)
const struct class_reg_data *regdata, REFCLSID rclsid, REFIID riid, void **ppv)
{
struct host_object_params params;
HWND apartment_hwnd = NULL;
@ -1442,7 +1506,7 @@ static HRESULT apartment_hostobject_in_hostapt(
}
}
params.hkeydll = hkeydll;
params.regdata = *regdata;
params.clsid = *rclsid;
params.iid = *riid;
hr = CreateStreamOnHGlobal(NULL, TRUE, &params.stream);
@ -2586,19 +2650,36 @@ HRESULT WINAPI CoRegisterClassObject(
return S_OK;
}
static void get_threading_model(HKEY key, LPWSTR value, DWORD len)
static enum comclass_threadingmodel get_threading_model(const struct class_reg_data *data)
{
if (data->hkey)
{
static const WCHAR wszThreadingModel[] = {'T','h','r','e','a','d','i','n','g','M','o','d','e','l',0};
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 threading_model[10 /* strlenW(L"apartment")+1 */];
DWORD dwLength = sizeof(threading_model);
DWORD keytype;
DWORD ret;
DWORD dwLength = len * sizeof(WCHAR);
ret = RegQueryValueExW(key, wszThreadingModel, NULL, &keytype, (LPBYTE)value, &dwLength);
ret = RegQueryValueExW(data->u.hkey, wszThreadingModel, NULL, &keytype, (BYTE*)threading_model, &dwLength);
if ((ret != ERROR_SUCCESS) || (keytype != REG_SZ))
value[0] = '\0';
threading_model[0] = '\0';
if (!strcmpiW(threading_model, wszApartment)) return ThreadingModel_Apartment;
if (!strcmpiW(threading_model, wszFree)) return ThreadingModel_Free;
if (!strcmpiW(threading_model, wszBoth)) return ThreadingModel_Both;
/* there's not specific handling for this case */
if (threading_model[0]) return ThreadingModel_Neutral;
return ThreadingModel_No;
}
else
return data->u.actctx.data->model;
}
static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll,
static HRESULT get_inproc_class_object(APARTMENT *apt, const struct class_reg_data *regdata,
REFCLSID rclsid, REFIID riid,
BOOL hostifnecessary, void **ppv)
{
@ -2607,37 +2688,30 @@ static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll,
if (hostifnecessary)
{
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 threading_model[10 /* strlenW(L"apartment")+1 */];
enum comclass_threadingmodel model = get_threading_model(regdata);
get_threading_model(hkeydll, threading_model, ARRAYSIZE(threading_model));
/* "Apartment" */
if (!strcmpiW(threading_model, wszApartment))
if (model == ThreadingModel_Apartment)
{
apartment_threaded = TRUE;
if (apt->multi_threaded)
return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, hkeydll, rclsid, riid, ppv);
return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, regdata, rclsid, riid, ppv);
}
/* "Free" */
else if (!strcmpiW(threading_model, wszFree))
else if (model == ThreadingModel_Free)
{
apartment_threaded = FALSE;
if (!apt->multi_threaded)
return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, hkeydll, rclsid, riid, ppv);
return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, regdata, rclsid, riid, ppv);
}
/* everything except "Apartment", "Free" and "Both" */
else if (strcmpiW(threading_model, wszBoth))
else if (model != ThreadingModel_Both)
{
apartment_threaded = TRUE;
/* 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 (model != ThreadingModel_No)
FIXME("unrecognised threading model %d for object %s, should be main-threaded?\n", model, debugstr_guid(rclsid));
if (apt->multi_threaded || !apt->main)
return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, hkeydll, rclsid, riid, ppv);
return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, regdata, rclsid, riid, ppv);
}
else
apartment_threaded = FALSE;
@ -2645,7 +2719,7 @@ static HRESULT get_inproc_class_object(APARTMENT *apt, HKEY hkeydll,
else
apartment_threaded = !apt->multi_threaded;
if (COM_RegReadPath(hkeydll, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
if (COM_RegReadPath(regdata, dllpath, ARRAYSIZE(dllpath)) != ERROR_SUCCESS)
{
/* failure: CLSID is not found in registry */
WARN("class %s not registered inproc\n", debugstr_guid(rclsid));
@ -2686,7 +2760,8 @@ HRESULT WINAPI CoGetClassObject(
REFCLSID rclsid, DWORD dwClsContext, COSERVERINFO *pServerInfo,
REFIID iid, LPVOID *ppv)
{
LPUNKNOWN regClassObject;
struct class_reg_data clsreg;
IUnknown *regClassObject;
HRESULT hres = E_UNEXPECTED;
APARTMENT *apt;
BOOL release_apt = FALSE;
@ -2713,6 +2788,37 @@ HRESULT WINAPI CoGetClassObject(
debugstr_w(pServerInfo->pwszName), pServerInfo->pAuthInfo);
}
if (CLSCTX_INPROC_SERVER & dwClsContext)
{
if (IsEqualCLSID(rclsid, &CLSID_InProcFreeMarshaler))
{
if (release_apt) apartment_release(apt);
return FTMarshalCF_Create(iid, ppv);
}
}
if (CLSCTX_INPROC & dwClsContext)
{
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))
{
clsreg.u.actctx.hactctx = data.hActCtx;
clsreg.u.actctx.data = data.lpData;
clsreg.u.actctx.section = data.lpSectionBase;
clsreg.hkey = FALSE;
hres = get_inproc_class_object(apt, &clsreg, rclsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
ReleaseActCtx(data.hActCtx);
if (release_apt) apartment_release(apt);
return hres;
}
}
/*
* First, try and see if we can't match the class ID with one of the
* registered classes.
@ -2739,12 +2845,6 @@ HRESULT WINAPI CoGetClassObject(
static const WCHAR wszInprocServer32[] = {'I','n','p','r','o','c','S','e','r','v','e','r','3','2',0};
HKEY hkey;
if (IsEqualCLSID(rclsid, &CLSID_InProcFreeMarshaler))
{
if (release_apt) apartment_release(apt);
return FTMarshalCF_Create(iid, ppv);
}
hres = COM_OpenKeyForCLSID(rclsid, wszInprocServer32, KEY_READ, &hkey);
if (FAILED(hres))
{
@ -2759,8 +2859,10 @@ HRESULT WINAPI CoGetClassObject(
if (SUCCEEDED(hres))
{
hres = get_inproc_class_object(apt, hkey, rclsid, iid,
!(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
clsreg.u.hkey = hkey;
clsreg.hkey = TRUE;
hres = get_inproc_class_object(apt, &clsreg, rclsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
RegCloseKey(hkey);
}
@ -2793,8 +2895,10 @@ HRESULT WINAPI CoGetClassObject(
if (SUCCEEDED(hres))
{
hres = get_inproc_class_object(apt, hkey, rclsid, iid,
!(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
clsreg.u.hkey = hkey;
clsreg.hkey = TRUE;
hres = get_inproc_class_object(apt, &clsreg, rclsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
RegCloseKey(hkey);
}
@ -4557,9 +4661,13 @@ HRESULT Handler_DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
hres = COM_OpenKeyForCLSID(rclsid, wszInprocHandler32, KEY_READ, &hkey);
if (SUCCEEDED(hres))
{
struct class_reg_data regdata;
WCHAR dllpath[MAX_PATH+1];
if (COM_RegReadPath(hkey, dllpath, ARRAYSIZE(dllpath)) == ERROR_SUCCESS)
regdata.u.hkey = hkey;
regdata.hkey = TRUE;
if (COM_RegReadPath(&regdata, dllpath, ARRAYSIZE(dllpath)) == ERROR_SUCCESS)
{
static const WCHAR wszOle32[] = {'o','l','e','3','2','.','d','l','l',0};
if (!strcmpiW(dllpath, wszOle32))

View File

@ -63,6 +63,8 @@ static const GUID IID_Testiface5 = { 0x62222222, 0x1234, 0x1234, { 0x12, 0x34, 0
static const GUID IID_Testiface6 = { 0x72222222, 0x1234, 0x1234, { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 } };
static const GUID IID_TestPS = { 0x66666666, 0x8888, 0x7777, { 0x66, 0x66, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 } };
DEFINE_GUID(CLSID_InProcFreeMarshaler, 0x0000033a,0x0000,0x0000,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
static WCHAR stdfont[] = {'S','t','d','F','o','n','t',0};
static const WCHAR wszNonExistent[] = {'N','o','n','E','x','i','s','t','e','n','t',0};
static WCHAR wszCLSID_StdFont[] =
@ -225,6 +227,14 @@ static const char actctx_manifest[] =
"<assemblyIdentity version=\"1.2.3.4\" name=\"Wine.Test\" type=\"win32\""
" publicKeyToken=\"6595b6414666f1df\" />"
"<file name=\"testlib.dll\">"
" <comClass"
" clsid=\"{0000033a-0000-0000-c000-000000000046}\""
" progid=\"FTMarshal\""
" />"
" <comClass"
" clsid=\"{5201163f-8164-4fd0-a1a2-5d5a3654d3bd}\""
" progid=\"WineOOPTest\""
" />"
" <comClass description=\"Test com class\""
" clsid=\"{12345678-1234-1234-1234-56789abcdef0}\""
" progid=\"ProgId.ProgId\""
@ -579,8 +589,9 @@ static void test_CoCreateInstance(void)
static void test_CoGetClassObject(void)
{
HRESULT hr;
HANDLE thread;
HANDLE thread, handle;
DWORD tid, exitcode;
ULONG_PTR cookie;
IUnknown *pUnk;
struct info info;
REFCLSID rclsid = &CLSID_InternetZoneManager;
@ -660,6 +671,22 @@ static void test_CoGetClassObject(void)
if (hr == S_OK) IUnknown_Release(pUnk);
RegCloseKey(hkey);
}
hr = CoGetClassObject(&CLSID_InProcFreeMarshaler, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void **)&pUnk);
ok(hr == S_OK, "got 0x%08x\n", hr);
IUnknown_Release(pUnk);
/* context redefines FreeMarshaler CLSID */
if ((handle = activate_context(actctx_manifest, &cookie)))
{
hr = CoGetClassObject(&CLSID_InProcFreeMarshaler, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void **)&pUnk);
ok(hr == S_OK, "got 0x%08x\n", hr);
IUnknown_Release(pUnk);
pDeactivateActCtx(0, cookie);
pReleaseActCtx(handle);
}
CoUninitialize();
}
@ -1147,6 +1174,8 @@ static void test_CoMarshalInterThreadInterfaceInStream(void)
static void test_CoRegisterClassObject(void)
{
ULONG_PTR ctxcookie;
HANDLE handle;
DWORD cookie;
HRESULT hr;
IClassFactory *pcf;
@ -1222,6 +1251,33 @@ static void test_CoRegisterClassObject(void)
if (0)
CoRevokeClassObject(cookie);
/* test that object is accessible */
hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, CLSCTX_INPROC_SERVER,
REGCLS_MULTIPLEUSE, &cookie);
ok(hr == S_OK, "got 0x%08x\n", hr);
hr = CoGetClassObject(&CLSID_WineOOPTest, CLSCTX_INPROC_SERVER, NULL, &IID_IClassFactory, (void**)&pcf);
ok(hr == S_OK, "got 0x%08x\n", hr);
IClassFactory_Release(pcf);
/* context now contains CLSID_WineOOPTest, test if registered one could still be used */
if ((handle = activate_context(actctx_manifest, &ctxcookie)))
{
hr = CoGetClassObject(&CLSID_WineOOPTest, CLSCTX_INPROC_SERVER, NULL, &IID_IClassFactory, (void**)&pcf);
todo_wine
ok(hr == HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND), "got 0x%08x\n", hr);
pDeactivateActCtx(0, ctxcookie);
pReleaseActCtx(handle);
}
hr = CoGetClassObject(&CLSID_WineOOPTest, CLSCTX_INPROC_SERVER, NULL, &IID_IClassFactory, (void**)&pcf);
ok(hr == S_OK, "got 0x%08x\n", hr);
IClassFactory_Release(pcf);
hr = CoRevokeClassObject(cookie);
ok(hr == S_OK, "got 0x%08x\n", hr);
CoUninitialize();
}
@ -1878,6 +1934,9 @@ START_TEST(compobj)
return;
}
if (!pCreateActCtxW)
win_skip("Activation contexts are not supported, some tests will be skipped.\n");
test_ProgIDFromCLSID();
test_CLSIDFromProgID();
test_CLSIDFromString();