I'm trying to call some ioctls from Go, and some of them take C strings as parameters. For example, in C:
/* When the user asks to bind a message name to an interface, they use: */
struct kbus_bind_request {
__u32 is_replier; /* are we a replier? */
__u32 name_len;
char *name;
};
extern int kbus_ksock_bind(kbus_ksock_t ksock,
const char *name,
uint32_t is_replier)
{
int rv;
kbus_bind_request_t bind_request;
bind_request.name = (char *) name;
bind_request.name_len = strlen(name);
bind_request.is_replier = is_replier;
rv = ioctl(ksock, KBUS_IOC_BIND, &bind_request);
if (rv < 0)
return -errno;
else
return rv;
}
I converted the struct to a Go struc like this:
type kbus_bind_request struct {
is_replier uint32 /* are we a replier? */
name_len uint32
name unsafe.Pointer // char*
}
Now, how do I convert a Go string to a C string stored in an unsafe.Pointer? I don't want to use CGo as I am cross-compiling and it makes things a pain.
Ah found the answer (well something that compiles anyway). First cast to []byte, then take the address of the first element:
func int_bind(ksock int, name string, is_replier uint32) int {
bind_request := &kbus_bind_request{}
s := []byte(name)
bind_request.name = unsafe.Pointer(&s[0])
bind_request.name_len = uint32(len(s))
bind_request.is_replier = is_replier
rv := ioctl(ksock, KBUS_IOC_BIND, unsafe.Pointer(bind_request))
if rv != 0 {
return -int(rv)
}
return 0
}
I am attempting to PInvoke into the CCKeyDerivationPBKDF here.
The method signature looks like this:
int
CCKeyDerivationPBKDF( CCPBKDFAlgorithm algorithm, const char *password, size_t passwordLen,
const uint8_t *salt, size_t saltLen,
CCPseudoRandomAlgorithm prf, uint rounds,
uint8_t *derivedKey, size_t derivedKeyLen)
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);
Currently I am attempting this:
[DllImport(ObjCRuntime.Constants.libSystemLibrary, EntryPoint = "CCKeyDerivationPBKDF")]
public extern static void CCKeyCerivationPBKDF(int algorithm, string password, int passwordLen,
string salt, int saltLen, int prf, int rounds, out byte[] derivedKey, int derivedKeyLength);
and am getting the error:
malloc: *** error for object 0xb9374fc61e8f9d1e: pointer being freed was not allocated
This is my first attempt to PInvoke something. I'm fairly certain that my signature is probably way off. What does it need to be?
You are getting the pointer being freed was not allocated error due to the out flag on the derivedKey parameter, just pass it as an IntPtr (byte[]) and Mono will marshall it correctly:
CCKeyCerivationPBKDF signature:
[DllImport(ObjCRuntime.Constants.libSystemLibrary, EntryPoint = "CCKeyDerivationPBKDF")]
public extern static int CCKeyCerivationPBKDF(
int algorithm,
string password,
nint passwordLen,
string salt,
nint saltLen,
UInt32 prf,
int rounds,
byte[] derivedKey,
int derivedKeyLength);
Example:
var passwordEntry = "StackOverflow";
var saltEntry = "SaltyMcSalty";
var keyBytes = new byte[32 + 1];
Array.Clear(keyBytes, 0, keyBytes.Length);
var result = CCKeyCerivationPBKDF(2, passwordEntry, passwordEntry.Length, saltEntry, saltEntry.Length, 3, 1, keyBytes, keyBytes.Length);
Use "native" objects / reduced marshaling:
(Had to due it this way to get passed on a military/aerospace certification/review)
[DllImport(ObjCRuntime.Constants.libSystemLibrary, EntryPoint = "CCKeyDerivationPBKDF")]
public extern static int CCKeyCerivationPBKDF(
int algorithm,
IntPtr password,
nuint passwordLen,
IntPtr salt,
nuint saltLen,
UInt32 prf,
nuint rounds,
IntPtr derivedKey,
nuint derivedKeyLength);
Example (w/ Base64):
var passwordEntry = "StackOverflow";
var passwordBytes = System.Text.Encoding.UTF8.GetBytes(passwordEntry);
var passwordBase64 = Convert.ToBase64String(passwordBytes);
var passwordNSStringBase64 = new NSString(passwordBase64);
var passwordNSData = new NSData(passwordNSStringBase64, NSDataBase64DecodingOptions.None);
var saltEntry = "SaltyMcSalty";
var saltBytes = System.Text.Encoding.UTF8.GetBytes(saltEntry);
var saltBase64 = Convert.ToBase64String(saltBytes);
var saltNSStringBase64 = new NSString(saltBase64);
var saltNSData = new NSData(saltNSStringBase64, NSDataBase64DecodingOptions.None);
var keyBytes = new NSMutableData();
keyBytes.Length = 33;
var result = CCKeyCerivationPBKDF(2, passwordNSData.Bytes, passwordNSData.Length, saltNSData.Bytes, saltNSData.Length, 3, 1, keyBytes.MutableBytes, keyBytes.Length - 1);
I am trying to install a driver during a windows-setup project.
The first step I do is to copy the INF file and preinstall the driver.
SetupCopyOEMInf(infFile, null, 1, 0, null, 0, 0, null);
This correctly preinstalls the driver, but the device is not ready to use until a hardware rescan is done in Device Manager. I want to automate this as well. I have tried using the setupapi.dll to invoke the hardware rescan, but it was not always successful for me. Using devcon.exe rescan always forces the hardware rescan, but it is a synchronous command, and it returns before the device is finished installing. Is there any way to get a return result after the hardware scan completes and the driver is successfully installed?
Thanks,
Misha
Edit
Here is my working code:
public const UInt32 CR_SUCCESS = 0;
public const UInt64 CM_REENUMERATE_SYNCHRONOUS = 1;
public const UInt64 CM_LOCATE_DEVNODE_NORMAL = 0;
[DllImport("setupapi.dll")]
public static extern bool SetupCopyOEMInf(
string SourceInfFileName,
string OEMSourceMediaLocation,
int OEMSourceMediaType,
int CopyStyle,
string DestinationInfFileName,
int DestinationInfFileNameSize,
int RequiredSize,
string DestinationInfFileNameComponent
);
[DllImport("cfgmgr32.dll")]
public static extern int CM_Locate_DevNode_Ex(ref IntPtr deviceHandle, int deviceId, uint flags, IntPtr machineHandle);
[DllImport("cfgmgr32.dll")]
public static extern int CM_Reenumerate_DevNode_Ex(IntPtr devInst, UInt64 flags);
[DllImport("cfgmgr32.dll")]
public static extern int CMP_WaitNoPendingInstallEvents(UInt32 timeOut);
static void Main() {
bool success = SetupCopyOEMInf(infFile, null, 1, 0, null, 0, 0, null);
if(!success) {
throw new Exception("Error installing driver");
}
success = RescanAllDevices();
if (!success) {
throw new Exception("Error installing driver");
}
}
public static bool RescanAllDevices() {
int ResultCode = 0;
IntPtr LocalMachineInstance = IntPtr.Zero;
IntPtr DeviceInstance = IntPtr.Zero;
UInt32 PendingTime = 30000;
ResultCode = CM_Locate_DevNode_Ex(ref DeviceInstance, 0, 0, LocalMachineInstance);
if (CR_SUCCESS == ResultCode) {
ResultCode = CM_Reenumerate_DevNode_Ex(DeviceInstance, CM_REENUMERATE_SYNCHRONOUS);
ResultCode = CMP_WaitNoPendingInstallEvents(PendingTime);
}
return ResultCode == CR_SUCCESS;
}
The source for devcon is available in the WDK. It's in the src\setup\devcon directory. The logic for the rescan command is in the cmdRescan function in cmds.cpp. It would be a simple matter to copy that logic into your own code and make sure it doesn't return immediately.
My goal is to open a printer connected via USB using the CreateFile (and then issue some WriteFiles and ReadFiles).
If the printer was an LPT one, I would simply do CreateFile("LPT1:", ...). But for USB printers, there is a special device path that must be passed to CreateFile in order to open that printer.
This device path, as I was able to find, is retrieved via SetupDiGetClassDevs -> SetupDiEnumDeviceInterfaces -> SetupDiGetDeviceInterfaceDetail -> DevicePath member and looks like this:
\\?\usb#vid_0a5f&pid_0027#46a072900549#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}
All that is fine, but what I have as the input is the human-readable printer name, as seen in Devices and Printers. The SetupDi* functions don't seem to use that, they only operate on device instance IDs. So the question is now how to get device instance ID from the printer name one would pass to OpenPrinter.
It's not difficult to observe that the GUID part of the above is the GUID_DEVINTERFACE_USBPRINT, and \\?\usb is fixed, so the only bit I'm really interested in is vid_0a5f&pid_0027#46a072900549#. This path I can easily look up manually in the printer properties dialog:
Go to Devices and Printers
Right-click the printer
Properties
Switch to Hardware tab
Select the printing device, such as ZDesigner LP2844-Z
Properties
Switch to Details tab
Select 'Parent' from the dropdown.
But I have no idea how to do that programmatically provided the only thing given is the printer name as seen in the Device and Printers panel.
P.S. 1: I'm not interested in opening the printer with OpenPrinter and then using WritePrinter / ReadPrinter. That has been done, works fine, but now the goal is different.
P.S. 2: I'll be OK with a simpler way to convert the readable printer name to something that can be passed to CreateFile.
P.S. 3: This question, to which I have posted an answer, is very related to what I ultimately want to do.
P.S. 4: The other way round is also fine: If it is possible to obtain the readable name from the SP_DEVINFO_DATA structure, that will also be the answer, although a less convenient one.
Below is what I finally have been able to come up with.
Please confirm that SYSTEM\CurrentControlSet\Control\Print\Printers\{0}\PNPData is a supported path, and not just happens to be there in the current implementation, subject to future changes.
There's a little problem with structure alignment, for which I've posted a separate question.
public static class UsbPrinterResolver
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct SP_DEVINFO_DATA
{
public uint cbSize;
public Guid ClassGuid;
public uint DevInst;
public IntPtr Reserved;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct SP_DEVICE_INTERFACE_DATA
{
public uint cbSize;
public Guid InterfaceClassGuid;
public uint Flags;
public IntPtr Reserved;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
private struct SP_DEVICE_INTERFACE_DETAIL_DATA // Only used for Marshal.SizeOf. NOT!
{
public uint cbSize;
public char DevicePath;
}
[DllImport("cfgmgr32.dll", CharSet = CharSet.Auto, SetLastError = false, ExactSpelling = true)]
private static extern uint CM_Get_Parent(out uint pdnDevInst, uint dnDevInst, uint ulFlags);
[DllImport("cfgmgr32.dll", CharSet = CharSet.Auto, SetLastError = false)]
private static extern uint CM_Get_Device_ID(uint dnDevInst, string Buffer, uint BufferLen, uint ulFlags);
[DllImport("cfgmgr32.dll", CharSet = CharSet.Auto, SetLastError = false, ExactSpelling = true)]
private static extern uint CM_Get_Device_ID_Size(out uint pulLen, uint dnDevInst, uint ulFlags);
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetupDiGetClassDevs([In(), MarshalAs(UnmanagedType.LPStruct)] System.Guid ClassGuid, string Enumerator, IntPtr hwndParent, uint Flags);
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, uint MemberIndex, ref SP_DEVINFO_DATA DeviceInfoData);
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, [In()] ref SP_DEVINFO_DATA DeviceInfoData, [In(), MarshalAs(UnmanagedType.LPStruct)] System.Guid InterfaceClassGuid, uint MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, [In()] ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, uint DeviceInterfaceDetailDataSize, out uint RequiredSize, IntPtr DeviceInfoData);
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
private static extern int SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
private const uint DIGCF_PRESENT = 0x00000002U;
private const uint DIGCF_DEVICEINTERFACE = 0x00000010U;
private const int ERROR_INSUFFICIENT_BUFFER = 122;
private const uint CR_SUCCESS = 0;
private const int FILE_SHARE_READ = 1;
private const int FILE_SHARE_WRITE = 2;
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const int OPEN_EXISTING = 3;
private static readonly Guid GUID_PRINTER_INSTALL_CLASS = new Guid(0x4d36e979, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18);
private static readonly Guid GUID_DEVINTERFACE_USBPRINT = new Guid(0x28d78fad, 0x5a12, 0x11D1, 0xae, 0x5b, 0x00, 0x00, 0xf8, 0x03, 0xa8, 0xc2);
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
private static string GetPrinterRegistryInstanceID(string PrinterName) {
if (string.IsNullOrEmpty(PrinterName)) throw new ArgumentNullException("PrinterName");
const string key_template = #"SYSTEM\CurrentControlSet\Control\Print\Printers\{0}\PNPData";
using (var hk = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
string.Format(key_template, PrinterName),
Microsoft.Win32.RegistryKeyPermissionCheck.Default,
System.Security.AccessControl.RegistryRights.QueryValues
)
)
{
if (hk == null) throw new ArgumentOutOfRangeException("PrinterName", "This printer does not have PnP data.");
return (string)hk.GetValue("DeviceInstanceId");
}
}
private static string GetPrinterParentDeviceId(string RegistryInstanceID) {
if (string.IsNullOrEmpty(RegistryInstanceID)) throw new ArgumentNullException("RegistryInstanceID");
IntPtr hdi = SetupDiGetClassDevs(GUID_PRINTER_INSTALL_CLASS, RegistryInstanceID, IntPtr.Zero, DIGCF_PRESENT);
if (hdi.Equals(INVALID_HANDLE_VALUE)) throw new System.ComponentModel.Win32Exception();
try
{
SP_DEVINFO_DATA printer_data = new SP_DEVINFO_DATA();
printer_data.cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
if (SetupDiEnumDeviceInfo(hdi, 0, ref printer_data) == 0) throw new System.ComponentModel.Win32Exception(); // Only one device in the set
uint cmret = 0;
uint parent_devinst = 0;
cmret = CM_Get_Parent(out parent_devinst, printer_data.DevInst, 0);
if (cmret != CR_SUCCESS) throw new Exception(string.Format("Failed to get parent of the device '{0}'. Error code: 0x{1:X8}", RegistryInstanceID, cmret));
uint parent_device_id_size = 0;
cmret = CM_Get_Device_ID_Size(out parent_device_id_size, parent_devinst, 0);
if (cmret != CR_SUCCESS) throw new Exception(string.Format("Failed to get size of the device ID of the parent of the device '{0}'. Error code: 0x{1:X8}", RegistryInstanceID, cmret));
parent_device_id_size++; // To include the null character
string parent_device_id = new string('\0', (int)parent_device_id_size);
cmret = CM_Get_Device_ID(parent_devinst, parent_device_id, parent_device_id_size, 0);
if (cmret != CR_SUCCESS) throw new Exception(string.Format("Failed to get device ID of the parent of the device '{0}'. Error code: 0x{1:X8}", RegistryInstanceID, cmret));
return parent_device_id;
}
finally
{
SetupDiDestroyDeviceInfoList(hdi);
}
}
private static string GetUSBInterfacePath(string SystemDeviceInstanceID) {
if (string.IsNullOrEmpty(SystemDeviceInstanceID)) throw new ArgumentNullException("SystemDeviceInstanceID");
IntPtr hdi = SetupDiGetClassDevs(GUID_DEVINTERFACE_USBPRINT, SystemDeviceInstanceID, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hdi.Equals(INVALID_HANDLE_VALUE)) throw new System.ComponentModel.Win32Exception();
try
{
SP_DEVINFO_DATA device_data = new SP_DEVINFO_DATA();
device_data.cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
if (SetupDiEnumDeviceInfo(hdi, 0, ref device_data) == 0) throw new System.ComponentModel.Win32Exception(); // Only one device in the set
SP_DEVICE_INTERFACE_DATA interface_data = new SP_DEVICE_INTERFACE_DATA();
interface_data.cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA));
if (SetupDiEnumDeviceInterfaces(hdi, ref device_data, GUID_DEVINTERFACE_USBPRINT, 0, ref interface_data) == 0) throw new System.ComponentModel.Win32Exception(); // Only one interface in the set
// Get required buffer size
uint required_size = 0;
SetupDiGetDeviceInterfaceDetail(hdi, ref interface_data, IntPtr.Zero, 0, out required_size, IntPtr.Zero);
int last_error_code = Marshal.GetLastWin32Error();
if (last_error_code != ERROR_INSUFFICIENT_BUFFER) throw new System.ComponentModel.Win32Exception(last_error_code);
IntPtr interface_detail_data = Marshal.AllocCoTaskMem((int)required_size);
try
{
// FIXME, don't know how to calculate the size.
// See https://stackoverflow.com/questions/10728644/properly-declare-sp-device-interface-detail-data-for-pinvoke
switch (IntPtr.Size)
{
case 4:
Marshal.WriteInt32(interface_detail_data, 4 + Marshal.SystemDefaultCharSize);
break;
case 8:
Marshal.WriteInt32(interface_detail_data, 8);
break;
default:
throw new NotSupportedException("Architecture not supported.");
}
if (SetupDiGetDeviceInterfaceDetail(hdi, ref interface_data, interface_detail_data, required_size, out required_size, IntPtr.Zero) == 0) throw new System.ComponentModel.Win32Exception();
// TODO: When upgrading to .NET 4, replace that with IntPtr.Add
return Marshal.PtrToStringAuto(new IntPtr(interface_detail_data.ToInt64() + Marshal.OffsetOf(typeof(SP_DEVICE_INTERFACE_DETAIL_DATA), "DevicePath").ToInt64()));
}
finally
{
Marshal.FreeCoTaskMem(interface_detail_data);
}
}
finally
{
SetupDiDestroyDeviceInfoList(hdi);
}
}
public static string GetUSBPath(string PrinterName) {
return GetUSBInterfacePath(GetPrinterParentDeviceId(GetPrinterRegistryInstanceID(PrinterName)));
}
public static Microsoft.Win32.SafeHandles.SafeFileHandle OpenUSBPrinter(string PrinterName) {
return new Microsoft.Win32.SafeHandles.SafeFileHandle(CreateFile(GetUSBPath(PrinterName), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero), true);
}
}
Usage:
using (var sh = UsbPrinterResolver.OpenUSBPrinter("Zebra Large"))
{
using (var f = new System.IO.FileStream(sh, System.IO.FileAccess.ReadWrite))
{
// Read from and write to the stream f
}
}
Try this (Python code):
import _winreg
HKLM = _winreg.HKEY_LOCAL_MACHINE
#------------------------------------------------------------------------------
def getDevicePath(printerName):
key = _winreg.OpenKey(HKLM,
r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\%s" \
% printerName)
value =_winreg.QueryValueEx(key, "Port")[0]
assert value.startswith("USB"), \
"Port does not start with 'USB': %s" % value
printerPortNumber = int(value.replace(u"USB", u""))
key = _winreg.OpenKey(HKLM,
r"SYSTEM\CurrentControlSet\Control\DeviceClasses" \
r"\{28d78fad-5a12-11d1-ae5b-0000f803a8c2}")
idx = 0
devicePath = None
while True:
try:
subKeyName = _winreg.EnumKey(key, idx)
subKey = _winreg.OpenKey(key, subKeyName)
try:
subSubKey = _winreg.OpenKey(subKey, r"#\Device Parameters")
baseName = _winreg.QueryValueEx(subSubKey, "Base Name")[0]
portNumber = _winreg.QueryValueEx(subSubKey, "Port Number")[0]
if baseName == "USB" and portNumber == printerPortNumber:
devicePath = subKeyName.replace("##?#USB", r"\\?\usb")
break
except WindowsError:
continue
finally:
idx += 1
except WindowsError:
break
return devicePath
Try this ... let me know if this helps ...
static void Main(string[] args)
{
ManagementObjectSearcher s = new ManagementObjectSearcher(#"Select * From Win32_PnPEntity");
foreach (ManagementObject device in s.Get())
{
// Try Name, Caption and/or Description (they seem to be same most of the time).
string Name = (string)device.GetPropertyValue("Name");
// >>>>>>>>>>>>>>>>>>>> Query String ...
if (Name == "O2Micro Integrated MMC/SD controller")
{
/*
class Win32_PnPEntity : CIM_LogicalDevice
{
uint16 Availability;
string Caption;
string ClassGuid;
string CompatibleID[];
uint32 ConfigManagerErrorCode;
boolean ConfigManagerUserConfig;
string CreationClassName;
string Description;
string DeviceID;
boolean ErrorCleared;
string ErrorDescription;
string HardwareID[];
datetime InstallDate;
uint32 LastErrorCode;
string Manufacturer;
string Name;
string PNPDeviceID;
uint16 PowerManagementCapabilities[];
boolean PowerManagementSupported;
string Service;
string Status;
uint16 StatusInfo;
string SystemCreationClassName;
string SystemName;
};
*/
try
{
Console.WriteLine("Name : {0}", Name);
Console.WriteLine("DeviceID : {0}", device.GetPropertyValue("DeviceID"));
Console.WriteLine("PNPDeviceID : {0}", device.GetPropertyValue("PNPDeviceID"));
Console.WriteLine("ClassGuid : {0}", device.GetPropertyValue("ClassGuid"));
Console.WriteLine("HardwareID :\n{0}", JoinStrings(device.GetPropertyValue("HardwareID") as string[]));
Console.WriteLine("CompatibleID :\n{0}", JoinStrings(device.GetPropertyValue("CompatibleID") as string[]));
}
catch (Exception e)
{
Console.WriteLine("ERROR: {0}", e.Message);
}
}
}
}
static string JoinStrings(string[] sarray)
{
StringBuilder b = new StringBuilder();
if (sarray != null)
{
foreach (string s in sarray)
b.Append(" '" + s + "'\n");
}
return b.ToString();
}
Don't have a USB printer to test against, but this provides the information you are looking for (including for USB devices)...
Description : O2Micro Integrated MMC/SD controller
DeviceID : PCI\VEN_1217&DEV_8221&SUBSYS_04931028&REV_05\4&26B31A7F&0&00E5
PNPDeviceID : PCI\VEN_1217&DEV_8221&SUBSYS_04931028&REV_05\4&26B31A7F&0&00E5
ClassGuid : {4d36e97b-e325-11ce-bfc1-08002be10318}
HardwareID :
'PCI\VEN_1217&DEV_8221&SUBSYS_04931028&REV_05'
'PCI\VEN_1217&DEV_8221&SUBSYS_04931028'
'PCI\VEN_1217&DEV_8221&CC_080501'
'PCI\VEN_1217&DEV_8221&CC_0805'
CompatibleID : 'PCI\VEN_1217&DEV_8221&REV_05'
'PCI\VEN_1217&DEV_8221'
'PCI\VEN_1217&CC_080501'
'PCI\VEN_1217&CC_0805'
'PCI\VEN_1217'
'PCI\CC_080501'
'PCI\CC_0805'
Also, for a URI, change the '\'s to '#'s in the URI you are intending of building.
so ..
usb\vid_0a5f&pid_0027\46a072900549\{28d78fad-5a12-11d1-ae5b-0000f803a8c2}
becomes
usb#vid_0a5f&pid_0027#46a072900549#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}
====
As GSerg pointed out that Win32_Printer Class helps with the above code, but doesn't provide the device id.
But if I use Win32_Printer class and print out the "PortName" property, that, for the printers I have installed gives be a port/filename that I can use with CreateFile() and open the device.
e.g.:
Name : Microsoft XPS Document Writer
Description :
DeviceID : Microsoft XPS Document Writer
PNPDeviceID :
PortName : XPSPort:
Name : Fax
Description :
DeviceID : Fax
PNPDeviceID :
PortName : SHRFAX:
Here, writing to "XPSPORT:" or "SHRFAX:" sends data to the printer. What does this do for your USB printer?
Use WinObj from Microsoft to get the specific device name. http://technet.microsoft.com/en-us/sysinternals/bb896657.aspx . This will quickly get you the proper device name to use with CreateFile to write directly to your USB printer or simply writing directly to a USB printer adapter with old school parallel port output for custom circuitry!
To open the port associated with a specific printer, you may need to use ntcreatefile. Use the EnumPrinters function to return a printer_info_2 structure containing the port name for each printer. This port name can then be opened with ntcreatefile (an NT internal version of CreateFile) which is explained here: http://msdn.microsoft.com/en-us/library/bb432380(v=vs.85).aspx
Why does this work? There are three namespace levels in windows NT file/device names and the port name retrieved from EnumPrinters can only be opened with ntcreatefile because it is only in the NT namespace. There may be an equivalent win32 namespace link for certain devices and roundabout ways to match them with a printer name but this is difficult as others have shown in prior answers.
Check out the Global?? folder in WinObj tool to show the symbolic links between win32 namespace and NT namespace on your machine. The old school COM1, COM2, LPT1, etc. device names are simply windows NT namespace symbolic links as well. Google "win32 nt namespace" for a more detailed explanation. (Sorry, but as a new user, I can only post 2 hyperlinks.)
I am not really a c++ guy, but I don't really think trying to generate the device id from the name is the way to go. However, you can
Use EnumPrinters and read the PRINTER_INFO_2 structure to get the driver name, and then read the driver details from registry like in this example.
Generate the name for yourself by finding out the printer details possibly from the PRINTER INFO structures and constructing it correctly. See http://msdn.microsoft.com/en-us/library/windows/hardware/ff553356(v=vs.85).aspx for details.
EDIT: You can actually get names + device instance ids of printers from registry:
HKLM\System\CurrentControlSet\Control\Print\Printers\
The Window-Procedure in the Win32 API must be static \ global function since it cannot take a class-object (the this) parameter. One can of-course use workarounds like a hWnd->object dictionary and such.
I wonder if D has a way to elegantly solve it, like create a tiny member function copy for each object (to call the object's real handler) or anonymous function that I can assign to WNDCLASS.lpfnWndProc (I know there are anonymous functions, but I cannot use the extern(Windows) property on them) ?
Can I do something like this :
class Window {
extern (Windows)
LRESULT delegate (HWND hWnd, UINT msg, WPARAM w, LPARAM l) MyWinProcDelegate;
this() {
MyWinProcDelegate = &Events;
}
extern (Windows)
LRESULT Events (HWND hWnd, UINT msg, WPARAM w, LPARAM l) {
MessageBoxA(null , "Success!!!" , null ,0);
return DefWindowProcA(hWnd, message, wParam, lParam);
}
}
(Omitting the registration\creation\msg-loop...)
The Events() doesn't seem to fire... am I missing something ?
Here I made this for you (based on BCS' answer):
version (Windows)
{
import std.c.windows.windows;
void makeExecutable(ubyte[] code)
{
DWORD old;
VirtualProtect(code.ptr, code.length, PAGE_EXECUTE_READWRITE, &old);
}
}
else
version (linux)
{
import core.sys.posix.sys.mman;
import core.sys.posix.unistd;
static if (!is(typeof(&mprotect)))
extern(C) int mprotect(void*, size_t, int);
void makeExecutable(ubyte[] code)
{
auto pageSize = sysconf(_SC_PAGE_SIZE);
auto address = ((cast(size_t)code.ptr) & ~(pageSize-1));
int pageCount =
(address/pageSize == (address+code.length)/pageSize) ? 1 : 2;
mprotect(cast(void*)address, pageSize * pageCount,
PROT_READ | PROT_WRITE | PROT_EXEC);
}
}
else
static assert(0, "TODO");
R function(A) delegate2function(R, A...)(R delegate(A) d)
{
enum size_t TEMPLATE1 = cast(size_t)0x01234567_01234567;
enum size_t TEMPLATE2 = cast(size_t)0x89ABCDEF_89ABCDEF;
static R functionTemplate(A args)
{
R delegate(A) d;
d.ptr = cast(typeof(d.ptr ))TEMPLATE1;
d.funcptr = cast(typeof(d.funcptr))TEMPLATE2;
return d(args);
}
static void functionTemplateEnd() {}
static void replaceWord(ubyte[] a, size_t from, size_t to)
{
foreach (i; 0..a.length - size_t.sizeof + 1)
{
auto p = cast(size_t*)(a.ptr + i);
if (*p == from)
{
*p = to;
return;
}
}
assert(0);
}
auto templateStart = cast(ubyte*)&functionTemplate;
auto templateEnd = cast(ubyte*)&functionTemplateEnd;
auto templateBytes = templateStart[0 .. templateEnd - templateStart];
// must allocate type with pointers, otherwise GC won't scan it
auto functionWords = new void*[(templateBytes.length / (void*).sizeof) + 3];
// store context in word-aligned boundary, so the GC can find it
functionWords[0] = d.ptr;
functionWords[1] = d.funcptr;
functionWords = functionWords[2..$];
auto functionBytes = (cast(ubyte[])functionWords)[0..templateBytes.length];
functionBytes[] = templateBytes[];
replaceWord(functionBytes, TEMPLATE1, cast(size_t)d.ptr );
replaceWord(functionBytes, TEMPLATE2, cast(size_t)d.funcptr);
makeExecutable(functionBytes);
return cast(typeof(return)) functionBytes.ptr;
}
void main()
{
import std.stdio;
auto context = 42;
void del(string s)
{
writeln(s);
writeln(context);
}
auto f = delegate2function(&del);
f("I am a pretty function");
}
Tested on Windows 32-bit and Linux 64-bit.
How about storing this in the window itself, with SetWindowLong?
One very un-portable solution would be to dynamically create a function that wraps the call. I would do this by writing a function that looks like this:
extern(C) RetType TestFn(Arg arg /* and any others */) {
Class c = cast(Class)(0xDEAD_BEEF);
return c.Method(arg);
}
You can then compile this function as un-optimized PIC, de-compile it, and find a byte sequence that can be mashed into what you need. The end result would be a type (likely a struct) that has a methoud returning a function pointer and that, when constructed, populates an internal void array with the bytes you found from the above step and pokes the object in question into the appropriate places.
A slightly more advanced solution would populate a delegate with both the object and the method pointer so both can be provided to the constructor. An even more advanced solution would template the type and take advantage of knowledge of the C and D calling conventions to dynamically generate the argument forwarding code.