String not referenced properly in C# structure with Layout - c#

I have a structure
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SERVER_USB_DEVICE
{
USB_HWID usbHWID;
byte status;
bool bExcludeDevice;
bool bSharedManually;
ulong ulDeviceId;
ulong ulClientAddr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
string usbDeviceDescr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
string locationInfo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
string nickName;
}
When I pass it in a win32 DLL function as below:
[DllImport ("abc.dll", EntryPoint="EnumDevices", CharSet=CharSet.Ansi)]
public static extern bool EnumDevices(IntPtr lpUsbDevices,
ref ulong pulBufferSize,
IntPtr lpES);
I get some missing text in the string members of the structure.
Suppose SERVER_USB_DEVICE.usbDeviceDescr contains value "Mass Storage Device" which is wrong it should contain value "USB Mass Storage Device"
What is wrong in the code?

Actually i was making a small mistake here ulong is 8 bytes in c# where as it is 4 bytes in c++ (as we all know). converting ulong to uint solved the problem.

Try with ByValTStr instead of ByValArray

Have you verified that the fields in the structure that are located before usbDeviceDescr (status, bExcludeDevice, bSharedManually, ulDeviceId, and ulClientAddr) get their correct values? Could it be that it is the marshalling of the USB_HWID structure that is wrong, so that the offsets are off by 4 bytes for the rest of the structure?

You can look at the structure in a byte array to make sure you have everything aligned properly. Try this:
int size = Marshal.SizeOf(typeof(SERVER_USB_DEVICE));
byte[] buffer1 = new byte[size];
SERVER_USB_DEVICE[] buffer2 = new SERVER_USB_DEVICE[1];
// put instance of SERVER_USB_DEVICE into buffer2
Buffer.BlockCopy(buffer2, 0, buffer1, 0, size);

Related

Use AMD ADL to acquire monitor EDID in C#

I am using the AMD ADL to enumerate and manipulate displays attached to my system. One of the necessary functions I need is the ability to read and parse the display EDID. I am able to parse a byte array representation of the EDID, however I am unable to acquire the EDID. Based on the ADL documentation, I have defined the ADLDisplayEDIDData struct and imported the ADL_Display_EdidData_Get function. However, any execution of my code results in an error of retvalue -3. This retvalue indicates invalid parameters.
The EDIDData structure:
[StructLayout(LayoutKind.Sequential)]
internal struct ADLDisplayEDIDData
{
internal int Size;
internal int Flag;
internal int EDIDSize;
internal int BlockIndex;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
internal byte[] EDIDData;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
internal int[] Reserved;
}
The DLLImport:
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL_Display_EdidData_Get(int adapterIndex, int displayIndex, ref ADLDisplayEDIDData EDIDData);
Is there any error with my declarations? Does anyone have any experience with the ADL and getting the EDID information?
Thank you in advance.
Try this:
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL_Display_EdidData_Get(int adapterIndex, int displayIndex, [In, Out] ADLDisplayEDIDData[] EDIDData);
ADLDisplayEDIDData[] EDID = new ADLDisplayEDIDData[2];
NVGetMonitorEDIDs(0, EDID);
It would be good to pass the dimension length to c++ like this:
[DllImport(Atiadlxx_FileName)]
internal static extern int ADL_Display_EdidData_Get(int adapterIndex, int displayIndex, [In, Out] ADLDisplayEDIDData[] EDIDData, int iLength);
ADLDisplayEDIDData[] EDID = new ADLDisplayEDIDData[2];
NVGetMonitorEDIDs(0, EDID, EDID.Length);

How do I marshall a pointer to a pointer of an array of structures?

