13332

C#: How to set member arrays of an unsafe struct

Question:

I am working on a VS 2015 MVC C# web application that loads a 3rd party C++ DLL I do not have source code for. The DLL requires an input param. The new spec requires a couple of array members. One is an int array and the other is a char array.

My C# struct defines the intended char array as byte to match the 8-bit C++ char:

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

The struct seems correct to me, but now I need to set values and I'm not having any success.

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

For both member arrays I am getting the following error:

<blockquote>

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

</blockquote>

There are at least a couple of things going on here - aside from the proper way of setting the values using a local I also have to make an encoding conversion from string to byte[].

Can someone provide me with a code example of a clean way to set these values?

Answer1:

Is there some reason you're using an unsafe struct? Can you not use marshalling attributes? See <a href="https://msdn.microsoft.com/en-us/library/eshywdt7(v=vs.110).aspx" rel="nofollow">https://msdn.microsoft.com/en-us/library/eshywdt7(v=vs.110).aspx</a>

Regardless, you need to know how you're converting from a C# string to a byte array, and that depends on what encoding your C++ DLL expects that string to be in. For example, on Windows, it is often the "ANSI code page", but on Linux/Unix it might be either "current locale" or explicitly "UTF-8".

So, one option that gives you the most control over the encoding would be to do somethiing like:

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

Alternatively, instead of doing the encoding conversion directly, you can try declaring the PathToData as a string and then using a marshalling attribute to have C# convert it for you; see <a href="https://msdn.microsoft.com/en-us/library/s9ts558h(v=vs.110).aspx" rel="nofollow">https://msdn.microsoft.com/en-us/library/s9ts558h(v=vs.110).aspx</a>:

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

In the second case, it's important that when you declare EntryPoint you set the CharSet property on the DllImportAttribute to get the string conversion to happen the way you want. In your case, you <em>probably</em> want CharSet.Ansi since your DLL takes a char* and not a wchar_t*. For example,

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

Recommend

  • Querying an Oracle Date field using a string in NHibernate HQL
  • Running .NET dll from coldfusion
  • entrypoint file not found
  • Easily override NuGet DLL in development (VS 2015)
  • gwt-exporter doesn't generate code (Java to Javascript)
  • Attempted to read or write protected memory with dllimport in c#
  • Polymer build not to create bundled and unbundled folder
  • In C what exactly happens if i use () to initialize a double dimension array instead of the {}?
  • Synchronize windows folders
  • What Makes These Two Array Adds Different?
  • Does Apple allow the usage of sysctl.h within iOS applications?
  • Possible to “watch” both HAML and SASS at the same time?
  • Laravel: Getting Session ID oddly truncates when using foreach
  • SetWindowsHookEx does not react on media keys
  • Very simple C++ DLL that can be called from .net
  • Yii2: Config params vs. const/define
  • Fetching methods from BroadcastReceiver to update UI
  • output of program is not same as passed argument
  • WinForms: two way TextBox problem
  • Fill an image in a square container while keeping aspect ratio
  • Join two tables and save into third-sql
  • Convert array of 8 bytes to signed long in C++
  • Adding custom controls to a full screen movie
  • Unit Testing MVC Web Application in Visual Studio and Problem with QTAgent
  • C# - Getting references of reference
  • How to set the response of a form post action to a iframe source?
  • Hits per day in Google Big Query
  • Trying to get generic when generic is not available
  • Understanding cpu registers
  • FormattedException instead of throw new Exception(string.Format(…)) in .NET
  • Change div Background jquery
  • How does Linux kernel interrupt the application?
  • Qt: Run a script BEFORE make
  • Linking SubReports Without LinkChild/LinkMaster
  • XCode 8, some methods disappeared ? ex: layoutAttributesClass() -> AnyClass
  • Busy indicator not showing up in wpf window [duplicate]
  • Recursive/Hierarchical Query Using Postgres
  • Running Map reduces the dimensions of the matrices
  • reshape alternating columns in less time and using less memory
  • Why do underscore prefixed variables exist?