Expose BootTime in TraceProcessor - c#

I see that Microsoft.Windows.EventTracing.Interop.Metadata.NativeTraceLogfileHeader contains a value for BootTime. That could be useful in some cases. Any chance that will be exposed via the ITraceMetaData interface or can that be somehow else accessed?
// Microsoft.Windows.EventTracing.Metadata.ITraceMetadata
using Microsoft.Windows.EventTracing;
public interface ITraceMetadata
{
Version OSVersion
bool Is32Bit
FrequencyValue ProcessorSpeed
TraceClockType ClockType
FrequencyValue PerformanceCounterFrequency
TraceTimestampValue? ReferenceTimestampValue
FrequencyValue ProcessorUsageTimerFrequency
TraceTimestamp FirstAnalyzerDisplayedEventTime
TraceTimestamp LastEventTime
TraceDuration AnalyzerDisplayedDuration
long LostBufferCount
long LostEventCount
string TracePath
DateTimeOffset StartTime
DateTimeOffset StopTime
int ProcessorCount
int KernelEventVersion
}
Update
I have added the suggested code of dmatsion to ETWAnalyzer.
Now you can do
ETWAnalyzer -dump stats

I'll let a current team member answer regarding what changes can be made. Conceptually, before the v1 API shipped, I think it would have made sense to add this data to ITraceMetadata.
I don't recall adding this data to anything else in the API. (I checked ISystemMetadata and didn't see it there.) The only workaround I'm aware of would be to use IEventConsumer/IFilteredEventConsumer to parse the payload of the event containing this data (always the first event in the trace, I think, with whatever ProviderId/Id that event has; I don't recall off-hand).
If I recall correctly, the payload is just the TRACE_LOGFILE_HEADER structure.
But note that there are both 32-bit and 64-bit versions of that event due to the LoggerName/LogFileName pointers in it (based on the bitness of the trace, not the bitness of the machine processing the trace).
EDIT:
Here's a sample exe that gets the boot time via the trace header event. (I'm using trace.Use(), but IFilteredEventConsumer would be equivalent.)
using Microsoft.Windows.EventTracing;
using System;
using System.Runtime.InteropServices;
class Program
{
static int Main(string[] args)
{
if (args.Length != 1)
{
Console.Error.WriteLine("Usage: GetTraceBootTime.exe <trace.etl>");
return 1;
}
string tracePath = args[0];
using (ITraceProcessor trace = TraceProcessor.Create(tracePath))
{
DateTime? bootTime = null;
Guid eventTraceProviderId = new Guid("68fdd900-4a3e-11d1-84f4-0000f80464e3");
trace.Use(new[] { eventTraceProviderId }, e =>
{
if (e.Event.Id != 0 || e.Event.Version != 2)
{
return;
}
var data = e.Event.Data;
long rawBootTime;
if (e.Event.Is32Bit)
{
if (data.Length < Marshal.SizeOf<NativeTraceHeaderEvent32>())
{
throw new InvalidOperationException("Invalid 32-bit trace header event.");
}
// FYI - Inefficient / lots of copies, but doesn't require compiling with /unsafe.
IntPtr pointer = Marshal.AllocHGlobal(data.Length);
Marshal.Copy(data.ToArray(), 0, pointer, data.Length);
NativeTraceHeaderEvent32 typedData = Marshal.PtrToStructure<NativeTraceHeaderEvent32>(pointer);
Marshal.FreeHGlobal(pointer);
rawBootTime = typedData.BootTime;
}
else
{
if (data.Length < Marshal.SizeOf<NativeTraceHeaderEvent64>())
{
throw new InvalidOperationException("Invalid 64-bit trace header event.");
}
// FYI - Inefficient / lots of copies, but doesn't require compiling with /unsafe.
IntPtr pointer = Marshal.AllocHGlobal(data.Length);
Marshal.Copy(data.ToArray(), 0, pointer, data.Length);
NativeTraceHeaderEvent64 typedData = Marshal.PtrToStructure<NativeTraceHeaderEvent64>(pointer);
Marshal.FreeHGlobal(pointer);
rawBootTime = typedData.BootTime;
}
// See https://learn.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-trace_logfile_header:
// BootTime is ticks since midnight, January 1, 1601 and is apparently UTC (despite documentation to the
// contrary).
DateTime epoch = new DateTime(1601, 1, 1, 0, 0, 0, DateTimeKind.Utc);
bootTime = epoch.AddTicks(rawBootTime);
e.Cancel();
});
trace.Process();
Console.WriteLine(bootTime);
}
return 0;
}
// https://learn.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-trace_logfile_header
[StructLayout(LayoutKind.Sequential)]
struct NativeTraceHeaderEvent64
{
public uint BufferSize;
public byte MajorVersion;
public byte MinorVersion;
public byte SubVersion;
public byte SubMinorVersion;
public uint ProviderVersion;
public uint NumberOfProcessors;
public long EndTime;
public uint TimerResolution;
public uint MaximumFileSize;
public uint LogFileMode;
public uint BuffersWritten;
public uint StartBuffers;
public uint PointerSize;
public uint EventsLost;
public uint CpuSpeedInMHz;
public ulong LoggerName;
public ulong LogFileName;
public NativeTimeZoneInformation TimeZone;
public long BootTime;
public long PerfFreq;
public long StartTime;
public uint ReservedFlags;
public uint BuffersLost;
}
// https://learn.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-trace_logfile_header
[StructLayout(LayoutKind.Sequential)]
struct NativeTraceHeaderEvent32
{
public uint BufferSize;
public byte MajorVersion;
public byte MinorVersion;
public byte SubVersion;
public byte SubMinorVersion;
public uint ProviderVersion;
public uint NumberOfProcessors;
public long EndTime;
public uint TimerResolution;
public uint MaximumFileSize;
public uint LogFileMode;
public uint BuffersWritten;
public uint StartBuffers;
public uint PointerSize;
public uint EventsLost;
public uint CpuSpeedInMHz;
public uint LoggerName;
public uint LogFileName;
public NativeTimeZoneInformation TimeZone;
public long BootTime;
public long PerfFreq;
public long StartTime;
public uint ReservedFlags;
public uint BuffersLost;
}
// https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/ns-timezoneapi-time_zone_information
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct NativeTimeZoneInformation
{
public int Bias;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[] StandardName;
public NativeSystemTime StandardDate;
public int StandardBias;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[] DaylightName;
public NativeSystemTime DaylightDate;
public int DaylightBias;
}
// https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime
[StructLayout(LayoutKind.Sequential)]
struct NativeSystemTime
{
public short wYear;
public short wMonth;
public short wDayOfWeek;
public short wDay;
public short wHour;
public short wMinute;
public short wSecond;
public short wMilliseconds;
}
}