My C declarations are as follows:
int myData(uint myHandle, tchar *dataName, long *Time, uint *maxData, DATASTRUCT **data);
typedef struct {
byte Rel;
__int64 Time;
char Validated;
unsigned char Data[1];
} DATASTRUCT;
My C# declarations are as follows:
[DllImport("myData.dll", EntryPoint = "myData")]
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref DATASTRUCT[] data);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DATASTRUCT
{
public sbyte Rel;
public long Time;
public byte Validated;
public double Data;
}
I then call the managed function as follows:
string dataToShow = "description";
long Time;
uint maxData; // How many structs will be returned, i.e., how much data is available?
uint myHandle = 1;
DATASTRUCT[] dataInformation = new DATASTRUCT[3]; // Doesn't it matter what I specify as the array size?
myData(myHandle, dataToShow, out Time, out maxData, ref dataInformation);
Upon execution the above function will return successfully with only one structure even though there are 3 to return. Why is this so?
Additional information; I have tried passing the pointer to a pointer of an array of structs the following ways:
ref DATASTRUCT[] data; // It works, but it only returns one struct
[Out, MarshalAs(UnmanagedType.LPArray)] DATASTRUCT[] data; // returns the number of defined structs with garbage
As I understand it I might need to do some manual marshalling using IntPtr, I do not know how to implement this however, so any advice would be appreciated.
Okay, it seems as though your native library does the allocation, so really all you need to do is provide a pointer through which you can access the allocated data.
Change your API definition to (note, I changed the maxData param to uint, long is 64 bits in .NET and 32 bits in native.
[DllImportAttribute("myData.dll", EntryPoint = "myData")]
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out uint Time, out uint maxData, out IntPtr pData);
Off the top of my head I can't quite remember if you need the out keyword for the final parameter, but I think so.
Then, call myData:
uint nAllocs = 0, time = 0;
IntPtr pAllocs = IntPtr.Zero;
myData(1, "description", out time, out nAllocs, out pAllocs);
Now, pAllocs should point to unmanaged memory, to marshal these into managed memory isn't too difficult:
[StructLayoutAttribute(LayoutKind.Sequential, Pack = 1)]
public struct DATASTRUCT
{
public byte Rel;
public long Time;
public byte Validated;
public IntPtr Data; //pointer to unmanaged string.
}
int szStruct = Marshal.SizeOf(typeof(DATASTRUCT));
DATASTRUCT[] localStructs = new DATASTRUCT[nAllocs];
for(uint i = 0; i < nallocs; i++)
localStructs[i] = (DATASTRUCT)Marshal.PtrToStructure(new IntPtr(pAllocs.ToInt32() + (szStruct * i)), typeof(DATASTRUCT));
And now you should have an array of local structs.
A point to note
You may need to set your project to compile as x86, to standardize the size of an IntPtr to 4 bytes (DWORD) instead of AnyCPU's default 8.
A pointer to a pointer could be represented in your dllimport declaration as ref IntPtr data, so your declaration would become:
[DllImportAttribute("myData.dll", EntryPoint = "myData")]
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref IntPtr data);
(As an aside, I think a long in C is just equivalent to an int in C#. Long in C# is an Int64, which would be a long long in C)
Marshalling your DATASTRUCT[] to an IntPtr can be done using the GCHandle class
DATASTRUCT [] dataInformation = new DATASTRUCT[3];
GCHandle gch = GCHandle.Alloc(dataInformation , GCHandleType.Pinned);
IntPtr ptr = gch.AddrOfPinnedObject();
myData(myHandle, dataToShow, out Time, out maxData, ref ptr);
//It's absolutely essential you do this next bit so the object can be garbage collected again,
//but it should only be done once the unmanaged code is definitely done with the reference.
gch.Free();
Using the Marshal class and the StructureToPtr or Copy methods of it would also be an option but for the purposes of proving the concept at least the GCHandle should do the trick, it's just not ideal for scenarios where the unmanaged code does long running operations because you've pinned this object in place and the GC can't move it until you free it.

PInvoke with SetupDiGetDriverInfoDetail

I'm trying to call SetupDiGetDriverInfoDetail from a C# application. The call fails and the win32 error I get back is 0x6F8 ("The supplied user buffer is not valid for the requested operation."). Up to this point I have been able to call other setupdi functions with success so I think the problem is with the way that I marshal either the function or SP_DRVINFO_DETAIL_DATA struct.
I'm not sure, but I think the problem may be with the HardwareID member of the SP_DRVINFO_DETAIL_DATA struct. I've tried specifying the HardwareID as different types (ex. a byte array and allocating the buffer before setting the size and calling the function), but always the same error. If anyone has any experience with this call or has any pointers, I would appreciate the help.
Below is my structure definition, function import and code snippet. In this version I use a fixed size HardwareID buffer. I've also tried specifying a buffer size of 1 expecting an "buffer too small" error, but I always get the "invalid buffer" error.
[DllImport("setupapi.dll", SetLastError = true)]
internal static extern Int32 SetupDiGetDriverInfoDetail(
IntPtr DeviceInfoSet,
SP_DEVINFO_DATA DeviceInfoData,
SP_DRVINFO_DATA DriverInfoData,
ref SP_DRVINFO_DETAIL_DATA DriverInfoDetailData,
Int32 DriverInfoDetailDataSize,
ref Int32 RequiredSize);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct SP_DRVINFO_DETAIL_DATA
{
public Int32 cbSize;
public System.Runtime.InteropServices.ComTypes.FILETIME InfDate;
public Int32 CompatIDsOffset;
public Int32 CompatIDsLength;
public IntPtr Reserved;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String SectionName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String InfFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String DrvDescription;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String HardwareID;
};
SetupApiWrapper.SP_DRVINFO_DETAIL_DATA DriverInfoDetailData = new SetupApiWrapper.SP_DRVINFO_DETAIL_DATA();
DriverInfoDetailData.cbSize = Marshal.SizeOf(DriverInfoDetailData);
result = SetupApiWrapper.SetupDiGetDriverInfoDetail(
DevInfo,
DeviceInfoData,
DriverInfoData,
ref DriverInfoDetailData,
DriverInfoDetailData.cbSize,
ref reqSize);
oAlthough I agree that the error code seems unexpected, I think the problem is that cbSize should be set to sizeof(SP_DRVINFO_DETAIL_DATA) (that's the proper C sizeof, not Marshal.SizeOf on your p/invoke structure.)
A quick test with a two line C program gives:
ANSI 797
UNICODE 1570
For the two proper sizeof values (you need to work out which one you need yourself...)
In contrast Marshal.SizeOf(typeof(SP_DRVINFO_DETAIL_DATA)) for your structure gives 1048 as a length.
I think you need to get that lined up before you go any further.
I suspect that it might be that the buffer-too-small error is returned if DriverInfoDetailDataSize is too small, but the invalid-buffer error is returned if cbSize is wrong.
The help for SetupDiGetDriverInfoDetail is also explicit that cbSize and DriverInfoDetailDataSize are not supposed to be the same value (because ANYSIZE_ARRAY is just defined as 1 as a placeholder), so you should not expect to get Marshal.SizeOf to work correctly with your deliberately oversized structure.
Additional correction:
Your InfFilename member is also the wrong length - a structure which exactly matches the structure from SETUPAPI.H is:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet=CharSet.Unicode)]
internal struct SP_DRVINFO_DETAIL_DATA
{
public Int32 cbSize;
public System.Runtime.InteropServices.ComTypes.FILETIME InfDate;
public Int32 CompatIDsOffset;
public Int32 CompatIDsLength;
public IntPtr Reserved;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String SectionName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public String InfFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String DrvDescription;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)]
public String HardwareID;
};
This gives the correct lengths, both in the ANSI and UNICODE versions. However, you don't want to use this as-is, because you need HardwareID to be longer, so you'll have to adjust the length of that and then live with Marshal.SizeOf giving the wrong value for plugging directly into cbSize.
The function declaration is wrong, the 2nd and 3rd arguments are passed by ref. Explains "invalid buffer", the API wants a pointer. Careful with Pack, it is only 1 on 32-bit operating systems. Set the Platform target to x86 to be sure. You are supposed to measure the required structure size first. Tricky to do, make the HardwareID nice and big, don't be frugal and throw 16K at it.

