Commit 5a2435cf authored by Nikolay Sivov's avatar Nikolay Sivov Committed by Alexandre Julliard

combase: Move CoGetClassObject().

parent 373c753e
......@@ -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];
......
......@@ -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.@)
*/
......@@ -1444,6 +1480,173 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstanceEx(REFCLSID rclsid, IUnknown *o
}
/***********************************************************************
* 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, &regClassObject) == 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.@)
*/
void WINAPI DECLSPEC_HOTPATCH CoFreeUnusedLibraries(void)
......
......@@ -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()
......
......@@ -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 */
......
......@@ -16,14 +16,19 @@
#include <stdarg.h>
#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;
}
......@@ -986,209 +986,6 @@ HRESULT WINAPI CoRegisterClassObject(
}
/***********************************************************************
* 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,
&regClassObject))
{
/* 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.@)
*
* Resumes all class objects registered with REGCLS_SUSPENDED.
......
......@@ -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;
......
......@@ -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
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment