diff --git a/dlls/combase/apartment.c b/dlls/combase/apartment.c index 2d1f7a75744..3df8b165c13 100644 --- a/dlls/combase/apartment.c +++ b/dlls/combase/apartment.c @@ -49,27 +49,6 @@ enum comclass_threadingmodel ThreadingModel_Neutral = 5 }; -enum class_reg_data_origin -{ - CLASS_REG_ACTCTX, - CLASS_REG_REGISTRY, -}; - -struct class_reg_data -{ - enum class_reg_data_origin origin; - union - { - struct - { - const WCHAR *module_name; - DWORD threading_model; - HANDLE hactctx; - } actctx; - HKEY hkey; - } u; -}; - static struct apartment *mta; static struct apartment *main_sta; /* the first STA */ static struct list apts = LIST_INIT(apts); @@ -1048,7 +1027,7 @@ static enum comclass_threadingmodel get_threading_model(const struct class_reg_d return data->u.actctx.threading_model; } -HRESULT WINAPI apartment_get_inproc_class_object(struct apartment *apt, const struct class_reg_data *regdata, +HRESULT apartment_get_inproc_class_object(struct apartment *apt, const struct class_reg_data *regdata, REFCLSID rclsid, REFIID riid, BOOL hostifnecessary, void **ppv) { WCHAR dllpath[MAX_PATH+1]; diff --git a/dlls/combase/combase.c b/dlls/combase/combase.c index 47e31ebad14..228260ba214 100644 --- a/dlls/combase/combase.c +++ b/dlls/combase/combase.c @@ -38,9 +38,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole); HINSTANCE hProxyDll; -#define CHARS_IN_GUID 39 - +/* 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 @@ -257,7 +257,7 @@ static LSTATUS open_classes_key(HKEY hkey, const WCHAR *name, REGSAM access, HKE return RtlNtStatusToDosError(NtOpenKey((HANDLE *)retkey, access, &attr)); } -static HRESULT open_key_for_clsid(REFCLSID clsid, const WCHAR *keyname, REGSAM access, HKEY *subkey) +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]; @@ -288,6 +288,42 @@ static HRESULT open_key_for_clsid(REFCLSID clsid, const WCHAR *keyname, REGSAM a 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; +} + /*********************************************************************** * InternalTlsAllocData (combase.@) */ @@ -1443,6 +1479,173 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstanceEx(REFCLSID rclsid, IUnknown *o 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) +{ + struct class_reg_data clsreg = { 0 }; + IUnknown *regClassObject; + HRESULT hr = E_UNEXPECTED; + struct apartment *apt; + + TRACE("%s, %s\n", debugstr_guid(rclsid), debugstr_guid(riid)); + + 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) || + 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 (InternalGetRegisteredClassObject(apt, rclsid, clscontext, ®ClassObject) == S_OK) + { + hr = IUnknown_QueryInterface(regClassObject, riid, obj); + IUnknown_Release(regClassObject); + 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; +} + /*********************************************************************** * CoFreeUnusedLibraries (combase.@) */ diff --git a/dlls/combase/combase.spec b/dlls/combase/combase.spec index 143de28d7d6..f2f4506ae50 100644 --- a/dlls/combase/combase.spec +++ b/dlls/combase/combase.spec @@ -101,7 +101,7 @@ @ stdcall CoGetCallState(long ptr) @ stdcall CoGetCallerTID(ptr) ole32.CoGetCallerTID @ stub CoGetCancelObject -@ stdcall CoGetClassObject(ptr long ptr ptr ptr) ole32.CoGetClassObject +@ stdcall CoGetClassObject(ptr long ptr ptr ptr) @ stub CoGetClassVersion @ stdcall CoGetContextToken(ptr) @ stdcall CoGetCurrentLogicalThreadId(ptr) @@ -355,7 +355,6 @@ @ stdcall apartment_release(ptr) @ stdcall enter_apartment(ptr long) @ stdcall leave_apartment(ptr) -@ stdcall apartment_get_inproc_class_object(ptr ptr ptr ptr long ptr) @ stdcall apartment_findfromoxid(int64) @ stdcall apartment_getwindow(ptr) @ stdcall apartment_global_cleanup() diff --git a/dlls/combase/combase_private.h b/dlls/combase/combase_private.h index 1f80f1a12be..c6d090707a7 100644 --- a/dlls/combase/combase_private.h +++ b/dlls/combase/combase_private.h @@ -54,10 +54,19 @@ struct apartment struct list usage_cookies; /* Used for refcount control with CoIncrementMTAUsage()/CoDecrementMTAUsage(). */ }; +extern HRESULT WINAPI InternalGetRegisteredClassObject(struct apartment *apt, REFGUID guid, + DWORD clscontext, IUnknown **obj); + +HRESULT open_key_for_clsid(REFCLSID clsid, const WCHAR *keyname, REGSAM access, HKEY *subkey) DECLSPEC_HIDDEN; +HRESULT open_appidkey_from_clsid(REFCLSID clsid, REGSAM access, HKEY *subkey) DECLSPEC_HIDDEN; + /* DCOM messages used by the apartment window (not compatible with native) */ #define DM_EXECUTERPC (WM_USER + 0) /* WPARAM = 0, LPARAM = (struct dispatch_params *) */ #define DM_HOSTOBJECT (WM_USER + 1) /* WPARAM = 0, LPARAM = (struct host_object_params *) */ +#define WINE_CLSCTX_DONT_HOST 0x80000000 +#define CHARS_IN_GUID 39 + /* this is what is stored in TEB->ReservedForOle */ struct tlsdata { @@ -102,6 +111,7 @@ void apartment_freeunusedlibraries(struct apartment *apt, DWORD unload_delay) DE /* RpcSs interface */ HRESULT rpcss_get_next_seqid(DWORD *id) DECLSPEC_HIDDEN; +HRESULT rpc_get_local_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN; /* stub managers hold refs on the object and each interface stub */ struct stub_manager @@ -131,12 +141,36 @@ struct stub_manager BOOL disconnected; /* CoDisconnectObject has been called (CS lock) */ }; +enum class_reg_data_origin +{ + CLASS_REG_ACTCTX, + CLASS_REG_REGISTRY, +}; + +struct class_reg_data +{ + enum class_reg_data_origin origin; + union + { + struct + { + const WCHAR *module_name; + DWORD threading_model; + HANDLE hactctx; + } actctx; + HKEY hkey; + } u; +}; + HRESULT WINAPI enter_apartment(struct tlsdata *data, DWORD model); void WINAPI leave_apartment(struct tlsdata *data); void WINAPI apartment_release(struct apartment *apt); +struct apartment * WINAPI apartment_get_current_or_mta(void); HRESULT apartment_increment_mta_usage(CO_MTA_USAGE_COOKIE *cookie) DECLSPEC_HIDDEN; void apartment_decrement_mta_usage(CO_MTA_USAGE_COOKIE cookie) DECLSPEC_HIDDEN; struct apartment * apartment_get_mta(void) DECLSPEC_HIDDEN; +HRESULT apartment_get_inproc_class_object(struct apartment *apt, const struct class_reg_data *regdata, + REFCLSID rclsid, REFIID riid, BOOL hostifnecessary, void **ppv) DECLSPEC_HIDDEN; /* Stub Manager */ diff --git a/dlls/combase/rpc.c b/dlls/combase/rpc.c index 3b531661726..2b655da8bad 100644 --- a/dlls/combase/rpc.c +++ b/dlls/combase/rpc.c @@ -16,14 +16,19 @@ #include +#define COBJMACROS + #include "windef.h" #include "winbase.h" #include "winsvc.h" +#include "servprov.h" #include "wine/debug.h" #include "wine/exception.h" #include "wine/heap.h" +#include "combase_private.h" + #include "irpcss.h" WINE_DEFAULT_DEBUG_CHANNEL(ole); @@ -221,3 +226,236 @@ HRESULT WINAPI InternalIrotRevoke(IrotCookie cookie, IrotContextHandle *ctxt_han hr = IrotRevoke(get_irot_handle(), cookie, ctxt_handle, object, moniker); RPCSS_CALL_END } + +static void get_localserver_pipe_name(WCHAR *pipefn, REFCLSID rclsid) +{ + static const WCHAR wszPipeRef[] = {'\\','\\','.','\\','p','i','p','e','\\',0}; + lstrcpyW(pipefn, wszPipeRef); + StringFromGUID2(rclsid, pipefn + ARRAY_SIZE(wszPipeRef) - 1, CHARS_IN_GUID); +} + +static DWORD start_local_service(const WCHAR *name, DWORD num, LPCWSTR *params) +{ + SC_HANDLE handle, hsvc; + DWORD r = ERROR_FUNCTION_FAILED; + + TRACE("Starting service %s %d params\n", debugstr_w(name), num); + + handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); + if (!handle) + return r; + hsvc = OpenServiceW(handle, name, SERVICE_START); + if (hsvc) + { + if(StartServiceW(hsvc, num, params)) + r = ERROR_SUCCESS; + else + r = GetLastError(); + if (r == ERROR_SERVICE_ALREADY_RUNNING) + r = ERROR_SUCCESS; + CloseServiceHandle(hsvc); + } + else + r = GetLastError(); + CloseServiceHandle(handle); + + TRACE("StartService returned error %u (%s)\n", r, (r == ERROR_SUCCESS) ? "ok":"failed"); + + return r; +} + +/* + * create_local_service() - start a COM server in a service + * + * To start a Local Service, we read the AppID value under + * the class's CLSID key, then open the HKCR\\AppId key specified + * there and check for a LocalService value. + * + * Note: Local Services are not supported under Windows 9x + */ +static HRESULT create_local_service(REFCLSID rclsid) +{ + HRESULT hr; + WCHAR buf[CHARS_IN_GUID]; + HKEY hkey; + LONG r; + DWORD type, sz; + + TRACE("Attempting to start Local service for %s\n", debugstr_guid(rclsid)); + + hr = open_appidkey_from_clsid(rclsid, KEY_READ, &hkey); + if (FAILED(hr)) + return hr; + + /* read the LocalService and ServiceParameters values from the AppID key */ + sz = sizeof buf; + r = RegQueryValueExW(hkey, L"LocalService", NULL, &type, (LPBYTE)buf, &sz); + if (r == ERROR_SUCCESS && type == REG_SZ) + { + DWORD num_args = 0; + LPWSTR args[1] = { NULL }; + + /* + * FIXME: I'm not really sure how to deal with the service parameters. + * I suspect that the string returned from RegQueryValueExW + * should be split into a number of arguments by spaces. + * It would make more sense if ServiceParams contained a + * REG_MULTI_SZ here, but it's a REG_SZ for the services + * that I'm interested in for the moment. + */ + r = RegQueryValueExW(hkey, L"ServiceParams", NULL, &type, NULL, &sz); + if (r == ERROR_SUCCESS && type == REG_SZ && sz) + { + args[0] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz); + num_args++; + RegQueryValueExW(hkey, L"ServiceParams", NULL, &type, (LPBYTE)args[0], &sz); + } + r = start_local_service(buf, num_args, (LPCWSTR *)args); + if (r != ERROR_SUCCESS) + hr = REGDB_E_CLASSNOTREG; /* FIXME: check retval */ + HeapFree(GetProcessHeap(),0,args[0]); + } + else + { + WARN("No LocalService value\n"); + hr = REGDB_E_CLASSNOTREG; /* FIXME: check retval */ + } + RegCloseKey(hkey); + + return hr; +} + +static HRESULT create_server(REFCLSID rclsid, HANDLE *process) +{ + static const WCHAR embeddingW[] = L" -Embedding"; + HKEY key; + HRESULT hr; + WCHAR command[MAX_PATH + ARRAY_SIZE(embeddingW)]; + DWORD size = (MAX_PATH+1) * sizeof(WCHAR); + STARTUPINFOW sinfo; + PROCESS_INFORMATION pinfo; + LONG ret; + + hr = open_key_for_clsid(rclsid, L"LocalServer32", KEY_READ, &key); + if (FAILED(hr)) + { + ERR("class %s not registered\n", debugstr_guid(rclsid)); + return hr; + } + + ret = RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)command, &size); + RegCloseKey(key); + if (ret) + { + WARN("No default value for LocalServer32 key\n"); + return REGDB_E_CLASSNOTREG; /* FIXME: check retval */ + } + + memset(&sinfo, 0, sizeof(sinfo)); + sinfo.cb = sizeof(sinfo); + + /* EXE servers are started with the -Embedding switch. */ + + lstrcatW(command, embeddingW); + + TRACE("activating local server %s for %s\n", debugstr_w(command), debugstr_guid(rclsid)); + + /* FIXME: Win2003 supports a ServerExecutable value that is passed into + * CreateProcess */ + if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &sinfo, &pinfo)) + { + WARN("failed to run local server %s\n", debugstr_w(command)); + return HRESULT_FROM_WIN32(GetLastError()); + } + *process = pinfo.hProcess; + CloseHandle(pinfo.hThread); + + return S_OK; +} + +/* FIXME: should call to rpcss instead */ +HRESULT rpc_get_local_class_object(REFCLSID rclsid, REFIID riid, void **obj) +{ + HRESULT hr; + HANDLE hPipe; + WCHAR pipefn[100]; + DWORD res, bufferlen; + char marshalbuffer[200]; + IStream *pStm; + LARGE_INTEGER seekto; + ULARGE_INTEGER newpos; + int tries = 0; + IServiceProvider *local_server; + + static const int MAXTRIES = 30; /* 30 seconds */ + + TRACE("rclsid %s, riid %s\n", debugstr_guid(rclsid), debugstr_guid(riid)); + + get_localserver_pipe_name(pipefn, rclsid); + + while (tries++ < MAXTRIES) + { + TRACE("waiting for %s\n", debugstr_w(pipefn)); + + WaitNamedPipeW(pipefn, NMPWAIT_WAIT_FOREVER); + hPipe = CreateFileW(pipefn, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + if (hPipe == INVALID_HANDLE_VALUE) + { + DWORD index; + DWORD start_ticks; + HANDLE process = 0; + if (tries == 1) + { + if ((hr = create_local_service(rclsid)) && (hr = create_server(rclsid, &process))) + return hr; + } + else + { + WARN("Connecting to %s, no response yet, retrying: le is %u\n", debugstr_w(pipefn), GetLastError()); + } + /* wait for one second, even if messages arrive */ + start_ticks = GetTickCount(); + do + { + if (SUCCEEDED(CoWaitForMultipleHandles(0, 1000, (process != 0), &process, &index)) && process && !index) + { + WARN("server for %s failed to start\n", debugstr_guid(rclsid)); + CloseHandle( hPipe ); + CloseHandle( process ); + return E_NOINTERFACE; + } + } while (GetTickCount() - start_ticks < 1000); + if (process) CloseHandle(process); + continue; + } + bufferlen = 0; + if (!ReadFile(hPipe, marshalbuffer, sizeof(marshalbuffer), &bufferlen, NULL)) + { + FIXME("Failed to read marshal id from classfactory of %s.\n", debugstr_guid(rclsid)); + CloseHandle(hPipe); + Sleep(1000); + continue; + } + TRACE("read marshal id from pipe\n"); + CloseHandle(hPipe); + break; + } + + if (tries >= MAXTRIES) + return E_NOINTERFACE; + + hr = CreateStreamOnHGlobal(0, TRUE, &pStm); + if (hr != S_OK) return hr; + hr = IStream_Write(pStm, marshalbuffer, bufferlen, &res); + if (hr != S_OK) goto out; + seekto.u.LowPart = 0;seekto.u.HighPart = 0; + hr = IStream_Seek(pStm, seekto, STREAM_SEEK_SET, &newpos); + TRACE("unmarshalling local server\n"); + hr = CoUnmarshalInterface(pStm, &IID_IServiceProvider, (void **)&local_server); + if(SUCCEEDED(hr)) + hr = IServiceProvider_QueryService(local_server, rclsid, riid, obj); + IServiceProvider_Release(local_server); +out: + IStream_Release(pStm); + return hr; +} diff --git a/dlls/ole32/compobj.c b/dlls/ole32/compobj.c index 975af46f854..45187fc8113 100644 --- a/dlls/ole32/compobj.c +++ b/dlls/ole32/compobj.c @@ -985,209 +985,6 @@ HRESULT WINAPI CoRegisterClassObject( return S_OK; } -/*********************************************************************** - * CoGetClassObject [OLE32.@] - * - * Creates an object of the specified class. - * - * PARAMS - * rclsid [I] Class ID to create an instance of. - * dwClsContext [I] Flags to restrict the location of the created instance. - * pServerInfo [I] Optional. Details for connecting to a remote server. - * iid [I] The ID of the interface of the instance to return. - * ppv [O] On returns, contains a pointer to the specified interface of the object. - * - * RETURNS - * Success: S_OK - * Failure: HRESULT code. - * - * NOTES - * The dwClsContext parameter can be one or more of the following: - *| CLSCTX_INPROC_SERVER - Use an in-process server, such as from a DLL. - *| CLSCTX_INPROC_HANDLER - Use an in-process object which handles certain functions for an object running in another process. - *| CLSCTX_LOCAL_SERVER - Connect to an object running in another process. - *| CLSCTX_REMOTE_SERVER - Connect to an object running on another machine. - * - * SEE ALSO - * CoCreateInstance() - */ -HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject( - REFCLSID rclsid, DWORD dwClsContext, COSERVERINFO *pServerInfo, - REFIID iid, LPVOID *ppv) -{ - struct class_reg_data clsreg = { 0 }; - IUnknown *regClassObject; - HRESULT hres = E_UNEXPECTED; - struct apartment *apt; - - TRACE("CLSID: %s,IID: %s\n", debugstr_guid(rclsid), debugstr_guid(iid)); - - if (!ppv) - return E_INVALIDARG; - - *ppv = NULL; - - if (!(apt = apartment_get_current_or_mta())) - { - ERR("apartment not initialised\n"); - return CO_E_NOTINITIALIZED; - } - - if (pServerInfo) { - FIXME("pServerInfo->name=%s pAuthInfo=%p\n", - debugstr_w(pServerInfo->pwszName), pServerInfo->pAuthInfo); - } - - if (CLSCTX_INPROC_SERVER & dwClsContext) - { - if (IsEqualCLSID(rclsid, &CLSID_InProcFreeMarshaler) || - IsEqualCLSID(rclsid, &CLSID_GlobalOptions) || - IsEqualCLSID(rclsid, &CLSID_ManualResetEvent) || - IsEqualCLSID(rclsid, &CLSID_StdGlobalInterfaceTable)) - { - apartment_release(apt); - return Ole32DllGetClassObject(rclsid, 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)) - { - 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; - - hres = apartment_get_inproc_class_object(apt, &clsreg, &comclass->clsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv); - ReleaseActCtx(data.hActCtx); - apartment_release(apt); - return hres; - } - } - - /* - * First, try and see if we can't match the class ID with one of the - * registered classes. - */ - if (S_OK == InternalGetRegisteredClassObject(apt, rclsid, dwClsContext, - ®ClassObject)) - { - /* Get the required interface from the retrieved pointer. */ - hres = IUnknown_QueryInterface(regClassObject, iid, ppv); - - /* - * Since QI got another reference on the pointer, we want to release the - * one we already have. If QI was unsuccessful, this will release the object. This - * is good since we are not returning it in the "out" parameter. - */ - IUnknown_Release(regClassObject); - apartment_release(apt); - return hres; - } - - /* First try in-process server */ - if (CLSCTX_INPROC_SERVER & dwClsContext) - { - static const WCHAR wszInprocServer32[] = {'I','n','p','r','o','c','S','e','r','v','e','r','3','2',0}; - HKEY hkey; - - hres = COM_OpenKeyForCLSID(rclsid, wszInprocServer32, KEY_READ, &hkey); - if (FAILED(hres)) - { - if (hres == REGDB_E_CLASSNOTREG) - ERR("class %s not registered\n", debugstr_guid(rclsid)); - else if (hres == REGDB_E_KEYMISSING) - { - WARN("class %s not registered as in-proc server\n", debugstr_guid(rclsid)); - hres = REGDB_E_CLASSNOTREG; - } - } - - if (SUCCEEDED(hres)) - { - clsreg.u.hkey = hkey; - clsreg.origin = CLASS_REG_REGISTRY; - - hres = apartment_get_inproc_class_object(apt, &clsreg, rclsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv); - RegCloseKey(hkey); - } - - /* return if we got a class, otherwise fall through to one of the - * other types */ - if (SUCCEEDED(hres)) - { - apartment_release(apt); - return hres; - } - } - - /* Next try in-process handler */ - if (CLSCTX_INPROC_HANDLER & dwClsContext) - { - static const WCHAR wszInprocHandler32[] = {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',0}; - HKEY hkey; - - hres = COM_OpenKeyForCLSID(rclsid, wszInprocHandler32, KEY_READ, &hkey); - if (FAILED(hres)) - { - if (hres == REGDB_E_CLASSNOTREG) - ERR("class %s not registered\n", debugstr_guid(rclsid)); - else if (hres == REGDB_E_KEYMISSING) - { - WARN("class %s not registered in-proc handler\n", debugstr_guid(rclsid)); - hres = REGDB_E_CLASSNOTREG; - } - } - - if (SUCCEEDED(hres)) - { - clsreg.u.hkey = hkey; - clsreg.origin = CLASS_REG_REGISTRY; - - hres = apartment_get_inproc_class_object(apt, &clsreg, rclsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv); - RegCloseKey(hkey); - } - - /* return if we got a class, otherwise fall through to one of the - * other types */ - if (SUCCEEDED(hres)) - { - apartment_release(apt); - return hres; - } - } - apartment_release(apt); - - /* Next try out of process */ - if (CLSCTX_LOCAL_SERVER & dwClsContext) - { - hres = RPC_GetLocalClassObject(rclsid,iid,ppv); - if (SUCCEEDED(hres)) - return hres; - } - - /* Finally try remote: this requires networked DCOM (a lot of work) */ - if (CLSCTX_REMOTE_SERVER & dwClsContext) - { - FIXME ("CLSCTX_REMOTE_SERVER not supported\n"); - hres = REGDB_E_CLASSNOTREG; - } - - if (FAILED(hres)) - ERR("no class object %s could be created for context 0x%x\n", - debugstr_guid(rclsid), dwClsContext); - return hres; -} - /*********************************************************************** * CoResumeClassObjects (OLE32.@) * diff --git a/dlls/ole32/compobj_private.h b/dlls/ole32/compobj_private.h index 9d6a1d1b794..5fc100e8163 100644 --- a/dlls/ole32/compobj_private.h +++ b/dlls/ole32/compobj_private.h @@ -252,11 +252,6 @@ extern HWND WINAPI apartment_getwindow(const struct apartment *apt) DECLSPEC_HID extern HRESULT WINAPI enter_apartment(struct oletls *info, DWORD model) DECLSPEC_HIDDEN; void WINAPI leave_apartment(struct oletls *info) DECLSPEC_HIDDEN; extern struct apartment * WINAPI apartment_get_current_or_mta(void) DECLSPEC_HIDDEN; - -struct class_reg_data; -extern HRESULT WINAPI apartment_get_inproc_class_object(struct apartment *apt, const struct class_reg_data *regdata, - REFCLSID rclsid, REFIID riid, BOOL hostifnecessary, void **ppv) DECLSPEC_HIDDEN; - extern HRESULT WINAPI apartment_get_local_server_stream(struct apartment *apt, IStream **ret) DECLSPEC_HIDDEN; extern void WINAPI apartment_global_cleanup(void) DECLSPEC_HIDDEN; diff --git a/dlls/ole32/ole32.spec b/dlls/ole32/ole32.spec index 0b38e74388a..9c6b9396e17 100644 --- a/dlls/ole32/ole32.spec +++ b/dlls/ole32/ole32.spec @@ -30,7 +30,7 @@ @ stdcall CoGetCallContext(ptr ptr) combase.CoGetCallContext @ stdcall CoGetCallState(long ptr) combase.CoGetCallState @ stdcall CoGetCallerTID(ptr) -@ stdcall CoGetClassObject(ptr long ptr ptr ptr) +@ stdcall CoGetClassObject(ptr long ptr ptr ptr) combase.CoGetClassObject @ stdcall CoGetContextToken(ptr) combase.CoGetContextToken @ stdcall CoGetCurrentLogicalThreadId(ptr) combase.CoGetCurrentLogicalThreadId @ stdcall CoGetCurrentProcess() combase.CoGetCurrentProcess diff --git a/dlls/ole32/rpc.c b/dlls/ole32/rpc.c index 1c88ae21543..eb76d9cb668 100644 --- a/dlls/ole32/rpc.c +++ b/dlls/ole32/rpc.c @@ -1649,150 +1649,6 @@ void RPC_StartRemoting(struct apartment *apt) start_apartment_remote_unknown(apt); } - -static HRESULT create_server(REFCLSID rclsid, HANDLE *process) -{ - static const WCHAR wszLocalServer32[] = { 'L','o','c','a','l','S','e','r','v','e','r','3','2',0 }; - static const WCHAR embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 }; - HKEY key; - HRESULT hres; - WCHAR command[MAX_PATH+ARRAY_SIZE(embedding)]; - DWORD size = (MAX_PATH+1) * sizeof(WCHAR); - STARTUPINFOW sinfo; - PROCESS_INFORMATION pinfo; - LONG ret; - - hres = COM_OpenKeyForCLSID(rclsid, wszLocalServer32, KEY_READ, &key); - if (FAILED(hres)) { - ERR("class %s not registered\n", debugstr_guid(rclsid)); - return hres; - } - - ret = RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)command, &size); - RegCloseKey(key); - if (ret) { - WARN("No default value for LocalServer32 key\n"); - return REGDB_E_CLASSNOTREG; /* FIXME: check retval */ - } - - memset(&sinfo,0,sizeof(sinfo)); - sinfo.cb = sizeof(sinfo); - - /* EXE servers are started with the -Embedding switch. */ - - lstrcatW(command, embedding); - - TRACE("activating local server %s for %s\n", debugstr_w(command), debugstr_guid(rclsid)); - - /* FIXME: Win2003 supports a ServerExecutable value that is passed into - * CreateProcess */ - if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &sinfo, &pinfo)) { - WARN("failed to run local server %s\n", debugstr_w(command)); - return HRESULT_FROM_WIN32(GetLastError()); - } - *process = pinfo.hProcess; - CloseHandle(pinfo.hThread); - - return S_OK; -} - -/* - * start_local_service() - start a service given its name and parameters - */ -static DWORD start_local_service(LPCWSTR name, DWORD num, LPCWSTR *params) -{ - SC_HANDLE handle, hsvc; - DWORD r = ERROR_FUNCTION_FAILED; - - TRACE("Starting service %s %d params\n", debugstr_w(name), num); - - handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); - if (!handle) - return r; - hsvc = OpenServiceW(handle, name, SERVICE_START); - if (hsvc) - { - if(StartServiceW(hsvc, num, params)) - r = ERROR_SUCCESS; - else - r = GetLastError(); - if (r == ERROR_SERVICE_ALREADY_RUNNING) - r = ERROR_SUCCESS; - CloseServiceHandle(hsvc); - } - else - r = GetLastError(); - CloseServiceHandle(handle); - - TRACE("StartService returned error %u (%s)\n", r, (r == ERROR_SUCCESS) ? "ok":"failed"); - - return r; -} - -/* - * create_local_service() - start a COM server in a service - * - * To start a Local Service, we read the AppID value under - * the class's CLSID key, then open the HKCR\\AppId key specified - * there and check for a LocalService value. - * - * Note: Local Services are not supported under Windows 9x - */ -static HRESULT create_local_service(REFCLSID rclsid) -{ - HRESULT hres; - WCHAR buf[CHARS_IN_GUID]; - static const WCHAR szLocalService[] = { 'L','o','c','a','l','S','e','r','v','i','c','e',0 }; - static const WCHAR szServiceParams[] = {'S','e','r','v','i','c','e','P','a','r','a','m','s',0}; - HKEY hkey; - LONG r; - DWORD type, sz; - - TRACE("Attempting to start Local service for %s\n", debugstr_guid(rclsid)); - - hres = COM_OpenKeyForAppIdFromCLSID(rclsid, KEY_READ, &hkey); - if (FAILED(hres)) - return hres; - - /* read the LocalService and ServiceParameters values from the AppID key */ - sz = sizeof buf; - r = RegQueryValueExW(hkey, szLocalService, NULL, &type, (LPBYTE)buf, &sz); - if (r==ERROR_SUCCESS && type==REG_SZ) - { - DWORD num_args = 0; - LPWSTR args[1] = { NULL }; - - /* - * FIXME: I'm not really sure how to deal with the service parameters. - * I suspect that the string returned from RegQueryValueExW - * should be split into a number of arguments by spaces. - * It would make more sense if ServiceParams contained a - * REG_MULTI_SZ here, but it's a REG_SZ for the services - * that I'm interested in for the moment. - */ - r = RegQueryValueExW(hkey, szServiceParams, NULL, &type, NULL, &sz); - if (r == ERROR_SUCCESS && type == REG_SZ && sz) - { - args[0] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz); - num_args++; - RegQueryValueExW(hkey, szServiceParams, NULL, &type, (LPBYTE)args[0], &sz); - } - r = start_local_service(buf, num_args, (LPCWSTR *)args); - if (r != ERROR_SUCCESS) - hres = REGDB_E_CLASSNOTREG; /* FIXME: check retval */ - HeapFree(GetProcessHeap(),0,args[0]); - } - else - { - WARN("No LocalService value\n"); - hres = REGDB_E_CLASSNOTREG; /* FIXME: check retval */ - } - RegCloseKey(hkey); - - return hres; -} - - static void get_localserver_pipe_name(WCHAR *pipefn, REFCLSID rclsid) { static const WCHAR wszPipeRef[] = {'\\','\\','.','\\','p','i','p','e','\\',0}; @@ -1800,90 +1656,6 @@ static void get_localserver_pipe_name(WCHAR *pipefn, REFCLSID rclsid) StringFromGUID2(rclsid, pipefn + ARRAY_SIZE(wszPipeRef) - 1, CHARS_IN_GUID); } -/* FIXME: should call to rpcss instead */ -HRESULT RPC_GetLocalClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv) -{ - HRESULT hres; - HANDLE hPipe; - WCHAR pipefn[100]; - DWORD res, bufferlen; - char marshalbuffer[200]; - IStream *pStm; - LARGE_INTEGER seekto; - ULARGE_INTEGER newpos; - int tries = 0; - IServiceProvider *local_server; - - static const int MAXTRIES = 30; /* 30 seconds */ - - TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid)); - - get_localserver_pipe_name(pipefn, rclsid); - - while (tries++ < MAXTRIES) { - TRACE("waiting for %s\n", debugstr_w(pipefn)); - - WaitNamedPipeW( pipefn, NMPWAIT_WAIT_FOREVER ); - hPipe = CreateFileW(pipefn, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); - if (hPipe == INVALID_HANDLE_VALUE) { - DWORD index; - DWORD start_ticks; - HANDLE process = 0; - if (tries == 1) { - if ( (hres = create_local_service(rclsid)) && - (hres = create_server(rclsid, &process)) ) - return hres; - } else { - WARN("Connecting to %s, no response yet, retrying: le is %u\n", debugstr_w(pipefn), GetLastError()); - } - /* wait for one second, even if messages arrive */ - start_ticks = GetTickCount(); - do { - if (SUCCEEDED(CoWaitForMultipleHandles(0, 1000, (process != 0), - &process, &index)) && process && !index) - { - WARN( "server for %s failed to start\n", debugstr_guid(rclsid) ); - CloseHandle( hPipe ); - CloseHandle( process ); - return E_NOINTERFACE; - } - } while (GetTickCount() - start_ticks < 1000); - if (process) CloseHandle( process ); - continue; - } - bufferlen = 0; - if (!ReadFile(hPipe,marshalbuffer,sizeof(marshalbuffer),&bufferlen,NULL)) { - FIXME("Failed to read marshal id from classfactory of %s.\n",debugstr_guid(rclsid)); - CloseHandle(hPipe); - Sleep(1000); - continue; - } - TRACE("read marshal id from pipe\n"); - CloseHandle(hPipe); - break; - } - - if (tries >= MAXTRIES) - return E_NOINTERFACE; - - hres = CreateStreamOnHGlobal(0,TRUE,&pStm); - if (hres != S_OK) return hres; - hres = IStream_Write(pStm,marshalbuffer,bufferlen,&res); - if (hres != S_OK) goto out; - seekto.u.LowPart = 0;seekto.u.HighPart = 0; - hres = IStream_Seek(pStm,seekto,STREAM_SEEK_SET,&newpos); - - TRACE("unmarshalling local server\n"); - hres = CoUnmarshalInterface(pStm, &IID_IServiceProvider, (void**)&local_server); - if(SUCCEEDED(hres)) - hres = IServiceProvider_QueryService(local_server, rclsid, iid, ppv); - IServiceProvider_Release(local_server); -out: - IStream_Release(pStm); - return hres; -} - - struct local_server_params { CLSID clsid;