Implement support for dispatch variant marshalling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This isn't the most efficient implementation possible, but it's simple
and easy to read. I don't know of any apps that would really stress this
part of the RPC system, so that shouldn't cause issues.

In the current codebase CoMarshalInterThreadInterfaceInStream() doesn't
do much of course, but that will hopefully change in future. 

This support was tested on my Java<->IE app, which makes use of
extensive IDispatch marshalling. I haven't tested it with other apps
though.

ChangeLog:
Implement support for dispatch variant marshalling
Index: dlls/oleaut32/usrmarshal.c
===================================================================
RCS file: /home/wine/wine/dlls/oleaut32/usrmarshal.c,v
retrieving revision 1.3
diff -u -r1.3 usrmarshal.c
--- dlls/oleaut32/usrmarshal.c	13 May 2003 00:41:58 -0000	1.3
+++ dlls/oleaut32/usrmarshal.c	21 Aug 2003 12:59:57 -0000
@@ -2,6 +2,7 @@
  * Misc marshalling routines
  *
  * Copyright 2002 Ove Kaaven
+ *           2003 Mike Hearn
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -19,6 +20,7 @@
  */
 
 #include <string.h>
+#include <assert.h>
 
 #define NONAMELESSUNION
 #define NONAMELESSSTRUCT
@@ -183,6 +185,9 @@
 
 static unsigned wire_extra(unsigned long *pFlags, VARIANT *pvar)
 {
+  ULONG size;
+  HRESULT hr;
+
   if (V_VT(pvar) & VT_ARRAY) {
     FIXME("wire-size safearray\n");
     return 0;
@@ -200,8 +205,15 @@
     return VARIANT_UserSize(pFlags, 0, V_VARIANTREF(pvar));
   case VT_UNKNOWN:
   case VT_DISPATCH:
-    FIXME("wire-size interfaces\n");
-    return 0;
+    /* find the buffer size of the marshalled dispatch interface */
+    hr = CoGetMarshalSizeMax(&size, &IID_IDispatch, (IUnknown*)V_DISPATCH(pvar), LOWORD(*pFlags), NULL, MSHLFLAGS_NORMAL);
+    if (FAILED(hr)) {
+      ERR("Dispatch variant buffer size calculation failed, HRESULT=0x%lx\n", hr);
+      return 0;
+    }
+    size += sizeof(ULONG); /* we have to store the buffersize in the stream */
+    TRACE("wire-size extra of dispatch variant is %ld\n", size);
+    return size;
   case VT_RECORD:
     FIXME("wire-size record\n");
     return 0;
@@ -210,6 +222,83 @@
   }
 }
 
