/* * C callable bridge to the .NET object model * * Managed C++ is used to access the .NET object model via * System.Reflection. Here we provide C callable functions * to that functionality, which we then export via a COM * component. * * Note: the _only_ reason why we're going via COM and not simply * exposing the required via some DLL entry points, is that COM * gives us location independence (i.e., the RTS doesn't need * be told where this interop layer resides in order to hoik * it in, the CLSID suffices (provided the component has been * registered, of course.)) It is a bit tiresome to have play * by the .NET COM Interop's rules as regards argument arrays, * so we may want to revisit this issue at some point. * * [ But why not simply use MC++ and provide C-callable entry * points to the relevant functionality, and avoid COM interop * alltogether? Because we have to be able to (statically) * link with gcc-compiled code, and linking MC++ and gcc-compiled * object files doesn't work.] * * Note: you need something never than gcc-2.95 to compile this * code (I'm using gcc-3.2, which comes with mingw-2). */ #define _WIN32_DCOM #define COBJMACROS #include #include #include #ifndef _MSC_VER #include #include #include # if defined(COBJMACROS) && !defined(_MSC_VER) #define IErrorInfo_QueryInterface(T,r,O) (T)->lpVtbl->QueryInterface(T,r,O) #define IErrorInfo_AddRef(T) (T)->lpVtbl->AddRef(T) #define IErrorInfo_Release(T) (T)->lpVtbl->Release(T) #define IErrorInfo_GetSource(T,pbstr) (T)->lpVtbl->GetSource(T,pbstr) #define IErrorInfo_GetDescription(T,pbstr) (T)->lpVtbl->GetDescription(T,pbstr) #define ISupportErrorInfo_QueryInterface(T,r,O) (T)->lpVtbl->QueryInterface(T,r,O) #define ISupportErrorInfo_AddRef(T) (T)->lpVtbl->AddRef(T) #define ISupportErrorInfo_Release(T) (T)->lpVtbl->Release(T) #define ISupportErrorInfo_InterfaceSupportsErrorInfo(T,iid) (T)->lpVtbl->InterfaceSupportsErrorInfo(T,iid) # endif #endif #include "DNInvoke.h" #define WANT_UUID_DECLS #include "InvokerClient.h" #include "Dotnet.h" /* Local prototypes */ static void genError( IUnknown* pUnk, HRESULT hr, char* loc, char** pErrMsg); static int startBridge(char**); static int fromVariant ( DotnetType resTy, VARIANT* pVar, void* res, char** pErrMsg); static VARIANT* toVariant ( DotnetArg* p ); /* Pointer to .NET COM component instance; instantiated on demand. */ static InvokeBridge* pBridge = NULL; /* convert a char* to a BSTR, copied from the HDirect comlib/ sources */ static HRESULT stringToBSTR( /*[in,ptr]*/const char* pstrz , /*[out]*/ BSTR* pbstr ) { int i; if (!pbstr) { return E_FAIL; } else { *pbstr = NULL; } if (!pstrz) { return S_OK; } i = MultiByteToWideChar(CP_ACP, 0, pstrz, -1, NULL, 0); if ( i < 0 ) { return E_FAIL; } *pbstr = SysAllocStringLen(NULL,i-1); if (*pbstr != NULL) { MultiByteToWideChar(CP_ACP, 0, pstrz, -1, *pbstr, i-1); // (*pbstr)[i]=0; return S_OK; } else { return E_FAIL; } } static char* bstrToString( BSTR bstr ) { int i,len; char *res; int blen; if (!bstr) { return NULL; } blen = SysStringLen(bstr); /* pass in NULL for the multi-byte arg in order to compute length first */ len = WideCharToMultiByte(CP_ACP, 0, bstr, blen, NULL, 0, NULL, NULL); if (len == 0) return NULL; /* Allocate string of required length. */ res = (char*)malloc(sizeof(char) * (len + 1)); if (!res) return NULL; i = WideCharToMultiByte(CP_ACP, 0, bstr, blen, res, (len+1), NULL, NULL); /* Poor error handling to map this to NULL. */ if ( i == 0 ) return NULL; /* Terminate and return */ res[i] = '\0'; return res; } static void freeArgs ( SAFEARRAY* psa ) { /* The argument SAFEARRAYs contain dynamically allocated * VARIANTs. Release the VARIANT contents and its memory here. */ long lb,ub; int i; HRESULT hr; VARIANT *pv = NULL; hr = SafeArrayGetLBound(psa, 1, &lb); if (FAILED(hr)) { fprintf(stderr, "freeArgs: failed fetching lower bound\n"); SafeArrayDestroy(psa); return; } hr = SafeArrayGetUBound(psa, 1, &ub); if (FAILED(hr)) { fprintf(stderr, "freeArgs: failed fetching upper bound\n"); SafeArrayDestroy(psa); return; } for ( i = 0; i < (ub - lb); i++ ) { hr = SafeArrayGetElement(psa,(long*)&i,(void*)pv); if (FAILED(hr)) { fprintf(stderr, "freeArgs: unable to fetch element %d\n", i); SafeArrayDestroy(psa); return; } VariantClear(pv); free(pv); } SafeArrayDestroy(psa); } static SAFEARRAY* marshalArgs ( DotnetArg* args, unsigned int n_args ) { SAFEARRAY *psa; SAFEARRAYBOUND rgsabound[1]; int i; long idxArr[1]; HRESULT hr; VARIANT* var; rgsabound[0].lLbound = 0; rgsabound[0].cElements = n_args; psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound); for(i=0;i < n_args; i++) { idxArr[0] = i; var = toVariant(&args[i]); hr = SafeArrayPutElement(psa, idxArr, (void*)var); } return psa; } /* * ***** Accessing the .NET object model ***** * * General remarks: * * - the functions report error conditions via their return value; a char*. * If NULL, the call was successful. If not, the returned string * contains the (dynamically allocated) error message. * * This unorthodox calling convetion is used to simplify the task * of interfacing to these funs from GHC-generated code. */ /* * Function: DN_invokeStatic() * * Given assembly and fully-qualified name of a static .NET method, * invoke it using the supplied arguments. * * Returns NULL on success, pointer to error message if an error. * */ char* DN_invokeStatic ( char *assemName, char *methName, DotnetArg *args, int n_args, DotnetType resultTy, void *res) { SAFEARRAY* psa; VARIANT result; HRESULT hr; BSTR b_assemName; BSTR b_methName; char* errMsg = NULL; if (!pBridge && !startBridge(&errMsg)) { return errMsg; } /* Package up arguments */ psa = marshalArgs(args, n_args); VariantInit(&result); hr = stringToBSTR(assemName, &b_assemName); hr = stringToBSTR(methName, &b_methName); hr = InvokeBridge_InvokeStaticMethod(pBridge, b_assemName, b_methName, psa, &result); SysFreeString(b_assemName); SysFreeString(b_methName); if (FAILED(hr)) { genError((IUnknown*)pBridge, hr, "DInvoke.invokeStatic", &errMsg); return errMsg; } fromVariant(resultTy, &result, res, &errMsg); freeArgs(psa); return errMsg; } /* * Function: DN_invokeMethod() * * Given method name and arguments, invoke .NET method on an object. * The object ref / this-pointer is passed in as the last argument. * * Returns NULL on success, pointer to error message if an error. * */ char* DN_invokeMethod ( char *clsAndMethName, DotnetArg *args, int n_args, DotnetType resultTy, void *res) { SAFEARRAY* psa; VARIANT result; HRESULT hr; char* methName; BSTR b_methName; char* errMsg = NULL; VARIANT *thisPtr; if (!pBridge && !startBridge(&errMsg)) { return errMsg; } if (n_args <= 0) { genError(NULL, 0x0, "Invoke.invokeMethod - missing this pointer", &errMsg); return errMsg; } /* The this-pointer is last */ thisPtr = toVariant(&args[n_args-1]); /* Package up arguments */ psa = marshalArgs(args, n_args-1); VariantInit(&result); /* If the user has qualified method with class, ignore the class bit. */ if ( (methName = strrchr(clsAndMethName, '.')) == NULL) { methName = clsAndMethName; } else { /* Skip past '.' */ methName++; } hr = stringToBSTR(methName, &b_methName); hr = InvokeBridge_InvokeMethod(pBridge, *thisPtr, b_methName, psa, &result); SysFreeString(b_methName); if (FAILED(hr)) { genError((IUnknown*)pBridge, hr, "Invoke.invokeMethod", &errMsg); return errMsg; } fromVariant(resultTy, &result, res, &errMsg); freeArgs(psa); return errMsg; } /* * Function: DN_getField() * * Given a field name and an object pointer, read a field value. * The object ref / this-pointer is passed in as the last argument. * * Returns NULL on success, pointer to error message if an error. * */ char* DN_getField ( char *clsAndMethName, DotnetArg *args, int n_args, DotnetType resultTy, void *res) { VARIANT result; HRESULT hr; char* methName; BSTR b_methName; char* errMsg = NULL; VARIANT *thisPtr; if (!pBridge && !startBridge(&errMsg)) { return errMsg; } if (n_args <= 0) { genError(NULL, 0x0, "Invoke.getField - missing this pointer", &errMsg); return errMsg; } /* The this-pointer is last */ thisPtr = toVariant(&args[n_args-1]); VariantInit(&result); /* If the user has qualified method with class, ignore the class bit. */ if ( (methName = strrchr(clsAndMethName, '.')) == NULL) { methName = clsAndMethName; } else { /* Skip past '.' */ methName++; } hr = stringToBSTR(methName, &b_methName); hr = InvokeBridge_GetField(pBridge, *thisPtr, b_methName, &result); SysFreeString(b_methName); if (FAILED(hr)) { genError((IUnknown*)pBridge, hr, "Invoke.getField", &errMsg); return errMsg; } fromVariant(resultTy, &result, res, &errMsg); return errMsg; } /* * Function: DN_setField() * * Given field name, a value and an object reference, set the field value of * an object. * The object ref / this-pointer is passed in as the last argument. * * Returns NULL on success, pointer to error message if an error. * */ char* DN_setField ( char *clsAndMethName, DotnetArg *args, int n_args, /* next two args are ignored */ DotnetType resultTy, void *res) { HRESULT hr; char* methName; BSTR b_methName; char* errMsg = NULL; VARIANT *thisPtr; VARIANT *pVal; if (!pBridge && !startBridge(&errMsg)) { return errMsg; } if (n_args != 2) { genError(NULL, 0x0, "Invoke.setField - missing this pointer", &errMsg); return errMsg; } /* The this-pointer is last */ thisPtr = toVariant(&args[1]); /* Package up arguments */ pVal = toVariant(&args[0]); /* If the user has qualified method with class, ignore the class bit. */ if ( (methName = strrchr(clsAndMethName, '.')) == NULL) { methName = clsAndMethName; } else { /* Skip past '.' */ methName++; } hr = stringToBSTR(methName, &b_methName); hr = InvokeBridge_SetField(pBridge, *thisPtr, b_methName, *pVal); SysFreeString(b_methName); VariantClear(pVal); free(pVal); free(thisPtr); if (FAILED(hr)) { genError((IUnknown*)pBridge, hr, "Invoke.setField", &errMsg); return errMsg; } return errMsg; } /* * Function: DN_createObject() * * Given assembly and fully-qualified name of a type, * invoke its (possibly parameterised) constructor. * * Returns NULL on success, pointer to error message if an error. * */ char* DN_createObject ( char *assemName, char *methName, DotnetArg *args, int n_args, DotnetType resultTy, void *res) { SAFEARRAY* psa; VARIANT result; HRESULT hr; BSTR b_assemName; BSTR b_methName; char* errMsg = NULL; if (!pBridge && !startBridge(&errMsg)) { return errMsg; } /* Package up arguments */ psa = marshalArgs(args, n_args); VariantInit(&result); hr = stringToBSTR(assemName, &b_assemName); hr = stringToBSTR(methName, &b_methName); hr = InvokeBridge_CreateObject(pBridge, b_assemName, b_methName, psa, &result); SysFreeString(b_assemName); SysFreeString(b_methName); if (FAILED(hr)) { genError((IUnknown*)pBridge, hr, "DN_createObject", &errMsg); return errMsg; } fromVariant(resultTy, &result, res, &errMsg); freeArgs(psa); return errMsg; } /* * Function: DN_getStatic() * * Given assembly and fully-qualified field name, fetch value of static * field. * * Returns NULL on success, pointer to error message if an error. * */ char* DN_getStatic ( char *assemName, char *fieldClsName, /* the next two args are ignored */ DotnetArg *args, int n_args, DotnetType resultTy, void *res) { VARIANT result; HRESULT hr; BSTR b_assemName; BSTR b_clsName; BSTR b_fieldName; char* errMsg = NULL; char* fieldName; char* clsName = fieldName; if (!pBridge && !startBridge(&errMsg)) { return errMsg; } fieldName = (char*)malloc(sizeof(char) * (strlen(fieldClsName) + 1)); strcpy(fieldName, fieldClsName); clsName = fieldName; if (( fieldName = strrchr(fieldName, '.')) == NULL ) { genError((IUnknown*)pBridge, 0x0, "Invoke.getStatic - malformed field spec", &errMsg); return errMsg; } *fieldName = '\0'; fieldName++; VariantInit(&result); hr = stringToBSTR(assemName, &b_assemName); hr = stringToBSTR(fieldName, &b_fieldName); hr = stringToBSTR(clsName, &b_clsName); /* ToDo: honour assembly spec */ hr = InvokeBridge_GetStaticField(pBridge, b_clsName, b_fieldName, &result); SysFreeString(b_assemName); SysFreeString(b_clsName); SysFreeString(b_fieldName); if (FAILED(hr)) { genError((IUnknown*)pBridge, hr, "Invoke.getStatic", &errMsg); return errMsg; } fromVariant(resultTy, &result, res, &errMsg); return errMsg; } /* * Function: DN_setStatic() * * Given assembly and fully-qualified field name, set value of static * field. * * Returns NULL on success, pointer to error message if an error. * */ char* DN_setStatic ( char *assemName, char *fieldClsName, DotnetArg *args, int n_args, /* the next two args are ignored */ DotnetType resultTy, void *res) { VARIANT result; VARIANT *pVal; HRESULT hr; BSTR b_assemName; BSTR b_clsName; BSTR b_fieldName; char* errMsg = NULL; char* fieldName; char* clsName = fieldName; if (!pBridge && !startBridge(&errMsg)) { return errMsg; } fieldName = (char*)malloc(sizeof(char) * (strlen(fieldClsName) + 1)); strcpy(fieldName, fieldClsName); clsName = fieldName; if (( fieldName = strrchr(fieldName, '.')) == NULL ) { genError((IUnknown*)pBridge, 0x0, "Invoke.setStatic - malformed field spec", &errMsg); return errMsg; } *fieldName = '\0'; fieldName++; pVal = toVariant(&args[0]); VariantInit(&result); hr = stringToBSTR(assemName, &b_assemName); hr = stringToBSTR(fieldName, &b_fieldName); hr = stringToBSTR(clsName, &b_clsName); /* ToDo: honour assembly spec */ hr = InvokeBridge_SetStaticField(pBridge, b_clsName, b_fieldName, *pVal); SysFreeString(b_assemName); SysFreeString(b_clsName); SysFreeString(b_fieldName); VariantClear(pVal); free(pVal); if (FAILED(hr)) { genError((IUnknown*)pBridge, hr, "Invoke.setStatic", &errMsg); return errMsg; } fromVariant(resultTy, &result, res, &errMsg); return errMsg; } /* * Function: startBridge(pErrMsg) * * Instantiates an InvokeBridge component, which is then * used to interact with the .NET world. * * If the component isn't available locally, zero is returned. * Otherwise, 1. */ static int startBridge(char** pErrMsg) { HRESULT hr; IUnknown *pUnk; hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.createBridge.CoInitializeEx", pErrMsg); return FALSE; } hr = CoCreateInstance( &CLSID_InvokeBridge, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&pUnk); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.createBridge.CoCreateInstance", pErrMsg); return 0; } hr = IUnknown_QueryInterface(pUnk, &IID_InvokeBridge, (void**)&pBridge); IUnknown_Release(pUnk); if (FAILED(hr)) { genError(pUnk, hr, "DInvoke.createBridge.QueryInterface.InvokeBridge", pErrMsg); return 0; } return 1; } /* * Function: stopBridge() * * Releases the InvokeBridge object and closes the COM library. * */ void stopDotnetBridge() { if (pBridge) { InvokeBridge_Release(pBridge); pBridge = NULL; CoUninitialize(); } /* Match up the call to CoInitializeEx() in startBridge(). */ } /* * Function: genError() * * Construct a string describing an error condition given * an HRESULT and a location. * * If an interface pointer is passed in via the first arg, * attempts are made to get at richer error information through * the IErrorInfo interface. (Note: we don't currently look for * the _Exception interface for even more detailed info.) * */ #define LOCATION_HDR "Location: " #define HRESULT_HDR "HRESULT: " #define SOURCE_HDR "Source: " #define DESCR_HDR "Description: " #define NEWLINE_EXTRA 3 static void genError(IUnknown* pUnk, HRESULT err, char* loc, char** pErrMsg) { HRESULT hr; HRESULT invoke_hr = err; char* invoke_src = NULL; char* invoke_descr = NULL; char* buf; int bufLen; /* If an interface pointer has been supplied, look for * IErrorInfo in order to get more detailed information * on the failure. * * The CLR's .NET COM Interop implementation does provide * IErrorInfo, so we're not really clutching at straws here.. * * Note: CLR also reflects .NET exceptions via the _Exception* * interface.. * */ if (pUnk) { ISupportErrorInfo *pSupp; IErrorInfo *pErrInfo; BSTR src = NULL; BSTR descr = NULL; hr = IUnknown_QueryInterface(pUnk, &IID_ISupportErrorInfo, (void**)&pSupp); if ( SUCCEEDED(hr) ) { hr = ISupportErrorInfo_InterfaceSupportsErrorInfo(pSupp, &IID_InvokeBridge); if ( SUCCEEDED(hr) ) { hr = GetErrorInfo(0,&pErrInfo); if ( SUCCEEDED(hr) ) { IErrorInfo_GetSource(pErrInfo,&src); IErrorInfo_GetDescription(pErrInfo,&descr); invoke_src = bstrToString(src); invoke_descr = bstrToString(descr); IErrorInfo_Release(pErrInfo); if (src) { SysFreeString(src); src = NULL; } if (descr) { SysFreeString(descr); descr = NULL; } } ISupportErrorInfo_Release(pSupp); } } } /* Putting it all together.. */ bufLen = sizeof(LOCATION_HDR) + strlen(loc) + NEWLINE_EXTRA + sizeof(HRESULT_HDR) + 16 + NEWLINE_EXTRA + sizeof(SOURCE_HDR) + (invoke_src ? strlen(invoke_src) : 16) + NEWLINE_EXTRA + sizeof(DESCR_HDR) + (invoke_descr ? strlen(invoke_descr) : 16) + NEWLINE_EXTRA; buf = (char*) malloc(sizeof(char) * (bufLen + 1)); if (!buf) { fprintf(stderr, "Unable to allocate %d for error message", (bufLen + 1)); *pErrMsg = NULL; return; } _snprintf(buf, bufLen, "%s%s\n%s0x%08x\n%s%s\n%s%s", LOCATION_HDR, loc, HRESULT_HDR, invoke_hr, SOURCE_HDR, invoke_src, DESCR_HDR, invoke_descr); /* Done with these chaps */ if (invoke_src) free(invoke_src); if (invoke_descr) free(invoke_descr); if (pErrMsg) *pErrMsg = buf; fprintf(stderr, "**InvokeBridge Error:\n%s", buf); fflush(stderr); } /* Converting to/from VARIANTs */ /* * Function: fromVariant() * * Unmarshal the contents of a VARIANT, converting its embedded value * into the desired DotnetType (if possible.) * * Returns 1 if successful, 0 otherwise. If the conversion fails, * *pErrMsg holds the error message string. */ static int fromVariant (DotnetType resTy, VARIANT* pVar, void* res, char** pErrMsg) { VARIANT vNew; HRESULT hr; VariantInit(&vNew); switch(resTy) { case Dotnet_Byte: case Dotnet_Char: hr = VariantChangeType (&vNew, pVar, 0, VT_UI1); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_UI1}", pErrMsg); return FALSE; } *((unsigned char*)res) = vNew.bVal; return 1; case Dotnet_Boolean: hr = VariantChangeType (&vNew, pVar, 0, VT_BOOL); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_BOOL}", pErrMsg); return 0; } *((unsigned char*)res) = vNew.bVal; return 1; case Dotnet_Int: hr = VariantChangeType (&vNew, pVar, 0, VT_INT); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_INT}", pErrMsg); return 0; } *((int*)res) = vNew.intVal; return 1; case Dotnet_Int8: hr = VariantChangeType (&vNew, pVar, 0, VT_I1); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_I1}", pErrMsg); return 0; } *((signed char*)res) = vNew.bVal; return 1; case Dotnet_Int16: hr = VariantChangeType (&vNew, pVar, 0, VT_I2); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_I2}", pErrMsg); return 0; } *((signed short*)res) = vNew.iVal; return 1; case Dotnet_Int32: hr = VariantChangeType (&vNew, pVar, 0, VT_I4); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_I4}", pErrMsg); return 0; } *((signed int*)res) = vNew.lVal; return 1; case Dotnet_Int64: hr = VariantChangeType (&vNew, pVar, 0, VT_I8); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_I8}", pErrMsg); return 0; } #ifdef _MSC_VER *((__int64*)res) = vNew.llVal; #else *((long long*)res) = vNew.lVal; #endif return 1; case Dotnet_Float: hr = VariantChangeType (&vNew, pVar, 0, VT_R4); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_R4}", pErrMsg); return 0; } *((float*)res) = vNew.fltVal; return 1; case Dotnet_Double: hr = VariantChangeType (&vNew, pVar, 0, VT_R8); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_R4}", pErrMsg); return 0; } *((double*)res) = vNew.dblVal; return 1; case Dotnet_Word8: hr = VariantChangeType (&vNew, pVar, 0, VT_UI1); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_UI1}", pErrMsg); return 0; } *((unsigned char*)res) = vNew.bVal; return 1; case Dotnet_Word16: hr = VariantChangeType (&vNew, pVar, 0, VT_UI2); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_UI2}", pErrMsg); return 0; } *((unsigned short*)res) = vNew.uiVal; return 1; case Dotnet_Word32: hr = VariantChangeType (&vNew, pVar, 0, VT_UI4); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_UI4}", pErrMsg); return 0; } *((unsigned int*)res) = vNew.ulVal; return 1; case Dotnet_Word64: hr = VariantChangeType (&vNew, pVar, 0, VT_UI8); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_UI8}", pErrMsg); return 0; } #ifdef _MSC_VER *((unsigned __int64*)res) = vNew.ullVal; #else *((unsigned long long*)res) = vNew.lVal; #endif return 1; case Dotnet_Ptr: hr = VariantChangeType (&vNew, pVar, 0, VT_BYREF); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_BYREF}", pErrMsg); return 0; } *((void**)res) = vNew.byref; return 1; case Dotnet_Unit: return 0; case Dotnet_Object: if ( pVar->vt == VT_BSTR ) { /* Special handling for strings. If the user has asked for * the string in object form, give him/her that. */ VARIANT res; VariantInit(&res); hr = InvokeBridge_NewString(pBridge, pVar->bstrVal, &res); if (SUCCEEDED(hr)) { pVar = &res; } } hr = VariantChangeType (&vNew, pVar, 0, VT_UNKNOWN); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_UNKNOWN}", pErrMsg); return 0; } *((IUnknown**)res) = vNew.punkVal; return 1; case Dotnet_String: hr = VariantChangeType (&vNew, pVar, 0, VT_BSTR); if (FAILED(hr)) { genError(NULL, hr, "DInvoke.fromVariant{VT_BSTR}", pErrMsg); return 0; } /* Storage is allocated by malloc(), caller is resp for freeing. */ *((char**)res) = bstrToString(vNew.bstrVal); return 1; } return 0; } /* * Function: toVariant() * * Convert a DotnetArg into a VARIANT. The VARIANT * is dynamically allocated. * * The result is the pointer to the filled-in VARIANT structure; * NULL if allocation failed. * */ static VARIANT* toVariant ( DotnetArg* p ) { VARIANT* v = (VARIANT*)malloc(sizeof(VARIANT)); if (!v) return NULL; VariantInit(v); switch (p->arg_type) { case Dotnet_Byte: v->vt = VT_UI1; v->bVal = p->arg.arg_byte; break; case Dotnet_Char: v->vt = VT_UI1; v->bVal = p->arg.arg_char; break; case Dotnet_Boolean: v->vt = VT_BOOL; v->boolVal = p->arg.arg_bool; break; case Dotnet_Int: v->vt = VT_INT; v->intVal = p->arg.arg_int; break; case Dotnet_Int8: v->vt = VT_I1; v->bVal = p->arg.arg_int8; break; case Dotnet_Int16: v->vt = VT_I2; v->iVal = p->arg.arg_int16; break; case Dotnet_Int32: v->vt = VT_I4; v->lVal = p->arg.arg_int32; break; case Dotnet_Int64: v->vt = VT_I8; #ifdef _MSC_VER v->llVal = p->arg.arg_int64; #else (long long*)(v->lVal) = p->arg.arg_int64; #endif break; case Dotnet_Float: v->vt = VT_R4; v->fltVal = p->arg.arg_float; break; case Dotnet_Double: v->vt = VT_R8; v->dblVal = p->arg.arg_double; break; case Dotnet_Word8: v->vt = VT_UI1; v->bVal = p->arg.arg_word8; break; case Dotnet_Word16: v->vt = VT_UI2; v->uiVal = p->arg.arg_word16; break; case Dotnet_Word32: v->vt = VT_UI4; v->ulVal = p->arg.arg_word32; break; case Dotnet_Word64: v->vt = VT_UI8; #ifdef _MSC_VER v->ullVal = p->arg.arg_word64; #else (unsigned long long*)(v->lVal) = p->arg.arg_word64; #endif break; case Dotnet_Ptr: v->vt = VT_BYREF; v->byref = p->arg.arg_ptr; break; case Dotnet_Unit: v->vt = VT_EMPTY; break; case Dotnet_Object: v->vt = VT_UNKNOWN; v->punkVal = (IUnknown*)p->arg.arg_obj; break; case Dotnet_String: { BSTR b; HRESULT hr; v->vt = VT_BSTR; hr = stringToBSTR((const char*)p->arg.arg_str,&b); v->bstrVal = b; break; } } return v; }