views:

275

answers:

2

Hi,

I am using Win32::API to call an arbitary function exported in a DLL which accepts a C++ structure pointer.

struct PluginInfo {
        int  nStructSize;   
        int  nType;     
        int  nVersion;    
        int  nIDCode;    
        char         szName[ 64 ];  
        char            szVendor[ 64 ];
        int  nCertificate;  
        int  nMinAmiVersion;
};

As we need to use the "pack" function to construct the structure and need to pass an argument

my $name = " " x 64;
my $vendor = " " x 64;
my $pluginInfo = pack('IIIIC64C64II',0,0,0,0,$name,$vendor,0,0);

Its not constructing the structure properly.
It seems that length argument applied to C will gobble those many arguments.
Can some one please suggest the best way to construct this structure form Perl and passon to dll call.

Thanks in advance,
Naga Kiran

+2  A: 

Use Z (NUL-padded string) in your template, as in

my $pluginInfo = pack('IIIIZ64Z64II',0,0,0,0,$name,$vendor,0,0);

Also, take a look at Win32::API::Struct, which is part of the Win32::API module.

Greg Bacon
The z in szName/szVendor implies to me that it should be Z64, not A64; this will put up to 63 bytes of the $name/$vendor into $pluginInfo, nul-padded to 64 bytes.
ysth
Good catch! Edited.
Greg Bacon
+1  A: 

For anything complicated, check out Convert::Binary::C. It may seem daunting at first, but once you realize its power, it's an eye opener.

Update: Let me add a bit of information. You need to have a look at a specific section of the module's manpage for the prime reason to use it. I'll quote it for convenience:

Why use Convert::Binary::C?

Say you want to pack (or unpack) data according to the following C structure:

struct foo {
  char ary[3];
  unsigned short baz;
  int bar;
};

You could of course use Perl's pack and unpack functions:

@ary = (1, 2, 3);
$baz = 40000;  
$bar = -4711;
$binary = pack 'c3 Si', @ary, $baz, $bar;

But this implies that the struct members are byte aligned. If they were long aligned (which is the default for most compilers), you'd have to write

 $binary = pack 'c3 x S x2 i', @ary, $baz, $bar;

which doesn't really increase readability.

Now imagine that you need to pack the data for a completely different architecture with different byte order. You would look into the pack manpage again and perhaps come up with this:

$binary = pack 'c3 x n x2 N', @ary, $baz, $bar;

However, if you try to unpack $foo again, your signed values have turned into unsigned ones.

All this can still be managed with Perl. But imagine your structures get more complex? Imagine you need to support different platforms? Imagine you need to make changes to the structures? You'll not only have to change the C source but also dozens of pack strings in your Perl code. This is no fun. And Perl should be fun.

Now, wouldn't it be great if you could just read in the C source you've already written and use all the types defined there for packing and unpacking? That's what Convert::Binary::C does.

tsee