[英]Passing C# structure with string array to c++ function which accepts void * for c# structure and char** for c# string array
我想将具有字符串数组的C#结构发送到C ++函数 ,该函数接受void *作为c#结构,并接受char **作为c#结构字符串数组成员。
我能够将结构发送到c ++函数,但是问题是, 无法从c ++函数访问c#结构的字符串数组数据成员 。 当单独发送字符串数组时,我能够访问数组元素。
示例代码是-
C# Code:
[StructLayout(LayoutKind.Sequential)]
public struct TestInfo
{
public int TestId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public String[] Parameters;
}
[DllImport("TestAPI.dll", CallingConvention = CallingConvention.StdCall, EntryPoint "TestAPI")]
private static extern void TestAPI(ref TestInfo data);
static unsafe void Main(string[] args)
{
TestInfo testinfoObj = new TestInfo();
testinfoObj.TestId = 1;
List<string> names = new List<string>();
names.Add("first");
names.Add("second");
names.Add("third");
testinfoObj.Parameters=names.ToArray();
TestAPI(ref testinfoObj);
}
VC++ Code:
/*Structure with details for TestInfo*/
typedef struct TestInfo
{
int TestId;
char **Parameters;
}TestInfo_t;
//c++ function
__declspec(dllexport) int TestAPI(void *data)
{
TestInfo *cmd_data_ptr= NULL;
cmd_data_ptr = (TestInfo) data;
printf("ID is %d \r\n",cmd_data_ptr->TestId);//Working fine
for(i = 0; i < 3; i++)
printf("value: %s \r\n",((char *)cmd_data_ptr->Parameters)[i]);/*Error-Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt*/
}
分析内存堆栈时,观察到,当我打印((char *)cmd_data_ptr-> Parameters)时,将打印第一个数组元素(“ first”),但使用((char *)cmd_data_ptr-> Parameters) [i]无法访问元素,并且上述异常即将到来。
结构内存地址包含所有结构元素的地址,但是从c ++访问数据时,它仅访问字符串数组的第一个元素。
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public String[] Parameters;
是一个内联数组。 匹配的C ++声明是:
char* Parameters[2];
但是您正在尝试将其匹配到:
char** Parameters;
这是完全不同的。
您将需要手动封送。 在C#结构中,将Parameters
声明为IntPtr
。 然后使用Marshal.AllocHGlobal
分配本机内存以保存指针数组。 然后使用指向您的字符串的指针填充这些指针。
[StructLayout(LayoutKind.Sequential)]
public struct TestInfo
{
public int TestId;
public IntPtr Parameters;
}
static void Main(string[] args) // no need for unsafe
{
TestInfo testInfo;
testInfo.TestId = 1;
testInfo.Parameters = Marshal.AllocHGlobal(2*Marshal.SizeOf(typeof(IntPtr)));
IntPtr ptr = testInfo.Parameters;
Marshal.WriteIntPtr(ptr, Marshal.StringToHGlobalAnsi("foo"));
ptr += Marshal.SizeOf(typeof(IntPtr));
Marshal.WriteIntPtr(ptr, Marshal.StringToHGlobalAnsi("bar"));
TestAPI(ref testinfoObj);
// now you want to call FreeHGlobal, I'll leave that code to you
}
一种替代方法是使用固定的IntPtr []并将其放在testInfo.Parameters中。
这实际上是对David答案的扩展/扩展,但这是包装自定义编组的一种方法:
public struct LocalTestInfo
{
public int TestId;
public IEnumerable<string> Parameters;
public static explicit operator TestInfo(LocalTestInfo info)
{
var marshalled = new TestInfo
{
TestId = info.TestId,
};
var paramsArray = info.Parameters
.Select(Marshal.StringToHGlobalAnsi)
.ToArray();
marshalled.pinnedHandle = GCHandle.Alloc(
paramsArray,
GCHandleType.Pinned);
marshalled.Parameters =
marshalled.pinnedHandle.AddrOfPinnedObject();
return marshalled;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct TestInfo : IDisposable
{
public int TestId;
public IntPtr Parameters;
[NonSerialized]
public GCHandle pinnedHandle;
public void Dispose()
{
if (pinnedHandle.IsAllocated)
{
Console.WriteLine("Freeing pinned handle");
var paramsArray = (IntPtr[])this.pinnedHandle.Target;
foreach (IntPtr ptr in paramsArray)
{
Console.WriteLine("Freeing @ " + ptr);
Marshal.FreeHGlobal(ptr);
}
pinnedHandle.Free();
}
}
}
请注意,我将测试交换到CDecl:
[DllImport(@"Test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int TestAPI(ref TestInfo info);
我也认为您在C ++方面有错别字:
extern "C"
__declspec(dllexport) int TestAPI(void *data)
{
TestInfo *cmd_data_ptr= NULL;
cmd_data_ptr = (TestInfo*) data;
printf("ID is %d \r\n",cmd_data_ptr->TestId);
// char**, not char*
char** paramsArray = ((char **)cmd_data_ptr->Parameters);
for(int i = 0; i < 3; i++)
{
printf("value: %s \r\n",paramsArray[i]);
}
return 0;
}
和测试台:
static void Main(string[] args)
{
var localInfo = new LocalTestInfo()
{
TestId = 1,
Parameters = new[]
{
"Foo",
"Bar",
"Baz"
}
};
TestInfo forMarshalling;
using (forMarshalling = (TestInfo)localInfo)
{
TestAPI(ref forMarshalling);
}
}
反向编组运算符作为练习留给读者,但基本上应该看起来像显式TestInfo
运算符的逆。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.