2016-11-07 268 views
6

我使用本教程为基础的我的代码在32位的非托管的DLL https://code.msdn.microsoft.com/CppHostCLR-e6581ee0如何将IntPtr传递给来自非托管C++ CLR托管代码的方法?

比方说,我想打电话给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; 

我曾尝试:

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预期的IUnknown IntPtr在那里?但是如何构建这样的实例呢?我试图用这个API调用IntPtr构造函数,但它返回V_INTEGER类型的变体,所以这是闭环。

我知道我可以使用COM以及如何使用DllExports破解暴露的C#库,我也可以改变C#部分接受只是INT或UINT。但所有这些方式都与这个问题无关。

目前,它为我的作品与下面的C#帮手

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

variant_t vtIntArg((INT32) dc); 

在C++中。但这很丑陋,因为我需要这个帮手来帮助我无法影响的图书馆。

+0

嗯,我从来没有尝试过从C++端做这个,但一般来说,'IntPtr'大致等同于'void *'。它当然不是COM对象,它甚至不必是有效的指针,但这是最接近的表示。请注意,'IntPtr'绝对*不是句柄* - 它是一个指针,不再是。如果您需要传递句柄,则需要使该函数接受句柄,而不是'IntPtr'。没有保证句柄是指针大小的。 – Luaan

+0

如果你的C++项目的目标是x64,你还没有尝试过LONGLONG。看看CoreCLR代码,我没有看到很多理由对此持乐观态度。他们确实添加了VT_INT_PTR标记类型,但尚未将其添加到CLR中。 –

+0

我已编辑该问题以添加位数并描述已知的解决方法。 – xdenser

回答

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重载。

+1

该文档链接是过时的。 SDK中的WTypes.idl文件是更好的资源。它显示了VT_PTR,VT_INT_PTR和VT_UINT_PTR标签类型。不是说它会帮助OP,CLR不会使用它们。 –

+0

@HansPassant - 我知道这是过时的,这就是我选择这个的原因,正因为它错过了不支持的东西:)而且我不清楚Windows的哪些部分使用这些新类型BTW(property system,other?)grep in SDK中的.h文件不会返回任何东西 –

+1

它不起作用。我怀疑VBA中的“PtrSafe”是主要灵感。必需的64位版本的Office。 –

相关问题