4 * Copyright 2002 Marcus Meissner
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #define NONAMELESSUNION
31 #define NONAMELESSSTRUCT
42 #include "wine/unicode.h"
44 #include "compobj_private.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(ole);
50 #define REQTYPE_REQUEST 0
51 typedef struct _wine_rpc_request_header {
56 } wine_rpc_request_header;
58 #define REQTYPE_RESPONSE 1
59 typedef struct _wine_rpc_response_header {
63 } wine_rpc_response_header;
65 /* used when shutting down a pipe, e.g. at the end of a process */
66 #define REQTYPE_DISCONNECT 2
67 typedef struct _wine_rpc_disconnect_header {
69 wine_marshal_id mid; /* mid of stub to delete */
70 } wine_rpc_disconnect_header;
73 #define REQSTATE_START 0
74 #define REQSTATE_REQ_QUEUED 1
75 #define REQSTATE_REQ_WAITING_FOR_REPLY 2
76 #define REQSTATE_REQ_GOT 3
77 #define REQSTATE_INVOKING 4
78 #define REQSTATE_RESP_QUEUED 5
79 #define REQSTATE_RESP_GOT 6
80 #define REQSTATE_DONE 6
82 typedef struct _wine_rpc_request {
84 HANDLE hPipe; /* temp copy of handle */
85 wine_rpc_request_header reqh;
86 wine_rpc_response_header resph;
90 static wine_rpc_request **reqs = NULL;
91 static int nrofreqs = 0;
93 /* This pipe is _thread_ based, each thread which talks to a remote
94 * apartment (mid) has its own pipe. The same structure is used both
95 * for outgoing and incoming RPCs.
97 typedef struct _wine_pipe {
98 wine_marshal_id mid; /* target mid */
99 DWORD tid; /* thread which owns this pipe */
104 CRITICAL_SECTION crit;
106 APARTMENT *apt; /* apartment of the marshalling thread for the stub dispatch case */
109 static wine_pipe *pipes = NULL;
110 static int nrofpipes = 0;
112 typedef struct _PipeBuf {
113 IRpcChannelBufferVtbl *lpVtbl;
120 static HRESULT WINAPI
121 read_pipe(HANDLE hf, LPVOID ptr, DWORD size) {
123 if (!ReadFile(hf,ptr,size,&res,NULL)) {
124 FIXME("Failed to read from %p, le is %lx\n",hf,GetLastError());
128 FIXME("Read only %ld of %ld bytes from %p.\n",res,size,hf);
137 static int nrofreaders = 0;
141 memset(states,0,sizeof(states));
142 for (i=nrofreqs;i--;)
143 states[reqs[i]->state]++;
144 FIXME("%lx/%s/%d: rq %d, w %d, rg %d, rsq %d, rsg %d, d %d\n",
145 GetCurrentProcessId(),
148 states[REQSTATE_REQ_QUEUED],
149 states[REQSTATE_REQ_WAITING_FOR_REPLY],
150 states[REQSTATE_REQ_GOT],
151 states[REQSTATE_RESP_QUEUED],
152 states[REQSTATE_RESP_GOT],
153 states[REQSTATE_DONE]
160 static HRESULT WINAPI
161 write_pipe(HANDLE hf, LPVOID ptr, DWORD size) {
163 if (!WriteFile(hf,ptr,size,&res,NULL)) {
164 FIXME("Failed to write to %p, le is %lx\n",hf,GetLastError());
168 FIXME("Wrote only %ld of %ld bytes to %p.\n",res,size,hf);
174 static DWORD WINAPI stub_dispatch_thread(LPVOID);
177 PIPE_RegisterPipe(wine_marshal_id *mid, HANDLE hPipe, BOOL startreader) {
180 wine_pipe *new_pipes;
182 for (i=0;i<nrofpipes;i++)
183 if (pipes[i].mid.oxid==mid->oxid)
186 new_pipes=(wine_pipe*)HeapReAlloc(GetProcessHeap(),0,pipes,sizeof(pipes[0])*(nrofpipes+1));
188 new_pipes=(wine_pipe*)HeapAlloc(GetProcessHeap(),0,sizeof(pipes[0]));
189 if (!new_pipes) return E_OUTOFMEMORY;
191 sprintf(pipefn,OLESTUBMGR"_%08lx%08lx",(DWORD)(mid->oxid >> 32),(DWORD)mid->oxid);
192 memcpy(&(pipes[nrofpipes].mid),mid,sizeof(*mid));
193 pipes[nrofpipes].hPipe = hPipe;
194 pipes[nrofpipes].apt = COM_CurrentApt();
195 assert( pipes[nrofpipes].apt );
196 InitializeCriticalSection(&(pipes[nrofpipes].crit));
199 pipes[nrofpipes-1].hThread = CreateThread(NULL,0,stub_dispatch_thread,(LPVOID)(pipes+(nrofpipes-1)),0,&(pipes[nrofpipes-1].tid));
201 pipes[nrofpipes-1].tid = GetCurrentThreadId();
207 PIPE_FindByMID(wine_marshal_id *mid) {
209 for (i=0;i<nrofpipes;i++)
210 if ((pipes[i].mid.oxid==mid->oxid) &&
211 (GetCurrentThreadId()==pipes[i].tid)
213 return pipes[i].hPipe;
214 return INVALID_HANDLE_VALUE;
218 PIPE_GetFromMID(wine_marshal_id *mid) {
220 for (i=0;i<nrofpipes;i++) {
221 if ((pipes[i].mid.oxid==mid->oxid) &&
222 (GetCurrentThreadId()==pipes[i].tid)
230 RPC_GetRequest(wine_rpc_request **req) {
231 static int reqid = 0xdeadbeef;
234 for (i=0;i<nrofreqs;i++) { /* try to reuse */
235 if (reqs[i]->state == REQSTATE_DONE) {
236 reqs[i]->reqh.reqid = reqid++;
237 reqs[i]->resph.reqid = reqs[i]->reqh.reqid;
238 reqs[i]->hPipe = INVALID_HANDLE_VALUE;
240 reqs[i]->state = REQSTATE_START;
246 reqs = (wine_rpc_request**)HeapReAlloc(
250 sizeof(wine_rpc_request*)*(nrofreqs+1)
253 reqs = (wine_rpc_request**)HeapAlloc(
256 sizeof(wine_rpc_request*)
259 return E_OUTOFMEMORY;
260 reqs[nrofreqs] = (wine_rpc_request*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(wine_rpc_request));
261 reqs[nrofreqs]->reqh.reqid = reqid++;
262 reqs[nrofreqs]->resph.reqid = reqs[nrofreqs]->reqh.reqid;
263 reqs[nrofreqs]->hPipe = INVALID_HANDLE_VALUE;
264 *req = reqs[nrofreqs];
265 reqs[nrofreqs]->state = REQSTATE_START;
271 RPC_FreeRequest(wine_rpc_request *req) {
272 req->state = REQSTATE_DONE; /* Just reuse slot. */
276 static HRESULT WINAPI
277 PipeBuf_QueryInterface(
278 LPRPCCHANNELBUFFER iface,REFIID riid,LPVOID *ppv
281 if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown)) {
282 *ppv = (LPVOID)iface;
283 IUnknown_AddRef(iface);
286 return E_NOINTERFACE;
290 PipeBuf_AddRef(LPRPCCHANNELBUFFER iface) {
291 PipeBuf *This = (PipeBuf *)iface;
292 return InterlockedIncrement(&This->ref);
296 PipeBuf_Release(LPRPCCHANNELBUFFER iface) {
297 PipeBuf *This = (PipeBuf *)iface;
299 wine_rpc_disconnect_header header;
301 DWORD reqtype = REQTYPE_DISCONNECT;
303 ref = InterlockedDecrement(&This->ref);
307 FIXME("Free all stuff\n");
309 memcpy(&header.mid, &This->mid, sizeof(wine_marshal_id));
311 pipe = PIPE_FindByMID(&This->mid);
313 write_pipe(pipe, &reqtype, sizeof(reqtype));
314 write_pipe(pipe, &header, sizeof(wine_rpc_disconnect_header));
316 TRACE("written disconnect packet\n");
318 HeapFree(GetProcessHeap(),0,This);
322 static HRESULT WINAPI
324 LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,REFIID riid
326 /*PipeBuf *This = (PipeBuf *)iface;*/
328 TRACE("(%p,%s)\n",msg,debugstr_guid(riid));
329 /* probably reuses IID in real. */
330 if (msg->cbBuffer && (msg->Buffer == NULL))
331 msg->Buffer = HeapAlloc(GetProcessHeap(),0,msg->cbBuffer);
336 COM_InvokeAndRpcSend(wine_rpc_request *req) {
337 IRpcStubBuffer *stub;
342 hres = MARSHAL_Find_Stub_Buffer(&(req->reqh.mid),&stub);
344 ERR("Stub not found?\n");
347 msg.Buffer = req->Buffer;
348 msg.iMethod = req->reqh.iMethod;
349 msg.cbBuffer = req->reqh.cbBuffer;
350 msg.dataRepresentation = NDR_LOCAL_DATA_REPRESENTATION;
351 req->state = REQSTATE_INVOKING;
352 req->resph.retval = IRpcStubBuffer_Invoke(stub,&msg,NULL);
353 IUnknown_Release(stub);
354 req->Buffer = msg.Buffer;
355 req->resph.cbBuffer = msg.cbBuffer;
356 reqtype = REQTYPE_RESPONSE;
357 hres = write_pipe(req->hPipe,&reqtype,sizeof(reqtype));
358 if (hres) return hres;
359 hres = write_pipe(req->hPipe,&(req->resph),sizeof(req->resph));
360 if (hres) return hres;
361 hres = write_pipe(req->hPipe,req->Buffer,req->resph.cbBuffer);
362 if (hres) return hres;
363 req->state = REQSTATE_DONE;
368 static HRESULT COM_RpcReceive(wine_pipe *xpipe);
371 RPC_QueueRequestAndWait(wine_rpc_request *req) {
373 wine_rpc_request *xreq;
376 wine_pipe *xpipe = PIPE_GetFromMID(&(req->reqh.mid));
379 FIXME("no pipe found.\n");
382 req->hPipe = xpipe->hPipe;
383 req->state = REQSTATE_REQ_WAITING_FOR_REPLY;
384 reqtype = REQTYPE_REQUEST;
385 hres = write_pipe(req->hPipe,&reqtype,sizeof(reqtype));
386 if (hres) return hres;
387 hres = write_pipe(req->hPipe,&(req->reqh),sizeof(req->reqh));
388 if (hres) return hres;
389 hres = write_pipe(req->hPipe,req->Buffer,req->reqh.cbBuffer);
390 if (hres) return hres;
392 /* This loop is about allowing re-entrancy. While waiting for the
393 * response to one RPC we may receive a request starting another. */
395 hres = COM_RpcReceive(xpipe);
398 for (i=0;i<nrofreqs;i++) {
400 if ((xreq->state==REQSTATE_REQ_GOT) && (xreq->hPipe==req->hPipe)) {
401 hres = COM_InvokeAndRpcSend(xreq);
405 if (req->state == REQSTATE_RESP_GOT)
409 WARN("-- 0x%08lx\n", hres);
413 static HRESULT WINAPI
415 LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,ULONG *status
417 PipeBuf *This = (PipeBuf *)iface;
418 wine_rpc_request *req;
423 if (This->mid.oxid == COM_CurrentApt()->oxid) {
424 ERR("Need to call directly!\n");
428 hres = RPC_GetRequest(&req);
429 if (hres) return hres;
430 req->reqh.iMethod = msg->iMethod;
431 req->reqh.cbBuffer = msg->cbBuffer;
432 memcpy(&(req->reqh.mid),&(This->mid),sizeof(This->mid));
433 req->Buffer = msg->Buffer;
434 hres = RPC_QueueRequestAndWait(req);
436 RPC_FreeRequest(req);
439 msg->cbBuffer = req->resph.cbBuffer;
440 msg->Buffer = req->Buffer;
441 *status = req->resph.retval;
442 RPC_FreeRequest(req);
447 static HRESULT WINAPI
448 PipeBuf_FreeBuffer(LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg) {
449 FIXME("(%p), stub!\n",msg);
453 static HRESULT WINAPI
455 LPRPCCHANNELBUFFER iface,DWORD* pdwDestContext,void** ppvDestContext
457 FIXME("(%p,%p), stub!\n",pdwDestContext,ppvDestContext);
461 static HRESULT WINAPI
462 PipeBuf_IsConnected(LPRPCCHANNELBUFFER iface) {
463 FIXME("(), stub!\n");
467 static IRpcChannelBufferVtbl pipebufvt = {
468 PipeBuf_QueryInterface,
479 PIPE_GetNewPipeBuf(wine_marshal_id *mid, IRpcChannelBuffer **pipebuf) {
480 wine_marshal_id ourid;
486 hPipe = PIPE_FindByMID(mid);
487 if (hPipe == INVALID_HANDLE_VALUE) {
489 sprintf(pipefn,OLESTUBMGR"_%08lx%08lx",(DWORD)(mid->oxid >> 32),(DWORD)mid->oxid);
492 GENERIC_READ|GENERIC_WRITE,
499 if (hPipe == INVALID_HANDLE_VALUE) {
500 FIXME("Could not open named pipe %s, le is %lx\n",pipefn,GetLastError());
503 hres = PIPE_RegisterPipe(mid, hPipe, FALSE);
504 if (hres) return hres;
505 memset(&ourid,0,sizeof(ourid));
506 ourid.oxid = COM_CurrentApt()->oxid;
507 if (!WriteFile(hPipe,&ourid,sizeof(ourid),&res,NULL)||(res!=sizeof(ourid))) {
508 ERR("Failed writing startup mid!\n");
512 pbuf = (PipeBuf*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(PipeBuf));
513 pbuf->lpVtbl = &pipebufvt;
515 memcpy(&(pbuf->mid),mid,sizeof(*mid));
516 *pipebuf = (IRpcChannelBuffer*)pbuf;
521 create_server(REFCLSID rclsid) {
522 static const WCHAR embedding[] = { ' ', '-','E','m','b','e','d','d','i','n','g',0 };
525 HRESULT hres = E_UNEXPECTED;
527 WCHAR exe[MAX_PATH+1];
528 DWORD exelen = sizeof(exe);
529 WCHAR command[MAX_PATH+sizeof(embedding)/sizeof(WCHAR)];
531 PROCESS_INFORMATION pinfo;
533 WINE_StringFromCLSID((LPCLSID)rclsid,xclsid);
535 sprintf(buf,"CLSID\\%s\\LocalServer32",xclsid);
536 hres = RegOpenKeyExA(HKEY_CLASSES_ROOT, buf, 0, KEY_READ, &key);
538 if (hres != ERROR_SUCCESS) {
539 WARN("CLSID %s not registered as LocalServer32\n", xclsid);
540 return REGDB_E_READREGDB; /* Probably */
543 memset(exe,0,sizeof(exe));
544 hres= RegQueryValueExW(key, NULL, NULL, NULL, (LPBYTE)exe, &exelen);
547 WARN("No default value for LocalServer32 key\n");
548 return REGDB_E_CLASSNOTREG; /* FIXME: check retval */
551 memset(&sinfo,0,sizeof(sinfo));
552 sinfo.cb = sizeof(sinfo);
554 /* EXE servers are started with the -Embedding switch. MSDN also claims /Embedding is used,
555 9x does -Embedding, perhaps an 9x/NT difference? */
557 strcpyW(command, exe);
558 strcatW(command, embedding);
560 TRACE("activating local server '%s' for %s\n", debugstr_w(command), xclsid);
562 if (!CreateProcessW(exe, command, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo)) {
563 WARN("failed to run local server %s\n", debugstr_w(exe));
569 /* http://msdn.microsoft.com/library/en-us/dnmsj99/html/com0199.asp, Figure 4 */
570 HRESULT create_marshalled_proxy(REFCLSID rclsid, REFIID iid, LPVOID *ppv) {
575 char marshalbuffer[200];
577 LARGE_INTEGER seekto;
578 ULARGE_INTEGER newpos;
580 #define MAXTRIES 10000
582 TRACE("rclsid=%s, iid=%s\n", debugstr_guid(rclsid), debugstr_guid(iid));
584 strcpy(pipefn,PIPEPREF);
585 WINE_StringFromCLSID(rclsid,pipefn+strlen(PIPEPREF));
587 while (tries++<MAXTRIES) {
588 TRACE("waiting for %s\n", pipefn);
590 WaitNamedPipeA( pipefn, NMPWAIT_WAIT_FOREVER );
593 GENERIC_READ|GENERIC_WRITE,
600 if (hPipe == INVALID_HANDLE_VALUE) {
602 if ((hres = create_server(rclsid)))
606 WARN("Could not open named pipe to broker %s, le is %lx\n",pipefn,GetLastError());
612 if (!ReadFile(hPipe,marshalbuffer,sizeof(marshalbuffer),&bufferlen,NULL)) {
613 FIXME("Failed to read marshal id from classfactory of %s.\n",debugstr_guid(rclsid));
617 TRACE("read marshal id from pipe\n");
622 return E_NOINTERFACE;
623 hres = CreateStreamOnHGlobal(0,TRUE,&pStm);
624 if (hres) return hres;
625 hres = IStream_Write(pStm,marshalbuffer,bufferlen,&res);
627 seekto.u.LowPart = 0;seekto.u.HighPart = 0;
628 hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
629 TRACE("unmarshalling classfactory\n");
630 hres = CoUnmarshalInterface(pStm,&IID_IClassFactory,ppv);
632 IStream_Release(pStm);
638 PIPE_StartRequestThread(HANDLE xhPipe) {
639 wine_marshal_id remoteid;
642 hres = read_pipe(xhPipe,&remoteid,sizeof(remoteid));
644 ERR("Failed to read remote mid!\n");
647 PIPE_RegisterPipe(&remoteid,xhPipe, TRUE);
651 COM_RpcReceive(wine_pipe *xpipe) {
654 HANDLE xhPipe = xpipe->hPipe;
656 /*FIXME("%lx %d reading reqtype\n",GetCurrentProcessId(),xhPipe);*/
657 hres = read_pipe(xhPipe,&reqtype,sizeof(reqtype));
659 EnterCriticalSection(&(xpipe->crit));
660 /*FIXME("%lx got reqtype %ld\n",GetCurrentProcessId(),reqtype);*/
662 if (reqtype == REQTYPE_DISCONNECT) { /* only received by servers */
663 wine_rpc_disconnect_header header;
664 IRpcStubBuffer *stub;
667 hres = read_pipe(xhPipe, &header, sizeof(header));
669 ERR("could not read disconnect header\n");
673 TRACE("read disconnect header\n");
675 hres = MARSHAL_Find_Stub_Buffer(&header.mid, &stub);
677 ERR("could not locate stub to disconnect, mid.oid=%s\n",
678 wine_dbgstr_longlong(header.mid.oid));
683 /* release reference added by MARSHAL_Find_Stub_Buffer call */
684 IRpcStubBuffer_Release(stub);
685 /* release it for real */
686 ret = IRpcStubBuffer_Release(stub);
689 MARSHAL_Invalidate_Stub_From_MID(&header.mid);
691 } else if (reqtype == REQTYPE_REQUEST) {
692 wine_rpc_request *xreq;
693 RPC_GetRequest(&xreq);
694 xreq->hPipe = xhPipe;
695 hres = read_pipe(xhPipe,&(xreq->reqh),sizeof(xreq->reqh));
697 xreq->resph.reqid = xreq->reqh.reqid;
698 xreq->Buffer = HeapAlloc(GetProcessHeap(),0, xreq->reqh.cbBuffer);
699 hres = read_pipe(xhPipe,xreq->Buffer,xreq->reqh.cbBuffer);
701 xreq->state = REQSTATE_REQ_GOT;
703 } else if (reqtype == REQTYPE_RESPONSE) {
704 wine_rpc_response_header resph;
707 hres = read_pipe(xhPipe,&resph,sizeof(resph));
709 for (i=nrofreqs;i--;) {
710 wine_rpc_request *xreq = reqs[i];
711 if (xreq->state != REQSTATE_REQ_WAITING_FOR_REPLY)
713 if (xreq->reqh.reqid == resph.reqid) {
714 memcpy(&(xreq->resph),&resph,sizeof(resph));
717 xreq->Buffer = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,xreq->Buffer,xreq->resph.cbBuffer);
719 xreq->Buffer = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,xreq->resph.cbBuffer);
721 hres = read_pipe(xhPipe,xreq->Buffer,xreq->resph.cbBuffer);
723 xreq->state = REQSTATE_RESP_GOT;
724 /*PulseEvent(hRpcChanged);*/
728 ERR("Did not find request for id %lx\n",resph.reqid);
732 ERR("Unknown reqtype %ld\n",reqtype);
735 LeaveCriticalSection(&(xpipe->crit));
739 /* This thread listens on the given pipe for requests to a particular stub manager */
740 static DWORD WINAPI stub_dispatch_thread(LPVOID param)
742 wine_pipe *xpipe = (wine_pipe*)param;
743 HANDLE xhPipe = xpipe->hPipe;
746 TRACE("starting for apartment OXID %08lx%08lx\n", (DWORD)(xpipe->mid.oxid >> 32), (DWORD)(xpipe->mid.oxid));
748 /* join marshalling apartment. fixme: this stuff is all very wrong, threading needs to work like native */
749 NtCurrentTeb()->ReservedForOle = xpipe->apt;
754 hres = COM_RpcReceive(xpipe);
757 for (i=nrofreqs;i--;) {
758 wine_rpc_request *xreq = reqs[i];
759 if ((xreq->state == REQSTATE_REQ_GOT) && (xreq->hPipe == xhPipe)) {
760 hres = COM_InvokeAndRpcSend(xreq);
766 /* fixme: this thread never quits naturally */
767 WARN("exiting with hres %lx\n",hres);
772 /* This thread listens on a named pipe for each apartment that exports
773 * objects. It deals with incoming connection requests. Each time a
774 * client connects a separate thread is spawned for that particular
777 * This architecture is different in native DCOM.
779 static DWORD WINAPI apartment_listener_thread(LPVOID param)
783 APARTMENT *apt = (APARTMENT *) param;
785 /* we must join the marshalling threads apartment */
786 NtCurrentTeb()->ReservedForOle = apt;
788 sprintf(pipefn,OLESTUBMGR"_%08lx%08lx", (DWORD)(apt->oxid >> 32), (DWORD)(apt->oxid));
789 TRACE("Apartment listener thread starting on (%s)\n",pipefn);
792 listenPipe = CreateNamedPipeA(
795 PIPE_TYPE_BYTE|PIPE_WAIT,
796 PIPE_UNLIMITED_INSTANCES,
799 NMPWAIT_USE_DEFAULT_WAIT,
802 if (listenPipe == INVALID_HANDLE_VALUE) {
803 FIXME("pipe creation failed for %s, error %lx\n",pipefn,GetLastError());
804 return 1; /* permanent failure, so quit stubmgr thread */
806 if (!ConnectNamedPipe(listenPipe,NULL)) {
807 ERR("Failure during ConnectNamedPipe %lx!\n",GetLastError());
808 CloseHandle(listenPipe);
811 PIPE_StartRequestThread(listenPipe);
816 void start_apartment_listener_thread()
818 APARTMENT *apt = COM_CurrentApt();
822 TRACE("apt->listenertid=%ld\n", apt->listenertid);
824 /* apt->listenertid is a hack which needs to die at some point, as
825 * it leaks information into the apartment structure. in fact,
826 * this thread isn't quite correct anyway as native RPC doesn't
827 * use a thread per apartment at all, instead the dispatch thread
828 * either enters the apartment to perform the RPC (for MTAs, RTAs)
829 * or does a context switch into it for STAs.
832 if (!apt->listenertid)
834 CreateThread(NULL, 0, apartment_listener_thread, apt, 0, &apt->listenertid);