[英]Passing a Structure containing an array of String and an array of Integer into a C++ DLL
I'm having problems with marshaling in VB.NET to C++, here's the code : 我在将VB.NET编组到C ++时遇到问题,这是代码:
In the C++ DLL : 在C ++ DLL中:
struct APP_PARAM
{
int numData;
LPCSTR *text;
int *values;
};
int App::StartApp(APP_PARAM params)
{
for (int i = 0; i < numLines; i++)
{
OutputDebugString(params.text[i]);
}
}
In VB.NET : 在VB.NET中:
<StructLayoutAttribute(LayoutKind.Sequential)> _
Public Structure APP_PARAM
Public numData As Integer
Public text As System.IntPtr
Public values As System.IntPtr
End Structure
Declare Function StartApp Lib "AppSupport.dll" (ByVal params As APP_PARAM) As Integer
Sub Main()
Dim params As APP_PARAM
params.numData = 3
Dim text As String() = {"A", "B", "C"}
Dim textHandle As GCHandle = GCHandle.Alloc(text)
params.text = GCHandle.ToIntPtr(textHandle)
Dim values As Integer() = {10, 20, 30}
Dim valuesHandle As GCHandle = GCHandle.Alloc(values)
params.values = GCHandle.ToIntPtr(heightHandle)
StartApp(params)
textHandle.Free()
valuesHandle.Free()
End Sub
I checked the C++ side, the output from the OutputDebugString is garbage, the text array contains random characters. 我检查了C ++方面,OutputDebugString的输出是垃圾,文本数组包含随机字符。 What is the correct way to do this?
正确的方法是什么?
GCHandle.Alloc
"Allocates a Normal handle for the specified object" , which "creates a handle to a managed object ... which prevents the managed object from being collected" . GCHandle.Alloc
“为指定的对象分配一个普通的句柄” ,它“为托管对象创建一个句柄...防止收集托管对象” 。
What you're looking for is the methods from System.Runtime.InteropServices.Marshal
, which allow you to do things like copy managed objects to memory accessible by unmanaged code. 您正在寻找的是
System.Runtime.InteropServices.Marshal
的方法,该方法使您可以执行将托管对象复制到非托管代码可访问的内存等操作。 Unfortunately, according to this , the pointers in your struct make it a little harder to marshal than many other things (in the sense that many other things can be automatically marshalled using the appropriate P/Invoke attributes), but it's still possible. 不幸的是,根据这个 ,在你的结构指针使它更难一点元帅比许多其他的事情(在这个意义上,许多其他的事情可以使用相应的P为自动编组/ Invoke的属性),但它仍然是可能的。 I've tried this out and it works:
我已经尝试过了,它的工作原理是:
APP_PARAM param = new APP_PARAM();
string[] text = new string[] { "A", "B", "C" };
param.numData = text.Length;
// Manually allocate an array of pointers, one for each string. arr holds the array's address.
IntPtr arr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)) * text.Length);
try
{
param.text = arr;
IntPtr[] unmanagedText = new IntPtr[text.Length];
try
{
// Create a null-terminated ANSI string in unmanaged memory for each element in text.
for (int i = 0; i < text.Length; i++)
unmanagedText[i] = Marshal.StringToHGlobalAnsi(text[i]);
// Copy the addresses of the unmanaged strings into the manually allocated array.
// I don't know of any way to make an unmanaged copy of a managed array in one call.
Marshal.Copy(unmanagedText, 0, arr, unmanagedText.Length);
// param now looks like what the C++ code is expecting (except for the array of int).
StartApp(param);
}
finally
{
foreach (IntPtr str in unmanagedText)
Marshal.FreeHGlobal(str);
}
}
finally
{
Marshal.FreeHGlobal(arr);
}
You'll have to have similar allocation/free code for your array of int values, with its own try/finally blocks to make sure FreeHGlobal is called. 您必须为int值数组提供类似的分配/免费代码,并使用其自己的try / finally块来确保调用FreeHGlobal。
You need to use one of the methods from the Marshal
class. 您需要使用
Marshal
类中的一种方法。
Dim str As String = "Hello World"
Dim ptr as IntPtr = Marshal.StringToHGlobalAnsi(str)
Try
SomePInvokeCall(ptr)
Finally
Marshal.FreeHGlobal(ptr)
End Try
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.