VMat has asked for the wisdom of the Perl Monks concerning the following question:

Hello, I've been trying (for a few days) to use Win32::API and Win32::API::Prototype to import a couple of functions from a DLL, but I can't get it to work properly. I already got a function with no parameters to work, but the other takes a complex struct as a parameter, and I'm obviously missing something. I wonder if you monks can help me out here.

The DLL was written in Delphi ("DiscIdCalcDLL.dll", from http://webplaza.pt.lu/public/vigatol/discidcalc/), and the documentation says:

The GetCdRomDrives scans for Cd-Rom installed on the system and retrie +ves informations over the Cd-Rom drive. function GetCdRomDrives(var CdRoms: TCdRoms): Boolean; Parameters CdRoms Return Values If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE.


The TCdRoms data type is defined as follows:

TCdRom = record HaId : Byte; Target : Byte; Lun : Byte; Vendor : ShortString; ProductId : ShortString; Revision : ShortString; VendorSpec: ShortString; end; TCdRoms = record CdRomCount: Byte; CdRom : array[0..25] of TCdRom; end;


So I did this in Perl:


#!/usr/bin/perl use Win32::API::Prototype; Win32::API::Struct->typedef( TCdRom => qw{ int HaId; int Target; int Lun; char Vendor[256]; char ProductId[256]; char Revision[256]; char VendorSpec[256]; }); Win32::API::Struct->typedef( TCdRoms => qw{ BYTE CdRomCount; TCdRom Drive[26]; }); ApiLink( 'DiscIdCalcDLL.dll', 'int GetCdRomDrives(LPTCdRoms CdRoms)' ) + || die; $cds = Win32::API::Struct->new('TCdRoms'); for (my $i = 0; $i < 26; $i++) { $drvs[$i] = Win32::API::Struct->new('TCdRom'); $cds->{Drive}[$i] = $drvs[$i]; } $x = GetCdRomDrives(\$cds); ########## (ETC) #########################


At this point, $x contains 1, and I'm not sure whether it means "true" or "false" (I would expect -1 for false, 0 for true). And $cds is empty. Using the perl debugger, I get this:

DB<4> x $cds 0 Win32::API::Struct=HASH(0x1c3dc30) 'Drive' => ARRAY(0x1c3e050) 0 Win32::API::Struct=HASH(0x1c12730) '__typedef__' => 'TCdRom' 'typedef' => ARRAY(0x1c12550) 0 ARRAY(0x1c12760) 0 'HaId' 1 'i' 2 'int' 1 ARRAY(0x1c127a8) 0 'Target' 1 'i' 2 'int' 2 ARRAY(0x1c127c0) 0 'Lun' 1 'i' 2 'int' 3 ARRAY(0x1c127fc) 0 'Vendor' 1 'c*256' 2 'char' 4 ARRAY(0x1c12838) 0 'ProductId' 1 'c*256' 2 'char' 5 ARRAY(0x1c12874) 0 'Revision' 1 'c*256' 2 'char' 6 ARRAY(0x1c128b0) 0 'VendorSpec' 1 'c*256' 2 'char' 1 Win32::API::Struct=HASH(0x1c3e158) '__typedef__' => 'TCdRom' 'typedef' => ARRAY(0x1c12550) -> REUSED_ADDRESS 2 Win32::API::Struct=HASH(0x1c3e194) '__typedef__' => 'TCdRom' 'typedef' => ARRAY(0x1c12550) -> REUSED_ADDRESS 3 Win32::API::Struct=HASH(0x1c3e1d0) '__typedef__' => 'TCdRom' 'typedef' => ARRAY(0x1c12550) -> REUSED_ADDRESS 4 Win32::API::Struct=HASH(0x1c3e20c) '__typedef__' => 'TCdRom' 'typedef' => ARRAY(0x1c12550) -> REUSED_ADDRESS ###### Repeat for indexes 5 through 23, and... ####### 24 Win32::API::Struct=HASH(0x1c3e6bc) '__typedef__' => 'TCdRom' 'typedef' => ARRAY(0x1c12550) -> REUSED_ADDRESS 25 Win32::API::Struct=HASH(0x1c3e6f8) '__typedef__' => 'TCdRom' 'typedef' => ARRAY(0x1c12550) -> REUSED_ADDRESS 'Drive[26]' => Win32::API::Struct=HASH(0x1c3e05c) '__typedef__' => 'TCdRom' 'typedef' => ARRAY(0x1c12550) -> REUSED_ADDRESS '__typedef__' => 'TCdRoms' 'typedef' => ARRAY(0x1c12778) 0 ARRAY(0x1c1276c) 0 'CdRomCount' 1 'C' 2 'BYTE' 1 ARRAY(0x1c12988) 0 'Drive[26]' 1 '>' 2 'TCdRom' ###################


I don't like that " 'Drive[26]' => Win32::API::Struct=HASH(0x1c3e05c)" after the drive entries, but I don't know how to get rid of it. So, my best guess is I'm passing something wrong as a parameter to GetCdRomDrives, and the function is returning an error. Or maybe it's not returning an error, but it's not returning what I need either (the TCdRoms structure with 1 or 2 entries defined, as I have a CD-ROM and a DVD-ROM drive installed).

Do you have any clues for me? I'm out of guesses.

Thanks a lot,

VMat

Replies are listed 'Best First'.
Re: Win32::API arguments
by BrowserUk (Patriarch) on Feb 01, 2009 at 18:27 UTC

    Are you sure that your optical drives are SCSI? Because they are the only type that dll looks for. I tried it on my system (which uses IDE drives) and I get no errors, but no information (as you might expect).

    #! perl -slw use strict; use Win32::API::Prototype; ApiLink( 'DiscIdCalcDLL', q[ int GetCdRomDrives( LPVOID p ) ], ) or die $^E; my $struc = chr(0) x ( 4 + 1036*3 ); GetCdRomDrives( $struc ) or die $^E; print "'$_'" for unpack 'v( VVV (a256)4 )3', $struc; __END__ c:\test>DiskId.pl '0' '0' '0' '0' ' ' ' ' '0' '0' '0' ' ' ' ' '0' '0' '0' ' ' ' '

    However, you can query most everything about your system using DBD-WMI (though working out the right classnames can be a pain):

    #! perl -slw use strict; use DBI; my $dbh = DBI->connect('dbi:WMI:'); my $sth = $dbh->prepare(<<WQL); SELECT Caption, Name, Drive FROM Win32_CDROMDrive WQL $sth->execute(); while( my @row = $sth->fetchrow ) { printf "Caption: %25s Name:%30s Drive; %s\n", @row; } __END__ c:\test>junk Caption: HL-DT-ST CD-ROM GCR-8481B Name: HL-DT-ST CD-ROM GCR-8481B D +rive; R: Caption: LITE-ON DVDRW SOHW-1693S Name: LITE-ON DVDRW SOHW-1693S D +rive; D:

    There are a whole bunch of other fields besides the 3 I've queried. They include:

    Availability Capabilities CapabilityDescriptions Caption CompressionMethod ConfigManagerErrorCode ConfigManagerUserConfig CreationClassName DefaultBlockSize Description DeviceID + Drive DriveIntegrity ErrorCleared ErrorDescription ErrorMethodology FileSystemFlags FileSystemFlagsEx Id InstallDate LastErrorCode Manufacturer MaxBlockSize MaximumComponentLength MaxMediaSize MediaLoaded MediaType MfrAssignedRevisionLevel MinBlockSize Name NeedsCleaning NumberOfMediaSupported PNPDeviceID + PowerManagementCapabilities PowerManagementSupported RevisionLevel SCSIBus SCSILogicalUnit SCSIPort SCSITargetId Size Status StatusInfo SystemCreationClassName SystemName TransferRate VolumeName VolumeSerialNumber

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Win32::API arguments
by CptSkripto (Acolyte) on Feb 01, 2009 at 18:28 UTC
    VMat,

    I wanted to reply with full code, but I am missing the ASPI drivers and ... I am not going to mess with it :-).

    A couple of observations:

    1) You have already "told" "Win32::API" that your argument is a pointer (LP prefix) to a struct/record, hence you should just pass the perl scalar and not a reference to it. In other words, use this

    "$x = GetCdRomDrives($cds);" instead of "$x = GetCdRomDrives(\$cds);"

    2) If I understand the documentation correctly you must call the "$x = GetCdRomDrives($cds)" once and then loop through the results:

    foreach $TCdRom (@{$cds->{TCdRom}}){ printf("%s \n", $TCdRom->{HaId}; ... ... }

    Of course I cannot check the code and expect some syntax issues/errors :-) YMMV.

    CptSkripto

