How to Marshal C pointer to C# array of struct - c#

I am trying to turn a pointer from a c dll into its equivelant C# struct array.
C Code
RECORD locked[MAX+1]; //MAX is a constant
typedef struct
{
State state; //enum
unsigned long allocated;
unsigned long lastUsed;
unsigned int useCount;
} RECORD;
API RECORD* __stdcall GetLocks( char* password )
{
if(strcmp(password, secret) == 0)
return locked;
else
return 0;
}
C# Code
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] // Pretty sure CharSet isnt actually needed here
public struct RECORD
{
public State state;
public UInt32 allocated;
public UInt32 lastUsed;
public UInt16 useCount;
}
[DllImport("gatewayapi.dll", CharSet = CharSet.Ansi)] // or here
static extern IntPtr GetLocks(string password);
public RECORD[] GetLocks(string password)
{
RECORD[] recs = new RECORD[MAX+1];
recs =(RECORD[])Marshal.PtrToStructure( GetLocks(password), typeof(RECORD[]));
if (recs.Length == 0)
{
throw new Exception();
}
return recs;
}
The above unfortunetly returns me a MissingMethodException -> No parameterless constructor defined for this object.
So in all im 100% new to Marshalling and would appreciate some advice on how to turn the pointer I receive from C into the actual C# struct array it represents.
Thanks

Given the originally posted C code, here is the answer I came up with that doesn't require compiling the code in unsafe mode:
[DllImport("gatewayapi.dll", CharSet = CharSet.Ansi)]
static extern IntPtr AMTgetLocks(string password);
public RECORD[] GetLocks(string password)
{
var channels = new RECORD[MAXCHANS + 1];
try
{
var c = AMTgetLocks(password);
var crSize = Marshal.SizeOf(typeof(RECORD));
for (int i = 0; i < MAXCHANS + 1; i++)
{
channels[i] = (CHANNELRECORD)Marshal.PtrToStructure(c, typeof(RECORD));
c = new IntPtr(c.ToInt64() + crSize);
}
}
catch (Exception)
{
throw new Exception();
}
if (channels.Length == 0)
{
throw new Exception();
}
return channels;
}

Related

Marshal array of struct into Ptr

I am calling a C library from a C# code. The function I am calling take as parameter a struct containing arrays of struct :
struct Example1Struct
{
char* a;
uint16_t b;
AnotherStruct* c;
}
c here is an array of pointer to AnotherStruct.
the struct in my C# code look like this
public struct Example1Struct
{
public IntPtr StationName;//is char*
public UInt16 IdCode;
public IntPtr AnotherStruct; //array of struct AnotherStruct
}
public static IntPtr MarshalToPointer(object data)
{
Type valueType = data.GetType();
IntPtr buf = IntPtr.Zero;
if (valueType.IsArray)
{
if (data is char[])
{
var d = data as char[];
buf = Marshal.AllocHGlobal(Marshal.SizeOf(d.GetType().GetElementType()) * d.Length);
}
else if (data is char[,])
{
var d = data as char[,];
buf = Marshal.AllocHGlobal(Marshal.SizeOf(d.GetType().GetElementType()) * d.Length);
}
else
{
buf = Marshal.AllocHGlobal(Marshal.SizeOf(data.GetType().GetElementType()) * count);
long LongPtr = buf.ToInt64(); // Must work both on x86 and x64
for (int I = 0; I < data.Lenght; I++)
{
IntPtr RectPtr = new IntPtr(LongPtr);
Marshal.StructureToPtr(data[I], RectPtr, false); // You do not need to erase struct in this case
LongPtr += Marshal.SizeOf(typeof(Rect));
}
}
return buf;
}
else
buf = Marshal.AllocHGlobal(Marshal.SizeOf(data));
Marshal.StructureToPtr(data, buf, false);
return buf;
}
my problem here is that I cannot cast data (who is an array of AnotherStruct) to object[] , neither in IEnumerable. So I cannot access to data[I] and don't have data.Lenght
Any idea ?
Usually I'd recommend using the MarshalAs attribute rather than writing manual marshalling code. It looks like:
public struct Example1Struct
{
public IntPtr StationName;//is char*
public UInt16 IdCode;
public IntPtr AnotherStruct; //array of struct AnotherStruct
}
Could be:
public struct Example1Struct
{
[MarshalAs(UnmanagedType.LPStr)]
public string StationName;
public UInt16 IdCode;
[MarshalAs(UnmanagedType.LPArray)]
public AnotherStruct[] OtherStructs;
}
And the marshaller should do the right thing for you when you pass it to unmanaged code.
You can get the length of the array like this:
if (data is Array a)
Console.WriteLine(a.Length);
Arrays in c# always derive from Array, so you can cast it to that.
But if possible in your real code, I'd recommend Damien's answer

