如何从非托管c++ CLR托管代码传递IntPtr到方法

本文关键字:托管代码 IntPtr 方法 CLR c++ | 更新日期: 2023-09-27 18:16:35

我使用本教程作为我的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");
        }
    }

我怎么能传递IntPtr参数如果在c++端它代表句柄?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; 

I have try:

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和如何使用DllExports hack暴露c#库,我也可以改变c#部分接受int或int。但所有这些方法都与问题无关。

目前它为我工作与以下c#帮助

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

 variant_t vtIntArg((INT32) dc);
在c++

。但这是丑陋的,因为我需要这个库的帮助,我不能影响。

如何从非托管c++ CLR托管代码传递IntPtr到方法

自动化兼容类型的列表记录在这里:2.2.49.3自动化兼容类型

如你所见,这里没有任何"指针"、句柄或任何"原生"(低级)的概念。这是因为自动化最初是为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重载