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