2016-06-15 3 views
0

Я работаю над веб-приложением VS 2015 MVC C, которое загружает стороннюю C++ DLL. У меня нет исходного кода. Для DLL требуется входной параметр. Для новой спецификации требуется несколько элементов массива. Один из них - массив int, а другой - массив char.C#: Как установить элементы массивов небезопасной структуры

Моя C# структуры определяет предполагаемую char массива в качестве byte в соответствии с 8-битным C++ символа:

[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); 

Для обоих массивов членов я получаю следующее сообщение об ошибке:

Fixed size buffers can only be accessed through locals or fields.

Там, по крайней мере несколько вещей происходит здесь - в сторону от правильного пути установки значений с помощью локального ввода также необходимо сделать преобразование кодировки от string до byte[].

Может ли кто-нибудь предоставить мне пример кода с чистым способом установки этих значений?

+0

Вам необходимо создать постоянные поля в небезопасном блоке. – igorushi

+1

Encoding.GetBytes решит вашу вторую проблему https://msdn.microsoft.com/en-us/library/ds4kkd55(v=vs.110).aspx – igorushi

ответ

3

Есть ли причина, по которой вы используете небезопасную структуру? Можете ли вы не использовать атрибуты сортировки? См. https://msdn.microsoft.com/en-us/library/eshywdt7(v=vs.110).aspx

Независимо от того, вам нужно знать, как вы преобразовываете строку C# в массив байтов, и это зависит от того, какая кодировка вашей C++ DLL ожидает, что строка будет в. Например, в Windows это часто «кодовая страница ANSI», но в Linux/Unix это может быть либо «текущая локаль», либо явно «UTF-8».

Таким образом, один вариант, который дает максимальный контроль над кодированием будет делать somethiing как:

[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 как строка а затем используя атрибут marshalling, чтобы 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.Ansi, так как ваша DLL принимает char *, а не wchar_t *. Например,

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

Ваш пример отличный, и я надеюсь, что это сработает для меня. Я не могу попробовать в этот момент, но немного позже, когда я доберусь до своего стола. Что касается использования атрибута marshalling, это еще один вариант, я не был уверен, как настроить, и проект приближается к запланированному сроку. Как только у меня было все это, я оставил его в покое, чтобы соответствовать сроку. Я попробую ваши предложения и предоставит обновление позднее этим вечером. Спасибо, что нашли время. Я искренне ценю вашу помощь. – rwkiii

+0

Я пробую ваш первый пример, но я получаю 'Тип не может быть маршализирован, потому что длина экземпляра встроенного массива не соответствует объявленной длине в макете.'Это вызвано тем, что мой' int [] SomeList' не содержит 288 элементов в массиве и/или 'byte [] PathToData' не является точно 256 байтами? SomeList не всегда будет инициализирован 288 элементами - иногда только 1 элемент, и, конечно, PathToData не имеет длину 256 байтов. Просто интересно, не потому ли это, почему я получаю ошибку, и если да, то как это можно преодолеть? – rwkiii

+1

Я думаю, что вы правы - я предполагал, что C# будет нулевым расширять массивы, но я думаю, что это не произойдет автоматически. Вы можете попробовать вручную расширить свои массивы до нужного размера. Например, вы можете выделить массив правильного размера, а затем скопировать из меньшего массива в массив правильного размера, используя Array.Copy(). – Steven

Смежные вопросы