Re: Win32::API arguments
by VMat (Novice) on Feb 03, 2009 at 14:49 UTC
    BrowserUK, thank you for the nice tips. I'm gonna try that out this weekend. By the way... the guy's software works fine with my IDE drives (the executable can be downloaded from the same website where the dll is), so I never worried about them being IDE or SCSI. I thought they would be the same through ASPI, wouldn't they?

    CptSkripto, thank you too. 1) I'll remove the backslash and give it a try (this weekend). 2) If I got you right, that's exactly what I did - just ommitted the code from the question to keep it short. But if the function worked, we should see some values set for "Drive[??]" in the debugger output that I pasted.

    I'll try all that on Saturday (at most) and keep you posted.

    Thank you guys,

    VMat
      I thought they would be the same through ASPI, wouldn't they?

      As far as I am aware, no. ASPI stands for the Advanced SCSI Programming Interface, so it seems unlikely.

      Update: I'm wrong! According to wikipedia: Support for ATAPI interface (such as IDE) was later added.. (Shows how long it is since I did anything with ASPI! :)

      However, the problem is that MS chose not to include an ASPI interface in XP/2000, so unless you've installed one yourself (such as the Adaptec freeware implementation), then using ASPI won't work.

      Maybe his program uses other means to detect IDE optical drives?


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        Yes, I do have ASPI installed (as a matter of fact, the "other function" that worked is the one that checks if ASPI is installed).

        His software is very, very simple, I'd doubt he used other interface to IDE drives. But I think the code is all there, so I'll check it out anyway.

        Thanks again!

        VMat