+/* helper: called for VT_DISPATCH variants to marshal the IDispatch* into the buffer */
+static unsigned char* dispatch_variant_marshal(unsigned long *pFlags, unsigned char *Buffer, VARIANT *pvar) {
+  IStream *working; 
+  HGLOBAL working_mem;
+  void *working_memlocked;
+  ULONG size;
+  HRESULT hr;
+  
+  TRACE("pFlags=%ld, Buffer=%p, pvar=%p\n", *pFlags, Buffer, pvar);
+
+  /* CoMarshalInterface needs a stream, whereas at this level we are operating in terms of buffers.
+   * We create a stream on an HGLOBAL, so we can simply do a memcpy to move it to the buffer.
+   * in rpcrt4/ndr_ole.c, a simple IStream implementation is wrapped around the buffer object,
+   * but that would be overkill here, hence this implementation. We save the size because the unmarshal
+   * code has no way to know how long the marshalled buffer is. */
+
+  size = wire_extra(pFlags, pvar);
+  
+  working_mem = GlobalAlloc(0, size);
+  assert( working_mem != NULL );
+
+  hr = CreateStreamOnHGlobal(working_mem, FALSE, &working);
+  assert( hr == S_OK );
+
+  hr = CoMarshalInterface(working, &IID_IDispatch, (IUnknown*)V_DISPATCH(pvar), LOWORD(*pFlags), NULL, MSHLFLAGS_NORMAL);
+  assert( hr == S_OK );
+
+  IStream_Release(working);
+  
+  working_memlocked = GlobalLock(working_mem);
+  memcpy(Buffer, &size, sizeof(ULONG)); /* copy the buffersize */
+  Buffer += sizeof(ULONG);
+  memcpy(Buffer, working_memlocked, size);
+  
+  GlobalUnlock(working_mem);
+  GlobalFree(working_mem);
+  TRACE("done, size=%ld\n", sizeof(ULONG) + size);
+  return Buffer + sizeof(ULONG) + size;
+}
+
+/* helper: called for VT_DISPATCH variants to unmarshal the buffer back into a dispatch variant */
+static unsigned char* dispatch_variant_unmarshal(unsigned long *pFlags, unsigned char *Buffer, VARIANT *pvar) {
+  IStream *working;
+  HGLOBAL working_mem;
+  void *working_memlocked;
+  ULONG size;
+  HRESULT hr;
+  
+  TRACE("pFlags=%ld, Buffer=%p, pvar=%p\n", *pFlags, Buffer, pvar);
+
+  /* get the buffersize */
+  memcpy(&size, Buffer, sizeof(ULONG));
+  TRACE("buffersize=%ld\n", size);
+  Buffer += sizeof(ULONG);
+  
+  working_mem = GlobalAlloc(0, size);
+  assert( working_mem != NULL );
+
+  hr = CreateStreamOnHGlobal(working_mem, TRUE, &working);
+  assert( hr == S_OK );
+
+  working_memlocked = GlobalLock(working_mem);
+  
+  /* now we copy the contents of the marshalling buffer to working_memlocked, unlock it, and demarshal the stream */
+  memcpy(working_memlocked, Buffer, size);
+  GlobalUnlock(working_mem);
+
+  hr = CoUnmarshalInterface(working, &IID_IDispatch, (void**)&V_DISPATCH(pvar));
+  assert( hr == S_OK );
+
+  IStream_Release(working); /* this also frees the underlying hglobal */
+
+  TRACE("done, processed=%ld bytes\n", sizeof(ULONG) + size);
+  return Buffer + sizeof(ULONG) + size;
+}
+
+
 unsigned long WINAPI VARIANT_UserSize(unsigned long *pFlags, unsigned long Start, VARIANT *pvar)
 {
   TRACE("(%lx,%ld,%p)\n", *pFlags, Start, pvar);
@@ -266,6 +355,13 @@
   case VT_VARIANT | VT_BYREF:
     Pos = VARIANT_UserMarshal(pFlags, Pos, V_VARIANTREF(pvar));
     break;
+  case VT_DISPATCH | VT_BYREF:
+    FIXME("handle DISPATCH by ref\n");
+    break;
+  case VT_DISPATCH:
+    /* this should probably call WdtpInterfacePointer_UserMarshal in ole32.dll */
+    Pos = dispatch_variant_marshal(pFlags, Pos, pvar);
+    break;
   case VT_RECORD:
     FIXME("handle BRECORD by val\n");
     break;
@@ -334,6 +430,9 @@
   case VT_RECORD | VT_BYREF:
     FIXME("handle BRECORD by ref\n");
     break;
+  case VT_DISPATCH:
+    Pos = dispatch_variant_unmarshal(pFlags, Pos, pvar);
+    break;
   default:
     FIXME("handle unknown complex type\n");
     break;
@@ -401,6 +500,7 @@
         lcid, wFlags, pDispParams, pVarResult,
         pExcepInfo, puArgErr);
 
+  TRACE("riid=%p\n", riid);
   /* [out] args can't be null, use dummy vars if needed */
   if (!pVarResult) pVarResult = &VarResult;
 
@@ -461,11 +561,11 @@
     DISPID dispIdMember,
     REFIID riid,
     LCID lcid,
-    DWORD dwFlags,
+    WORD wFlags,
     DISPPARAMS* pDispParams,
     VARIANT* pVarResult,
     EXCEPINFO* pExcepInfo,
-    UINT* pArgErr,
+    UINT* puArgErr,
     UINT cVarRef,
     UINT* rgVarRefIdx,
     VARIANTARG* rgVarRef)
@@ -474,32 +574,41 @@
   VARIANTARG *rgvarg, *arg;
   UINT u;
 
+  TRACE("(%p)->(%ld,%s,%lx,%x,%p,%p,%p,%p)\n", This,
+        dispIdMember, debugstr_guid(riid),
+        lcid, wFlags, pDispParams, pVarResult,
+        pExcepInfo, puArgErr);
+  
   /* let the real Invoke operate on a copy of the in parameters,
    * so we don't risk losing pointers to allocated memory */
   rgvarg = pDispParams->rgvarg;
   arg = CoTaskMemAlloc(sizeof(VARIANTARG)*pDispParams->cArgs);
   for (u=0; u<pDispParams->cArgs; u++) {
+    TRACE("marshalling\n");
     VariantInit(&arg[u]);
     VariantCopy(&arg[u], &rgvarg[u]);
   }
+  TRACE("done\n");
   pDispParams->rgvarg = arg;
 
   /* initialize out parameters, so that they can be marshalled
    * in case the real Invoke doesn't initialize them */
   VariantInit(pVarResult);
   memset(pExcepInfo, 0, sizeof(*pExcepInfo));
-  *pArgErr = 0;
+  *puArgErr = 0;
 
+  TRACE("calling invoke\n");
   hr = IDispatch_Invoke(This,
 			dispIdMember,
 			riid,
 			lcid,
-			dwFlags,
+			wFlags,
 			pDispParams,
 			pVarResult,
 			pExcepInfo,
-			pArgErr);
+			puArgErr);
 
+  TRACE("returned\n");
   /* copy ref args to out list */
   for (u=0; u<cVarRef; u++) {
     unsigned i = rgVarRefIdx[u];

[Index of Archives]     [Gimp for Windows]     [Red Hat]     [Samba]     [Yosemite Camping]     [Graphics Cards]     [Wine Home]

  Powered by Linux