C#:如何设置不安全结构的成员数组

时间:2016-06-15 18:25:00

标签: c# c++

我正在开发一个VS 2015 MVC C#Web应用程序,它加载了我没有源代码的第三方C ++ DLL。 DLL需要输入参数。新规范需要几个阵列成员。一个是int数组,另一个是char数组。

My C#struct将目标char数组定义为byte以匹配8位C ++ char:

[StructLayout(LayoutKind.Sequential)]
public unsafe struct MyDLLInput
{
    public fixed int SomeList[288];
    public fixed byte PathToData[256];
};

这个结构对我来说似乎是正确的,但现在我需要设定价值并且我没有取得任何成功。

MyDLLInput dllInput = new MyDLLInput()
{
    SomeList = new int[] {0,12,33,67,93},
    PathToData = "C:\\some\\path\\to\\data"
}

// Call 3rd Party DLL
MyDLLOutput = MyDLL.EntryPoint(MyDLLInput);

对于两个成员数组,我收到以下错误:

  

固定大小的缓冲区只能通过本地或字段访问。

这里至少有几件事情 - 除了使用本地设置值的正确方法之外,我还必须进行从stringbyte[]的编码转换。

有人能为我提供一个设置这些值的简洁方法的代码示例吗?

1 个答案:

答案 0 :(得分:3)

你有什么理由使用不安全的结构吗?你不能使用编组属性吗?见https://msdn.microsoft.com/en-us/library/eshywdt7(v=vs.110).aspx

无论如何,您需要知道如何从C#字符串转换为字节数组,这取决于您的C ++ DLL期望该字符串所在的编码。例如,在Windows上,它通常是" ANSI代码页",但在Linux / Unix上它可能是"当前的语言环境"或明确地" UTF-8"。

因此,一个让您最能控制编码的选项就是做一些像:

    [StructLayout(LayoutKind.Sequential)]
    public struct MyDllInput
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 288)]
        public int[] SomeList;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
        public byte[] PathToData;
    }
    public static void Main()
    {
        MyDllInput dllInput = new MyDllInput()
        {
            SomeList = new int[288],
            PathToData = new byte[256]
        };

        var listData = new int[] { 0, 12, 33, 67, 93 };
        Array.Copy(listData, dllInput.SomeList, listData.Length);

        var pathToDataBytes = Encoding.UTF8.GetBytes("C:\\some\\path\\to\\data");
        Array.Copy(pathToDataBytes, dllInput.PathToData, pathToDataBytes.Length);
    }

或者,您可以尝试将PathToData声明为字符串,然后使用编组属性让C#为您转换,而不是直接进行编码转换;见https://msdn.microsoft.com/en-us/library/s9ts558h(v=vs.110).aspx

    [StructLayout(LayoutKind.Sequential)]
    public struct MyDllInput
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 288)]
        public int[] SomeList;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst =256)]
        public String PathToData;
    }
    public static void Main()
    {
        MyDllInput dllInput = new MyDllInput()
        {
            SomeList = new int[288],
            PathToData = "C:\\some\\path\\to\\data"
        };            

        var listData = new int[] { 0, 12, 33, 67, 93 };
        Array.Copy(listData, dllInput.SomeList, listData.Length);
    }

在第二种情况下,当您声明EntryPoint时,在DllImportAttribute上设置CharSet属性以使字符串转换按您希望的方式发生,这一点非常重要。在你的情况下,你可能想要CharSet.Ansi,因为你的DLL采用char *而不是wchar_t *。例如,

    [DllImport("MyDll.dll", CharSet = CharSet.Ansi)]
    private static extern void EntryPoint(ref MyDllInput input);