I am using this tutorial as base for my code in 32bit unmanaged DLL https://code.msdn.microsoft.com/CppHostCLR-e6581ee0
Let's 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 can I pass IntPtr parameter if on C++ side it represents handle? TestInt works, but for TestIntPtr I get the error that the method is not found. This is because the type of parameter is wrong.
In code from 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 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 have 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 CLR expects IUNKNOWN of IntPtr there? But how to construct such instance? I have tried to call IntPtr constructor with this API but it returns variant of type V_INTEGER, so this is closed loop.
I know I can expose C# library using COM and how to use DllExports hack, I also can change C# part to accept just int or uint. But all these ways are not related to the question.
Currently it works for me with 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 the library I cannot influence.
The list of Automation compatible type is documented here: 2.2.49.3 Automation-Compatible Types
As you see, there isn't any concept of a "pointer", handle, or anything that smells "native" (low level). This is because Automation was meant originally for VB (not the .NET one, VB/VBA/VBScript, etc.) that was a language and IDE designed for ease of use, not pointer handling fun, in a time when 64-bit Windows did not existed yet.
So, IntPtr, a raw and opaque pointer (not a handle) which has the particularity to be variable in storage size depending on the executing process bitness, is not a COM automation compatible type, so it can't be put as is, as a pointer, in a VARIANT, because in a VARIANT you want to use in interop code, you can only put automation compatible things.
There are many solutions/workarounds however, because VARIANT can transport 64 bits size things, if you ask nicely. So, you could define the method like this:
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 proper function, still using the __int64 overload for the Test64 method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With