How to pass IntPtr to a method from unmanaged C ++ CLR hosting code?

I use this tutorial as a base for my code in a 32-bit unmanaged DLL https://code.msdn.microsoft.com/CppHostCLR-e6581ee0

Say I want to call TestIntPtr

public class IntPtrTester { public static void TestIntPtr(IntPtr p) { MessageBox.Show("TestIntPtr Method was Called"); } public static void TestInt(int p) { MessageBox.Show("TestInt Method was Called"); } } 

How to pass an IntPtr parameter if on the C ++ side it represents a descriptor? TestInt works, but for TestIntPtr I get an error that the method was not found. This is because the type of the parameter is incorrect.

In the code from the tutorial for TestInt, I use

 // HDC dc; // The static method in the .NET class to invoke. bstr_t bstrStaticMethodName(L"TestInt"); SAFEARRAY *psaStaticMethodArgs = NULL; variant_t vtIntArg((INT) dc); variant_t vtLengthRet; ... psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1); LONG index = 0; hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtIntArg); if (FAILED(hr)) { wprintf(L"SafeArrayPutElement failed w/hr 0x%08lx\n", hr); goto Cleanup; } 

The question is what is the correct code for TestIntPtr

 // The static method in the .NET class to invoke. // HDC dc; bstr_t bstrStaticMethodName(L"TestIntPtr"); SAFEARRAY *psaStaticMethodArgs = NULL; variant_t vtIntArg((INT) dc); // what do I have to write here? variant_t vtLengthRet; 

I tried:

 variant_t vtIntArg((INT) dc); variant_t vtIntArg((UINT) dc); variant_t vtIntArg((long) dc); variant_t vtIntArg((UINT32) dc); variant_t vtIntArg((INT32) dc); 

Maybe the CLR expects IUNKNOWN from IntPtr? But how to build such an instance? I tried calling the IntPtr constructor with this API, but it returns a variant of type V_INTEGER, so this is a closed loop.

I know that I can open the C # library using COM, and how to use the DllExports hack, I can also change the C # part to accept only int or uint. But all these methods are not related to the issue.

It currently works for me with the following C # helper

  public class Helper { public static void help(int hdc) { IntPtrTester.TestIntPtr(new IntPtr(hdc)); } } 

and

  variant_t vtIntArg((INT32) dc); 

in C ++. But this is ugly because I need this helper for a library that I cannot influence.

+6
source share
1 answer

A list of Automation compatible types is documented here: 2.2.49.3 Automation compatible types

As you can see, there is no concept of a โ€œpointerโ€, a descriptor, or anything that smells โ€œnativeโ€ (low level). This is because Automation was originally intended for VB (rather than .NET one, VB / VBA / VBScript, etc.), which was a language and IDE designed for usability and not for handling pointers, at that time when 64-bit windows did not exist yet.

Thus, IntPtr, an unprocessed and opaque pointer (not a descriptor), which has the peculiarity of being variable in storage size depending on the bit rate of the executable process, is not compatible with the COM type, therefore it cannot be used as is, as a pointer, in VARIANT , because in VARIANT, which you want to use in interop code, you can only put things compatible with automation.

There are many solutions / workarounds because VARIANT can transport 64-bit things if you ask nicely. So you can define a method as follows:

 public static void Test(object input) { // check for int (Int32) or long (Int64) here } 

And in C ++ code do something like this:

 variant_t vtIntArg; if (64-bit mode) { vtIntArg = (__int64)dc; // force VT_I8, this overload available only if _WIN32_WINNT >= 0x0501 } else { vtIntArg = (long)dc; // force VT_I4 } 

Another solution is to define this in C #

 public static void Test32(int ptr) { } public static void Test64(long ptr) { } 

And call the function you want, still using the __int64 overload for the Test64 method.

+3
source

Source: https://habr.com/ru/post/1012099/


All Articles