PInvoke with SetupDiGetDriverInfoDetail - c#

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.

Related

SHGetFileInfo: Description for a files extension too short

I'm using SHGetFileInfo to retrieve certain information about a file or directory, i.e. the icon or the description the file extension.
When retrieving the description of the file extension, the string returned from SHGetFileInfo is missing the first four characters.
For example, the description of a .pdf file is Adobe Acrobat Document but I only get e Acrobat Document or the description of a .exe file is Anwendung (as I'm German, in English it's Application i suppose), but I only get ndung.
I'm using
public static string GetFileTypeDescription(this FileInfo file)
{
SHFILEINFO shFileInfo;
if (SHGetFileInfo(
file.Extension,
SHGFI_FILE_ATTRIBUTE_NORMAL,
out shFileInfo,
(uint)Marshal.SizeOf(typeof(SHFILEINFO)),
SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME)
!= IntPtr.Zero)
{
return shFileInfo.szTypeName;
}
return null;
}
with the usual implementation of SHGetFileInfo:
[DllImport("shell32.dll")]
internal static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, out SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
[StructLayout(LayoutKind.Sequential)]
internal struct SHFILEINFO
{
public IntPtr hIcon;
public IntPtr iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
//I omitted all flags which are not used above
private const uint SHGFI_FILE_ATTRIBUTE_NORMAL = 0x80,
SHGFI_USEFILEATTRIBUTES = 0x10,
SHGFI_TYPENAME = 0x400;
What is wrong? Did I miss something? Or how can I retrieve the full description?
The iIcon field in the C++ struct has type int. On Windows that is a 4 byte signed integer. It corresponds to int in C#.
You have declared the field as IntPtr in your C# code. That is a signed integer, the same size as a pointer. So it is 4 bytes in 32 bit code, and 8 bytes in 64 bit code. It seems likely that you are running 64 bit code.
So, the error is the declaration of this field which simply has the wrong type. The solution is to change the type of iIcon to int.
Change IntPtr on the iIcon to int. That should work. Or use x86 as the platform target. Either of the two.

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

String not referenced properly in C# structure with Layout

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

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