2 * C callable bridge to the .NET object model
4 * Managed C++ is used to access the .NET object model via
5 * System.Reflection. Here we provide C callable functions
6 * to that functionality, which we then export via a COM
9 * Note: the _only_ reason why we're going via COM and not simply
10 * exposing the required via some DLL entry points, is that COM
11 * gives us location independence (i.e., the RTS doesn't need
12 * be told where this interop layer resides in order to hoik
13 * it in, the CLSID suffices (provided the component has been
14 * registered, of course.)) It is a bit tiresome to have play
15 * by the .NET COM Interop's rules as regards argument arrays,
16 * so we may want to revisit this issue at some point.
18 * [ But why not simply use MC++ and provide C-callable entry
19 * points to the relevant functionality, and avoid COM interop
20 * alltogether? Because we have to be able to (statically)
21 * link with gcc-compiled code, and linking MC++ and gcc-compiled
22 * object files doesn't work.]
24 * Note: you need something never than gcc-2.95 to compile this
25 * code (I'm using gcc-3.2, which comes with mingw-2).
36 # if defined(COBJMACROS) && !defined(_MSC_VER)
37 #define IErrorInfo_QueryInterface(T,r,O) (T)->lpVtbl->QueryInterface(T,r,O)
38 #define IErrorInfo_AddRef(T) (T)->lpVtbl->AddRef(T)
39 #define IErrorInfo_Release(T) (T)->lpVtbl->Release(T)
40 #define IErrorInfo_GetSource(T,pbstr) (T)->lpVtbl->GetSource(T,pbstr)
41 #define IErrorInfo_GetDescription(T,pbstr) (T)->lpVtbl->GetDescription(T,pbstr)
43 #define ISupportErrorInfo_QueryInterface(T,r,O) (T)->lpVtbl->QueryInterface(T,r,O)
44 #define ISupportErrorInfo_AddRef(T) (T)->lpVtbl->AddRef(T)
45 #define ISupportErrorInfo_Release(T) (T)->lpVtbl->Release(T)
46 #define ISupportErrorInfo_InterfaceSupportsErrorInfo(T,iid) (T)->lpVtbl->InterfaceSupportsErrorInfo(T,iid)
50 #define WANT_UUID_DECLS
51 #include "InvokerClient.h"
54 /* Local prototypes */
55 static void genError( IUnknown* pUnk,
59 static int startBridge(char**);
60 static int fromVariant
65 static VARIANT* toVariant ( DotnetArg* p );
67 /* Pointer to .NET COM component instance; instantiated on demand. */
68 static InvokeBridge* pBridge = NULL;
70 /* convert a char* to a BSTR, copied from the HDirect comlib/ sources */
73 stringToBSTR( /*[in,ptr]*/const char* pstrz
74 , /*[out]*/ BSTR* pbstr
88 i = MultiByteToWideChar(CP_ACP, 0, pstrz, -1, NULL, 0);
92 *pbstr = SysAllocStringLen(NULL,i-1);
94 MultiByteToWideChar(CP_ACP, 0, pstrz, -1, *pbstr, i-1);
104 bstrToString( BSTR bstr )
114 blen = SysStringLen(bstr);
116 /* pass in NULL for the multi-byte arg in order to compute length first */
117 len = WideCharToMultiByte(CP_ACP, 0, bstr, blen,
118 NULL, 0, NULL, NULL);
119 if (len == 0) return NULL;
121 /* Allocate string of required length. */
122 res = (char*)malloc(sizeof(char) * (len + 1));
123 if (!res) return NULL;
125 i = WideCharToMultiByte(CP_ACP, 0, bstr, blen,
126 res, (len+1), NULL, NULL);
128 /* Poor error handling to map this to NULL. */
129 if ( i == 0 ) return NULL;
131 /* Terminate and return */
138 freeArgs ( SAFEARRAY* psa )
140 /* The argument SAFEARRAYs contain dynamically allocated
141 * VARIANTs. Release the VARIANT contents and its memory here.
148 hr = SafeArrayGetLBound(psa, 1, &lb);
150 fprintf(stderr, "freeArgs: failed fetching lower bound\n");
151 SafeArrayDestroy(psa);
154 hr = SafeArrayGetUBound(psa, 1, &ub);
156 fprintf(stderr, "freeArgs: failed fetching upper bound\n");
157 SafeArrayDestroy(psa);
160 for ( i = 0; i < (ub - lb); i++ ) {
161 hr = SafeArrayGetElement(psa,(long*)&i,(void*)pv);
163 fprintf(stderr, "freeArgs: unable to fetch element %d\n", i);
164 SafeArrayDestroy(psa);
170 SafeArrayDestroy(psa);
175 marshalArgs ( DotnetArg* args,
176 unsigned int n_args )
179 SAFEARRAYBOUND rgsabound[1];
185 rgsabound[0].lLbound = 0;
186 rgsabound[0].cElements = n_args;
187 psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound);
189 for(i=0;i < n_args; i++) {
191 var = toVariant(&args[i]);
192 hr = SafeArrayPutElement(psa, idxArr, (void*)var);
198 * ***** Accessing the .NET object model *****
202 * - the functions report error conditions via their return value; a char*.
203 * If NULL, the call was successful. If not, the returned string
204 * contains the (dynamically allocated) error message.
206 * This unorthodox calling convetion is used to simplify the task
207 * of interfacing to these funs from GHC-generated code.
211 * Function: DN_invokeStatic()
213 * Given assembly and fully-qualified name of a static .NET method,
214 * invoke it using the supplied arguments.
216 * Returns NULL on success, pointer to error message if an error.
220 DN_invokeStatic ( char *assemName,
234 if (!pBridge && !startBridge(&errMsg)) {
238 /* Package up arguments */
239 psa = marshalArgs(args, n_args);
240 VariantInit(&result);
242 hr = stringToBSTR(assemName, &b_assemName);
243 hr = stringToBSTR(methName, &b_methName);
245 hr = InvokeBridge_InvokeStaticMethod(pBridge,
250 SysFreeString(b_assemName);
251 SysFreeString(b_methName);
253 genError((IUnknown*)pBridge, hr, "DInvoke.invokeStatic", &errMsg);
257 fromVariant(resultTy, &result, res, &errMsg);
264 * Function: DN_invokeMethod()
266 * Given method name and arguments, invoke .NET method on an object.
267 * The object ref / this-pointer is passed in as the last argument.
269 * Returns NULL on success, pointer to error message if an error.
273 DN_invokeMethod ( char *clsAndMethName,
287 if (!pBridge && !startBridge(&errMsg)) {
292 genError(NULL, 0x0, "Invoke.invokeMethod - missing this pointer", &errMsg);
296 /* The this-pointer is last */
297 thisPtr = toVariant(&args[n_args-1]);
299 /* Package up arguments */
300 psa = marshalArgs(args, n_args-1);
301 VariantInit(&result);
303 /* If the user has qualified method with class, ignore the class bit. */
304 if ( (methName = strrchr(clsAndMethName, '.')) == NULL) {
305 methName = clsAndMethName;
311 hr = stringToBSTR(methName, &b_methName);
312 hr = InvokeBridge_InvokeMethod(pBridge,
317 SysFreeString(b_methName);
319 genError((IUnknown*)pBridge, hr, "Invoke.invokeMethod", &errMsg);
323 fromVariant(resultTy, &result, res, &errMsg);
330 * Function: DN_getField()
332 * Given a field name and an object pointer, read a field value.
333 * The object ref / this-pointer is passed in as the last argument.
335 * Returns NULL on success, pointer to error message if an error.
339 DN_getField ( char *clsAndMethName,
352 if (!pBridge && !startBridge(&errMsg)) {
357 genError(NULL, 0x0, "Invoke.getField - missing this pointer", &errMsg);
361 /* The this-pointer is last */
362 thisPtr = toVariant(&args[n_args-1]);
363 VariantInit(&result);
365 /* If the user has qualified method with class, ignore the class bit. */
366 if ( (methName = strrchr(clsAndMethName, '.')) == NULL) {
367 methName = clsAndMethName;
373 hr = stringToBSTR(methName, &b_methName);
374 hr = InvokeBridge_GetField(pBridge,
378 SysFreeString(b_methName);
380 genError((IUnknown*)pBridge, hr, "Invoke.getField", &errMsg);
384 fromVariant(resultTy, &result, res, &errMsg);
389 * Function: DN_setField()
391 * Given field name, a value and an object reference, set the field value of
393 * The object ref / this-pointer is passed in as the last argument.
395 * Returns NULL on success, pointer to error message if an error.
399 DN_setField ( char *clsAndMethName,
402 /* next two args are ignored */
413 if (!pBridge && !startBridge(&errMsg)) {
418 genError(NULL, 0x0, "Invoke.setField - missing this pointer", &errMsg);
422 /* The this-pointer is last */
423 thisPtr = toVariant(&args[1]);
425 /* Package up arguments */
426 pVal = toVariant(&args[0]);
428 /* If the user has qualified method with class, ignore the class bit. */
429 if ( (methName = strrchr(clsAndMethName, '.')) == NULL) {
430 methName = clsAndMethName;
436 hr = stringToBSTR(methName, &b_methName);
437 hr = InvokeBridge_SetField(pBridge,
441 SysFreeString(b_methName);
447 genError((IUnknown*)pBridge, hr, "Invoke.setField", &errMsg);
455 * Function: DN_createObject()
457 * Given assembly and fully-qualified name of a type,
458 * invoke its (possibly parameterised) constructor.
460 * Returns NULL on success, pointer to error message if an error.
464 DN_createObject ( char *assemName,
478 if (!pBridge && !startBridge(&errMsg)) {
482 /* Package up arguments */
483 psa = marshalArgs(args, n_args);
484 VariantInit(&result);
486 hr = stringToBSTR(assemName, &b_assemName);
487 hr = stringToBSTR(methName, &b_methName);
489 hr = InvokeBridge_CreateObject(pBridge,
494 SysFreeString(b_assemName);
495 SysFreeString(b_methName);
497 genError((IUnknown*)pBridge, hr, "DN_createObject", &errMsg);
501 fromVariant(resultTy, &result, res, &errMsg);
508 * Function: DN_getStatic()
510 * Given assembly and fully-qualified field name, fetch value of static
513 * Returns NULL on success, pointer to error message if an error.
517 DN_getStatic ( char *assemName,
519 /* the next two args are ignored */
532 char* clsName = fieldName;
534 if (!pBridge && !startBridge(&errMsg)) {
538 fieldName = (char*)malloc(sizeof(char) * (strlen(fieldClsName) + 1));
539 strcpy(fieldName, fieldClsName);
542 if (( fieldName = strrchr(fieldName, '.')) == NULL ) {
543 genError((IUnknown*)pBridge, 0x0, "Invoke.getStatic - malformed field spec", &errMsg);
549 VariantInit(&result);
551 hr = stringToBSTR(assemName, &b_assemName);
552 hr = stringToBSTR(fieldName, &b_fieldName);
553 hr = stringToBSTR(clsName, &b_clsName);
554 /* ToDo: honour assembly spec */
555 hr = InvokeBridge_GetStaticField(pBridge,
559 SysFreeString(b_assemName);
560 SysFreeString(b_clsName);
561 SysFreeString(b_fieldName);
563 genError((IUnknown*)pBridge, hr, "Invoke.getStatic", &errMsg);
566 fromVariant(resultTy, &result, res, &errMsg);
572 * Function: DN_setStatic()
574 * Given assembly and fully-qualified field name, set value of static
577 * Returns NULL on success, pointer to error message if an error.
581 DN_setStatic ( char *assemName,
585 /* the next two args are ignored */
597 char* clsName = fieldName;
599 if (!pBridge && !startBridge(&errMsg)) {
603 fieldName = (char*)malloc(sizeof(char) * (strlen(fieldClsName) + 1));
604 strcpy(fieldName, fieldClsName);
607 if (( fieldName = strrchr(fieldName, '.')) == NULL ) {
608 genError((IUnknown*)pBridge, 0x0, "Invoke.setStatic - malformed field spec", &errMsg);
614 pVal = toVariant(&args[0]);
615 VariantInit(&result);
617 hr = stringToBSTR(assemName, &b_assemName);
618 hr = stringToBSTR(fieldName, &b_fieldName);
619 hr = stringToBSTR(clsName, &b_clsName);
620 /* ToDo: honour assembly spec */
621 hr = InvokeBridge_SetStaticField(pBridge,
625 SysFreeString(b_assemName);
626 SysFreeString(b_clsName);
627 SysFreeString(b_fieldName);
631 genError((IUnknown*)pBridge, hr, "Invoke.setStatic", &errMsg);
634 fromVariant(resultTy, &result, res, &errMsg);
643 * Function: startBridge(pErrMsg)
645 * Instantiates an InvokeBridge component, which is then
646 * used to interact with the .NET world.
648 * If the component isn't available locally, zero is returned.
653 startBridge(char** pErrMsg)
658 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
660 genError(NULL, hr, "DInvoke.createBridge.CoInitializeEx", pErrMsg);
664 hr = CoCreateInstance( &CLSID_InvokeBridge,
666 CLSCTX_INPROC_SERVER,
670 genError(NULL, hr, "DInvoke.createBridge.CoCreateInstance", pErrMsg);
674 hr = IUnknown_QueryInterface(pUnk, &IID_InvokeBridge, (void**)&pBridge);
675 IUnknown_Release(pUnk);
677 genError(pUnk, hr, "DInvoke.createBridge.QueryInterface.InvokeBridge", pErrMsg);
685 * Function: stopBridge()
687 * Releases the InvokeBridge object and closes the COM library.
694 InvokeBridge_Release(pBridge);
698 /* Match up the call to CoInitializeEx() in startBridge(). */
702 * Function: genError()
704 * Construct a string describing an error condition given
705 * an HRESULT and a location.
707 * If an interface pointer is passed in via the first arg,
708 * attempts are made to get at richer error information through
709 * the IErrorInfo interface. (Note: we don't currently look for
710 * the _Exception interface for even more detailed info.)
713 #define LOCATION_HDR "Location: "
714 #define HRESULT_HDR "HRESULT: "
715 #define SOURCE_HDR "Source: "
716 #define DESCR_HDR "Description: "
717 #define NEWLINE_EXTRA 3
721 genError(IUnknown* pUnk,
727 HRESULT invoke_hr = err;
728 char* invoke_src = NULL;
729 char* invoke_descr = NULL;
733 /* If an interface pointer has been supplied, look for
734 * IErrorInfo in order to get more detailed information
737 * The CLR's .NET COM Interop implementation does provide
738 * IErrorInfo, so we're not really clutching at straws here..
740 * Note: CLR also reflects .NET exceptions via the _Exception*
745 ISupportErrorInfo *pSupp;
746 IErrorInfo *pErrInfo;
750 hr = IUnknown_QueryInterface(pUnk,
751 &IID_ISupportErrorInfo,
753 if ( SUCCEEDED(hr) ) {
754 hr = ISupportErrorInfo_InterfaceSupportsErrorInfo(pSupp,
756 if ( SUCCEEDED(hr) ) {
757 hr = GetErrorInfo(0,&pErrInfo);
758 if ( SUCCEEDED(hr) ) {
759 IErrorInfo_GetSource(pErrInfo,&src);
760 IErrorInfo_GetDescription(pErrInfo,&descr);
761 invoke_src = bstrToString(src);
762 invoke_descr = bstrToString(descr);
764 IErrorInfo_Release(pErrInfo);
765 if (src) { SysFreeString(src); src = NULL; }
766 if (descr) { SysFreeString(descr); descr = NULL; }
768 ISupportErrorInfo_Release(pSupp);
772 /* Putting it all together.. */
773 bufLen = sizeof(LOCATION_HDR) + strlen(loc) + NEWLINE_EXTRA +
774 sizeof(HRESULT_HDR) + 16 + NEWLINE_EXTRA +
775 sizeof(SOURCE_HDR) + (invoke_src ? strlen(invoke_src) : 16) + NEWLINE_EXTRA +
776 sizeof(DESCR_HDR) + (invoke_descr ? strlen(invoke_descr) : 16) + NEWLINE_EXTRA;
777 buf = (char*) malloc(sizeof(char) * (bufLen + 1));
779 fprintf(stderr, "Unable to allocate %d for error message", (bufLen + 1));
784 _snprintf(buf, bufLen, "%s%s\n%s0x%08x\n%s%s\n%s%s",
786 HRESULT_HDR, invoke_hr,
787 SOURCE_HDR, invoke_src,
788 DESCR_HDR, invoke_descr);
790 /* Done with these chaps */
791 if (invoke_src) free(invoke_src);
792 if (invoke_descr) free(invoke_descr);
794 if (pErrMsg) *pErrMsg = buf;
795 fprintf(stderr, "**InvokeBridge Error:\n%s", buf); fflush(stderr);
798 /* Converting to/from VARIANTs */
801 * Function: fromVariant()
803 * Unmarshal the contents of a VARIANT, converting its embedded value
804 * into the desired DotnetType (if possible.)
806 * Returns 1 if successful, 0 otherwise. If the conversion fails,
807 * *pErrMsg holds the error message string.
811 fromVariant (DotnetType resTy,
823 hr = VariantChangeType (&vNew, pVar, 0, VT_UI1);
825 genError(NULL, hr, "DInvoke.fromVariant{VT_UI1}", pErrMsg);
828 *((unsigned char*)res) = vNew.bVal;
831 hr = VariantChangeType (&vNew, pVar, 0, VT_BOOL);
833 genError(NULL, hr, "DInvoke.fromVariant{VT_BOOL}", pErrMsg);
836 *((unsigned char*)res) = vNew.bVal;
839 hr = VariantChangeType (&vNew, pVar, 0, VT_INT);
841 genError(NULL, hr, "DInvoke.fromVariant{VT_INT}", pErrMsg);
844 *((int*)res) = vNew.intVal;
847 hr = VariantChangeType (&vNew, pVar, 0, VT_I1);
849 genError(NULL, hr, "DInvoke.fromVariant{VT_I1}", pErrMsg);
852 *((signed char*)res) = vNew.bVal;
855 hr = VariantChangeType (&vNew, pVar, 0, VT_I2);
857 genError(NULL, hr, "DInvoke.fromVariant{VT_I2}", pErrMsg);
860 *((signed short*)res) = vNew.iVal;
863 hr = VariantChangeType (&vNew, pVar, 0, VT_I4);
865 genError(NULL, hr, "DInvoke.fromVariant{VT_I4}", pErrMsg);
868 *((signed int*)res) = vNew.lVal;
871 hr = VariantChangeType (&vNew, pVar, 0, VT_I8);
873 genError(NULL, hr, "DInvoke.fromVariant{VT_I8}", pErrMsg);
877 *((__int64*)res) = vNew.llVal;
879 *((long long*)res) = vNew.lVal;
883 hr = VariantChangeType (&vNew, pVar, 0, VT_R4);
885 genError(NULL, hr, "DInvoke.fromVariant{VT_R4}", pErrMsg);
888 *((float*)res) = vNew.fltVal;
891 hr = VariantChangeType (&vNew, pVar, 0, VT_R8);
893 genError(NULL, hr, "DInvoke.fromVariant{VT_R4}", pErrMsg);
896 *((double*)res) = vNew.dblVal;
899 hr = VariantChangeType (&vNew, pVar, 0, VT_UI1);
901 genError(NULL, hr, "DInvoke.fromVariant{VT_UI1}", pErrMsg);
904 *((unsigned char*)res) = vNew.bVal;
907 hr = VariantChangeType (&vNew, pVar, 0, VT_UI2);
909 genError(NULL, hr, "DInvoke.fromVariant{VT_UI2}", pErrMsg);
912 *((unsigned short*)res) = vNew.uiVal;
915 hr = VariantChangeType (&vNew, pVar, 0, VT_UI4);
917 genError(NULL, hr, "DInvoke.fromVariant{VT_UI4}", pErrMsg);
920 *((unsigned int*)res) = vNew.ulVal;
923 hr = VariantChangeType (&vNew, pVar, 0, VT_UI8);
925 genError(NULL, hr, "DInvoke.fromVariant{VT_UI8}", pErrMsg);
929 *((unsigned __int64*)res) = vNew.ullVal;
931 *((unsigned long long*)res) = vNew.lVal;
935 hr = VariantChangeType (&vNew, pVar, 0, VT_BYREF);
937 genError(NULL, hr, "DInvoke.fromVariant{VT_BYREF}", pErrMsg);
940 *((void**)res) = vNew.byref;
945 if ( pVar->vt == VT_BSTR ) {
946 /* Special handling for strings. If the user has asked for
947 * the string in object form, give him/her that.
952 hr = InvokeBridge_NewString(pBridge,
959 hr = VariantChangeType (&vNew, pVar, 0, VT_UNKNOWN);
961 genError(NULL, hr, "DInvoke.fromVariant{VT_UNKNOWN}", pErrMsg);
964 *((IUnknown**)res) = vNew.punkVal;
967 hr = VariantChangeType (&vNew, pVar, 0, VT_BSTR);
969 genError(NULL, hr, "DInvoke.fromVariant{VT_BSTR}", pErrMsg);
972 /* Storage is allocated by malloc(), caller is resp for freeing. */
973 *((char**)res) = bstrToString(vNew.bstrVal);
980 * Function: toVariant()
982 * Convert a DotnetArg into a VARIANT. The VARIANT
983 * is dynamically allocated.
985 * The result is the pointer to the filled-in VARIANT structure;
986 * NULL if allocation failed.
991 toVariant ( DotnetArg* p )
993 VARIANT* v = (VARIANT*)malloc(sizeof(VARIANT));
996 switch (p->arg_type) {
999 v->bVal = p->arg.arg_byte;
1003 v->bVal = p->arg.arg_char;
1005 case Dotnet_Boolean:
1007 v->boolVal = p->arg.arg_bool;
1011 v->intVal = p->arg.arg_int;
1015 v->bVal = p->arg.arg_int8;
1019 v->iVal = p->arg.arg_int16;
1023 v->lVal = p->arg.arg_int32;
1028 v->llVal = p->arg.arg_int64;
1030 (long long*)(v->lVal) = p->arg.arg_int64;
1035 v->fltVal = p->arg.arg_float;
1039 v->dblVal = p->arg.arg_double;
1043 v->bVal = p->arg.arg_word8;
1047 v->uiVal = p->arg.arg_word16;
1051 v->ulVal = p->arg.arg_word32;
1056 v->ullVal = p->arg.arg_word64;
1058 (unsigned long long*)(v->lVal) = p->arg.arg_word64;
1063 v->byref = p->arg.arg_ptr;
1070 v->punkVal = (IUnknown*)p->arg.arg_obj;
1072 case Dotnet_String: {
1076 hr = stringToBSTR((const char*)p->arg.arg_str,&b);