in reply to Generating a windows Device Interface Path in perl? SOLVED!

Win32::API and Inline::C should be able to help out here. Here's an Inline::C demo (based on some C code I found in my msdn documentation):
use warnings; use Inline C => Config => LIBS => '-lSetupAPI', BUILD_NOISY => 1; use Inline C => <<'EOC'; #include <windows.h> #include <setupapi.h> #include <stdio.h> #include <devguid.h> #include <regstr.h> int wrap_SetupDiGetClassDevs() { HDEVINFO hDevInfo; // Create a HDEVINFO with all present devices. hDevInfo = SetupDiGetClassDevs(NULL, 0, // Enumerator 0, DIGCF_PRESENT | DIGCF_ALLCLASSES ); if (hDevInfo == INVALID_HANDLE_VALUE) croak("INVALID_HANDLE_VALUE returned"); return hDevInfo; } void wrap_SetupDiDestroyDeviceInfoList(int hDevInfo) { SetupDiDestroyDeviceInfoList(hDevInfo); } void foo(int hDevInfo) { INLINE_STACK_VARS; SP_DEVINFO_DATA DeviceInfoData; DWORD i, DataT, buffersize; LPTSTR buffer; // Enumerate through all devices in Set. DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); INLINE_STACK_RESET; for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i, &DeviceInfoData);i++) { buffer = NULL; buffersize = 0; // // Call function with null to begin with, // then use the returned buffer size // to Alloc the buffer. Keep calling until // success or an unknown failure. // while (!SetupDiGetDeviceRegistryProperty( hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC, &DataT, (PBYTE)buffer, buffersize, &buffersize)) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { // Change the buffer size. if (buffer) LocalFree(buffer); buffer = LocalAlloc(LPTR,buffersize); } else croak("ERROR 1 in sub foo"); } INLINE_STACK_PUSH(sv_2mortal(newSVpv(buffer, 0))); if (buffer) LocalFree(buffer); } if ( GetLastError()!=NO_ERROR && GetLastError()!=ERROR_NO_MORE_ITEMS ) croak("ERROR 2 in sub foo"); INLINE_STACK_DONE; INLINE_STACK_RETURN(i); } EOC # perl code starts here: my $handle = wrap_SetupDiGetClassDevs(); my @devices = foo($handle); # clean up wrap_SetupDiDestroyDeviceInfoList($handle); print $_, "\n" for @devices;
Feel free to modify the code so that it does what you want. I don't think MinGW contains the requisite C header files, so you would need an MS compiler to run that code. And because the code passes a handle around, the compiler probably needs to be VC 6 (not VC 7 or 8) - assuming that your perl was built with VC 6.

So ... it may not be all that useful to you ... but it kept me occupied for a while :-)

Cheers,
Rob

