【バグ】WideCharToMultiByte の動作がOSごとに違う!その2

WideCharToMultiByte の動作がOSごとに違う の追試を行いました。
そして、細かい仕様が明らかに…。


まず、こちらがソースコード、今回はNTDLL.DLLの RtlUnicodeToMultiByteSize も使用してみました。

#include "stdafx.h"
#include "windows.h"
#include <stdio.h>

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
//  50220   |JIS
//  50221   |JIS(Allow 1 byte Kana)
//  50222   |JIS(Allow 1 byte Kana)(SO/SI)
  int cp;
  HINSTANCE hDll=LoadLibraryA("NTDLL.dll");
  FARPROC bxx=GetProcAddress(hDll,"RtlUnicodeToMultiByteSize");
  char uni[256]="W\x00i\x00n\x00\x12\xff\x2b\xff\x00\x00",str[512],str2[512];
//Win2K
  char uni2[256]="\x0a\xff\x73\xff\x68\xff\x9d\xff\x32\x00\x6b\x00\x00\x00";
//*ウィン2k
  int r0,r1,r2,r3,r4,r5,r6,r7;
  OSVERSIONINFO OS;
  OS.dwOSVersionInfoSize=sizeof(OS);
  GetVersionEx(&OS);
  cp = 50222;
  r0= WideCharToMultiByte(cp, 0, (wchar_t*)uni, 5, NULL, 0, NULL, NULL);
  r1= WideCharToMultiByte(cp, 0, (wchar_t*)uni, 5, str2, 256, NULL, NULL);
  r2= WideCharToMultiByte(cp, 0, (wchar_t*)uni, 6, NULL, 0, NULL, NULL);
  r3= WideCharToMultiByte(cp, 0, (wchar_t*)uni, 6, str2, 256, NULL, NULL);
  _asm{
      push 0x5*2
      lea eax,uni
      push eax
      lea eax,r4
      push eax
      call bxx 
  }
  _asm{
      push 0x6*2
      lea eax,uni
      push eax
      lea eax,r5
      push eax
      call bxx 
  }

  wsprintfA(str,"OS: %d.%d(Build:%d)\r\n"
               "ExNull 1:%d 2:%d 3:%d\r\n"
               "IncNull 4:%d 5:%d 6:%d\r\n",
          OS.dwMajorVersion,OS.dwMinorVersion,OS.dwBuildNumber,
          r0,r1,r4,r2,r3,r5);
  cp = 50221;
  r0 = WideCharToMultiByte(cp, 0, (wchar_t*)uni2, 6, NULL, 0, NULL, NULL);
  r2 = WideCharToMultiByte(cp, 0, (wchar_t*)uni2, 6, str2, 256, NULL, NULL);
  cp = 50220;
  r4 = WideCharToMultiByte(cp, 0, (wchar_t*)uni2, 6, NULL, 0, NULL, NULL);
  r6 = WideCharToMultiByte(cp, 0, (wchar_t*)uni2, 6, str2, 256, NULL, NULL);

  cp = 50221;
  r1 = WideCharToMultiByte(cp, 0, (wchar_t*)uni2, 7, NULL, 0, NULL, NULL);
  r3 = WideCharToMultiByte(cp, 0, (wchar_t*)uni2, 7, str2, 256, NULL, NULL);
  cp = 50220;
  r5 = WideCharToMultiByte(cp, 0, (wchar_t*)uni2, 7, NULL, 0, NULL, NULL);
  r7 = WideCharToMultiByte(cp, 0, (wchar_t*)uni2, 7, str2, 256, NULL, NULL);
     
  wsprintfA(str+lstrlenA(str),"ExNull2 7:%d 8:%d 9:%d 10:%d\r\n"
        "IncNull2 11:%d 12:%d 13:%d 14:%d",r0,r2,r4,r6,r1,r3,r5,r7);
  MessageBoxA(0,str,"Result",0);
  return 0;
}

まず、結果から。

wm2
Windows 2000

wm2a
Windows XP

wm2b
Vista

見てのとおり、NULLを入れて、バッファサイズを調べたときの返り値と、実際値を入れたときのサイズに乖離があることが分かります。
でも、XPもVistaも RtlUnicodeToMultiByteSize については、Windows2000と同じ結果を返しています。

サンプルプログラムはこちら

あと、Vistaとの違いですが、NULLまで含んだ文字列の場合は同じ動作をするのですが、含まない場合、Vistaでは、全角文字のエスケースが行われるのに対して、2000/XPでは行われないということがわかりました。
問題としては、終端が無いので、次に半角文字が来た場合結合文字列が化ける可能性がある ということです。

つまり、修正を入れるならば、
・十分なバッファを確保して、実際に変換を行う。
・変換後の文字列のサイズを取得。
・バッファの解放。
・値を返す。

そんなわけでkdw 0.78jでパッチを当てた、kernel32.dllを作ってみた。
wm2c
パッチ後の結果。完璧!

almx
エンコに失敗するという ALM2Thunderbird 1.0

almx2
kernel32.dllラッパーを入れたバージョンでは問題なし!

関連記事:
WideCharToMultiByte の動作がOSごとに違う その1

関連サイト:
Windows XPでWideCharToMultiByte関数を使い、ISO-2022-JPに変換するときの注意点 | 山本隆の開発日誌
komatの古往今来: ALM2Thunderbird 1.0の動作OSについて

おすすめ

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です