【问题标题】:Perl Win32::API and PointersPerl Win32::API 和指针
【发布时间】:2011-07-28 15:30:41
【问题描述】:

我正在尝试使用 Perl 的 Win32::API 模块来利用 Win32 API 函数 DsGetSiteName()。根据 Windows SDK,DsGetSiteName 的函数原型是:

DWORD DsGetSiteName(LPCTSTR ComputerName, LPTSTR *SiteName)

我使用此 API 成功编写了一个小型 C++ 函数,以便更好地了解它的实际工作原理(我正在自学 C++,但我跑题了)。

无论如何,根据我对API文档的理解,第二个参数应该是一个指向变量的指针,该变量接收指向字符串的指针。在我的 C++ 代码中,我这样写:

LPSTR site;
LPTSTR *psite = &site;

并已使用 psite 指针成功调用 API。

现在我的问题是,有没有办法使用 Perl 的 Win32::API 来做同样的事情?我尝试了以下 Perl 代码:

my $site = " " x 256;
my $computer = "devwin7";

my $DsFunc = Win32::API->new("netapi32","DWORD DsGetSiteNameA(LPCTSTR computer, LPTSTR site)");
my $DsResult = $DsFunc->Call($computer, $site);
print $site;

$DsResult 中调用的结果为零(表示成功),但 $site 中的数据不是我想要的,它看起来是 ASCII 和不可打印字符的混合体。

$site 变量可以保存分配字符串的指针地址吗?如果是这样,有没有办法使用 Win32::API 取消引用该地址以获取字符串?

提前致谢。

【问题讨论】:

    标签: c++ perl winapi types


    【解决方案1】:

    Win32::API 无法处理 char**。您需要自己提取字符串。

    use strict;
    use warnings;
    use feature qw( say state );
    
    use Encode     qw( encode decode );
    use Win32::API qw( );
    
    use constant {
       NO_ERROR                => 0,
       ERROR_NO_SITENAME       => 1919,
       ERROR_NOT_ENOUGH_MEMORY => 8,
    };
    
    use constant PTR_SIZE => $Config{ptrsize};
    
    use constant PTR_FORMAT =>
         PTR_SIZE == 8 ? 'Q'
       : PTR_SIZE == 4 ? 'L'
       : die("Unrecognized ptrsize\n");
    
    use constant PTR_WIN32API_TYPE =>
         PTR_SIZE == 8 ? 'Q'
       : PTR_SIZE == 4 ? 'N'
       : die("Unrecognized ptrsize\n");
    
    # Inefficient. Needs a C implementation.
    sub decode_LPCWSTR {
       my ($ptr) = @_;
    
       return undef if !$ptr;
    
       my $sW = '';
       for (;;) {
          my $chW = unpack('P2', pack(PTR_FORMAT, $ptr));
          last if $chW eq "\0\0";
          $sW .= $chW;
          $ptr += 2;
       }
    
       return decode('UTF-16le', $sW);   
    }
    
    
    sub NetApiBufferFree {
       my ($Buffer) = @_;
    
       state $NetApiBufferFree = Win32::API->new('netapi32.dll', 'NetApiBufferFree', PTR_WIN32API_TYPE, 'N')
          or die($^E);
    
       $NetApiBufferFree->Call($Buffer);
    }
    
    
    sub DsGetSiteName {
       my ($ComputerName) = @_;
    
       state $DsGetSiteName = Win32::API->new('netapi32.dll', 'DsGetSiteNameW', 'PP', 'N')
          or die($^E);
    
       my $packed_ComputerName = encode('UTF-16le', $ComputerName."\0");
       my $packed_SiteName_buf_ptr = pack(PTR_FORMAT, 0);
    
       $^E = $DsGetSiteName->Call($packed_ComputerName, $packed_SiteName_buf_ptr)
          and return undef;
    
       my $SiteName_buf_ptr = unpack(PTR_FORMAT, $packed_SiteName_buf_ptr);
    
       my $SiteName = decode_LPCWSTR($SiteName_buf_ptr);
    
       NetApiBufferFree($SiteName_buf_ptr);
    
       return $SiteName;
    }
    
    
    {
        my $computer_name = 'devwin7';
    
        my ($site_name) = DsGetSiteName($computer_name)
           or die("DsGetSiteName: $^E\n");
    
        say $site_name;
    }
    

    decode_LPCWSTR 之外的所有内容都未经测试。

    我使用 WIDE 接口而不是 ANSI 接口。使用 ANSI 接口是不必要的限制。

    PS — 我编写了 John Zwinck 链接到的代码。

    【讨论】:

    • 非常感谢 ikegami 的解决方案!打包/解包功能是我特别需要的。
    • @Eugene C., ."\0" 不见了。
    【解决方案2】:

    我认为您对 $site 持有字符串地址的看法是正确的。下面是一些演示如何在 Perl 的 Win32 模块中使用输出参数的代码: http://www.perlmonks.org/?displaytype=displaycode;node_id=890698

    【讨论】:

    • 谢谢。您的链接将我引向 Perl 的打包/解包功能,我对此进行了研究,这正是我所需要的,直到 ikegami 发布了一个解决方案。 :)
    猜你喜欢
    • 2021-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多