Struct array never receives items, but the method says it did

Yet another one of my idiotic P/Invoke questions... Sometimes I think this stuff is going to be a piece of cake, but it blows up in my face.
I have a simple unmanaged method that takes a destination array and fills it.
unsigned short NvtlCommon_GetAvailableDevices(SdkHandle session,
DeviceDetail * pDev_list, unsigned long * dev_list_size)
typedef struct
{
DeviceTechType eTechnology;
DeviceFormFactorType eFormFactor;
char szDescription[NW_MAX_PATH];
char szPort[NW_MAX_PATH];
char szFriendlyName[NW_MAX_PATH];
} DeviceDetail;
I converted these to C#:
[DllImport(NvtlConstants.LIB_CORE, EntryPoint = "NvtlCommon_GetAvailableDevices")]
public static extern NvtlErrorCode GetAvailableDevices(IntPtr session,
DeviceDetail[] pDev_list, ref long dev_list_size);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DeviceDetail {
public DeviceTechType eTechnology;
public DeviceFormFactorType eFormFactor;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NvtlConstants.NW_MAX_PATH)]
public string szDescription;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NvtlConstants.NW_MAX_PATH)]
public string szPort;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NvtlConstants.NW_MAX_PATH)]
public string szFriendlyName;
}
The documentation states that on input, dev_list_size should be the size of the allocated array. On output, it's the number of items that were actually filled. If I use out or ref on the pDev_list argument, my application crashes. This seems to be the only signature that even begins to work.
DeviceDetail[] devices = new DeviceDetail[5];
long count = 5;
GetAvailableDevices(coreHandle, devices, ref count);
That code returns a success message and count is set to 1, which is indeed the number of available devices. However, the array is still 5 uninitialized DeviceDetail structs.
Am I doing something wrong here, or is this a problem with the underlying unmanaged library?
Have you tried [Out]?
public static extern NvtlErrorCode GetAvailableDevices(IntPtr session,
[Out] DeviceDetail[] pDev_list, ref long dev_list_size);
↑

Marshal Unmanaged struct to managed code using C#

I need to process the bytes[] when I get from external application. The external application is also done in C# and they send the bytes thru UDP. They are sending the bytes converted from struct which is stated below :
public struct DISPATCH_MESSAGE
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public char[] federation_name; // Units: nil Range: nil
}
So, when I get the bytes, I need to take out the char[] inside that,
and get the string out of that char[].
I am new to this kind of unmanaged coding.
Probably you should declare it as ByValTStr (depending on the nature of the string, it might be different):
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DISPATCH_MESSAGE{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string federation_name;
}
UPDATE: If it's already giving out a char[], it's probably doing the necessary conversion (includes handling encoding) correctly, so I think you'd just need:
string str = new string(instance.federation_name);

Categories

Resources