当您指定返回值为字符串时,Win32::API 假定它以值为 0 的字节终止,但是在 UTF-16le 文本中,具有该值的字节很常见。
如同 Win32::API 建议的那样,您应该使用 N
类型(或 64 位构建上的 Q
)将指针作为数字返回,然后自己读取指向的内存。Win32::API 提供了 ReadMemory
来读取内存,但它需要知道要读取多少内存。对于以 NUL 终止的字符串和宽 NUL 终止的字符串,这没有用。
对于宽 NUL 终止的字符串,Win32::API 提供了 SafeReadWideCString
。但是 SafeReadWideCString
可能会在出错时返回与输入无关的字符串,因此我会使用自己的 decode_LPCWSTR
。
use strict;
use warnings;
use feature qw( say state );
use open ':std', ':encoding('.do { require Win32; "cp".Win32::GetConsoleOutputCP() }.')';
use Config qw( %Config );
use Encode qw( decode encode );
use Win32::API qw( ReadMemory );
use constant PTR_SIZE => $Config{ptrsize};
use constant PTR_PACK_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");
sub lstrlenW {
my ($ptr) = @_;
state $lstrlenW = Win32::API->new('kernel32', 'lstrlenW', PTR_WIN32API_TYPE, 'i')
or die($^E);
return $lstrlenW->Call($ptr);
}
sub decode_LPCWSTR {
my ($ptr) = @_;
return undef if !$ptr;
my $num_chars = lstrlenW($ptr)
or return '';
return decode('UTF-16le', ReadMemory($ptr, $num_chars * 2));
}
sub LocalFree {
my ($ptr) = @_;
state $LocalFree = Win32::API->new('kernel32', 'LocalFree', PTR_WIN32API_TYPE, PTR_WIN32API_TYPE)
or die($^E);
return $LocalFree->Call($ptr) == 0;
}
sub GetCommandLine {
state $GetCommandLine = Win32::API->new('kernel32', 'GetCommandLineW', '', PTR_WIN32API_TYPE)
or die($^E);
return decode_LPCWSTR($GetCommandLine->Call());
}
sub CommandLineToArgv {
my ($cmd_line) = @_;
state $CommandLineToArgv = Win32::API->new('shell32', 'CommandLineToArgvW', 'PP', PTR_WIN32API_TYPE)
or die($^E);
my $cmd_line_encoded = encode('UTF-16le', $cmd_line."\0");
my $num_args_buf = pack('i', 0);
my $arg_ptrs_ptr = $CommandLineToArgv->Call($cmd_line_encoded, $num_args_buf)
or return undef;
my $num_args = unpack('i', $num_args_buf);
my @args =
map { decode_LPCWSTR($_) }
unpack PTR_PACK_FORMAT.'*',
ReadMemory($arg_ptrs_ptr, PTR_SIZE * $num_args);
LocalFree($arg_ptrs_ptr);
return \@args;
}
{
my $cmd_line = GetCommandLine();
say $cmd_line;
my $args = CommandLineToArgv($cmd_line)
or die("CommandLineToArgv: $^E\n");
for my $arg (@$args) {
say "<$arg>";
}
}
Unicode::String
和Encode::decode
。以下是我尝试的代码,并描述了我遇到的问题。这将有助于我们编写更准确的答案,而您的问题的主要价值在于可能正在寻找类似问题的许多其他人。use Unicode::String qw(utf8); my $string = "你好"; my $unicode_string = utf8($string);
我尝试使用Unicode::String
将字符串转换为Unicode格式,但它没有按预期工作。然后我尝试使用Encode::decode
,但也没有成功。 - Borodin