Implemented Local Server COM.
[wine] / dlls / ole32 / oleproxy.c
1 /*
2  *      OLE32 proxy/stub handler
3  *
4  *  Copyright 2002  Marcus Meissner
5  */
6
7 /* Documentation on MSDN:
8  *
9  * (COM Proxy)
10  * http://msdn.microsoft.com/library/en-us/com/comext_1q0p.asp
11  *
12  * (COM Stub)
13  * http://msdn.microsoft.com/library/en-us/com/comext_1lia.asp
14  *
15  * (Marshal)
16  * http://msdn.microsoft.com/library/en-us/com/comext_1gfn.asp
17  *
18  */
19
20 #include "config.h"
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "windef.h"
27 #include "objbase.h"
28 #include "ole2.h"
29 #include "rpc.h"
30 #include "winerror.h"
31 #include "winreg.h"
32 #include "wtypes.h"
33 #include "wine/obj_base.h"
34 #include "wine/obj_marshal.h"
35 #include "wine/obj_channel.h"
36
37 #include "compobj_private.h"
38
39 #include "debugtools.h"
40
41 DEFAULT_DEBUG_CHANNEL(ole);
42
43 /* From: http://msdn.microsoft.com/library/en-us/com/cmi_m_4lda.asp
44  *
45  * The first time a client requests a pointer to an interface on a
46  * particular object, COM loads an IClassFactory stub in the server
47  * process and uses it to marshal the first pointer back to the
48  * client. In the client process, COM loads the generic proxy for the
49  * class factory object and calls its implementation of IMarshal to
50  * unmarshal that first pointer. COM then creates the first interface
51  * proxy and hands it a pointer to the RPC channel. Finally, COM returns
52  * the IClassFactory pointer to the client, which uses it to call
53  * IClassFactory::CreateInstance, passing it a reference to the interface.
54  *
55  * Back in the server process, COM now creates a new instance of the
56  * object, along with a stub for the requested interface. This stub marshals
57  * the interface pointer back to the client process, where another object
58  * proxy is created, this time for the object itself. Also created is a
59  * proxy for the requested interface, a pointer to which is returned to
60  * the client. With subsequent calls to other interfaces on the object,
61  * COM will load the appropriate interface stubs and proxies as needed.
62  */
63 typedef struct _CFStub {
64     ICOM_VTABLE(IRpcStubBuffer) *lpvtbl;
65     DWORD                       ref;
66
67     LPUNKNOWN                   pUnkServer;
68 } CFStub;
69
70 static HRESULT WINAPI
71 CFStub_QueryInterface(LPRPCSTUBBUFFER iface, REFIID riid, LPVOID *ppv) {
72     if (IsEqualIID(&IID_IUnknown,riid)||IsEqualIID(&IID_IRpcStubBuffer,riid)) {
73         *ppv = (LPVOID)iface;
74         IUnknown_AddRef(iface);
75         return S_OK;
76     }
77     FIXME("(%s), interface not supported.\n",debugstr_guid(riid));
78     return E_NOINTERFACE;
79 }
80
81 static ULONG WINAPI
82 CFStub_AddRef(LPRPCSTUBBUFFER iface) {
83     ICOM_THIS(CFStub,iface);
84
85     This->ref++;
86     return This->ref;
87 }
88
89 static ULONG WINAPI
90 CFStub_Release(LPRPCSTUBBUFFER iface) {
91     ICOM_THIS(CFStub,iface);
92
93     This->ref--;
94     if (This->ref)
95         return This->ref;
96     HeapFree(GetProcessHeap(),0,This);
97     return 0;
98 }
99
100 static HRESULT WINAPI
101 CFStub_Connect(LPRPCSTUBBUFFER iface, IUnknown *pUnkServer) {
102     ICOM_THIS(CFStub,iface);
103
104     This->pUnkServer = pUnkServer;
105     IUnknown_AddRef(pUnkServer);
106     return S_OK;
107 }
108
109 static void WINAPI
110 CFStub_Disconnect(LPRPCSTUBBUFFER iface) {
111     ICOM_THIS(CFStub,iface);
112
113     IUnknown_Release(This->pUnkServer);
114     This->pUnkServer = NULL;
115 }
116 static HRESULT WINAPI
117 CFStub_Invoke(
118     LPRPCSTUBBUFFER iface,RPCOLEMESSAGE* msg,IRpcChannelBuffer* chanbuf
119 ) {
120     ICOM_THIS(CFStub,iface);
121     HRESULT hres;
122
123     if (msg->iMethod == 3) { /* CreateInstance */
124         IID iid;
125         IClassFactory   *classfac;
126         IUnknown        *ppv;
127         IStream         *pStm;
128         STATSTG         ststg;
129         ULARGE_INTEGER  newpos;
130         LARGE_INTEGER   seekto;
131         ULONG           res;
132
133         if (msg->cbBuffer < sizeof(IID)) {
134             FIXME("Not enough bytes in buffer (%ld instead of %d)?\n",msg->cbBuffer,sizeof(IID));
135             return E_FAIL;
136         }
137         memcpy(&iid,msg->Buffer,sizeof(iid));
138         TRACE("->CreateInstance(%s)\n",debugstr_guid(&iid));
139         hres = IUnknown_QueryInterface(This->pUnkServer,&IID_IClassFactory,(LPVOID*)&classfac);
140         if (hres) {
141             FIXME("Ole server does not provide a IClassFactory?\n");
142             return hres;
143         }
144         hres = IClassFactory_CreateInstance(classfac,NULL,&iid,(LPVOID*)&ppv);
145         IClassFactory_Release(classfac);
146         if (hres) {
147             msg->cbBuffer = 0;
148             FIXME("Failed to create an instance of %s\n",debugstr_guid(&iid));
149             return hres;
150         }
151         hres = CreateStreamOnHGlobal(0,TRUE,&pStm);
152         if (hres) {
153             FIXME("Failed to create stream on hglobal\n");
154             return hres;
155         }
156         hres = CoMarshalInterface(pStm,&iid,ppv,0,NULL,0);
157         if (hres) {
158             FIXME("CoMarshalInterface failed, %lx!\n",hres);
159             msg->cbBuffer = 0;
160             return hres;
161         }
162         hres = IStream_Stat(pStm,&ststg,0);
163         if (hres) {
164             FIXME("Stat failed.\n");
165             return hres;
166         }
167
168         msg->cbBuffer = ststg.cbSize.s.LowPart;
169         msg->Buffer = HeapReAlloc(GetProcessHeap(),0,msg->Buffer,ststg.cbSize.s.LowPart);
170         seekto.s.LowPart = 0;seekto.s.HighPart = 0;
171         hres = IStream_Seek(pStm,seekto,SEEK_SET,&newpos);
172         if (hres) {
173             FIXME("IStream_Seek failed, %lx\n",hres);
174             return hres;
175         }
176         hres = IStream_Read(pStm,msg->Buffer,msg->cbBuffer,&res);
177         if (hres) {
178             FIXME("Stream Read failed, %lx\n",hres);
179             return hres;
180         }
181         IStream_Release(pStm);
182         return S_OK;
183     }
184     FIXME("(%p,%p), stub!\n",msg,chanbuf);
185     FIXME("iMethod is %ld\n",msg->iMethod);
186     FIXME("cbBuffer is %ld\n",msg->cbBuffer);
187     return E_FAIL;
188 }
189
190 static LPRPCSTUBBUFFER WINAPI
191 CFStub_IsIIDSupported(LPRPCSTUBBUFFER iface,REFIID riid) {
192     FIXME("(%s), stub!\n",debugstr_guid(riid));
193     return NULL;
194 }
195
196 static ULONG WINAPI
197 CFStub_CountRefs(LPRPCSTUBBUFFER iface) {
198     FIXME("(), stub!\n");
199     return 1;
200 }
201
202 static HRESULT WINAPI
203 CFStub_DebugServerQueryInterface(LPRPCSTUBBUFFER iface,void** ppv) {
204     FIXME("(%p), stub!\n",ppv);
205     return E_FAIL;
206 }
207 static void    WINAPI
208 CFStub_DebugServerRelease(LPRPCSTUBBUFFER iface,void *pv) {
209     FIXME("(%p), stub!\n",pv);
210 }
211
212 static ICOM_VTABLE(IRpcStubBuffer) cfstubvt = {
213     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
214     CFStub_QueryInterface,
215     CFStub_AddRef,
216     CFStub_Release,
217     CFStub_Connect,
218     CFStub_Disconnect,
219     CFStub_Invoke,
220     CFStub_IsIIDSupported,
221     CFStub_CountRefs,
222     CFStub_DebugServerQueryInterface,
223     CFStub_DebugServerRelease
224 };
225
226 static HRESULT
227 CFStub_Construct(LPRPCSTUBBUFFER *ppv) {
228     CFStub *cfstub;
229     cfstub = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CFStub));
230     if (!cfstub)
231         return E_OUTOFMEMORY;
232     *ppv = (LPRPCSTUBBUFFER)cfstub;
233     cfstub->lpvtbl      = &cfstubvt;
234     cfstub->ref         = 1;
235     return S_OK;
236 }
237
238 /* Since we create proxy buffers and classfactory in a pair, there is 
239  * no need for 2 seperate structs. Just put them in one, but remember 
240  * the refcount.
241  */
242 typedef struct _CFProxy {
243     ICOM_VTABLE(IClassFactory)          *lpvtbl_cf;
244     ICOM_VTABLE(IRpcProxyBuffer)        *lpvtbl_proxy;
245     DWORD                               ref;
246
247     IRpcChannelBuffer                   *chanbuf;
248 } CFProxy;
249
250 static HRESULT WINAPI IRpcProxyBufferImpl_QueryInterface(LPRPCPROXYBUFFER iface,REFIID riid,LPVOID *ppv) {
251     *ppv = NULL;
252     if (IsEqualIID(riid,&IID_IRpcProxyBuffer)||IsEqualIID(riid,&IID_IUnknown)) {
253         IRpcProxyBuffer_AddRef(iface);
254         *ppv = (LPVOID)iface;
255         return S_OK;
256     }
257     FIXME("(%s), no interface.\n",debugstr_guid(riid));
258     return E_NOINTERFACE;
259 }
260
261 static ULONG WINAPI IRpcProxyBufferImpl_AddRef(LPRPCPROXYBUFFER iface) {
262     ICOM_THIS_MULTI(CFProxy,lpvtbl_proxy,iface);
263     return ++(This->ref);
264 }
265
266 static ULONG WINAPI IRpcProxyBufferImpl_Release(LPRPCPROXYBUFFER iface) {
267     ICOM_THIS_MULTI(CFProxy,lpvtbl_proxy,iface);
268
269     if (!--(This->ref)) {
270         IRpcChannelBuffer_Release(This->chanbuf);This->chanbuf = NULL;
271         HeapFree(GetProcessHeap(),0,This);
272         return 0;
273     }
274     return This->ref;
275 }
276
277 static HRESULT WINAPI IRpcProxyBufferImpl_Connect(LPRPCPROXYBUFFER iface,IRpcChannelBuffer* pRpcChannelBuffer) {
278     ICOM_THIS_MULTI(CFProxy,lpvtbl_proxy,iface);
279
280     This->chanbuf = pRpcChannelBuffer;
281     IRpcChannelBuffer_AddRef(This->chanbuf);
282     return S_OK;
283 }
284 static void WINAPI IRpcProxyBufferImpl_Disconnect(LPRPCPROXYBUFFER iface) {
285     ICOM_THIS_MULTI(CFProxy,lpvtbl_proxy,iface);
286     if (This->chanbuf) {
287         IRpcChannelBuffer_Release(This->chanbuf);
288         This->chanbuf = NULL;
289     }
290 }
291
292 static HRESULT WINAPI
293 CFProxy_QueryInterface(LPCLASSFACTORY iface,REFIID riid, LPVOID *ppv) {
294     *ppv = NULL;
295     if (IsEqualIID(&IID_IClassFactory,riid) || IsEqualIID(&IID_IUnknown,riid)) {
296         *ppv = (LPVOID)iface;
297         IClassFactory_AddRef(iface);
298         return S_OK;
299     }
300     if (IsEqualIID(riid,&IID_IMarshal)) /* just to avoid debugoutput */
301         return E_NOINTERFACE;
302     FIXME("Unhandled interface: %s\n",debugstr_guid(riid));
303     return E_NOINTERFACE;
304 }
305
306 static ULONG   WINAPI CFProxy_AddRef(LPCLASSFACTORY iface) {
307     ICOM_THIS_MULTI(CFProxy,lpvtbl_cf,iface);
308     This->ref++;
309     return This->ref;
310 }
311
312 static ULONG   WINAPI CFProxy_Release(LPCLASSFACTORY iface) {
313     ICOM_THIS_MULTI(CFProxy,lpvtbl_cf,iface);
314     This->ref--;
315     if (This->ref)
316         return This->ref;
317     HeapFree(GetProcessHeap(),0,This);
318     return 0;
319 }
320
321 static HRESULT WINAPI CFProxy_CreateInstance(
322     LPCLASSFACTORY iface,
323     LPUNKNOWN pUnkOuter,/* [in] */
324     REFIID riid,        /* [in] */
325     LPVOID *ppv         /* [out] */
326 ) {
327     ICOM_THIS_MULTI(CFProxy,lpvtbl_cf,iface);
328     HRESULT             hres;
329     LPSTREAM            pStream;
330     HGLOBAL             hGlobal;
331     ULONG               srstatus;
332     RPCOLEMESSAGE       msg;
333
334     TRACE("(%p,%s,%p)\n",pUnkOuter,debugstr_guid(riid),ppv);
335
336     /* Send CreateInstance to the remote classfactory. 
337      *
338      * Data: Only the 'IID'.
339      */
340     msg.iMethod  = 3;
341     msg.cbBuffer = sizeof(*riid);
342     msg.Buffer   = NULL;
343     hres = IRpcChannelBuffer_GetBuffer(This->chanbuf,&msg,&IID_IClassFactory);
344     if (hres) {
345         FIXME("IRpcChannelBuffer_GetBuffer failed with %lx?\n",hres);
346         return hres;
347     }
348     memcpy(msg.Buffer,riid,sizeof(*riid));
349     hres = IRpcChannelBuffer_SendReceive(This->chanbuf,&msg,&srstatus);
350     if (hres) {
351         FIXME("IRpcChannelBuffer_SendReceive failed with %lx?\n",hres);
352         return hres;
353     }
354
355     if (!msg.cbBuffer) /* interface not found on remote */
356         return srstatus;
357
358     /* We got back: [Marshaled Interface data] */
359     TRACE("got %ld bytes data.\n",msg.cbBuffer);
360     hGlobal = GlobalAlloc(GMEM_MOVEABLE|GMEM_NODISCARD|GMEM_SHARE,msg.cbBuffer);
361     memcpy(GlobalLock(hGlobal),msg.Buffer,msg.cbBuffer);
362     hres = CreateStreamOnHGlobal(hGlobal,TRUE,&pStream);
363     if (hres) {
364         FIXME("CreateStreamOnHGlobal failed with %lx\n",hres);
365         return hres;
366     }
367     hres = CoUnmarshalInterface(
368             pStream,
369             riid,
370             ppv
371     );
372     IStream_Release(pStream); /* Does GlobalFree hGlobal too. */
373     if (hres) {
374         FIXME("CoMarshalInterface failed, %lx\n",hres);
375         return hres;
376     }
377     return S_OK;
378 }
379
380 static HRESULT WINAPI CFProxy_LockServer(LPCLASSFACTORY iface,BOOL fLock) {
381     /*ICOM_THIS_MULTI(CFProxy,lpvtbl_cf,iface);*/
382     FIXME("(%d), stub!\n",fLock);
383     /* basically: write BOOL, read empty */
384     return S_OK;
385 }
386
387 static ICOM_VTABLE(IRpcProxyBuffer) pspbvtbl = {
388     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
389     IRpcProxyBufferImpl_QueryInterface,
390     IRpcProxyBufferImpl_AddRef,
391     IRpcProxyBufferImpl_Release,
392     IRpcProxyBufferImpl_Connect,
393     IRpcProxyBufferImpl_Disconnect
394 };
395 static ICOM_VTABLE(IClassFactory) cfproxyvt = {
396     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
397     CFProxy_QueryInterface,
398     CFProxy_AddRef,
399     CFProxy_Release,
400     CFProxy_CreateInstance,
401     CFProxy_LockServer
402 };
403
404 static HRESULT
405 CFProxy_Construct(LPVOID *ppv,LPVOID *ppProxy) {
406     CFProxy *cf;
407
408     cf = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CFProxy));
409     if (!cf)
410         return E_OUTOFMEMORY;
411
412     cf->lpvtbl_cf       = &cfproxyvt;
413     cf->lpvtbl_proxy    = &pspbvtbl;
414     cf->ref             = 2; /* we return 2 references to the object! */
415     *ppv                = &(cf->lpvtbl_cf);
416     *ppProxy            = &(cf->lpvtbl_proxy);
417     return S_OK;
418 }
419
420
421 /********************* OLE Proxy/Stub Factory ********************************/
422 static HRESULT WINAPI
423 PSFacBuf_QueryInterface(LPPSFACTORYBUFFER iface, REFIID iid, LPVOID *ppv) {
424     if (IsEqualIID(iid,&IID_IPSFactoryBuffer)||IsEqualIID(iid,&IID_IUnknown)) {
425         *ppv = (LPVOID)iface; 
426         /* No ref counting, static class */
427         return S_OK;
428     }
429     FIXME("(%s) unknown IID?\n",debugstr_guid(iid));
430     return E_NOINTERFACE;
431 }
432
433 static ULONG WINAPI PSFacBuf_AddRef(LPPSFACTORYBUFFER iface) { return 2; }
434 static ULONG WINAPI PSFacBuf_Release(LPPSFACTORYBUFFER iface) { return 1; }
435
436 static HRESULT WINAPI
437 PSFacBuf_CreateProxy(
438     LPPSFACTORYBUFFER iface, IUnknown* pUnkOuter, REFIID riid,
439     IRpcProxyBuffer **ppProxy, LPVOID *ppv
440 ) {
441     if (IsEqualIID(&IID_IClassFactory,riid))
442         return CFProxy_Construct(ppv,(LPVOID*)ppProxy);
443     FIXME("proxying not implemented for (%s) yet!\n",debugstr_guid(riid));
444     return E_FAIL;
445 }
446
447 static HRESULT WINAPI
448 PSFacBuf_CreateStub(
449     LPPSFACTORYBUFFER iface, REFIID riid,IUnknown *pUnkServer,
450     IRpcStubBuffer** ppStub
451 ) {
452     HRESULT hres;
453
454     TRACE("(%s,%p,%p)\n",debugstr_guid(riid),pUnkServer,ppStub);
455
456     if (IsEqualIID(&IID_IClassFactory,riid)) {
457         hres = CFStub_Construct(ppStub);
458         if (!hres)
459             IRpcStubBuffer_Connect((*ppStub),pUnkServer);
460         return hres;
461     }
462     FIXME("stubbing not implemented for (%s) yet!\n",debugstr_guid(riid));
463     return E_FAIL;
464 }
465
466 static ICOM_VTABLE(IPSFactoryBuffer) psfacbufvtbl = {
467     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
468     PSFacBuf_QueryInterface,
469     PSFacBuf_AddRef,
470     PSFacBuf_Release,
471     PSFacBuf_CreateProxy,
472     PSFacBuf_CreateStub
473 };
474
475 /* This is the whole PSFactoryBuffer object, just the vtableptr */
476 static ICOM_VTABLE(IPSFactoryBuffer) *lppsfac = &psfacbufvtbl;
477
478 /***********************************************************************
479  *           DllGetClassObject [OLE32.63]
480  */
481 HRESULT WINAPI OLE32_DllGetClassObject(REFCLSID rclsid, REFIID iid,LPVOID *ppv)
482 {
483     *ppv = NULL;
484     if (IsEqualIID(rclsid,&CLSID_PSFactoryBuffer)) {
485         *ppv = &lppsfac;
486         /* If we create a ps factory, we might need a stub manager later 
487          * anyway
488          */
489         STUBMGR_Start();
490         return S_OK;
491     }
492     if (IsEqualIID(rclsid,&CLSID_DfMarshal)&&IsEqualIID(iid,&IID_IClassFactory))
493         return MARSHAL_GetStandardMarshalCF(ppv);
494     FIXME("\n\tCLSID:\t%s,\n\tIID:\t%s\n",debugstr_guid(rclsid),debugstr_guid(iid));
495     return CLASS_E_CLASSNOTAVAILABLE;
496 }