Commit 2b747d4e authored by Robert Shearman's avatar Robert Shearman Committed by Alexandre Julliard

Implement COM local servers using table marshaling to avoid doing the

marshaling in a child thread where COM has not been initialized.
parent 5475a2e6
......@@ -101,8 +101,7 @@ typedef struct tagRegisteredClass
DWORD runContext;
DWORD connectFlags;
DWORD dwCookie;
HANDLE hThread; /* only for localserver */
APARTMENT *apt; /* owning apartment */
LPSTREAM pMarshaledData; /* FIXME: only really need to store OXID and IPID */
struct tagRegisteredClass* nextClass;
} RegisteredClass;
......@@ -1101,116 +1100,12 @@ end:
return hr;
}
static DWORD WINAPI
_LocalServerThread(LPVOID param) {
HANDLE hPipe;
char pipefn[200];
RegisteredClass *newClass = (RegisteredClass*)param;
HRESULT hres;
IStream *pStm;
STATSTG ststg;
unsigned char *buffer;
int buflen;
IClassFactory *classfac;
LARGE_INTEGER seekto;
ULARGE_INTEGER newpos;
ULONG res;
TRACE("Starting classfactory server thread for %s.\n",debugstr_guid(&newClass->classIdentifier));
/* we need to enter the apartment of the thread which registered
* the class object to perform the next stage
*/
assert( newClass->apt );
NtCurrentTeb()->ReservedForOle = newClass->apt;
strcpy(pipefn,PIPEPREF);
WINE_StringFromCLSID(&newClass->classIdentifier,pipefn+strlen(PIPEPREF));
hPipe = CreateNamedPipeA( pipefn, PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
4096, 4096, NMPWAIT_USE_DEFAULT_WAIT, NULL );
if (hPipe == INVALID_HANDLE_VALUE) {
FIXME("pipe creation failed for %s, le is %lx\n",pipefn,GetLastError());
return 1;
}
while (1) {
if (!ConnectNamedPipe(hPipe,NULL)) {
ERR("Failure during ConnectNamedPipe %lx, ABORT!\n",GetLastError());
break;
}
TRACE("marshalling IClassFactory to client\n");
hres = IUnknown_QueryInterface(newClass->classObject,&IID_IClassFactory,(LPVOID*)&classfac);
if (hres) return hres;
hres = CreateStreamOnHGlobal(0,TRUE,&pStm);
if (hres) {
FIXME("Failed to create stream on hglobal.\n");
return hres;
}
hres = CoMarshalInterface(pStm,&IID_IClassFactory,(LPVOID)classfac,0,NULL,0);
if (hres) {
FIXME("CoMarshalInterface failed, %lx!\n",hres);
return hres;
}
IUnknown_Release(classfac); /* is this right? */
hres = IStream_Stat(pStm,&ststg,0);
if (hres) return hres;
buflen = ststg.cbSize.u.LowPart;
buffer = HeapAlloc(GetProcessHeap(),0,buflen);
seekto.u.LowPart = 0;
seekto.u.HighPart = 0;
hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
if (hres) {
FIXME("IStream_Seek failed, %lx\n",hres);
return hres;
}
hres = IStream_Read(pStm,buffer,buflen,&res);
if (hres) {
FIXME("Stream Read failed, %lx\n",hres);
return hres;
}
IStream_Release(pStm);
WriteFile(hPipe,buffer,buflen,&res,NULL);
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
TRACE("done marshalling IClassFactory\n");
}
CloseHandle(hPipe);
return 0;
}
/******************************************************************************
* CoRegisterClassObject [OLE32.@]
*
* This method will register the class object for a given class
* ID. Servers housed in EXE files use this method instead of
* exporting DllGetClassObject to allow other code to connect to their
* objects.
*
* When a class object (an object which implements IClassFactory) is
* registered in this way, a new thread is started which listens for
* connections on a named pipe specific to the registered CLSID. When
* something else connects to it, it writes out the marshalled
* IClassFactory interface to the pipe. The code on the other end uses
* this buffer to unmarshal the class factory, and can then call
* methods on it.
*
* In Windows, such objects are registered with the RPC endpoint
* mapper, not with a unique named pipe.
*
* MSDN claims that multiple interface registrations are legal, but we
* can't do that with our current implementation.
* Registers the class object for a given class ID. Servers housed in EXE
* files use this method instead of exporting DllGetClassObject to allow
* other code to connect to their objects.
*
* RETURNS
* S_OK on success,
......@@ -1219,6 +1114,10 @@ _LocalServerThread(LPVOID param) {
*
* SEE ALSO
* CoRevokeClassObject, CoGetClassObject
*
* BUGS
* MSDN claims that multiple interface registrations are legal, but we
* can't do that with our current implementation.
*/
HRESULT WINAPI CoRegisterClassObject(
REFCLSID rclsid, /* [in] CLSID of the object to register */
......@@ -1264,10 +1163,9 @@ HRESULT WINAPI CoRegisterClassObject(
newClass->classIdentifier = *rclsid;
newClass->runContext = dwClsContext;
newClass->connectFlags = flags;
newClass->apt = COM_CurrentApt();
/*
* Use the address of the chain node as the cookie since we are sure it's
* unique.
* unique. FIXME: not on 64-bit platforms.
*/
newClass->dwCookie = (DWORD)newClass;
newClass->nextClass = firstRegisteredClass;
......@@ -1285,10 +1183,30 @@ HRESULT WINAPI CoRegisterClassObject(
*lpdwRegister = newClass->dwCookie;
if (dwClsContext & CLSCTX_LOCAL_SERVER) {
DWORD tid;
IClassFactory *classfac;
hr = IUnknown_QueryInterface(newClass->classObject, &IID_IClassFactory,
(LPVOID*)&classfac);
if (hr) return hr;
hr = CreateStreamOnHGlobal(0, TRUE, &newClass->pMarshaledData);
if (hr) {
FIXME("Failed to create stream on hglobal, %lx\n", hr);
IUnknown_Release(classfac);
return hr;
}
hr = CoMarshalInterface(newClass->pMarshaledData, &IID_IClassFactory,
(LPVOID)classfac, MSHCTX_LOCAL, NULL,
MSHLFLAGS_TABLESTRONG);
if (hr) {
FIXME("CoMarshalInterface failed, %lx!\n",hr);
IUnknown_Release(classfac);
return hr;
}
IUnknown_Release(classfac);
start_apartment_listener_thread();
newClass->hThread = CreateThread(NULL,0,_LocalServerThread,newClass,0,&tid);
RPC_StartLocalServer(&newClass->classIdentifier, newClass->pMarshaledData);
}
return S_OK;
}
......@@ -1334,6 +1252,15 @@ HRESULT WINAPI CoRevokeClassObject(
*/
IUnknown_Release(curClass->classObject);
if (curClass->pMarshaledData)
{
LARGE_INTEGER zero;
memset(&zero, 0, sizeof(zero));
/* FIXME: stop local server thread */
IStream_Seek(curClass->pMarshaledData, zero, SEEK_SET, NULL);
CoReleaseMarshalData(curClass->pMarshaledData);
}
/*
* Free the memory used by the chain node.
*/
......
......@@ -194,6 +194,7 @@ IRpcStubBuffer *mid_to_stubbuffer(wine_marshal_id *mid);
void start_apartment_listener_thread(void);
extern HRESULT PIPE_GetNewPipeBuf(wine_marshal_id *mid, IRpcChannelBuffer **pipebuf);
void RPC_StartLocalServer(REFCLSID clsid, IStream *stream);
/* This function initialize the Running Object Table */
HRESULT WINAPI RunningObjectTableImpl_Initialize(void);
......
......@@ -829,3 +829,90 @@ void start_apartment_listener_thread()
CreateThread(NULL, 0, apartment_listener_thread, apt, 0, &apt->listenertid);
}
}
struct local_server_params
{
CLSID clsid;
IStream *stream;
};
static DWORD WINAPI local_server_thread(LPVOID param)
{
struct local_server_params * lsp = (struct local_server_params *)param;
HANDLE hPipe;
char pipefn[200];
HRESULT hres;
IStream *pStm = lsp->stream;
STATSTG ststg;
unsigned char *buffer;
int buflen;
LARGE_INTEGER seekto;
ULARGE_INTEGER newpos;
ULONG res;
TRACE("Starting threader for %s.\n",debugstr_guid(&lsp->clsid));
strcpy(pipefn,PIPEPREF);
WINE_StringFromCLSID(&lsp->clsid,pipefn+strlen(PIPEPREF));
HeapFree(GetProcessHeap(), 0, lsp);
hPipe = CreateNamedPipeA( pipefn, PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
4096, 4096, NMPWAIT_USE_DEFAULT_WAIT, NULL );
if (hPipe == INVALID_HANDLE_VALUE) {
FIXME("pipe creation failed for %s, le is %ld\n",pipefn,GetLastError());
return 1;
}
while (1) {
if (!ConnectNamedPipe(hPipe,NULL)) {
ERR("Failure during ConnectNamedPipe %ld, ABORT!\n",GetLastError());
break;
}
TRACE("marshalling IClassFactory to client\n");
hres = IStream_Stat(pStm,&ststg,0);
if (hres) return hres;
buflen = ststg.cbSize.u.LowPart;
buffer = HeapAlloc(GetProcessHeap(),0,buflen);
seekto.u.LowPart = 0;
seekto.u.HighPart = 0;
hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
if (hres) {
FIXME("IStream_Seek failed, %lx\n",hres);
return hres;
}
hres = IStream_Read(pStm,buffer,buflen,&res);
if (hres) {
FIXME("Stream Read failed, %lx\n",hres);
return hres;
}
IStream_Release(pStm);
WriteFile(hPipe,buffer,buflen,&res,NULL);
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
TRACE("done marshalling IClassFactory\n");
}
CloseHandle(hPipe);
return 0;
}
void RPC_StartLocalServer(REFCLSID clsid, IStream *stream)
{
DWORD tid;
HANDLE thread;
struct local_server_params *lsp = HeapAlloc(GetProcessHeap(), 0, sizeof(*lsp));
lsp->clsid = *clsid;
lsp->stream = stream;
thread = CreateThread(NULL, 0, local_server_thread, lsp, 0, &tid);
CloseHandle(thread);
/* FIXME: failure handling */
}
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