Replies are listed 'Best First'.
Enumeratng Windows CDROM devices
by tlhackque (Beadle) on May 31, 2018 at 19:55 UTC

    This code was very helpful. It does have some drawbacks, such as casting pointers to and from int, and not enumerating interfaces.

    Below is a require file that builds on the ideas and exports a single function, which I use to provide a sensible UI for CDROM devices. Of course, it can be modified for other uses. I don't claim it's perfect, but I thought that in exchange for the excellent starting point, I should give back. Note that it is customized for CDROMs; for other disks, you'll want to retrieve the partition number and for non-storage, do something else...

    The code may be freely used and modified - no warranty, but please follow the usual rules: credit the source, add a comment if you make any modifications, and do not remove the copyright. Enjoy.

    # CD drive information # Copyright (C) 2018 Timothe Litt litt _at acm ddot org # # This code may be freely used and modified. There is no # warranty - use at your own risk. You may not remove # the copyright. If you make any changes, add a comment # identifying yourself and the change. # use 5.10.0; use warnings; use strict; package CdInfo; our $VERSION = '1.001'; my $gitid = '$Format:%H$'; our $cdate = '$Format:%cD$'; $VERSION .= ", commit $gitid" if( $gitid !~ /^\044Format:.*\$$/ ); use Exporter; our @ISA = qw/Exporter/; our @EXPORT_OK = qw/cdinfo/; use File::Find; my $libpath; my $incpath; $libpath = '' unless( $libpath ); $incpath = '' unless( $incpath ); BEGIN { find( { wanted => sub { /^libsetupapi\.a$/i && ($libpath ||= $File::Find::dir); /^setupapi\.h$/i && ($incpath ||= $File::Find::dir); }, follow => 1, follow_skip => 2 }, @INC ); die( "No MinGW libsetupapi $libpath $incpath\n" ) unless( $libpath && $incpath ); } use Inline( C => Config => libs => "-L$libpath -lSetupAPI", INC => "-I$incpath", # print_info => 1, # BUILD_NOISY => 1, ); use Inline 'C'; Inline->init; 1; __DATA__ __C__ #include <windows.h> #include <stdio.h> #include <initguid.h> #include <devguid.h> #include <regstr.h> #include <setupapi.h> #include <WinioCtl.h> /* Error codes can be looked-up at * https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v +=vs.85).aspx */ /* Returns an array that may be suitable for initializing a hash. * Each CDROM device returns two elements. * The second is always the "Friendly" name of the device. * item selects the first: * 0 - The storage device number (note: not persisted across reboots) * 1 - The device path (suitable for calling CreateFile) * * '' is returned if he value can't be obtained or item is out of rang +e. * Multiple errors will, of course, be replaced by the last entry if * used as a hash initializer. In that case, to get all the errors, * inspect the array. */ void cdinfo( int item ) { INLINE_STACK_VARS; HDEVINFO hDevInfo; SP_DEVINFO_DATA DeviceInfoData; DWORD i, n = 0, DataT; INLINE_STACK_RESET; static GUID GUID_DEVINTERFACE_CDROM = { 0x53F56308, 0xB6BF, 0x11D0, {0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B } }; /* Create a set of all present CDROM devices. */ hDevInfo = SetupDiGetClassDevs( &GUID_DEVINTERFACE_CDROM, NULL, NU +LL, DIGCF_PRESENT | DIGCF_DEVICEINTERF +ACE ); if( hDevInfo == INVALID_HANDLE_VALUE) { char buf[132]; sprintf( buf, "cdinfo: SetupDiGetClassDevs error %08x", GetLast +Error() ); croak( buf ); } /* Enumerate all devices in set. */ memset( &DeviceInfoData, 0, sizeof(DeviceInfoData ) ); DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); i = 0; while( SetupDiEnumDeviceInfo( hDevInfo, i++, &DeviceInfoData ) ) { SP_DEVICE_INTERFACE_DATA ifdata; DWORD j = 0; memset( &ifdata, 0, sizeof( ifdata )); ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); while( SetupDiEnumDeviceInterfaces( hDevInfo, &DeviceInfoData, &GUID_DEVINTERFACE_CDROM, +j++, &ifdata ) ) { PSP_DEVICE_INTERFACE_DETAIL_DATA detail = NULL; LPTSTR buffer = NULL; DWORD buffersize = 0; SP_DEVINFO_DATA DevInfoData; HANDLE devhandle; STORAGE_DEVICE_NUMBER devnum; DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA); /* Obtain device path name (\\?\bus...) */ if( !SetupDiGetDeviceInterfaceDetail( hDevInfo, &ifdata, NULL, 0, &buffersize +, NULL ) && GetLastError() != ERROR_INSUFFICIENT_BUFFER ) { char buf[132]; sprintf( buf, "cdinfo: SetupDiGetDeviceInterfaceDetail erro +r %08x", GetLastError() ); croak( buf ); } detail = (PSP_DEVICE_INTERFACE_DETAIL_DATA) LocalAlloc( LPTR, buffersize ); detail->cbSize = sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA +); if( !SetupDiGetDeviceInterfaceDetail( hDevInfo, &ifdata, detail, buffersize, + NULL, &DevInfoData ) ) { char buf[132]; sprintf( buf, "cdinfo: SetupDiGetDeviceInterfaceDetail err +or %08x", GetLastError() ); if( detail ) LocalFree( detail ); croak( buf ); } switch( item ) { case 0: devhandle = CreateFile( detail->DevicePath, 0, FILE_SHARE_DELETE|FILE_SHARE_ +READ| FILE_SHARE_ +WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ) +; if( devhandle == INVALID_HANDLE_VALUE || ! DeviceIoControl( devhandle, IOCTL_STORAGE_GET_DEVICE_NUMBE +R, NULL, 0, &devnum, sizeof( devn +um ), &buffersize, NULL ) ) { INLINE_STACK_PUSH(sv_2mortal(newSVpv("", 0))); } else { INLINE_STACK_PUSH(sv_2mortal(newSVuv(devnum.Devic +eNumber))); } if( devhandle != INVALID_HANDLE_VALUE ) CloseHandle( devhandle ); break; case 1: INLINE_STACK_PUSH(sv_2mortal(newSVpv(detail->DevicePa +th, 0))); break; default: INLINE_STACK_PUSH(sv_2mortal(newSVpv("", 0))); break; } ++n; LocalFree( detail ); detail = NULL; buffersize = 0; while( !SetupDiGetDeviceRegistryProperty( hDevInfo, &DevInfoData, SPDRP_FRIENDLYNAME, &DataT, (PBYTE)buffer, buffersize, &buffersize)) { if( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) { /* Increase the buffer size. */ if( buffer) LocalFree(buffer); buffer = (LPTSTR)LocalAlloc( LPTR, buffersize ); } else { char buf[132]; sprintf( buf, "cdinfo: SetupDiGetDeviceRegistryPropert +y error %08x", GetLastError() ); if( buffer) LocalFree(buffer); croak(buf); } } SetLastError( 0 ); INLINE_STACK_PUSH(sv_2mortal(newSVpv(buffer, 0))); ++n; if( buffer) LocalFree(buffer); buffer = NULL; buffersize = 0; } if( GetLastError() != NO_ERROR && GetLastError() != ERROR_NO_MORE_ITEMS ) { char buf[132]; sprintf( buf, "cdinfo: SetupDiEnumDeviceInterfaces error %08x", GetLastError() ); croak(buf); } } if( GetLastError() != NO_ERROR && GetLastError() != ERROR_NO_MORE_ITEMS ) { char buf[132]; sprintf( buf, "cdinfo: SetupDiEnumDeviceInfo error %08x", GetLastEr +ror() ); croak(buf); } SetupDiDestroyDeviceInfoList(hDevInfo); INLINE_STACK_DONE; INLINE_STACK_RETURN(n); }
Re^2: Generating a windows Device Interface Path in perl?
by cmv (Chaplain) on Sep 22, 2007 at 15:02 UTC
    Rob-

    Wow! Great response. I've never knew about Inline before -- pretty cool stuff. Thanks for the time and effort!

    I can see me playing around with this for a while, but I wonder if there isn't already some windows command that either does this, or gets me a bit closer!?! If not, it may be most efficient just to build one, and call that from the perl script.

    Either way, I sure am learning a lot!

    -Craig