如何从非托管C ++ CLR托管代码传递IntPtr方法?

时间:2016-11-07 10:58:12

标签: c# c++ .net

我使用本教程作为我的32位非托管DLL代码的基础 https://code.msdn.microsoft.com/CppHostCLR-e6581ee0

我想说我想打电话给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");
        }
    }

如果在C ++端它代表句柄,我如何传递IntPtr参数? TestInt有效,但对于TestIntPtr,我得到的错误是找不到该方法。这是因为参数类型错误。

在TestInt教程的代码中,我使用

// 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; 
} 

问题是什么是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; 

我试过了:

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); 

也许CLR期待IntPtr的IUNKNOWN?但是如何构建这样的实例呢?我试图用这个API调用IntPtr构造函数,但它返回V_INTEGER类型的变体,所以这是闭环。

我知道我可以使用COM公开C#库以及如何使用DllExports hack,我也可以将C#部分改为接受int或uint。但所有这些方式都与这个问题无关。

目前,我可以使用以下C#helper

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

 variant_t vtIntArg((INT32) dc);

在c ++中。但这很难看,因为我需要这个帮助我的图书馆无法影响。

1 个答案:

答案 0 :(得分:3)

此处记录了自动化兼容类型列表:2.2.49.3 Automation-Compatible Types

如你所见,没有任何“指针”,句柄或任何闻起来“本机”(低级别)的概念。这是因为自动化最初是为VB(不是.NET,VB / VBA / VBScript等)而设计的,它是一种语言和IDE,设计易于使用,而不是指针处理的乐趣,在64位Windows时还不存在。

因此,IntPtr是一个原始且不透明的指针(不是句柄),它具有可变的存储大小,具体取决于执行的进程位数,不是COM自动化兼容类型,因此它不能作为作为指针,在VARIANT中,因为在您想要在互操作代码中使用的VARIANT中,您只能放置自动化兼容的东西。

然而,有很多解决方案/解决方法,因为VARIANT可以传输64位大小的东西,如果你问得好的话。所以,您可以像这样定义方法:

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

在C ++代码中执行以下操作:

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
}

另一个解决方案是在C#

中定义它
public static void Test32(int ptr)
{
}

public static void Test64(long ptr)
{
}

并调用正确的函数,仍然使用Test64方法的__int64重载。