Related

C# read and save propertys of printer devmode

I have a simple application to copy the properties of the printer devmode and restore them after a manual change.
I read through topics the whole day and can't understand why my code isn't working. I compared the output of two different printer settings against each other and can confirm that they are different, so my guess is that something with restoring these settings isn't working.
I also tried thisĀ applicationĀ but found some strange behavior. When I export the devmode data, change the properties manually and load the save again, the data only shows changed if I call the printer property dialog directly with the button in the application itself. If I go to windows settings and navigate to the printer properties myself, the data isn't changed.
Here is the code, got it from this thread
public static class PrinterSettingsExtensions
{
public static byte[] GetDevModeData(this PrinterSettings settings)
{
//Contract.Requires(settings != null);
byte[] devModeData;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
// cer since hDevMode is not a SafeHandle
}
finally
{
var hDevMode = settings.GetHdevmode();
try
{
IntPtr pDevMode = NativeMethods.GlobalLock(hDevMode);
try
{
var devMode = (NativeMethods.DEVMODE)Marshal.PtrToStructure(
pDevMode, typeof(NativeMethods.DEVMODE));
var devModeSize = devMode.dmSize + devMode.dmDriverExtra;
devModeData = new byte[devModeSize];
Marshal.Copy(pDevMode, devModeData, 0, devModeSize);
}
finally
{
NativeMethods.GlobalUnlock(hDevMode);
}
}
finally
{
Marshal.FreeHGlobal(hDevMode);
}
}
return devModeData;
}
public static void SetDevModeData(this PrinterSettings settings, byte[] data)
{
//Contract.Requires(settings != null);
//Contract.Requires(data != null);
//Contract.Requires(data.Length >= Marshal.SizeOf(typeof(NativeMethods.DEVMODE)));
RuntimeHelpers.PrepareConstrainedRegions();
try
{
// cer since AllocHGlobal does not return SafeHandle
}
finally
{
var pDevMode = Marshal.AllocHGlobal(data.Length);
try
{
// we don't have to worry about GlobalLock since AllocHGlobal only uses LMEM_FIXED
Marshal.Copy(data, 0, pDevMode, data.Length);
var devMode = (NativeMethods.DEVMODE)Marshal.PtrToStructure(
pDevMode, typeof(NativeMethods.DEVMODE));
// The printer name must match the original printer, otherwise an AV will be thrown
settings.PrinterName = devMode.dmDeviceName;
// SetHDevmode creates a copy of the devmode, so we don't have to keep ours around
settings.SetHdevmode(pDevMode);
}
finally
{
Marshal.FreeHGlobal(pDevMode);
}
}
}
}
static class NativeMethods
{
private const string Kernel32 = "kernel32.dll";
[DllImport(Kernel32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern IntPtr GlobalLock(IntPtr handle);
[DllImport(Kernel32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern bool GlobalUnlock(IntPtr handle);
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Auto)]
public struct DEVMODE
{
private const int CCHDEVICENAME = 32;
private const int CCHFORMNAME = 32;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public int dmPositionX;
public int dmPositionY;
public int dmDisplayOrientation;
public int dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
public string dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
}
I'm using .Net Core 6 and Windows 11 22000.795 if this helps.
DevMode is what I am working on at the moment. Using .net 4.8.1 unfortunately but I am using Heap functions and I am pulling the default settings using p/Invoke.
My goal is to modify settings and then stream the changed settings to another API. So, I'll look into this and see what I can find.
Several steps could be giving you problems with settings. Mostly around the printer/document/settings/defaults/drivers themselves. Once you make your settings and pass need to make sure your end test is picking up the changes. But, as you suggest, most likely an invalid format due to each driver being unique.
Any progress?
Appears for the data to show it has been changed, you need to flag the fields that have been modified in public int dmFields; (Says it is defined as a long).
dmFields
Specifies bit flags identifying which of the following DEVMODEW members are in use. For example, the DM_ORIENTATION flag is set when the dmOrientation member contains valid data. The DM_XXX flags are defined in wingdi.h.

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

Marshalling Struct with Array of Struct Member

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

Convert c++ struct to struct c#

i have a problem with convert a struct c++ to c# struct, i will leave the following code:
C++
struct SDK_ALARM_INPUTCONFIG
{
bool bEnable;
int iSensorType;
SDK_EventHandler hEvent;
};
struct SDK_EventHandler
{
unsigned int dwRecord;
int iRecordLatch;
unsigned int dwTour;
unsigned int dwSnapShot;
unsigned int dwAlarmOut;
unsigned int dwMatrix;
int iEventLatch;
int iAOLatch;
SDK_PtzLinkConfig PtzLink[NET_MAX_CHANNUM];
SDK_CONFIG_WORKSHEET schedule;
bool bRecordEn;
bool bTourEn;
bool bSnapEn;
bool bAlarmOutEn;
bool bPtzEn;
bool bTip;
bool bMail;
bool bMessage;
bool bBeep;
bool bVoice;
bool bFTP;
bool bMatrixEn;
bool bLog;
bool bMessagetoNet;
bool bShowInfo;
unsigned int dwShowInfoMask;
char pAlarmInfo[8];
bool bShortMsg;
bool bMultimediaMsg;
};
struct SDK_PtzLinkConfig
{
int iType;
int iValue;
};
struct SDK_CONFIG_WORKSHEET
{
SDK_TIMESECTION tsSchedule[6][7];
};
struct SDK_TIMESECTION
{
int enable;
int startHour;
int startMinute;
int startSecond;
int endHour;
int endMinute;
int endSecond;
};
C#:
[StructLayout(LayoutKind.Sequential)]
public struct SDK_ALARM_INPUTCONFIG
{
public bool bEnable;
public int iSensorType;
public SDK_EventHandler hEvent;
};
[StructLayout(LayoutKind.Sequential)]
public struct SDK_EventHandler
{
public ushort dwRecord;
public int iRecordLatch;
public ushort dwTour;
public ushort dwSnapShot;
public ushort dwAlarmOut;
public ushort dwMatrix;
public int iEventLatch;
public int iAOLatch;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.Struct, SizeConst = 32)]
public SDK_PtzLinkConfig[] PtzLink;
public SDK_CONFIG_WORKSHEET schedule;
public bool bRecordEn;
public bool bTourEn;
public bool bSnapEn;
public bool bAlarmOutEn;
public bool bPtzEn;
public bool bTip;
public bool bMail;
public bool bMessage;
public bool bBeep;
public bool bVoice;
public bool bFTP;
public bool bMatrixEn;
public bool bLog;
public bool bMessagetoNet;
public bool bShowInfo;
public ushort dwShowInfoMask;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string pAlarmInfo;
//public bool bShortMsg;
//public bool bMultimediaMsg;
};
[StructLayout(LayoutKind.Sequential)]
public struct SDK_PtzLinkConfig
{
public int iType;
public int iValue;
};
[StructLayout(LayoutKind.Sequential)]
public struct SDK_CONFIG_WORKSHEET
{
[MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.Struct, SizeConst = 6*7)]
public SDK_TIMESECTION[] tsSchedule;
};
[StructLayout(LayoutKind.Sequential)]
struct SDK_TIMESECTION
{
public int enable;
public int startHour;
public int startMinute;
public int startSecond;
public int endHour;
public int endMinute;
public int endSecond;
};
I'm calling this method:
C++:
long H264_DVR_GetDevConfig(long lLoginID, unsigned long dwCommand, int nChannelNO, char * lpOutBuffer, unsigned long dwOutBufferSize, unsigned long* lpBytesReturned,int waittime = 1000);
C#:
[DllImport("NetSdk.dll")]
public static extern int H264_DVR_GetDevConfig(int lLoginID, uint dwCommand, int nChannelNO, IntPtr lpOutBuffer,
uint dwOutBufferSize, ref uint lpBytesReturned, int waittime);
I'm calling this way:
C#:
var vAlarmConfig = new SDK_ALARM_INPUTCONFIG();
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SDK_ALARM_INPUTCONFIG)));
Marshal.StructureToPtr(vAlarmConfig, ptr, true);
uint lpBytesReturned = (uint)Marshal.SizeOf(typeof(SDK_ALARM_INPUTCONFIG));
int CHANNEL = 2;
int result = XMSDK.H264_DVR_GetDevConfig(lLoginID, (uint)SDK_CONFIG_TYPE.E_SDK_CONFIG_ALARM_IN, CHANNEL, ptr,
(uint)Marshal.SizeOf(typeof(SDK_ALARM_INPUTCONFIG)), ref lpBytesReturned, 10000);
Seems everything work fine, but when i run the program, the method return an illegal paramater.
What i'm doing wrong?. I think the problem is the conversion.
Regards.
Your C++ bool is a single byte. But the C# bool default marshaling is as the 4 byte Winapi BOOL type. You'll need to add [MarshalAs(UnmanagedType.U1)] for every bool in your structs.
Your use of ushort is wrong. On the C++ side you have unsigned int. That is uint on the C# side.
You will also need to translate the structs accurately. Perhaps while you were debugging you made pAlarmInfo have length 64 rather than 8. Obviously you need to go back an do that correctly.
I guess you made that change because the struct size was incorrect. That should have been a tell-tale sign that there was a more serious problem. Your structs have to line up for each and every member. You can't just add a load of padding at the end to get the size right. If the sizes don't match then the layout will be wrong. You make the sizes match as a consequence of making the layouts match. When the latter is done correctly, the former happens as a consequence.

