Marshalling Struct with Array of Struct Member - c#

I'm trying to marshal the MIB_TCPTABLE_OWNER_MODULE struct from a P/Invoked' call into GetExtendedTcpTable, defined in iphlpapi.dll.
My P/Invoke signature is defined as this:
[DllImport("iphlpapi.dll", SetLastError = true)]
private static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwSize, bool sort, int ipVersion, int tableClass, int reserved);
According to the documentation on MSDN (and looking through the header files), this should set the pTcpTable parameter to the address of a MIB_TCPTABLE_OWNER_MODULE structure, who has a member which is an array of MIB_TCPROW_OWNER_MODULE structures. From tcpmib.h:
typedef struct _MIB_TCPTABLE_OWNER_MODULE
{
DWORD dwNumEntries;
MIB_TCPROW_OWNER_MODULE table[ANY_SIZE];
} MIB_TCPTABLE_OWNER_MODULE, *PMIB_TCPTABLE_OWNER_MODULE;
ANY_SIZE is defined to be 1.
Here is my problem; I've defined the MIB_TCPTABLE_OWNER_MODULE and MIB_TCPROW_OWNER_MODULE structs in C# like so:
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPTABLE_OWNER_MODULE
{
public uint dwNumEntries;
MIB_TCPROW_OWNER_MODULE table;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPROW_OWNER_MODULE
{
public uint dwState;
public uint dwLocalAddr;
public uint dwLocalPort;
public uint dwRemoteAddr;
public uint dwRemotePort;
public uint dwOwningPid;
public ulong liCreateTimestamp;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TCPIP_OWNING_MODULE_SIZE)]
public ulong[] OwningModuleInfo;
}
Since I won't know the size of the returned MIB_TCPTABLE_OWNER_MODULE's table member at declaration, my plan was to increment the IntPtr and use Marshal.PtrToStructure to extract each array member from the table member.
The call to Marshal.PtrToStructure returns (no memory violation exceptions), but I wind up with garbage values in the struct members. Here is my complete code:
[DllImport("iphlpapi.dll", SetLastError = true)]
private static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwSize, bool sort, int ipVersion, int tableClass, int reserved);
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPROW_OWNER_MODULE
{
public uint dwState;
public uint dwLocalAddr;
public uint dwLocalPort;
public uint dwRemoteAddr;
public uint dwRemotePort;
public uint dwOwningPid;
public ulong liCreateTimestamp;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TCPIP_OWNING_MODULE_SIZE)]
public ulong[] OwningModuleInfo;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPTABLE_OWNER_MODULE
{
public uint dwNumEntries;
MIB_TCPROW_OWNER_MODULE table;
}
private const int TCPIP_OWNING_MODULE_SIZE = 16;
private const int AF_INET = 2;
private const int TCP_TABLE_OWNER_MODULE_ALL = 8;
public static void GetConnectionDetails()
{
var bufferSize = 0;
var ret = GetExtendedTcpTable(IntPtr.Zero, ref bufferSize, true, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0);
var tableBuffer = Marshal.AllocHGlobal(bufferSize);
try
{
ret = GetExtendedTcpTable(tableBuffer, ref bufferSize, true, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0);
if (ret != 0)
throw new Exception("Oh noes!");
var convertedTable = (MIB_TCPTABLE_OWNER_MODULE)Marshal.PtrToStructure(tableBuffer, typeof (MIB_TCPTABLE_OWNER_MODULE));
var finalTable = new MIB_TCPROW_OWNER_MODULE[convertedTable.dwNumEntries];
var rowPtr = (IntPtr) ((long) tableBuffer + Marshal.SizeOf(convertedTable.dwNumEntries));
for (int i = 0; i < convertedTable.dwNumEntries; i++)
{
var row = (MIB_TCPROW_OWNER_MODULE)Marshal.PtrToStructure(rowPtr, typeof (MIB_TCPROW_OWNER_MODULE));
finalTable[i] = row;
rowPtr = (IntPtr) ((long) rowPtr + Marshal.SizeOf(row)); // Move to the next entry
}
foreach (var entry in finalTable)
{
// do something with each entry
Console.WriteLine(entry.dwState);
Console.WriteLine(entry.dwRemoteAddr);
}
}
finally
{
Marshal.FreeHGlobal(tableBuffer);
}
}
Comparing memory between this and an unmanaged version (that works properly), I do see some differences in the memory of the marshaled struct that I can't account for; there are a few bytes different.
Any assistance is most appreciated!

