C# Marshalling (C# call C++ DLL) - c#

Could you guys please help me solve the following issue? I have a C++ function dll, and it will be called by another C# application. One of the functions I needed is as follow:
unsigned long makeArray(unsigned char* sendArr, unsigned long sendArrLen, unsigned char *recvArr, unsigned long *recvArrLen);
I wrote the following code in C#:
[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern ulong makeArray(byte[] sendArr, ulong sendArrLen, byte[] recvArr, ulong recvArrLen);
private byte[] MakeArray()
{
byte[] arrSend = new byte[] { 0x00, 0x12, 0x34 };
ulong nRecvArrLen = 0;
byte[] arrRecv = null; // assign in c++ dll function (variable size)
if(makeArray(arrSend, (ulong)arrSend.Length, arrRecv, nRecvArrLen) == 1)
{
return arrRecv;
}
return null;
}
Unfortunately, the above code is not working...
May I know how can I pass a pointer-to-pointer to the C++ func? If it is not possible, is there any workaround?
Thank you.

unsigned long in MSVC is a 32-bit unsigned integer, so you should map it to the System.UInt32 .NET type, corresponding to the uint keyword in C#.
C# ulong is an unsigned 64-bit integer, corresponding to MSVC's unsigned __int64 or unsigned long long.
The unsigned long *recvArrLen parameter should me mapped using ref in the C# PInvoke declaration, as you have a level of indirection via pointer.
It also seems that the arrRecv array parameter should be allocated by the caller (C# in your case), and filled by the DLL function.
If the DLL function allocates the buffer, you should add another level of indirection (unsigned char **recvArr), and you should provide a way to release the allocated memory (e.g. the DLL should export a release function as well).
I would try with something like this for PInvoke:
[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint makeArray(
byte[] sendArr,
uint sendArrLen,
[Out] byte[] recvArr,
ref uint recvArrLen
);

Where is your 'test.dll'? I think it is a path problem...
The file must be located at the one of following directories..
[%SystemRoot%] (Windows directory)
[%SystemRoot%]\system32\(32 bit) or
[%SystemRoot%]\sysWOW64\(64 bit)
The same location with your executable file
PATH variable
Or it can be a type mismatch ... refer to the [site].
I matched the ulong type of csharp to unsigned __int64 in c/c++ on windows.
Declaration of the C# code is a little bit changed.
[DllImport(#"testdll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern ulong makeArray
(
byte[] sendArr,
ulong sendArrLen,
[Out] byte[] recvArr,
ref ulong recvArrLen
);
Here are the testdll.cpp abd testdll.h i tested
#include "testdll.h"
unsigned __int64 makeArray(
unsigned char* sendArr,
unsigned __int64 sendArrLen,
unsigned char *recvArr,
unsigned __int64 *recvArrLen
)
{
int i;
for(i=0; i < sendArrLen; i++)
{
recvArr[i] = sendArr[i];
}
memcpy(recvArrLen, &sendArrLen, sizeof(unsigned __int64));
return i;
}
testdll.h code.
#pragma once
#ifdef EXPORT_TESTDLL
#define TESTDLL_API __declspec(dllexport)
#else
#define TESTDLL_API __declspec(dllimport)
#endif
extern "C" TESTDLL_API unsigned __int64 makeArray(
unsigned char* sendArr,
unsigned __int64 sendArrLen,
unsigned char *recvArr,
unsigned __int64 *recvArrLen
);
Finally, C# code of console application as follows, call the native dll function in c++ - testdll.dll
print items on the console.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
[DllImport(#"testdll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern ulong makeArray(byte[] sendArr, ulong sendArrLen, [Out] byte[] recvArr, ref ulong recvArrLen);
static byte[] MakeArray()
{
byte[] arrSend = new byte[] { 0x00, 0x12, 0x34 };
ulong nRecvArrLen = 0;
ulong ret = 0;
byte[] arrRecv = new byte[3]; // assign in c++ dll function (variable size)
try
{
if ((ret = makeArray(arrSend, (ulong)arrSend.Length, arrRecv, ref nRecvArrLen)) > 0)
{
if(arrRecv != null)
Console.WriteLine("nRecvArrLen2============>" + arrRecv.Length);
return arrRecv;
}
}
catch (DllNotFoundException dne)
{
Console.WriteLine("============> dll not found....");
}
return null;
}
static void Main(string[] args)
{
byte[] retbytes = MakeArray();
if (retbytes != null)
{
Console.WriteLine("=====LEN=======>" + retbytes.Length);
for (int i = 0; i < retbytes.Length; i++)
Console.WriteLine("====ITEM========>" + retbytes[i]);
}
else
Console.WriteLine("=====NULL=======>");
}
}
}

Related

Calling c struct in C#. Cannot marshal field '***' of type '***'

I get a c project named Clib2CS with two files which are Clib2CS.c and Clib2CS.h.
Clib2CS.h is as following:
__declspec(dllexport) typedef struct BTreeNode {
int value;
struct BTreeNode* left;
struct BTreeNode* right;
}BTnode;
__declspec(dllexport) unsigned long ConnectSession(unsigned long handle,
unsigned char * publicKey,
unsigned char publicKeyLen);
__declspec(dllexport) void bulidTree(BTnode* root, int value);
Clib2CS.c is as following:
#include "Clib2CS.h"
#include <stdio.h>
#include <stdlib.h>
unsigned unsigned long ConnectSession(unsigned long handle,
unsigned char * publicKey,
unsigned char publicKeyLen)
{
return 42;
}
void bulidTree(BTnode* root, int value) {
if (root == NULL) {
BTnode* node = (BTnode*)malloc(sizeof(BTnode));
node->value = value;
}
if (value < root->value) bulidTree(root->left, value);
else bulidTree(root->right, value);
}
This c project generates a Clib2CS.dll which will be called in a c-sharp project.
The c# project contains only one file named Program.cs.
Progarm.cs is as following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
[StructLayout(LayoutKind.Sequential)]
public class BTnode {
public int value;
[MarshalAs(UnmanagedType.LPStruct)]
public BTnode left;
[MarshalAs(UnmanagedType.LPStruct)]
public BTnode right;
}
class Program
{
[DllImport("Clib2CS.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe static extern UInt32 ConnectSession(UInt32 handle, char* publickey, char publicKeyLen);
[DllImport("Clib2CS.dll", CharSet = CharSet.Auto)]
unsafe static extern void bulidTree([In, Out, MarshalAs(UnmanagedType.LPStruct)] BTnode root, int value);
public unsafe static UInt32 GetConnectSession(UInt32 handle, string publickey, char publicKeyLen) {
// "Convert" string to char*
char* pubKey;
fixed (char* bptr = publickey) {
pubKey = (char*)bptr;
}
return ConnectSession(handle, pubKey, publicKeyLen);
}
static void Main(string[] args)
{
UInt32 ret = GetConnectSession((UInt32)1, "helloworld", 'c');
Console.WriteLine("####################: {0}", ret);
BTnode root = new BTnode();
root.value = 666;
Console.WriteLine("value of root is : {0}", root.value);
int[] vec = { 4, 5, 6, 7, 8, 9 };
foreach (int item in vec) {
bulidTree(root, item);
}
Console.WriteLine("----------------------------------------------");
for (; root != null; root = root.right) {
Console.WriteLine("the tree node is: {0}", root.value);
}
}
}
}
Run it, and I get this error:
Unhandled Exception:System.TypeLoadException: Cannot marshal field 'left' of
type 'ConsoleApplication1.BTnode': There is no marshaling support for this type.
so, how do we invoke c struct of DLL from c# gracefully?
You're too close.
I suggest you to use this for string conversion.
String managedString = Marshal.PtrToStringAnsi((IntPtr) (char *) myUnmanagedString);
You have to know that char* in c# and in c are different a char in c is in 1 byte and in c# in 2 bytes so the pointer are not the same.
Your question is a little generale.
The first way is to marshall like you did in your first attempt.
The second way is to create a CLI C++ DLL use your library or your DLL code as you did in this C project and because it's C++ so it's easy to use with your C code and C# both because of its nature to handle managed and unmanaged code as result it will like a normal .net dll all you have to do is to add it like a reference so i suggest to take a look to this article for more information:
Quick C++/CLI - Learn C++/CLI in less than 10 minutes
C++/CLI wrapper for native C++ to use as reference in C#

Using a C++ DLL in C# when signature includes BYTE**

I am working on a C# project, and am wrapping a C++ DLL for use in the project. I have captured this behaviour in a test project, with function calls renamed to protect the innocent.
Everything seems fine, except for one type of function that I am having a hard time understanding. The signature for that function in the DLL header is:
int32_t DoTheWork(BYTE **dest, BYTE *Src, int32_t szSrc);
My wrapper receives the Src byte array with no problem (easily tested since this is just a char string). The return dest paramater is not quite so simple.
I have tried different ways to pass the dest parameter from C# to the wrapped function, but when I receive it back, either the dest byte array in C# has a length of 1 (instead of the expected 32) bytes, or the return crashes. The instance I have below is a crash. I need to understand how to pass a byte array as reference, copy results into that byte array, and return it with the full complement of bytes without crashing. I have spent more than a day on this looking online and making changes to code, but am still not getting it to work correctly.
Also, would it be better for me to just take the pointer created in the C++ DLL all the way up into the C# calling function, instead of copying the values into the C# byte array in my C++ wrapper? If so, how do I correctly go about cleaning up that memory inside of C#?
I am using VS2010 on Win8. Here's my code:
** OriginalCPPClass.h for OriginalCPPDll.dll
class OriginalCPPClass {
public:
OriginalCPPDLLClass();
virtual ~OriginalCPPDLLClass();
int32_t DoTheWork(BYTE **dest, BYTE *Src, int32_t szSrc);
};
** WrapperDLL.cpp (no accompanying .h file)
#include "CytoCrypto.h"
extern "C"
{
#define WRAPPERCLASS_EXPORT __declspec(dllexport)
WRAPPERCLASS_EXPORT OriginalCPPClass* Wrap_Create()
{
return new OriginalCPPClass();
}
WRAPPERCLASS_EXPORT void Wrap_Destroy(OriginalCPPClass* pObj)
{
delete pObj;
}
WRAPPERCLASS_EXPORT int32_t __cdecl Wrap_DoTheWork(OriginalCPPClass* pObj, BYTE **pDest, BYTE *pSrc, int32_t szSrc)
{
BYTE *result = NULL;
int32_t sz = pObj->DoTheWork(&result, pSrc, szSrc);
*(result+sz) = '\0';
if (sz > 0)
{
memcpy(pDest, result, sz );
}
return (sz >= 0) ? sz : 0;
}
}
** Program.cs
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
[DllImport("OriginalCPPDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Wrap_Create();
[DllImport("OriginalCPPDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Wrap_Destroy(IntPtr pObj);
[DllImport("OriginalCPPDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 Wrap_DoTheWork(IntPtr pObj, out IntPtr pDest, byte[] src, Int32 szSrc);
static void Main(string[] args)
{
string src = "this is the source string";
IntPtr pnt = Marshal.AllocHGlobal(1000);
byte[] bytearray = new byte[1000];
byte[] srcBytes = Encoding.ASCII.GetBytes(src);
Int32 szSrc = srcBytes.Length;
IntPtr obj = Wrap_Create();
Int32 size = Wrap_DoTheWork(obj, out pnt, srcBytes, szSrc);
Marshal.Copy(pnt, bytearray, 0, size);
Wrap_Destroy(obj);
Marshal.Copy(pnt, bytearray, 0, size);
}
}
}
Error dialog says:
An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I finally found the right way to do what I needed. Turns out that just passing a pre-sized byte array to collect the results is good (unfortunately have to pass a large enough buffer to handle the result, the length of which is unknown ahead of time but will never be greater than twice the original size). Then in the wrapper class, I receive a newly allocated chunk of memory when I call the original C++ library, and copy the contents into my "BYTE* dest" parameter (no longer passed as BYTE**), and delete the chunk received from the library. I just leave it up to automatic marshaling to handle the transfer of the array in both directions. Works perfectly, and the string of bytes I am getting back is proven to be correct. Thanks for all the help.
Here's my final code:
** OriginalCPPClass.h for OriginalCPPDll.dll
class OriginalCPPClass {
public:
OriginalCPPDLLClass();
virtual ~OriginalCPPDLLClass();
int32_t DoTheWork(BYTE **dest, BYTE *Src, int32_t szSrc);
};
** WrapperDLL.cpp
#include "CytoCrypto.h"
extern "C"
{
#define WRAPPERCLASS_EXPORT __declspec(dllexport)
WRAPPERCLASS_EXPORT OriginalCPPClass* Wrap_Create()
{
return new OriginalCPPClass();
}
WRAPPERCLASS_EXPORT void Wrap_DestroyPtr(BYTE* ptr)
{
HeapFree(GetProcessHeap(), 0, ptr);
}
WRAPPERCLASS_EXPORT int32_t __cdecl Wrap_DoTheWork(OriginalCPPClass* pObj, BYTE *pDest, BYTE *pSrc, int32_t szSrc)
{
BYTE *result = NULL;
int32_t sz = pObj->DoTheWork(&result, pSrc, szSrc);
if (sz > 0)
{
memcpy(pDest, result, ret+1);
}
if (result)
pObj->DestroyPtr(result);
return (sz >= 0) ? sz : 0;
}
}
** Program.cs
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
[DllImport("OriginalCPPDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Wrap_Create();
[DllImport("OriginalCPPDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Wrap_Destroy(IntPtr pObj);
[DllImport("OriginalCPPDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 Wrap_DoTheWork(IntPtr pObj, byte[] dest, byte[] src, Int32 szSrc);
static void Main(string[] args)
{
string srcStr = "this is the source string";
byte[] resBytes = new byte[srcStr.Length*2];
byte[] srcBytes = Encoding.ASCII.GetBytes(srcStr);
Int32 srcSize = srcBytes.Length;
IntPtr obj = Wrap_Create();
Int32 size = Wrap_DoTheWork(obj, resBytes, srcBytes, srcSize);
Wrap_Destroy(obj);
}
}
}

Corrupted heap when calling unmanaged function via DllImport

I am using an unmanaged dll that is written in C/C++ from a C# application. I'm interested in using the following function from the dll:
static void StorePath(const std::string& path, wchar_t *out_path,
int *out_path_length){
wcslcpy(out_path, c_str_w(path), *out_path_length);
*out_path_length = path.size();
}
int WINAPI BrowseForDirectory(
int allow_portable, int allow_online,
wchar_t *t_directory, int *e_directory_length,
wchar_t *m_directory, int *m_directory_length){
.
.
. //initializing new forms and checking product keys
StorePath(form->SelectedEDirectory().TopDir(), e_directory,
e_directory_length);
StorePath(form->SelectedMDirectory(), m_directory,
m_directory_length);
}
Header file:
#if defined(_WIN32) && !BUILD_WITHOUT_DLLS &&!defined(ECLIPSE_CBUILDER_WORKAROUNDS)
# if BUILDING_EXPORT_LIBRARY
# define EXPORT_DLL __declspec(dllexport)
# else
# define EXPORT_DLL __declspec(dllimport)
# endif
#else
# define EXPORT_DLL
#endif
extern "C" {
int WINAPI BrowseForDirectory(
int allow_portable, int allow_online,
wchar_t *t_directory, int *e_directory_length,
wchar_t *m_directory, int *m_directory_length)
}
Then, I am trying to invoke this function in my own managed, C# class library by doing the following:
[DllImport("MyDLL.dll", CharSet = CharSet.Ansi)]
public static extern int BrowseForDirectory(Int32 allowOnline,
Int32 allowPortable,
[MarshalAs(UnmanagedType.LPStr)] StringBuilder eDirectory,
ref Int32 eDirLength,
[MarshalAs(UnmanagedType.LPStr)] StringBuilder mDirectory,
ref Int32 mDirLength);
Finally, I'm trying to use it in a C# application by calling it like:
var eDir = new StringBuilder(260);
var mDir = new StringBuilder(260);
var eDirLength = eDir.Length;
var mDirLength = mDir.Length;
try
{
var result = Viewer.BrowseForDirectory(1, 1, eDir,
ref eDirLength, mDir, ref mDirLength);
}
catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
However, I was getting a heap corruption, but now my application is exiting because of a STATUS_STACK_BUFFER_OVERRUN--something about an embedded breakpoint. Changing the C++ code is not an option. I have the proper reference and assemblies.
What am I doing wrong?
The problem that I can see is that your character sets do not match. The unmanaged code returns the text as UTF-16, but your p/invoke specifies ANSI encoded text. Change the p/invoke to:
[DllImport("MyDLL.dll", CharSet = CharSet.Unicode)]
public static extern int BrowseForDirectory(
int allowOnline,
int allowPortable,
StringBuilder eDirectory,
ref int eDirLength,
StringBuilder mDirectory,
ref int mDirLength
);
I'm assuming that c_str_w() takes an 8 bit encoded string and returns a pointer to null-terminated array of wchar_t.

C# wrapper class for c++ lib dll

I am trying to create a class in c# to access the function in a c++ lib. The function in the c++ dll :
bool WriteReply(const unsigned char *reply, const unsigned long reply_length).
A sample of how its used in c++:-
unsigned short msg_id = 0x0000;
byte msg_body[] = {(byte)(GetTickCount()/0x100)}; // a random value for loopback data
// combine the message id and message body into an big msg
unsigned long msg_length = sizeof(msg_id)+sizeof(msg_body);
byte* big_msg = new byte[msg_length];
big_msg[0] = LOBYTE(msg_id);
big_msg[1] = HIBYTE(msg_id);
memcpy((void*)&big_msg[2], (void*)msg_body, sizeof(msg_body));
// send the big message
if (!big_dev.WriteReply(big_msg, msg_length))
{
//do something here
}
I can't seem to pass the function from c# to the dll (AccessViolationException). This is the command i've tried:-
byte[] bytearray = new byte[3] { 0x01, 0x02, 0x03 };
IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytearray.Length);
Marshal.Copy(bytearray, 0, unmanagedPointer, bytearray.Length);
bool writestatus = (bool)NativeMethods.WriteReply(unmanagedPointer, (uint)bytearray.Length);
and on the import side:-
[DllImport("dllname.dll", EntryPoint = "WriteReply")]
[return: MarshalAs(UnmanagedType.U1)]
internal static extern bool WriteReply(IntPtr msg, uint reply_length);
Please let me know where have i gone wrong?Thanks!
Assuming your C++ method uses the string and does not modify it...
Try this
__declspec(dllexport) bool __cdecl WriteReply(const unsigned char *reply, const unsigned long reply_length);
[DllImport("libfile.dll", EntryPoint = "WriteReply")]
private static extern bool WriteReplyExternal(
[MarshalAs(UnmanagedType.LPStr)] [Out] string replyString,
[Out] UInt32 replyLength);
Or better yet (since C strings are null-terminated and the buffer is readonly, so you don't have to worry about buffer overflow, the length parameter is redudant):
__declspec(dllexport) bool __cdecl WriteReply(const unsigned char *reply);
[DllImport("libfile.dll", EntryPoint = "WriteReply")]
private static extern bool WriteReplyExternal(
[MarshalAs(UnmanagedType.LPStr)] [Out] string replyString);
These will work if the method is not within a class, otherwise you will need to use the C++ mangled name as the entry point.
If your string contains characters outside the 1...127 ASCII range (e.g. non-English letters), you should use wchar_t instead of char in the C++ and LPWStr instead of LPStr in the marshalling.
Edit:
You need to wrap the private method with another method with a signature that is more appropriate for .NET e.g.
public void WriteReply(string message)
{
var result = WriteReplyExternal(message, message.Length);
if (result == false)
throw new ApplicationException("WriteReplay failed ...");
}
I think the latest addition of code provides a clue as to the real problem:
if (!big_dev.WriteReply(big_msg, msg_length))
This cannot work because WriteReply is an member function. You need to be calling a C style function rather than a C++ member function. The latter requires an instance (big_dev in the code sample).

How does one create structures for C# originally written in C++

I am working on an embedded ARM platform and wish to have control over some of the GPIO pins. I've experience in C and am a newcomer to C#. It seems that controlling low-level hardware is difficult from managed-code applications. I have Windows CE6.0 and .NET Compact Framework 2 running on my hardware.
I've found an example, written in C++ that would allow me access to GPIO port pins, however, I am struggling to implement the example in C#.
The following snippet shows how DeviceIOcontrol is used to control port pins:
const struct pio_desc hw_pio[] =
{
{"LED1", AT91C_PIN_PA(13), 0, PIO_DEFAULT, PIO_OUTPUT},
{"LED2", AT91C_PIN_PA(14), 0, PIO_DEFAULT, PIO_OUTPUT},
};
T_GPIOIOCTL_STATE * pSetState;
T_GPIOIOCTL_STATE * pGetState;
// Configure PIOs
bSuccessDevIOC = DeviceIoControl(hGPIO, IOCTL_GPIO_CONFIGURE, (LPBYTE*)hw_pio, sizeof(hw_pio), NULL, 0, NULL, NULL);
Other definitions:
struct pio_desc
{
const char *pin_name; /* Pin Name */
unsigned int pin_num; /* Pin number */
unsigned int dft_value; /* Default value for outputs */
unsigned char attribute;
enum pio_type type;
};
/* I/O type */
enum pio_type
{
PIO_PERIPH_A,
PIO_PERIPH_B,
PIO_INPUT,
PIO_OUTPUT,
PIO_UNDEFINED
};
/* I/O attributes */
#define PIO_DEFAULT (0 << 0)
#define PIO_PULLUP (1 << 0)
#define PIO_DEGLITCH (1 << 1)
#define PIO_OPENDRAIN (1 << 2)
The following is the C# definition of DeviceIOControl. The problem parameter is lpInBuffer.
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern bool DeviceIoControlCE(int hDevice,
int dwIoControlCode,
byte[] lpInBuffer,
int nInBufferSize,
byte[] lpOutBuffer,
int nOutBufferSize,
ref int lpBytesReturned,
IntPtr lpOverlapped);
The questions:
How does one create these equivalent structures in C#?
How does one pass these as a byte array (byte[] lpInBuffer) to the DeviceIOControl function?
Any assistance appreciated!
Use some interop decoration, for instance, a structure like following:
typedef struct
{
char Data[MAXCHARS];//assuming a #define MAXCHARS 15
int Values[MAXCHARS];
} StSomeData;
would look like following in C#:
[StructLayout(LayoutKind.Sequential)]
private struct StSomeData
{
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 15)]
public string Data;
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 15)]
public int[] Values;
}
And use it like: StSomeData[] array = new StSomeData[3];
Note that you can use IntPtr when dealing with pointers.
For instance your call to:
DeviceIoControl(hGPIO, IOCTL_GPIO_CONFIGURE, (LPBYTE*)hw_pio
, sizeof(hw_pio), NULL, 0, NULL, NULL);
may look something like following:
IntPtr ipByte;
Marshal.StructureToPtr(StLPByte, ipByte,false);
IntPtr ipConfig;
Marshal.StructureToPtr(StIOCTL_GPIO_CONFIGURE, ipConfig, false);
IntPtr iphGPIO;
Marshal.StructureToPtr(SthGPIO, iphGPIO, false);
bool bSuccessDevIOC = DeviceIoControl(iphGPIO
, ipConfig
, ipByte
, Marshal.SizeOf(typeof(StLPByte))
, IntPtr.Zero
, IntPtr.Zero
, IntPtr.Zero
, IntPtr.Zero);
Also, you can look into the usage of unsafe keyword and try put your code within unsafe blocks; this may be a dirty solution since this code wont be a managed code.
a byte[] is converted to LPBYTE automatically
a char* in c# is equivalent to unsigned short* in c++
a byte* in c# is a unsigned char* in c++
c#-enums behave similar enough to c++ enums. you may just write it
one important thing:
[StructLayout(LayoutKind.Sequential,Pack=1)]

Categories