Im making a C# managed extension dll for NPS witch looks like this:
public enum RADIUS_CODE
{
rcUnknown = 0,
rcAccessRequest = 1,
rcAccessAccept = 2,
rcAccessReject = 3,
rcAccountingRequest = 4,
rcAccountingResponse = 5,
rcAccessChallenge = 11,
rcDiscard = 256
}
public enum RADIUS_EXTENSION_POINT
{
repAuthentication,
repAuthorization
}
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate ref RADIUS_ATTRIBUTE_ARRAY GetRequestDelegate(ref RADIUS_EXTENSION_CONTROL_BLOCK ECB);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate ref RADIUS_ATTRIBUTE_ARRAY GetResponseDelegate(ref RADIUS_EXTENSION_CONTROL_BLOCK ECB, RADIUS_CODE rcResponseType);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate uint SetResponseTypeDelegate(ref RADIUS_EXTENSION_CONTROL_BLOCK ECB, RADIUS_CODE rcResponseType);
[StructLayout(LayoutKind.Sequential)]
public struct RADIUS_EXTENSION_CONTROL_BLOCK
{
public uint cbSize;
public uint dwVersion;
public RADIUS_EXTENSION_POINT repPoint;
public RADIUS_CODE rcRequestType;
public RADIUS_CODE rcResponseType;
public GetRequestDelegate GetRequest;
public GetResponseDelegate GetResponse;
public SetResponseTypeDelegate SetResponseType;
}
[DllExport("RadiusExtensionProcess2", CallingConvention = CallingConvention.Winapi)]
public static uint RadiusExtensionProcess2(ref RADIUS_EXTENSION_CONTROL_BLOCK pECB)
{
//var result = pECB.SetResponseType(ref pECB, RADIUS_CODE.rcAccessAccept);
return 0;
}
NPS passed a control block to the function that contains 5 fields and 3 pointers to functions, the fields map correctly but i cant get the functions working.
If I uncomment the line before the return NPS discard the request noting that the dll crashed, with the line commented out NPS processes the request successfully.
I'm well versed in assembly and C# but a newbie at C++ and managed/unmamaged interop.
I've tried all sorts of marshaling methods, calling conventions that i could find and stepping thru the disassembly but this is as far as I've gotten after a week and I'm out of ideas...
The documentation I've been going by can be found here
Some Progress
I had previously tried Marshal.GetDelegateForFunctionPointer as suggested by #RonBeyer with no success but it's working now, i think i was missing some
ref keywords at that time, so this seem to be working
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate ref RADIUS_ATTRIBUTE AttributeAtDelegate([In] ref RADIUS_ATTRIBUTE_ARRAY Array, [In] uint dwIndex);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate uint GetSizeDelegate([In] ref RADIUS_ATTRIBUTE_ARRAY Array);
[StructLayout(LayoutKind.Sequential)]
public struct RADIUS_ATTRIBUTE_ARRAY
{
public uint cbSize;
public IntPtr Add;
public IntPtr AttributeAt;
public IntPtr GetSize;
public IntPtr InsertAt;
public IntPtr RemoveAt;
public IntPtr SetAt;
}
[StructLayout(LayoutKind.Sequential)]
public struct RADIUS_ATTRIBUTE
{
public uint dwAttrType;
public RADIUS_DATA_TYPE fDataType;
public uint cbDataLength;
public IntPtr lpValue;
}
[StructLayout(LayoutKind.Sequential)]
public struct RADIUS_EXTENSION_CONTROL_BLOCK
{
public uint cbSize;
public uint dwVersion;
public RADIUS_EXTENSION_POINT repPoint;
public RADIUS_CODE rcRequestType;
public RADIUS_CODE rcResponseType;
public IntPtr GetRequest;
public IntPtr GetResponse;
public IntPtr SetResponseType;
}
[DllExport("RadiusExtensionProcess2", CallingConvention = CallingConvention.Winapi)]
public static uint RadiusExtensionProcess2(ref RADIUS_EXTENSION_CONTROL_BLOCK pECB)
{
GetRequestDelegate GetRequest = (GetRequestDelegate)Marshal.GetDelegateForFunctionPointer(pECB.GetRequest, typeof(GetRequestDelegate));
GetResponseDelegate GetResponse = (GetResponseDelegate)Marshal.GetDelegateForFunctionPointer(pECB.GetResponse, typeof(GetResponseDelegate));
SetResponseTypeDelegate SetResponseType = (SetResponseTypeDelegate)Marshal.GetDelegateForFunctionPointer(pECB.SetResponseType, typeof(SetResponseTypeDelegate));
uint SetResponseTypeResult = SetResponseType(ref pECB, RADIUS_CODE.rcAccessAccept); //SetResponseTypeResult is 0 and NPS sends a access granted message
RADIUS_ATTRIBUTE_ARRAY AttrArray = GetRequest(ref pECB);
return 0;
}
SetResponseType works without crashing the dll and GetRequest returns the expected value for cbSize and what look like valid pointers for the functions defined in the RADIUS_ATTRIBUTE_ARRAY struct.
So i proceeded to map AttributeAt and GetSize the same way but the data they return is nonsence.
I double and triple checked the definitions here and here but so far i haven't been able to get the proper data from these functions, since the dll dosen't crash and i dont get null back, i can only assume there is something wrong with how I'm passing the parameters to the functions
On that note, are the in/out keywords in the C++ function definitions just info for developers or do they effect how parameters are handled on the stack?
Related
I'm trying to return a struct from a C++ callback from C# and I get the error as described in the title.
I appreciate there is a lot of information, but I wanted to include too much rather than not enough.
I've tried returning the function as a structure, but then read it may be easier to return the function as an IntPtr and use Marshal.PtrtoStructure.
The function that I want to call is from a .dll that comes from a different source and the input is:
C++:
rVDACQ_Connect(int, tVDACQ_CallBackProc, void*, short*, int)
the function in the sample (C++) code returns a struct (rACQ_CallBackRec) which is as follows:
rACQ_CallBackRec = theApp.m_Intf.rVDACQ_Connect(cVDACQ_FBright, CALLBACK_Acquisition, this, m_FrmBuffer, 0);
//rACQ_CallBackRec is the struct type tVDACQ_CallBackRec (as described below)
With CALLBACK_Aquisition being:
extern "C" {
__declspec(dllexport) void _stdcall CALLBACK_Acquisition(tVDACQ_CallBackRec* AR)
{
tVDACQ_CallBackProc test;
((CPreviewDlg*)AR->rUserParam)->My_ACQ_CallBack(AR, test);
}
}
The layout of the struct is as follows:
typedef struct {
int rFlags, // combination of cVDACQ_Fxxxx
rType, // cVDACQ_ETxxx
rEvent, // cVDACQ_Exxx
rSocket; // 0:no relation to a 'socket'; otherwise socket's ID>0 (event's source ID)
TCHAR rMsg[256]; // message (trace, wrn, err)
int rFrameWidth, // full frame width
rFrameHeight; // full frame height
short* rFrameBuffer; // user supplied frame buffer "AFrameBuffer"
union {
int rCaptureRows; // # of received rows (for single frame acquisition)
int rCaptureFrames; // # of received full frames (for framegrabber)
};
int rCapturePercent; // received data in percents
void* rUserCallBackProc, // user supplied "ACallBackProc"
* rUserParam; // user supplied "AUserParam"
int rAborted; // 1: VDACQ_Abort -1:internally
void* rPacketData; // pointer to received packet; usually it is nil
int rFGControl; // frame-grabber's control flags
} tVDACQ_CallBackRec;
typedef void(_stdcall* tVDACQ_CallBackProc)(tVDACQ_CallBackRec*); //The Callback
C#
I've created the callback procedure in C#, as well as the struct and PInvoke:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void tVDACQ_CallBackProc(tVDACQ_CallBackRec AR);
[DllImport("C:\\Users\\jch\\source\\repos\\FlatPanelSensor\\x64\\Debug\\VADAV_AcqS.dll", EntryPoint = "CALLBACK_Acquisition", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern void CALLBACK_Acquisition(tVDACQ_CallBackRec AR);
[DllImport("C:\\Users\\jch\\source\\repos\\FlatPanelSensor\\FlatPanelSensor\\bin\\Debug\\VADAV_FGM_64.dll", EntryPoint = "VDACQ_Connect", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I4)]
public unsafe static extern IntPtr VDACQ_Connect(int i, [MarshalAs(UnmanagedType.FunctionPtr)] tVDACQ_CallBackProc proc, dynamic n, IntPtr[] buffer, int j);
Struct:
[StructLayout(LayoutKind.Sequential)]
public struct tVDACQ_CallBackRec
{
public int rFlags;
public int rType;
public int rEvent;
public int rSocket;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string rMsg;
public int rFrameWidth;
public int rFrameHeight;
public IntPtr rFrameBuffer;
public int rCaptureRows;
public int rCaptureFrames;
public int rCapturePercent;
public IntPtr rUserCallBackProc;
public IntPtr rUserParam;
public int rAborted;
public IntPtr rPacketData;
public int rFGControl;
}
CallBack:
tVDACQ_CallBackProc callBack =
(AR) =>
{
CALLBACK_Acquisition(AR); //Don't know how necessary this
is
};
Function call:
IntPtr work = VDACQ_Connect(0, callBack, this, m_FrmBuffer, 0);
//I know 'this' isn't defined as a void* in my DLLImport,
//but making it an IntPtr didn't work either so
//the only type I could think of was dynamic.
If I could return this as a IntPtr or, even better, the struct I defined in my C# code that would be great.
If you need anymore information then please let me know, as I think I included everything.
i'm new and i need your help resolving this issue.
I'm trying to create a simple debugger to understand how debuggers works and how is loaded exes in memory. I have already written the code which works as well, but now there is the problem: when i try to call WaitForDebugEvent (a kernel32 function) to get the debug event it works, in fact the debug_event variable is written, but this function clears all the variables in my application. So it clear also:
this (current form)
EventArgs (arguments of my form load function)
object sender (the object who called the function)
So i can't continue executing my app because all the vars were deleted. I wouldn't think this is a kernel32 or a Visual Studio bug...
This is the code:
(Structs and imports got from pInvoke.net and MSDN)
[DllImport("kernel32.dll")]
static extern bool DebugActiveProcess(uint dwProcessId);
[DllImport("kernel32.dll", EntryPoint = "WaitForDebugEvent")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool WaitForDebugEvent([In] ref DEBUG_EVENT lpDebugEvent, uint dwMilliseconds);
[DllImport("kernel32.dll")]
static extern bool ContinueDebugEvent(uint dwProcessId, uint dwThreadId, uint dwContinueStatus);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DebugActiveProcessStop([In] int Pid);
public struct DEBUG_EVENT
{
public int dwDebugEventCode;
public int dwProcessId;
public int dwThreadId;
public struct u
{
public EXCEPTION_DEBUG_INFO Exception;
public CREATE_THREAD_DEBUG_INFO CreateThread;
public CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
public EXIT_THREAD_DEBUG_INFO ExitThread;
public EXIT_PROCESS_DEBUG_INFO ExitProcess;
public LOAD_DLL_DEBUG_INFO LoadDll;
public UNLOAD_DLL_DEBUG_INFO UnloadDll;
public OUTPUT_DEBUG_STRING_INFO DebugString;
public RIP_INFO RipInfo;
};
};
[StructLayout(LayoutKind.Sequential)]
public struct EXCEPTION_DEBUG_INFO
{
public EXCEPTION_RECORD ExceptionRecord;
public uint dwFirstChance;
}
[StructLayout(LayoutKind.Sequential)]
public struct EXCEPTION_RECORD
{
public uint ExceptionCode;
public uint ExceptionFlags;
public IntPtr ExceptionRecord;
public IntPtr ExceptionAddress;
public uint NumberParameters;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 15, ArraySubType = UnmanagedType.U4)]
public uint[] ExceptionInformation;
}
public delegate uint PTHREAD_START_ROUTINE(IntPtr lpThreadParameter);
[StructLayout(LayoutKind.Sequential)]
public struct CREATE_THREAD_DEBUG_INFO
{
public IntPtr hThread;
public IntPtr lpThreadLocalBase;
public PTHREAD_START_ROUTINE lpStartAddress;
}
//public delegate uint PTHREAD_START_ROUTINE(IntPtr lpThreadParameter);
[StructLayout(LayoutKind.Sequential)]
public struct CREATE_PROCESS_DEBUG_INFO
{
public IntPtr hFile;
public IntPtr hProcess;
public IntPtr hThread;
public IntPtr lpBaseOfImage;
public uint dwDebugInfoFileOffset;
public uint nDebugInfoSize;
public IntPtr lpThreadLocalBase;
public PTHREAD_START_ROUTINE lpStartAddress;
public IntPtr lpImageName;
public ushort fUnicode;
}
[StructLayout(LayoutKind.Sequential)]
public struct EXIT_THREAD_DEBUG_INFO
{
public uint dwExitCode;
}
[StructLayout(LayoutKind.Sequential)]
public struct EXIT_PROCESS_DEBUG_INFO
{
public uint dwExitCode;
}
[StructLayout(LayoutKind.Sequential)]
public struct LOAD_DLL_DEBUG_INFO
{
public IntPtr hFile;
public IntPtr lpBaseOfDll;
public uint dwDebugInfoFileOffset;
public uint nDebugInfoSize;
public IntPtr lpImageName;
public ushort fUnicode;
}
[StructLayout(LayoutKind.Sequential)]
public struct UNLOAD_DLL_DEBUG_INFO
{
public IntPtr lpBaseOfDll;
}
[StructLayout(LayoutKind.Sequential)]
public struct OUTPUT_DEBUG_STRING_INFO
{
[MarshalAs(UnmanagedType.LPStr)]
public string lpDebugStringData;
public ushort fUnicode;
public ushort nDebugStringLength;
}
[StructLayout(LayoutKind.Sequential)]
public struct RIP_INFO
{
public uint dwError;
public uint dwType;
}
And the main loop of debugger:
private void Form1_Load(object sender, EventArgs e)
{
DebugActiveProcess((uint)Process.GetProcessesByName("notepad")[0].Id);
DEBUG_EVENT debug_event = new DEBUG_EVENT();
CONTEXT context = new CONTEXT();
context.ContextFlags = (uint)CONTEXT_FLAGS.CONTEXT_ALL;
while (true)
{
unchecked
{
if (WaitForDebugEvent(ref debug_event, (uint)double.PositiveInfinity))
{
...
ContinueDebugEvent((uint)debug_event.dwProcessId, (uint)debug_event.dwThreadId, (uint)0x10002);
}
}
}
}
What can i do? Thanks in advance...
EDIT:
I have rewritten the code in C++ to see if the problem there was there too. But there was no issue... so i think the problem lays only in C#. This is the code in C++:
IMPORTS:
#include <windows.h>
#include <tlhelp32.h>
#include <msclr\marshal_cppstd.h>
CODE:
private: System::Void DebuggerForm_Load(System::Object^ sender, System::EventArgs^ e) {
std::wstring processName = msclr::interop::marshal_as<std::wstring, String^>("myExe.exe");
DWORD id = getProcessId(processName);
if (id == 0) std::exit(0);
DebugActiveProcess(id);
DEBUG_EVENT debug_event = { 0 };
while (true)
{
if (WaitForDebugEvent(&debug_event, INFINITE)) {
// TODO
ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, DBG_CONTINUE);
}
}
}
DWORD getProcessId(const std::wstring& processName)
{
PROCESSENTRY32 processInfo;
processInfo.dwSize = sizeof(processInfo);
HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (processesSnapshot == INVALID_HANDLE_VALUE)
return 0;
Process32First(processesSnapshot, &processInfo);
if (!processName.compare(processInfo.szExeFile))
{
CloseHandle(processesSnapshot);
return processInfo.th32ProcessID;
}
while (Process32Next(processesSnapshot, &processInfo))
{
if (!processName.compare(processInfo.szExeFile))
{
CloseHandle(processesSnapshot);
return processInfo.th32ProcessID;
}
}
CloseHandle(processesSnapshot);
return 0;
}
First of all, You should enable the SE_DEBUG_NAME privilege on the targeted process:
(SE_DEBUG_NAME = "SeDebugPrivilege")
Use OpenProcessToken with TOKEN_ADJUST_PRIVILEGES and TOKEN_QUERY access flags.
Use LookupPrivilegeValue to retrieve the LUID of the SE_DEBUG_NAME privilege name.
Use AdjustTokenPrivileges to enable the SE_DEBUG_NAME privilege.
CloseHandle
For the 3rd step you need a TOKEN_PRIVILEGES structure, where the PrivilegesCount field must set to 1. Also You must set the first(zero index) item of the Privilege field (Luid: see 2nd step, Attributes: SE_PRIVILEGE_ENABLED)
The DEBUG_EVENT structure:
The u field of the structure is an union, it means that it only contains one of the listed structs. Try to use LayoutKind.Explicit and decorate every field with the attribute FieldOffset to define the correct offset inside the structure. Try this:
[StructLayout(LayoutKind.Explicit)]
public struct DEBUG_EVENT
{
[FieldOffset(0)]
public int dwDebugEventCode;
[FieldOffset(4)]
public int dwProcessId;
[FieldOffset(8)]
public int dwThreadId;
[FieldOffset(12)]
[StructLayout(LayoutKind.Explicit)]
public struct u {
[FieldOffset(0)]
public EXCEPTION_DEBUG_INFO Exception;
[FieldOffset(0)]
public CREATE_THREAD_DEBUG_INFO CreateThread;
[FieldOffset(0)]
public CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
[FieldOffset(0)]
public EXIT_THREAD_DEBUG_INFO ExitThread;
[FieldOffset(0)]
public EXIT_PROCESS_DEBUG_INFO ExitProcess;
[FieldOffset(0)]
public LOAD_DLL_DEBUG_INFO LoadDll;
[FieldOffset(0)]
public UNLOAD_DLL_DEBUG_INFO UnloadDll;
[FieldOffset(0)]
public OUTPUT_DEBUG_STRING_INFO DebugString;
[FieldOffset(0)]
public RIP_INFO RipInfo;
}
};
I want to use API from Omron V4KU, the documentation described like this :
Original c# code :
const string DllLocation = #"..\..\Libs\Omron\OMCR.dll";
[DllImport(DllLocation)]
public static extern LPCOMCR OMCR_OpenDevice(string lpcszDevice, LPCOMCR_OPTION lpcOption);
public void Start()
{
var lpcOption = new LPCOMCR_OPTION();
var result = OMCR_OpenDevice(null, lpcOption); // error method's type signature is not pinvoke compatible
}
[StructLayout(LayoutKind.Sequential)]
public struct LPCOMCR
{
public string lpcszDevice;
public IntPtr hDevice;
public uint lpcDevice;
}
[StructLayout(LayoutKind.Sequential)]
public struct LPCOMCR_OPTION
{
public uint dwReserved0;
public uint dwReserved1;
public uint dwReserved2;
public uint dwReserved3;
}
if I missed or wrong in writing code?
sorry, my english is bad. thanks for help.
Start by defining the union structure correctly:
// OMCR_OPTION.COM
[StructLayout(LayoutKind.Sequential)]
public struct OmcrCom
{
public IntPtr Reserved0;
public uint BaudRate;
public uint Reserved1;
public uint Reserved2;
public uint Reserved3;
public IntPtr Reserved1;
public IntPtr Reserved2;
}
// OMCR_OPTION.USB
[StructLayout(LayoutKind.Sequential)]
public struct OmcrUsb
{
public uint Reserved0;
public uint Reserved1;
public uint Reserved2;
public uint Reserved3;
}
// OMCR_OPTION (union of COM and USB)
[StructLayout(LayoutKind.Explicit)]
public struct OmcrOptions
{
[FieldOffset(0)]
public OmcrCom Com;
[FieldOffset(0)]
public OmcrUsb Usb;
}
// OMCR
[StructLayout(LayoutKind.Sequential)]
public struct OmcrDevice
{
public string Device;
public IntPtr DeviceHandle;
public IntPtr DevicePointer;
}
[DllImport(dllName: DllLocation, EntryPoint = "OMCR_OpenDevice"]
public static extern IntPtr OmcrOpenDevice(string type, ref OmcrOptions options);
And then call the method, something like:
var options = new OmcrOptions();
options.Com.BaudRate = 115200; // or whatever you need to set
var type = "COM"; // is this USB/COM? not sure
OmcrDevice device;
var devicePtr = OmcrOpenDevice(type, ref options);
if (devicePtr == IntPtr.Zero)
device = (OmcrDevice)Marshal.PtrToStructure(devicePtr, typeof(OmcrDevice));
Well, for one, the documentation is asking you to pass LPCOMCR_OPTION as a pointer - you're passing it as a value. Using ref should help. There's another problem, though, and that's the return value - again, you're trying to interpret it as a value, while the docs say it's a pointer. However, this is a lot trickier than the first error - as far as I'm aware, your only options are using a C++/CLI interop library, or expecting IntPtr as a return value. In any case, you need to handle proper deallocation of the memory you get this way.
You need to do it like this:
[StructLayout(LayoutKind.Sequential)]
public struct OMCR
{
[MarshalAs(UnmanagedType.LPStr)]
public string lpcszDevice;
public IntPtr hDevice;
public IntPtr lpcDevice;
}
[StructLayout(LayoutKind.Sequential)]
public struct OMCR_OPTION
{
public uint dwReserved0;
public uint dwReserved1;
public uint dwReserved2;
public uint dwReserved3;
}
[DllImport(DllLocation, CallingConvention = CallingConvention.???,
SetLastError = true)]
public static extern IntPtr OMCR_OpenDevice(string lpcszDevice,
ref OMCR_OPTION lpcOption);
You need to replace CallingConvention.??? with the appropriate calling convention. We cannot tell from the question what that is. You will have to find out by reading the header file.
The return value is a pointer to OMCR. You need to hold on to this pointer and pass it to OMCR_CloseDevice when you are finished with it.
In order to obtain an OMCR value you would do the following:
OMCR_OPTION Option = new OMCR_OPTION(); // not sure how to initialize this
IntPtr DevicePtr = OMCR_OpenDevice(DeviceType, ref Option);
if (DevicePtr == IntPtr.Zero)
throw new Win32Exception();
OMCR Device = (OMCR)Marshal.PtrToStructure(DevicePtr, typeof(OMCR));
I'm trying to get back this struct from a native C library through P/Invoke:
struct ndb_mgm_cluster_state
{
int no_of_nodes;
struct ndb_mgm_node_state node_states[1];
};
where ndb_mgm_node_state is:
struct ndb_mgm_node_state {
int node_id;
enum ndb_mgm_node_type node_type;
enum ndb_mgm_node_status node_status;
int start_phase;
int dynamic_id;
int node_group;
int version;
int connect_count;
char connect_address[sizeof("000.000.000.000")+1 ];
int mysql_version;
};
The method's signature is:
ndb_mgm_cluster_state* WINAPI wrap_ndb_mgm_get_status(HANDLE handle);
All of this is provided by a 3rd party library, so nothing can be changed.
In C# i have the follow definitions:
[DllImport("Ndb_CWrapper.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr wrap_ndb_mgm_get_status(IntPtr handle);
the structures are:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ndb_mgm_cluster_state {
public int no_of_nodes;
public IntPtr node_states;
};
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ndb_mgm_node_state
{
public int node_id;
public ndb_mgm_node_type node_type;
public ndb_mgm_node_status node_status;
public int start_phase;
public int dynamic_id;
public int node_group;
public int version;
public int connect_count;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 17)]
public string connect_address;
public int mysql_version;
};
I tried to unmarshal the results without success (i recieve an oddly error (not an Exception) Fatal error execution, can be a CLR bug or a miscall P/invoke.
Obviously the reason is a problem in my P/Invoke call
I tried in this way:
First of all i unmarshalled the ndb_mgm_cluster_state structure:
var res=(ndb_mgm_cluster_state)Marshal.PtrToStructure(
tmpPtr, typeof(ndb_mgm_cluster_state));
where IntPtr is the result of the native call.
Up to this step everything "seems" to be done right, but when i try to unmarshall the node_states i get the error:
ndb_mgm_node_state tmpNode = (ndb_mgm_node_state)Marshal.PtrToStructure(
status.node_states, typeof(ndb_mgm_node_state));
What can be the problem? I supposed is something related to the strange declaration of ndb_mgm_cluster_state because is defined an array of 1 element, but it contain several elements. (the number of elements is in no_of_nodes)
WORKAROUND:
The only way to let everything work i found is to change a bit the signature, in this way:
ndb_mgm_node_state* WINAPI wrap_ndb_mgm_get_status(HANDLE handle,int* length);
in C#:
[DllImport("Ndb_CWrapper.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr wrap_ndb_mgm_get_status(IntPtr handle,out int length);
where length contains no_of_nodes
the unmarshall will be in this way:
IntPtr tmpPtr = wrap_ndb_mgm_get_status(raw,out length);
ndb_mgm_cluster_state tmpRes = new ndb_mgm_cluster_state();
tmpRes.no_of_nodes = length;
tmpRes.node_states = new ndb_mgm_node_state[length];
int step=0;
for (int i = 0; i < tmpRes.no_of_nodes; i++)
{
tmpRes.node_states[i] = (ndb_mgm_node_state)Marshal.PtrToStructure(
tmpPtr+(step*i), typeof(ndb_mgm_node_state));
step = Marshal.SizeOf(tmpRes.node_states[i]);
}
I know the step calculation is odd at the monent, but its not that the point.
there is no way to let the thing work returning directly the ndb_mgm_cluster_state struct instead of do all of this?
I have mapped C++ function (from WLanapi.dll):
DWORD WINAPI WlanHostedNetworkQueryStatus(
_In_ HANDLE hClientHandle,
_Out_ PWLAN_HOSTED_NETWORK_STATUS *ppWlanHostedNetworkStatus,
_Reserved_ PVOID pvReserved
);
To the following C# code:
[DllImport("Wlanapi.dll", SetLastError = true)]
static extern UInt32 WlanHostedNetworkQueryStatus(
[In] IntPtr hClientHandle,
[Out] out _WLAN_HOSTED_NETWORK_STATUS ppWlanHostedNetworkStatus,
[In, Out] IntPtr pvReserved
);
I have also mapped all the structs and enums required and other stuff (for example to get the clientHandle pointer and to start the hosted network).
The _WLAN_HOSTED_NETWORK_STATUS is mapped like so:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_STATUS
{
public _WLAN_HOSTED_NETWORK_STATE HostedNetworkState;
public Guid IPDeviceID;
public _DOT11_MAC_ADDRESS wlanHostedNetworkBSSID;
public _DOT11_PHY_TYPE dot11PhyType;
public UInt32 ulChannelFrequency;
public UInt32 dwNumberOfPeers;
public _WLAN_HOSTED_NETWORK_PEER_STATE[] PeerList;
}
Now when executing that function, I am not sure how to use ppWlanHostedNetworkStatus correctly and such. The function returns ERROR_SUCCESS (0) which means I have called it and passed parameters correctly as far as I am concerned:
_WLAN_HOSTED_NETWORK_STATUS netStatus = new _WLAN_HOSTED_NETWORK_STATUS();
WlanHostedNetworkQueryStatus(clientHandle, out netStatus, IntPtr.Zero);
But while querying ppWlanHostedNetworkStatus for values (like state of the network, or number of connected peers) I am getting just some strange long integers (I would say memory addresses, but I am not sure), for example call:
netStatus.HostedNetworkState.ToString();
Returns
11465720
HostedNetworkState is an enumeration defined like:
public enum _WLAN_HOSTED_NETWORK_STATE
{
wlan_hosted_network_unavailable,
wlan_hosted_network_idle,
wlan_hosted_network_active
}
So .toString() should have returned one of these strings from the enumeration, right?
I am pretty sure it is something to do with pointers etc, since in the documentation of the _WLAN_HOSTED_NETWORK_STATUS ( MS documentation ) it says that before the call to that function, the ppWlanHostedNetworkStatus should be a NULL, and that it is itself a pointer to the structure...
How can I debug it? I am coding in C#, VS 2012...
Thanks for your help.
-----EDIT-----
I further tried to map the function with IntPtr as an argument, pass IntPtr.Zero and Marshal.PtrToStruct, but I am getting AccessViolationException when trying to do that...
[DllImport("Wlanapi.dll", SetLastError = true)]
static extern UInt32 WlanHostedNetworkQueryStatus(
[In] IntPtr hClientHandle,
[Out] out IntPtr ppWlanHostedNetworkStatus,
[In, Out] IntPtr pvReserved
);
And then:
IntPtr ppStatus = IntPtr.Zero;
WlanHostedNetworkQueryStatus(clientHandle, out ppStatus, IntPtr.Zero);
_WLAN_HOSTED_NETWORK_STATUS netStatus = (_WLAN_HOSTED_NETWORK_STATUS)Marshal.PtrToStructure(ppStatus, typeof(_WLAN_HOSTED_NETWORK_STATUS));
------EDIT 2-------
Following advice from Fermat2357, I have uncommented part of the struct to map, and change the following to count for a pointer to pointer:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_STATUS
{
public _WLAN_HOSTED_NETWORK_STATE HostedNetworkState;
public Guid IPDeviceID;
public _DOT11_MAC_ADDRESS wlanHostedNetworkBSSID;
public _DOT11_PHY_TYPE dot11PhyType;
public UInt32 ulChannelFrequency;
public UInt32 dwNumberOfPeers;
//public _WLAN_HOSTED_NETWORK_PEER_STATE[] PeerList;
}
I call it like this:
IntPtr ppStatus = IntPtr.Zero;
WlanHostedNetworkQueryStatus(clientHandle, out ppStatus, IntPtr.Zero);
IntPtr ppStatus2 = new IntPtr(ppStatus.ToInt32());
_WLAN_HOSTED_NETWORK_STATUS stat = (_WLAN_HOSTED_NETWORK_STATUS)Marshal.PtrToStructure(ppStatus2, typeof(_WLAN_HOSTED_NETWORK_STATUS));
netStatus = stat.HostedNetworkState.ToString();
This finally gives me the correct network status (active after starting it)... Now I have to find a way to marshal that dynamic array...
Thanks so far for help Fermat2357
Your mapping is incorrect. Take a look at the definition of the API function WlanHostedNetworkQueryStatus.
DWORD WINAPI WlanHostedNetworkQueryStatus(
_In_ HANDLE hClientHandle,
_Out_ PWLAN_HOSTED_NETWORK_STATUS *ppWlanHostedNetworkStatus,
_Reserved_ PVOID pvReserved
);
Please take care, the parameter ppWlanHostedNetworkStatus is a pointer to a pointer to a WLAN_HOSTED_NETWORK_STATUS structure.
Take a deeper look in the documentation of the function you will find
ppWlanHostedNetworkStatus [out]
On input, this parameter must be NULL.
On output, this parameter receives a pointer to the current status of the wireless Hosted Network, if the call to the WlanHostedNetworkQueryStatus function succeeds. The current status is returned in a WLAN_HOSTED_NETWORK_STATUS structure.
If you give in a NULL pointer (as described in the documentation) the underlying API will allocate a buffer for you holding the structure and initializing the pointer to this buffer. Later dont forget to free it by a call to WlanFreeMemory. Otherwise you will have a resource leak here.
However the documentation seems to be not 100% complete for this function. In my tests (Win7 32Bit) the API will not allocate memory for you if you intitalize the pointer to a enough big memory buffer. In this case a later call to WlanFreeMemory seems not be necessary. But in this case its hard to guess how much memory you need for the following WLAN_HOSTED_NETWORK_PEER_STATE structures. For that reason this seems not to be useable anyway.
Here is the C code I used for testing
#include <Wlanapi.h>
int _tmain(int argc, _TCHAR* argv[])
{
DWORD dwRes;
HANDLE hHandle;
DWORD negotiatedVersion;
dwRes = WlanOpenHandle(1, NULL, &negotiatedVersion, &hHandle);
if (ERROR_SUCCESS == dwRes)
{
PWLAN_HOSTED_NETWORK_STATUS pStatus = NULL;
dwRes = WlanHostedNetworkQueryStatus(hHandle, &pStatus, NULL);
if (ERROR_SUCCESS == dwRes)
{
if (wlan_hosted_network_unavailable != pStatus->HostedNetworkState)
{
// Do something with the result
}
WlanFreeMemory(pStatus);
}
else
{
// handle Error
}
WlanCloseHandle(hHandle, NULL);
}
else
{
// handle Error
}
return 0;
}
If you want to make your sample work you need to modify the way you marshal the structure.
Edit
To marshal correctly you can try the following:
...
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_STATUS
{
public _WLAN_HOSTED_NETWORK_STATE HostedNetworkState;
public Guid IPDeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string wlanHostedNetworkBSSID;
public _DOT11_PHY_TYPE dot11PhyType;
public UInt32 ulChannelFrequency;
public UInt32 dwNumberOfPeers;
public _WLAN_HOSTED_NETWORK_PEER_STATE PeerList;
}
...
IntPtr ptr = new IntPtr();
uint hostedNetworkQueryStatusSuccess = WlanHostedNetworkQueryStatus(clientHandle, out ptr, IntPtr.Zero);
if (openHandleSuccess == 0)
{
var netStat = (_WLAN_HOSTED_NETWORK_STATUS)Marshal.PtrToStructure(ptr, typeof(_WLAN_HOSTED_NETWORK_STATUS));
Console.WriteLine(netStat.HostedNetworkState);
if (netStat.HostedNetworkState != _WLAN_HOSTED_NETWORK_STATE.wlan_hosted_network_unavailable)
{
IntPtr offset = Marshal.OffsetOf(typeof(_WLAN_HOSTED_NETWORK_STATUS), "PeerList");
for (int i = 0; i < netStat.dwNumberOfPeers; i++)
{
var peer = (_WLAN_HOSTED_NETWORK_PEER_STATE)Marshal.PtrToStructure(
new IntPtr(ptr.ToInt64() + offset.ToInt64()),
typeof(_WLAN_HOSTED_NETWORK_PEER_STATE));
System.Console.WriteLine(peer.PeerMacAddress);
offset += Marshal.SizeOf(peer);
}
}
}
The Idea: Im using a structure here that I can be sure it is marshaled correctly all the time. Later with the knowledge of the dwNumberOfPeers member (if it is valid) I get all the inner structures step by step.
EDIT: One thing to try is a trick I use to help debugging - replace your struct with a byte array, and see what you pull back (ok, in this case a uint array):
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_STATUS
{
[MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.U4, SizeConst = 48)]
public uint[] scratch;
}
I get very strange values when I run this locally, however - the first (10x4) = 40 bytes are zeroed out:
0 0 0 0
0 0 0 0
0 0 4D454D4C 28
16DCD870 0 787F6447 80000020
...omitted...
Try this set of P/Invoke declarations:
(done in LINQPad, so replace "Dump" method accordingly)
void Main()
{
IntPtr clientHandle;
int negotiatedVersion;
if(WlanOpenHandle(2, IntPtr.Zero, out negotiatedVersion, out clientHandle) != 0)
{
throw new InvalidOperationException("Could not open handle");
}
Console.WriteLine("Negotiated version:{0}", negotiatedVersion);
IntPtr pNetStatus = IntPtr.Zero;
if(WlanHostedNetworkQueryStatus(clientHandle, out pNetStatus, IntPtr.Zero) != 0)
{
throw new InvalidOperationException("Could not query network status");
}
var netStatus = (WLAN_HOSTED_NETWORK_STATUS)Marshal.PtrToStructure(pNetStatus, typeof(WLAN_HOSTED_NETWORK_STATUS));
Console.WriteLine(netStatus.PeerList[0]);
WlanFreeMemory(pNetStatus);
WlanCloseHandle(clientHandle, IntPtr.Zero);
}
[DllImport("Wlanapi.dll", SetLastError = true)]
[return:MarshalAs(UnmanagedType.Bool)]
public static extern bool WlanOpenHandle(
[In] int dwClientVersion,
IntPtr pReserved,
[Out] out int pdwNegotiatedVersion,
[Out] out IntPtr phClientHandle
);
[DllImport("Wlanapi.dll", SetLastError = true)]
[return:MarshalAs(UnmanagedType.Bool)]
public static extern bool WlanCloseHandle(
[In] IntPtr hClientHandle,
IntPtr pReserved
);
[DllImport("Wlanapi.dll", SetLastError = true)]
static extern UInt32 WlanHostedNetworkQueryStatus(
[In] IntPtr hClientHandle,
[Out] out IntPtr ppWlanHostedNetworkStatus,
[In, Out] IntPtr pvReserved
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_STATUS
{
public _WLAN_HOSTED_NETWORK_STATE HostedNetworkState;
public Guid IPDeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=6)]
public string wlanHostedNetworkBSSID;
public _DOT11_PHY_TYPE dot11PhyType;
public UInt32 ulChannelFrequency;
public UInt32 dwNumberOfPeers;
public IntPtr PeerList;
}
public enum _WLAN_HOSTED_NETWORK_STATE
{
wlan_hosted_network_unavailable,
wlan_hosted_network_idle,
wlan_hosted_network_active
}
public enum _DOT11_PHY_TYPE : uint
{
dot11_phy_type_unknown = 0,
dot11_phy_type_any = 0,
dot11_phy_type_fhss = 1,
dot11_phy_type_dsss = 2,
dot11_phy_type_irbaseband = 3,
dot11_phy_type_ofdm = 4,
dot11_phy_type_hrdsss = 5,
dot11_phy_type_erp = 6,
dot11_phy_type_ht = 7,
dot11_phy_type_IHV_start = 0x80000000,
dot11_phy_type_IHV_end = 0xffffffff
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_PEER_STATE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=6)]
public string PeerMacAddress;
_WLAN_HOSTED_NETWORK_PEER_AUTH_STATE PeerAuthState;
}
public enum _WLAN_HOSTED_NETWORK_PEER_AUTH_STATE
{
wlan_hosted_network_peer_state_invalid,
wlan_hosted_network_peer_state_authenticated
}