2017-06-11 26 views
3

wide char and win32::apiで提供される答えは、の場合、Win APIに utf-16を渡します。しかし、私はどのようにutf16文字列を変換するのですか?はWin APIによってを返しましたか? (私はGetCommandLineWを使用しようとしています)。Win32 :: APIによって返されるワイドchar値の処理

私は成功せずUnicode::StringEncode::decodeの両方を試してみました。おそらく、データを最初に圧縮したり解凍したりする必要があると思いますが、どうすればよいでしょうか?

その後、次の問題は、CommandLineToArgvWによって返されたようなutf16へのポインタへの対処方法です。

ありがとうございました。

+1

あなたが試したもの、予想したもの、そしてあなたが得たもののコードを投稿してください。 – andlabs

+0

@andlabs、私はその質問を理解していますが、私はOPが提供できることはそれほど多くないことを知っています。私は答えを書いている途中です。 – ikegami

+0

は*あなたがしようとしたまさに表示され、あなたが持っていた問題を記述するためのコードを記載してください*「私は 'はUnicode :: STRING'と'エンコード:: decode'の両方を試してみました」。それは私たちがより正確な答えを書くのに役立ちます。あなたの質問の主な価値は、同様の問題の解決策を探している多くの人々にとってです。 「成功しなければ」というのは問題の声明ではなく、あなたの状況がそれと一致するかどうかを判断することは不可能です。 – Borodin

答えて

3

これは良い質問です。戻り値が文字列であると指定すると、Win32 :: APIは値0のバイトで終了するとみなしますが、その値を持つバイトはUTF-16leのテキストで共通です。

Win32 :: APIの示唆しているように、Nタイプ(または、64ビットビルドではQ)を使用してポインタを数値として取得し、次にポイントメモリを読んでください。 Win32 :: APIはメモリを読み取るのにReadMemoryを提供しますが、読み取るメモリの量を知る必要があります。これは、NUL終了文字列とNUL終了文字列には役に立ちません。

この後者の場合、Win32 :: APIは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 decode_LPCWSTR { 
    my ($ptr) = @_; 

    state $lstrlenW = Win32::API->new('kernel32', 'lstrlenW', PTR_WIN32API_TYPE, 'i') 
     or die($^E); 

    return undef if !$ptr; 

    my $num_chars = $lstrlenW->Call($ptr) 
     or return ''; 

    return decode('UTF-16le', ReadMemory($ptr, $num_chars * 2)); 
} 


# Returns true on success. Returns false and sets $^E on error. 
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()); 
} 


# Returns a reference to an array on success. Returns undef and sets $^E on error. 
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); # Allocate space for an "int". 

    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>"; 
    } 
} 
+0

答えるための 'CommandLineToArgv'の実装が追加されました。 – ikegami

+0

Perlの32ビットビルドと64ビットビルドの両方が正しいように修正されました。 – ikegami

+0

このような明確で有用な実装をしていただきありがとうございます。これは、Win32 :: APIを効果的に使用するために必要な概念をうまく示しています。私は、ほとんどの目的のために十分な、おそらく効率的であること)decode_LPCWSTRの交換を(書いた: 'code' サブdecode_LPCWSTR { 状態$ lstrlenW =のWin32 :: API->新しい( 'KERNEL32'、 'lstrlenW'、PTR_WIN32API_TYPE ' N ') またはdie($^E); my($ ptr)= @_; !$ ptrの場合はundefを返します。 私の$ nchars = $ lstrlenW->コール($のPTR); $ nchars == 0の場合は ''を返します。 私の$ SW = ReadMemory($ PTR、$ nchars * 2)。 return decode( 'UTF-16le'、$ sW); } –

関連する問題