calling unmanaged dll from C# maybe im marshalling wrong

I'm not quite sure what i might be doing wrong but i keep getting System.ArgumentException
when i look at the IDL(generated by ITyteLib Veiwer) I can see both struct and modules
typedef struct tagXxxStruct {
short type;
short file;
short rec;
short word;
short start_bit;
short length;
short flags;
short padding;
short value;
short padV[3];
short status;
short padA[3];
} XxxStruct;
[entry("dat"), helpstring("...")]
short _stdcall dat_Int(
[in] LPSTR Server,
[in] short num_points,
[in, out] SAFEARRAY(XxxStruct)* XxxStruct_data);
I when use DllImport like so:
[DllImport("hscnetapi.dll", EntryPoint = "dat", CallingConvention = CallingConvention.StdCall)]
public static extern unsafe short dat_SA([MarshalAs(UnmanagedType.LPStr)] string host, short num_points, [MarshalAs(UnmanagedType.SafeArray)] XxxStruct[] dat_data);
And call it like so
public struct XxxStruct
{
public short type;
public short file;
public short rec;
public short word;
public short start_bit;
public short Length;
public short Flags;
public short padding;
public short value;
public short[] padV;
public short Status;
public short[] padA;
}
server = "localhost";
XxxStruct[] xobj= new XxxStruct[2];
for (i = 0; i <= 1; i++)
{
var _with1 = xobj[i];
_with1.type = 2;
_with1.file = 8;
_with1.rec = 1;
_with1.word = ((short)(359 + (i)));
_with1.Flags = 0;
_with1.padV = new short[3];
_with1.padA = new short[3];
xobj[i] = _with1;
}
dat_SA(server, (short)2, xobj);
You need to add a MarshalAs attribute to the padV and padA arrays. And quite possibly a StructLayout attribute.
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct XxxStruct
{
public short type;
public short file;
public short rec;
public short word;
public short start_bit;
public short Length;
public short Flags;
public short padding;
public short value;
[MarshalAs(UnmanagedType.LPArray, SizeConst=3)]
public short[] padV;
public short Status;
[MarshalAs(UnmanagedType.LPArray, SizeConst=3)]
public short[] padA;
}

Categories

Resources