Consider this struct:
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPTABLE_OWNER_MODULE
{
public uint dwNumEntries;
MIB_TCPROW_OWNER_MODULE table;
}
You are assuming that the offset of table is equal to the size of dwNumEntries. But you are forgetting about alignment. Since the largest type in MIB_TCPROW_OWNER_MODULE is 8 bytes wide, that type has alignment of 8. Which means that in order for it to be aligned it must be placed at an offset that is a multiple of 8. And hence there is padding between dwNumEntries and table. You need to allow for that padding.
So, at this point:
var rowPtr = (IntPtr) ((long) tableBuffer +
Marshal.SizeOf(convertedTable.dwNumEntries));
you add 4 to the address held in tableBuffer. You actually need to add 8. You should use Marshal.OffsetOf to calculate the offset:
var rowPtr = (IntPtr)((long)tableBuffer +
(long)Marshal.OffsetOf(typeof(MIB_TCPTABLE_OWNER_MODULE), "table"));

Related

How to use GetRawInputData from C#?

I'm trying to acquire keyboard input using GetRawInputData and C#. I don't seem to be able to actually read the RAWINPUTDATA fields, no matter what I use for the definition of the call.
I have tried this:
In response to the WM_INPUT message, I try this...
void ProcessRawInput(Message message)
{
uint dwSize = 0;
uint retval;
User32.RAWINPUT rawInput;
retval = User32.GetRawInputData(message.LParam, User32.RAWINPUT.RID_HEADER, out rawInput, ref dwSize, Marshal.SizeOf<User32.RAWINPUTHEADER>());
if (rawInput.header.dwType == (int)User32.RIM_TYPE.RIM_TYPEKEYBOARD)
{
}
}
It seems to read some data into the header structure, but nothing in the RAWINPUTDATA structure.
I tried something like:
if (rawInput.header.dwType == (int)User32.RIM_TYPE.RIM_TYPEKEYBOARD)
{
// set dwSize = Marshal.SizeOf<User32.RAWINPUT>()
retval = User32.GetRawInputData(message.LParam, User32.RAWINPUT.RID_INPUT, out rawInput, ref dwSize, Marshal.SizeOf<User32.RAWINPUTHEADER>());
}
But the only difference seems to be a huge value returned as retval.
I cannot seem to find a useable example (in C#).
My definitions and structures look like this:
[DllImport("User32.dll", SetLastError = true)]
internal static extern uint GetRawInputData(IntPtr hRawInput, uint uiCommand, out RAWINPUT pData, ref uint pcbSize, int cbSizeHeader);
[StructLayout(LayoutKind.Sequential)]
public struct RAWINPUT
{
public const int RID_INPUT = 0x10000003,
RID_HEADER = 0x10000005;
public RAWINPUTHEADER header;
public RAWINPUTDATA data;
}
[StructLayout(LayoutKind.Sequential)]
public struct RAWINPUTHEADER
{
public int dwType;
public int dwSize;
public IntPtr hDevice;
public IntPtr wParam;
}
[StructLayout(LayoutKind.Explicit)]
public struct RAWINPUTDATA
{
[FieldOffset(0)]
public RAWMOUSE Mouse;
[FieldOffset(0)]
public RAWKEYBOARD Keyboard;
[FieldOffset(0)]
public RAWHID HID;
}
[StructLayout(LayoutKind.Sequential)]
public struct RAWKEYBOARD
{
public ushort MakeCode;
public ushort Flags;
public ushort Reserved;
public ushort VKey;
public uint Message;
public ulong ExtraInformation;
}

C# SendInput() always returns 0 in Console Application

I have developed a simple Console application to poll an Xbox Controller using xinput. I would like to use the values obtained from one of the thumbsticks to move the mouse. I am able to get the x and y values from the thumbstick, but when I use those values to SendInput() (using the User32.dll), the mouse does not move and the return value is 0.
According to Microsoft, "If the function returns zero, the input was already blocked by another thread."
How do I find the other thread that is blocking it? It is just a simple Console Application (exe) started by Visual Studio that prints the x and y values to the screen and attempts to move the mouse.
long x = controller.x; // values from the controller
long y = controller.y; // these are checked and do contain numbers
INPUT mouseMoveInput = new INPUT();
mouseMoveInput.type = 0; // mouse
mouseMoveInput.mi.dx = x;
mouseMoveInput.mi.dy = y;
mouseMoveInput.mi.mouseData = 0;
mouseMoveInput.mi.dwFlags = MOUSEEVENTF_MOVE;
var result = SendInput(1, ref mouseMoveInput, Marshal.SizeOf(new INPUT());
// result always returns 0
Am I missing something? Should this work?
Here are declarations:
[StructLayout(LayoutKind.Explicit)]
public struct MOUSEINPUT
{
[FieldOffset(0)]
public long X;
[FieldOffset(8)]
public long Y;
[FieldOffset(16)]
public uint MouseData;
[FieldOffset(20)]
public uint Flags;
[FieldOffset(24)]
public uint Time;
[FieldOffset(28)]
public IntPtr ExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
internal struct KEYBOARDINPUT
{
public ushort Vk;
public ushort Scan;
public uint Flags;
public uint Time;
public IntPtr ExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
internal struct HARDWAREINPUT
{
public uint Msg;
public ushort ParamL;
public ushort ParamH;
}
[StructLayout(LayoutKind.Explicit)]
public struct INPUT
{
[FieldOffset(0)]
public uint type;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public KEYBOARDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
}
UPDATE: using mouse-event does work, but this function is deprecated. Is there a problem with using it anyway since it works?
There is something odd I'm getting with the struct sizes:
Size of tagINPUT: 40
Size of mouseMoveInput: 40
Size of MOUSEINPUT: 32
Size of uint: 4
But if tagINPUT consists of MOUSEINPUT and uint then shouldn't it's size be 36?
The 2nd parameter of SendInput should be a pointer to an array, not a ref parameter, and especially not a ref directly to the struct.
I would also use explicit layout only for the struct that actually needs it, and let the rest be sequential. It's easier.
This code works for me:
const int INPUT_MOUSE = 0;
const int MOUSEEVENTF_MOVE = 0x0001;
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint numberOfInputs, [MarshalAs(UnmanagedType.LPArray)] INPUT[] inputs, int sizeOfInputStructure);
void Main()
{
INPUT mouseMoveInput = new INPUT();
mouseMoveInput.type = INPUT_MOUSE;
mouseMoveInput.mi.dx = 10;
mouseMoveInput.mi.dy = 10;
mouseMoveInput.mi.mouseData = 0;
mouseMoveInput.mi.dwFlags = MOUSEEVENTF_MOVE;
var result = SendInput(1, new INPUT[] { mouseMoveInput}, Marshal.SizeOf(mouseMoveInput));
if(result == 0) {
throw new Win32Exception();
}
}
[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct HARDWAREINPUT
{
public uint Msg;
public ushort ParamL;
public ushort ParamH;
}
[StructLayout(LayoutKind.Explicit)]
public struct INPUT
{
[FieldOffset(0)]
public uint type;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public KEYBDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
}

C# generate Self Signed Cert with Subject Alternative Name?

We have an app that generates a Self Signed Cert but now with Chrome 58 we need to add the Subject Alternative Name. The Cert is generated using C# but invoking the CertCreateSelfSignCertificate function in win32. So far all the examples I am finding are not passing the extensions param and I'm finding it hard to create an extension to pass to generate the SAN.
Note I will be cleaning this up once I get it working
This is what I am using to create an Entry and then the Extension:
CERT_ALT_NAME_ENTRY entry = new CERT_ALT_NAME_ENTRY {
dwAltNameChoice = AlternativeNameType.Dns, // 3
Name = Marshal.StringToHGlobalUni("127.0.0.1")
};
IntPtr entryBlob Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY)));
var pvStructInfo = new CERT_ALT_NAME_INFO { cAltEntry = 1, rgAltEntry = entryBlob };
IntPtr pvEncoded = IntPtr.Zero;
int pcbEncoded = 0;
var status = InvokeMethods.CryptEncodeObjectEx(
CertEncodingType.X509_ASN_ENCODING | CertEncodingType.PKCS_7_ASN_ENCODING, // 1 | 0x10000
new IntPtr(12),
ref pvStructInfo,
EncodeObjectFlags.CRYPT_ENCODE_ALLOC_FLAG, // 0x8000
IntPtr.Zero,
ref pvEncoded,
ref pcbEncoded);
Marshal.FreeHGlobal(entryBlob);
if (!status)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var extension = new CERT_EXTENSION
{
ExtensionOid = OidSubjectAltName, //2.5.29.17
IsCritical = false,
Value = new CRYPTOAPI_BLOB
{
Length = (uint)pcbEncoded,
Data = pvEncoded
}
};
var result = new CertExtensions
{
cExtension = 1,
rgExtension = extension
};
Structs Used
internal struct CertExtensions
{
public uint cExtension;
public CERT_EXTENSION rgExtension;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct CRYPTOAPI_BLOB
{
public uint Length;
public IntPtr Data;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal class CERT_EXTENSION
{
[MarshalAs(UnmanagedType.LPWStr)]
public string ExtensionOid;
public bool IsCritical;
public CRYPTOAPI_BLOB Value;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct CERT_ALT_NAME_INFO
{
/// DWORD->unsigned int
public uint cAltEntry;
public IntPtr rgAltEntry;
}
[StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
internal struct CERT_ALT_NAME_ENTRY
{
public AlternativeNameType dwAltNameChoice;
public IntPtr Name;
}
Full Code - Pieced together from a few examples I found
private static CertExtensions CreateExtensions(IList<CERT_ALT_NAME_ENTRY> items)
{
IntPtr itemBlob = Marshal.AllocHGlobal(items.Count * Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY)));
for (int i = 0; i < items.Count; i++)
{
var offset = (IntPtr)((long)itemBlob + i * Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY)));
Marshal.StructureToPtr(items[i], offset, false);
}
var pvStructInfo = new CERT_ALT_NAME_INFO { cAltEntry = (uint)items.Count, rgAltEntry = itemBlob };
IntPtr pvEncoded = IntPtr.Zero;
int pcbEncoded = 0;
var status = InvokeMethods.CryptEncodeObjectEx(
CertEncodingType.X509_ASN_ENCODING | CertEncodingType.PKCS_7_ASN_ENCODING,
new IntPtr(12),
ref pvStructInfo,
EncodeObjectFlags.CRYPT_ENCODE_ALLOC_FLAG,
IntPtr.Zero,
ref pvEncoded,
ref pcbEncoded);
Marshal.FreeHGlobal(itemBlob);
if (!status)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var extension = new CERT_EXTENSION
{
ExtensionOid = OidSubjectAltName,
IsCritical = false,
Value = new CRYPTOAPI_BLOB
{
Length = (uint)pcbEncoded,
Data = pvEncoded
}
};
var result = new CertExtensions
{
cExtension = 1,
rgExtension = extension
};
return result;
}
Well, this going to be a hell of C#/C++ interop and is hard to understand without knowing how pointers, structs and C-like arrays work in C++ and how marshalong works in interop.
You have incorrectly defined CERT_ALT_NAME_ENTRY structure. It used union in C++ definition. When translating unions to C# they must be aligned to the largest struct size in union (which is CRYPTOAPI_BLOB and which is 8 bytes) and plus other field size: 8 + 4 = 12 bytes. Your struct signature is only 8 bytes.
rgExtension member in CERT_EXTENSIONS structure doesn't accept single CERT_EXTENSION struct, actually it is a pointer to an array of CERT_EXTENSION structs. This means that rgExtension member must be defined as IntPtr.
Solution:
Drop your entire code and use examples below.
Correct struct signature definitions:
using System;
using System.Runtime.InteropServices;
namespace TestApp {
static class Wincrypt {
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPTOAPI_BLOB {
public UInt32 cbData;
public IntPtr pbData;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_EXTENSION {
[MarshalAs(UnmanagedType.LPStr)]
public String pszObjId;
public Boolean fCritical;
public CRYPTOAPI_BLOB Value;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_EXTENSIONS {
public UInt32 cExtension;
public IntPtr rgExtension;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_ALT_NAME_INFO {
public UInt32 cAltEntry;
public IntPtr rgAltEntry;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_ALT_NAME_ENTRY {
public UInt32 dwAltNameChoice;
// since there is no direct translation from C-like unions in C#
// make additional struct to represent union options.
public CERT_ALT_NAME_UNION Value;
}
// create mapping to dwAltNameChoice
public const UInt32 CERT_ALT_NAME_OTHER_NAME = 1;
public const UInt32 CERT_ALT_NAME_RFC822_NAME = 2;
public const UInt32 CERT_ALT_NAME_DNS_NAME = 3;
public const UInt32 CERT_ALT_NAME_X400_ADDRESS = 4;
public const UInt32 CERT_ALT_NAME_DIRECTORY_NAME = 5;
public const UInt32 CERT_ALT_NAME_EDI_PARTY_NAME = 6;
public const UInt32 CERT_ALT_NAME_URL = 7;
public const UInt32 CERT_ALT_NAME_IP_ADDRESS = 8;
public const UInt32 CERT_ALT_NAME_REGISTERED_ID = 9;
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto)]
public struct CERT_ALT_NAME_UNION {
[FieldOffset(0)]
public IntPtr pOtherName;
[FieldOffset(0)]
public IntPtr pwszRfc822Name;
[FieldOffset(0)]
public IntPtr pwszDNSName;
[FieldOffset(0)]
public CRYPTOAPI_BLOB DirectoryName;
[FieldOffset(0)]
public IntPtr pwszURL;
[FieldOffset(0)]
public IntPtr IPAddress;
[FieldOffset(0)]
public IntPtr pszRegisteredID;
}
// not really used in this scenario, but is necessary when want to add
// UPN alt name, for example.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_OTHER_NAME {
[MarshalAs(UnmanagedType.LPStr)]
public String pszObjId;
public CRYPTOAPI_BLOB Value;
}
}
}
CryptEncodeObject signature (haven't tried Ex version):
using System;
using System.Runtime.InteropServices;
namespace TestApp {
static class Crypt32 {
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean CryptEncodeObject(
[In] UInt32 CertEncodingType,
[In] UInt32 lpszStructType,
[In, Out]ref Wincrypt.CERT_ALT_NAME_INFO pvStructInfo,
[Out] Byte[] pbEncoded,
[In, Out] ref UInt32 cbEncoded);
}
}
and the whole story with comments:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace TestApp {
class Program {
static void Main(String[] args) {
//return;
// suppose we want to add three alternative DNS names to SAN extension
String[] dnsNames = { "contoso.com", "www.contoso.com", "mail.contoso.com" };
// calculate size of CERT_ALT_NAME_ENTRY structure. Since it is C-like
// struct, use Marshal.SizeOf(), not C# sizeof().
var altEntrySize = Marshal.SizeOf(typeof(Wincrypt.CERT_ALT_NAME_ENTRY));
// create CERT_ALT_NAME_INFO structure and set initial data:
// cAltEntry -- number of alt names in the extension
// rgAltEntry -- starting pointer in unmanaged memory to an array of alt names
// the size is calculated as: CERT_ALT_NAME_ENTRY size * alt name count
var altInfo = new Wincrypt.CERT_ALT_NAME_INFO {
cAltEntry = (UInt32)dnsNames.Length,
rgAltEntry = Marshal.AllocHGlobal(altEntrySize * dnsNames.Length)
};
// now create CERT_ALT_NAME_ENTRY for each alt name and copy structure to
// a pointer allocated in altInfo structure with a shift.
// Create a loop to save some coding
for (Int32 i = 0; i < dnsNames.Length; i++) {
var altEntry = new Wincrypt.CERT_ALT_NAME_ENTRY {
dwAltNameChoice = Wincrypt.CERT_ALT_NAME_DNS_NAME,
// use Uni, because the pwszDNSName is defined as LPWStr (unicode)
Value = { pwszDNSName = Marshal.StringToHGlobalUni(dnsNames[i]) },
};
// copy alt name entry to altInfo.rgAltEntry at the specified index.
// In unmanaged memory you have to calculate shift based on managed
// index and structure size
Marshal.StructureToPtr(altEntry, altInfo.rgAltEntry + i * altEntrySize, false);
}
// encode CERT_ALT_NAME_INFO to ASN.1 DER byte array
UInt32 pcbEncoded = 0;
if (Crypt32.CryptEncodeObject(1, 12, ref altInfo, null, ref pcbEncoded)) {
Byte[] encodedSANvalue = new Byte[pcbEncoded];
Crypt32.CryptEncodeObject(1, 12, ref altInfo, encodedSANvalue, ref pcbEncoded);
// create certificate extension array:
var extensions = new Wincrypt.CERT_EXTENSIONS {
cExtension = 1,
rgExtension = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Wincrypt.CERT_EXTENSION)))
};
// create SAN extension:
var san = new Wincrypt.CERT_EXTENSION {
fCritical = false,
pszObjId = "2.5.29.17",
Value = { cbData = (UInt32)encodedSANvalue.Length, pbData = Marshal.AllocHGlobal(encodedSANvalue.Length) }
};
// copy SAN bytes to SAN extension:
Marshal.Copy(encodedSANvalue,0,san.Value.pbData, encodedSANvalue.Length);
// copy CERT_EXTENSION structure to extensions:
Marshal.StructureToPtr(san, extensions.rgExtension, false);
// use 'extensions' variable in CertCreateSelfSignCertificate call.
} else {
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
One note: the provided code do not release unmanaged resources. You have to release them after calling CertCreateSelfSignCertificate function.

C# method's type signature is not pinvoke compatible

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

How to marshall an array of structs?

using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
const int SystemPowerInformation = 11;
const uint STATUS_SUCCESS = 0;
[StructLayout(LayoutKind.Sequential)]
struct PROCESSOR_POWER_INFORMATION
{
public uint Number;
public uint MaxMhz;
public uint CurrentMhz;
public uint MhzLimit;
public uint MaxIdleState;
public uint CurrentIdleState;
}
[DllImport("powrprof.dll")]
static extern uint CallNtPowerInformation(
int InformationLevel,
IntPtr lpInputBuffer,
int nInputBufferSize,
[MarshalAs(UnmanagedType.LPArray)]
out byte[] lpOutputBuffer,
int nOutputBufferSize
);
static void Main(string[] args)
{
byte[] buffer = new byte[4 * Marshal.SizeOf(typeof(PROCESSOR_POWER_INFORMATION))];
uint retval = CallNtPowerInformation(
SystemPowerInformation,
IntPtr.Zero,
0,
out buffer,
4 * Marshal.SizeOf(typeof(PROCESSOR_POWER_INFORMATION))
);
if (retval == STATUS_SUCCESS)
Console.WriteLine(buffer);
}
}
}
I am trying to get some data out of CallNtPowerInformation. I tried to create a struct and call CallNtPowerInformation and marshal the data from it, but that didn't work. So I am trying to see if I can get the data into a byte array, but I get the following:
Object reference not set to an instance of an object.
I believe I am allocating the memory to the buffer.
I am not sure why. Any pointers would be helpful.
Your constant named SystemPowerInformation with value 11 has the wrong name. It should be named ProcessorInformation.
You should declare the p/invoke like this:
[DllImport("powrprof.dll")]
static extern uint CallNtPowerInformation(
int InformationLevel,
IntPtr lpInputBuffer,
int nInputBufferSize,
[Out] PROCESSOR_POWER_INFORMATION[] processorPowerInformation,
int nOutputBufferSize
);
In order to call the function you need to allocate a suitably sized array of PROCESSOR_POWER_INFORMATION structs. Like this:
PROCESSOR_POWER_INFORMATION[] powerInfo =
new PROCESSOR_POWER_INFORMATION[procCount];
The documentation for CallNtPowerInformation tells you to use GetSystemInfo to work out how many processors you have. You can use Environment.ProcessorCount.
Then you call the function like this:
uint retval = CallNtPowerInformation(
ProcessorInformation,
IntPtr.Zero,
0,
powerInfo,
powerInfo.Length*Marshal.SizeOf(typeof(PROCESSOR_POWER_INFORMATION))
);
Here's a complete program:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
const int ProcessorInformation = 11;
const uint STATUS_SUCCESS = 0;
[StructLayout(LayoutKind.Sequential)]
struct PROCESSOR_POWER_INFORMATION
{
public uint Number;
public uint MaxMhz;
public uint CurrentMhz;
public uint MhzLimit;
public uint MaxIdleState;
public uint CurrentIdleState;
}
[DllImport("powrprof.dll")]
static extern uint CallNtPowerInformation(
int InformationLevel,
IntPtr lpInputBuffer,
int nInputBufferSize,
[Out] PROCESSOR_POWER_INFORMATION[] lpOutputBuffer,
int nOutputBufferSize
);
static void Main(string[] args)
{
int procCount = Environment.ProcessorCount;
PROCESSOR_POWER_INFORMATION[] procInfo =
new PROCESSOR_POWER_INFORMATION[procCount];
uint retval = CallNtPowerInformation(
ProcessorInformation,
IntPtr.Zero,
0,
procInfo,
procInfo.Length * Marshal.SizeOf(typeof(PROCESSOR_POWER_INFORMATION))
);
if (retval == STATUS_SUCCESS)
{
foreach (var item in procInfo)
{
Console.WriteLine(item.CurrentMhz);
}
}
}
}
}
Change the parameter type of your umnanaged call to IntPtr:
[DllImport("powrprof.dll")]
static extern uint CallNtPowerInformation(
int InformationLevel,
IntPtr lpInputBuffer,
int nInputBufferSize,
IntPtr lpOutputBuffer,
int nOutputBufferSize
);
And use this before calling it:
GCHandle handle = GCHandle.Alloc(obj, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
Then call it passing that IntPtr as the parameter.
Don't forget to release after use!

Categories

Resources