[reply cross-posted to Module::Build list; reply-to header set to perl-xs@perl.org ]

On Sep 3, 2008, at 6:23 AM, Sisyphus wrote:

Basically, if you have a perl dll (by which I mean, eg, the Digest::SHA dll) and you want to directly access a function from within that dll using XS or Inline::C, how do you do that ?

The brute-force way is cram function addresses through Perl variables, as suggested at <http://www.perlmonks.org/?node_id=691174> and described in the Time::HiRes POD documentation at <http://perldoc.perl.org/Time/HiRes.html#C-API >. The latest version of Lingua::Stem::Snowball (0.952) takes this route so that svn trunk for KinoSearch can access those addresses. (The API isn't public yet, but I can get away with private API access because I maintain both distros.) The XS BOOT: section which does the exporting for L::S::Snowball is pasted below my sig.

The advantage of this technique is that it's highly portable. The big disadvantage is that it doesn't scale. You also have to worry about C symbol conflicts and you have to use function pointers rather than real C functions.

With most Windows dll's, you can just use LoadLibrary() to get a handle to the dll, and then use GetProcAddress() to return the address of the function you want to access.

For the first part, there's no problem - one can easily use LoadLibrary() to return a handle to SHA.dll. It's the second part that presents the problem. GetProcAddress() can only return the address of functions/variables that are *exported* ... and most of the functions in SHA.dll (eg 'hashsize') are *not* exported.

When the Windows DLL is linked, it has to be told what symbols should be exportable. The process is analogous to exporting functions from Perl modules using Exporter:

  package Foo;
  use base qw( Exporter );
  BEGIN { our @EXPORT_OK = qw( do_stuff ); }

The problem is that the way you tell the linker to export symbols is platform-specific. For MSVC, the interface is described at <http://msdn.microsoft.com/en-us/library/z4zxe9k8(VS.80).aspx >:

  You can export functions from a DLL using two methods:

    * Create a module definition (.def) file and use the .def
      file when building the DLL. Use this approach if you want
      to export functions from your DLL by ordinal rather than
      by name.
* Use the keyword __declspec(dllexport) in the function's definition.

The .def file is a text file that's analogous to the @EXPORT_OK array. The format is described at <http://msdn.microsoft.com/en-us/library/d91k01sh(VS.80).aspx >.

What might be nice is to be able to feed a list of C function names to ExtUtils::CBuilder or Module::Build (which would pass to CBuilder), and have CBuilder generate the .def file. We'd then have covered symbol exporting both unixen (easy if cryptic: use Dynaloader rather than XSLoader and put C<sub dl_load_flags {0x01}> in your Perl module code) and MSVC. That would cover most Perl installations out there.

Marvin Humphrey
Rectangular Research
http://www.rectangular.com/

=============================================================
In Snowball.xs:

BOOT:
{
    SV *sb_stemmer_list_sv   = newSViv(PTR2IV(sb_stemmer_list));
    SV *sb_stemmer_new_sv    = newSViv(PTR2IV(sb_stemmer_new));
    SV *sb_stemmer_delete_sv = newSViv(PTR2IV(sb_stemmer_delete));
    SV *sb_stemmer_stem_sv   = newSViv(PTR2IV(sb_stemmer_stem));
    SV *sb_stemmer_length_sv = newSViv(PTR2IV(sb_stemmer_length));
hv_store(PL_modglobal, "Lingua::Stem::Snowball::sb_stemmer_list", 39,
        sb_stemmer_list_sv, 0);
hv_store(PL_modglobal, "Lingua::Stem::Snowball::sb_stemmer_new", 38,
        sb_stemmer_new_sv, 0);
hv_store(PL_modglobal, "Lingua::Stem::Snowball::sb_stemmer_delete", 41,
        sb_stemmer_delete_sv, 0);
hv_store(PL_modglobal, "Lingua::Stem::Snowball::sb_stemmer_stem", 39,
        sb_stemmer_stem_sv, 0);
hv_store(PL_modglobal, "Lingua::Stem::Snowball::sb_stemmer_length", 41,
        sb_stemmer_length_sv, 0);
}



Reply via email to