Unexpected results while marshaling list of struct

I could not find out what I'm doing wrong. I think I'm doing everything by the numbers, but it looks like I'm missing something.
On the managed side:
public override List<TimerPair> GetNativeTimers()
{
var listPtr = HgrDll.GetNativeTimers(out var size, out var ptrShift);
if (listPtr == IntPtr.Zero)
throw new NullReferenceException("sounds like the list"
+ " pointer was not given");
var timerPairList = new List<TimerPair>(size);
var structSize = Marshal.SizeOf(typeof(TimerPairStruct2));
for (var i = 0; i < size; i++)
{
var o = Marshal.PtrToStructure<TimerPairStruct2>(listPtr).ToTimerPair();
timerPairList.Add(o);
listPtr += structSize;
}
return timerPairList;
}
The target struct (used for intermediate compute) and class:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal struct TimerPairStruct2 : ITimerPairStruct
{
public long Time;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)]
public string Descr;
public TimerPair ToTimerPair() => new TimerPair(Descr, Time);
}
public class TimerPair
{
public string Descr { get; set; }
public TimeSpan Time { get; set; }
public TimerPair(string descr, long time)
{
Descr = descr;
Time = new TimeSpan(0,0,0,0, (int)((double)time/1000000));
}
}
For the Pinvoke part:
[DllImport("DwarfHomograph.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr GetNativeTimers(out int size, out int ptrShift);
and now on the native side:
DwarfHgr* DwarfAsset = nullptr;
extern "C" __declspec(dllexport) list<TimerPair>* GetNativeTimers(
int* size,
int* ptrShift)
{
return DwarfAsset->GetNativeTimers(size, ptrShift);
}
the used method from the DwarfHgr class:
list<TimerPair>* DwarfHgr::GetNativeTimers(int* size, int* ptrShift)
{
TimerPairsList = Sw.AsTimerPairs();
*size = static_cast<int>(TimerPairsList.size());
*ptrShift = sizeof(TimerPair);
return &TimerPairsList;
}
and the member is list<TimerPair> TimerPairsList;
The struct I'm marshaling is:
//#pragma pack(push,8)
struct __declspec(dllexport) TimerPair final
{
long long Time{}; // ns
char Descr[64]{};
};
//#pragma pack(pop)
I'm having unit tests in both native and managed sides; the expected result looks like (native):
ImageA processing: 22.0704ms
ImageB processing: 22.0448ms
MatFullA.copyTo: 1.87452ms
MatToSparse: 1.93482ms
calcOpticalFlowFarneback: 1115.48ms
create flowSparse image: 10.1416ms
But I have this (managed):
°óŠqÉ: 1,964,675.000000ms
q$Fý: 140,725,780.000000ms
xr$Fý: 140,725,780.000000ms
: 549,755.000000ms
à¡CUÉ: 1,964,675.000000ms
: 0.000000ms

Passing Array of Structures from C#(.NET Core) to C++(unamnaged)

So I've read the documentation and countless examples online how to marshal array of structures. I've marshalled array of int's, I've marshalled structures, but now I'm completely stuck and can't get it to work no matter what I've try. Been stuck on it for over a day now.
Structure/class, tried as both
[StructLayout(LayoutKind.Sequential,CharSet = CharSet.Unicode)]
public class SaveDetails
{
[MarshalAs(UnmanagedType.LPWStr)]
public string Log;
public FILETIME FileTime;
[MarshalAs(UnmanagedType.Bool)]
public bool Saved;
}
Pinvoke and call delegate
public class LogSaveFiles : IDisposable
{
[UnmanagedFunctionPointer(CallingConvention.Winapi,CharSet = CharSet.Unicode)]
private delegate Status DLogSaveFiles([ In, Out] SaveDetails[] logsToSave, string destinationPath);
private static DLogSaveFiles _dLogSaveFiles;
private IntPtr PLogSaveFiles { get; set; }
public bool LogSaveFilesAvailable => PLogSaveFiles != IntPtr.Zero;
public LogSaveFiles(Importer importer)
{
if (importer.dllLibraryPtr!= IntPtr.Zero)
{
PLogSaveFiles = Importer.GetProcAddress(importer.dllLibrary, "LogSaveFiles");
}
}
public Status SaveFiles(SaveDetails[] logsToSave,string destinationPath)
{
Status result = Status.FunctionNotAvailable;
if (LogSaveFilesAvailable)
{
_dLogSaveFiles = (DLogSaveFiles)Marshal.GetDelegateForFunctionPointer(PLogSaveFiles, typeof(DLogSaveFiles));
result = _dLogSaveFiles(logsToSave, destinationPath);
}
return result;
}
public void Dispose()
{
}
}
Call
private void SaveLogs()
{
var logsToSave = new[]{
new SaveDetails{
FileTime = new FILETIME {dwHighDateTime = 3,dwLowDateTime = 5},
Log = LogTypes.logDeviceLog,
Saved = true},
new SaveDetails{
FileTime = new FILETIME {dwHighDateTime = 1,dwLowDateTime = 2},
Log = LogTypes.logDeviceLog,
Saved = false}
};
var pathToSave = "C:\\Logs";
_logSaveFiles.SaveFiles(logsToSave, pathToSave);
}
c++ exposed call
typedef struct _LOG_SAVE_DETAILS
{
LPTSTR szLog;
FILETIME fromFileTime;
BOOL bSaved;
} LOG_SAVE_DETAILS, *PLOG_SAVE_DETAILS;
/* Function definitions */
ULY_STATUS _API LogSaveFiles (PLOG_SAVE_DETAILS ppLogs [],
LPCTSTR szDestinationPath);
Path to destination gets passed properly, but array of structures never goes through resulting in access violation when trying to access it. At first I thought it was issue with LPTSTR not going through properly but I've implemented other calls with it on its own and succeeded marshalling it through.
I've read everything on https://learn.microsoft.com/en-us/dotnet/framework/interop/marshaling-data-with-platform-invoke , it all indicates that my approach is correct, but it doesn't work.
Any help is appreciated.
Simple solution: C side change PLOG_SAVE_DETAILS ppLogs [] to LOG_SAVE_DETAILS ppLogs [], then C#-side change public class SaveDetails to public struct SaveDetails.
Marshaling array of objects seems to be difficult (I wasn't able to do it). Marshaling array of structs works. An alternative is to do the marshaling manually, but it is a pain.
The "pain" of manual marshaling (only modified lines of code):
[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)]
private delegate Status DLogSaveFiles(IntPtr[] logsToSave, string destinationPath);
and then
public Status SaveFiles(SaveDetails[] logsToSave, string destinationPath)
{
Status result = Status.FunctionNotAvailable;
if (LogSaveFilesAvailable)
{
if (_dLogSaveFiles == null)
{
_dLogSaveFiles = (DLogSaveFiles)Marshal.GetDelegateForFunctionPointer(PLogSaveFiles, typeof(DLogSaveFiles));
}
int size = Marshal.SizeOf(typeof(SaveDetails));
IntPtr basePtr = IntPtr.Zero;
IntPtr[] ptrs = new IntPtr[logsToSave.Length + 1];
try
{
basePtr = Marshal.AllocHGlobal(size * logsToSave.Length);
for (int i = 0; i < logsToSave.Length; i++)
{
ptrs[i] = IntPtr.Add(basePtr, (i * size));
Marshal.StructureToPtr(logsToSave[i], ptrs[i], false);
}
result = _dLogSaveFiles(ptrs, destinationPath);
}
finally
{
if (basePtr != IntPtr.Zero)
{
for (int i = 0; i < logsToSave.Length; i++)
{
if (ptrs[i] != IntPtr.Zero)
{
Marshal.DestroyStructure(ptrs[i], typeof(SaveDetails));
}
}
Marshal.FreeHGlobal(basePtr);
}
}
}
return result;
}
Important: this is a marshaler C#->C++. The C++ mustn't modify the received array in any way or there will be a memory leak.

PInvoke Passing structure with a nested structure array C#

I have a structure in C# which needs to be passed to a C++ DLL.
typedef struct
{
TDate fDate;
double fRate;
} TRatePt;
typedef struct _TCurve2
{
int fNumItems; /* Number of TRatePts in fArray */
TRatePt *fArray;
TDate fBaseDate;
} TCurve2;
The following is the structure I've created in C#.
[StructLayout(LayoutKind.Sequential), Serializable]
public struct TRatePt
{
public int fDate;
public double fRate;
}
[StructLayout(LayoutKind.Sequential), Serializable]
public struct TCurve2
{
public int fNumItems;
public IntPtr fArray;
public int fBaseDate;
}
I have a simple method in the C DLL.
EXPORT int TestMethodForZC(TCurve2 *discCurve)
{
printf("\n\nIn TestMethodForZC\n");
printf("discCurve->fBaseDate = %d\n\n\n",discCurve->fBaseDate );
return 0;
}
And I'm using PInvoke to call it using the following:
[DllImport("clibrary.dll", EntryPoint = "TestMethodForZC", , CallingConvention = CallingConvention.Cdecl)]
private static extern int _TestMethodForZC(ref TCurve2 discCurve);
Marshaling of TRatePt:
TRatePt[] items = new TRatePt[2];
items[0].fDate = 200;
items[1].fDate = 300;
items[0].fRate = 0.2d;
items[1].fRate = 0.3d;
TCurve2 tc2 = new TCurve2() { fBaseDate = 12000, fNumItems = 2 };
tc2.fNumItems = items.Length;
tc2.fArray = Marshal.AllocHGlobal(items.Length * Marshal.SizeOf(typeof(TRatePt)));
IntPtr item = tc2.fArray;
for (int i = 0; i < items.Length; i++)
{
Marshal.StructureToPtr(items[i], item, false);
item = new IntPtr(item.ToInt32() + Marshal.SizeOf(typeof(TRatePt)));
}
_TestMethodForZC(ref tc2);
I'm marshaling the TRatePt manually and passing the IntPtr to the method but it is printing junk values. I tried Int32 and Int64 on the Marshaling but it makes no difference. Am I doing something wrong? Any suggestions?
PS: I tried looking up for this question in stackoverflow and I found one with quite a bit of discussion but none of the suggestions worked for me.

Setting IE Proxy by C#

Hello i want to set IE proxy using a C# program as WebProxy class have get proxy method.But there is no method to set it!
Here are some alternatives found by Googling:
1- GlobalProxySelection
This is from http://www.hccp.org/csharp-http-proxy.html
Sample Code:
System.Net.Uri proxyURI = new System.Net.Uri("http://64.202.165.130:3128");
System.Net.GlobalProxySelection.Select = new System.Net.WebProxy(proxyURI);
2- Another discussion on StackOverflow: Programmatically Set Browser Proxy Settings in C#
Read it if you want the proxy only for your app.
For global change, it suggests looking at: http://msdn.microsoft.com/en-us/library/aa384113.aspx
Sample Code:
WINHTTP_PROXY_INFO proxyInfo;
// Allocate memory for string members.
proxyInfo.lpszProxy = new WCHAR[25];
proxyInfo.lpszProxyBypass = new WCHAR[25];
// Set the members of the proxy info structure.
proxyInfo.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
swprintf_s(proxyInfo.lpszProxy, 25, L"proxy_server");
swprintf_s(proxyInfo.lpszProxyBypass, 25, L"<local>");
// Set the default proxy configuration.
if (WinHttpSetDefaultProxyConfiguration( &proxyInfo ))
printf("Proxy Configuration Set.\n");
// Free memory allocated to the strings.
delete [] proxyInfo.lpszProxy;
delete [] proxyInfo.lpszProxyBypass;
3- Using Native Code
This is from http://huddledmasses.org/setting-windows-internet-connection-proxy-from-c/
Sample Code:
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace PoshHttp
{
public class Proxies
{
public static bool UnsetProxy()
{
return SetProxy(null, null);
}
public static bool SetProxy(string strProxy)
{
return SetProxy(strProxy, null);
}
public static bool SetProxy(string strProxy, string exceptions)
{
InternetPerConnOptionList list = new InternetPerConnOptionList();
int optionCount = string.IsNullOrEmpty(strProxy) ? 1 : (string.IsNullOrEmpty(exceptions) ? 2 : 3);
InternetConnectionOption[] options = new InternetConnectionOption[optionCount];
// USE a proxy server ...
options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
options[0].m_Value.m_Int = (int)((optionCount < 2) ? PerConnFlags.PROXY_TYPE_DIRECT : (PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY));
// use THIS proxy server
if (optionCount > 1)
{
options[1].m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_SERVER;
options[1].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(strProxy);
// except for these addresses ...
if (optionCount > 2)
{
options[2].m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_BYPASS;
options[2].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(exceptions);
}
}
// default stuff
list.dwSize = Marshal.SizeOf(list);
list.szConnection = IntPtr.Zero;
list.dwOptionCount = options.Length;
list.dwOptionError = 0;
int optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
// make a pointer out of all that ...
IntPtr optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length);
// copy the array over into that spot in memory ...
for (int i = 0; i < options.Length; ++i)
{
IntPtr opt = new IntPtr(optionsPtr.ToInt32() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
list.options = optionsPtr;
// and then make a pointer out of the whole list
IntPtr ipcoListPtr = Marshal.AllocCoTaskMem((Int32)list.dwSize);
Marshal.StructureToPtr(list, ipcoListPtr, false);
// and finally, call the API method!
int returnvalue = NativeMethods.InternetSetOption(IntPtr.Zero,
InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
ipcoListPtr, list.dwSize) ? -1 : 0;
if (returnvalue == 0)
{ // get the error codes, they might be helpful
returnvalue = Marshal.GetLastWin32Error();
}
// FREE the data ASAP
Marshal.FreeCoTaskMem(optionsPtr);
Marshal.FreeCoTaskMem(ipcoListPtr);
if (returnvalue > 0)
{ // throw the error codes, they might be helpful
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return (returnvalue < 0);
}
}
#region WinInet structures
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct InternetPerConnOptionList
{
public int dwSize; // size of the INTERNET_PER_CONN_OPTION_LIST struct
public IntPtr szConnection; // connection name to set/query options
public int dwOptionCount; // number of options to set/query
public int dwOptionError; // on error, which option failed
//[MarshalAs(UnmanagedType.)]
public IntPtr options;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct InternetConnectionOption
{
static readonly int Size;
public PerConnOption m_Option;
public InternetConnectionOptionValue m_Value;
static InternetConnectionOption()
{
InternetConnectionOption.Size = Marshal.SizeOf(typeof(InternetConnectionOption));
}
// Nested Types
[StructLayout(LayoutKind.Explicit)]
public struct InternetConnectionOptionValue
{
// Fields
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME m_FileTime;
[FieldOffset(0)]
public int m_Int;
[FieldOffset(0)]
public IntPtr m_StringPtr;
}
}
#endregion
#region WinInet enums
//
// options manifests for Internet{Query|Set}Option
//
public enum InternetOption : uint
{
INTERNET_OPTION_PER_CONNECTION_OPTION = 75
}
//
// Options used in INTERNET_PER_CONN_OPTON struct
//
public enum PerConnOption
{
INTERNET_PER_CONN_FLAGS = 1, // Sets or retrieves the connection type. The Value member will contain one or more of the values from PerConnFlags
INTERNET_PER_CONN_PROXY_SERVER = 2, // Sets or retrieves a string containing the proxy servers.
INTERNET_PER_CONN_PROXY_BYPASS = 3, // Sets or retrieves a string containing the URLs that do not use the proxy server.
INTERNET_PER_CONN_AUTOCONFIG_URL = 4//, // Sets or retrieves a string containing the URL to the automatic configuration script.
}
//
// PER_CONN_FLAGS
//
[Flags]
public enum PerConnFlags
{
PROXY_TYPE_DIRECT = 0x00000001, // direct to net
PROXY_TYPE_PROXY = 0x00000002, // via named proxy
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
#endregion
internal static class NativeMethods
{
[DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InternetSetOption(IntPtr hInternet, InternetOption dwOption, IntPtr lpBuffer, int dwBufferLength);
}
}
Please check the links themselves for details and complete parts of the solutions.

Categories