This is a followup to earlier thread Calling a function form an external DLL with Inline::C on windows.

With the help of some wise monks I was able to hack together an Inline::C-based solution to my original problem. However, during that process I was sidetracked to Win32::API for a while, and I encountered some strange phenomena with that module. I'll list them here, and I'd like to have comments on them.

1) The documentation for the Win32::API module lists several methods for accessing a function from a library.

Out of these, I've tried Win32::API->Import first, because it seemed that it allows for a simpler syntax when calling the function.
However, the script exited with an error message the first time it tried to call a function thus imported:

Can't call method "Call" on an undefined value at (eval 4) line 2.
(See an example in the previous thread)

The DLL's path and the function's name were correct, because the other methods (Win32::API->new) were able to find the DLL and load the function from it.

2) So I had to use Win32::API->new. According to the documentation, the preferred method is to use the full C function prototype:
my $DLL = 'D:\path\to\CrappyLibrary.dll'; my $IP = "192.168.186.140"; my $RegisterClient2 = Win32::API->new($DLL, 'int RegisterClient2(int * +pnClientId, char *pszIPAddress)'); ... my $result = $RegisterClient2->Call($client_id, $IP);

This caused the script to crash.
I realized that the DLL was built with the _cdecl convention, and that caused the crash. So I wanted to include the calling convention in the prototype:
my $RegisterClient2 = Win32::API->new($DLL, 'int _cdecl RegisterClient +2(int *pnClientId, char *pszIPAddress)');

But it had no effect.

4) There is a third method, said to be deprecated by the documentation:
my $RegisterClient2 = Win32::API->new($S{DLL_path}, 'RegisterClient2', + 'PP', 'I', '_cdecl'); my $result = $RegisterClient2->Call($client_id, $IP);

This finally worked without crashing.
There was an other problem, though.
The value returned in $client_id was not what I expected.
I had to do an extra $client_id = unpack "i", $client_id; to get the value I really wanted.

And there is an other thing. The module documentation says that the variables thus passed to a function must be initialized (to ensure that enough memory is allocated). So I inserted my $client_id = 0 before calling the function.
No good; it crashed. However, when I changed to 0 to something non-zero, it worked. What's wrong with 0 as initial value?

5) The documentation says that if a parameter of the C function is a pointer, the corresponding part in the Perl calling statement must be a variable, not a constant expression.
It seems that it can't be a hash element, either.

my $hash{IP} = "192.168.186.140"; my $result = $RegisterClient2->Call($client_id, $hash{IP}); # fails

6) And finally, the problem that caused me to abandon Win32::API altogether:

I had a function that had a prototype like this:
int GetServerData2(int nClientId, int *pnSetup, double *pdTime,double +*pdX, double *pdY, double *pdZ, double *pdTotal)

So I imported it like this:
my $GetServerData2 = Win32::API->new($S{DLL_path}, 'GetServerData2', ' +IPPPPPP', 'I', '_cdecl');

When I called it:
my ($time, $Ex, $Ey, $Ez, $Eabs, $SAR); $time = $Ex = $Ey = $Ez = $Eabs = 5.0; my $result = $GetServerData2->Call($client_id, $read_setup_no, $time, +$Ex, $Ey, $Ez, $Eabs);

I got back garbage in the $Ex, etc. variables. I tried unpacking them ($Ex = unpack "d", $Ex), but to no avail. The garbage didn't look like any meaningful double.

As this step (getting data from the server) were to be the point of the entire application, the failure of it meant that I had to abandon this entire approach (luckily, Inline::C came to the rescue).

Are these really known limitations of the Win32::API module, or is it I who is doing it wrong?

In reply to Calling a function from an external DLL with Win32::API on windows: paranormal activity by kikuchiyo

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.