1.VBでの文字列 |
VB内部では右の図上段のように、文字列は全てUnicodeで処理されています。マス目は1マス2バイト(Nullも"00 00")です。そして、ダークグレーの場所には文字列の長さが書込まれます。これによって、文字列の途中にNull文字が含まれていても正常に認識されるのです。 対して、Unicodeが登場する以前使用され、現在でも使用され続けている1バイトAscii文字、2バイトマルチバイト文字(漢字など)(総称してANSI文字)は、マス目は1マス1バイトです。VBから文字列に関係したAPIを呼び出す場合、ANSI文字列を使用する必要があります。ですから、関数名は"〜A"、引数の型には"ByVal ... As String"とするのです。 また、文字列を使用するAPIの文字列に対応する変数の型として、Byte配列もあります。これは、右図下段のように文字列をバイト配列として処理するものです。この時、値渡し(ByVal)ではなく参照渡し(ByRef、もしくは"As Any")で呼び出す必要があります。なぜなら、配列の内容を直接読み込む/書込む必要があるからです。引数は、 "ByRef byteStr() As Byte"ではなく(プロシージャの引数ではないので)、 "ByRef byteStr As Byte"もしくは"byteStr As Any"とし、 Dim b(6) As Byte b = StrConv("ばいと", vbFromUnicode) ' ANSI文字列に変換 SomeFunc b(0) ' Declare Sub SomeFunc Lib "a_dll.dll" (ByRef byteStr As Byte) で定義 というようにして使います。もっとも、 Dim r As Long, b(255) As Byte ' 256バイト分の配列を確保 Erase b() ' 配列の初期化(全て0になる) r = GetWindowsDirectoryA(b(0)) MsgBox StrConv(b, vbUnicode) という風に使う方が一般的です。(文字列への変換時にNullを見つける必要がないので。) ※ 図中の「LPSR」は「LPSTR」の間違いです。 |
2.文字列の文字数を数える |
文字列の中に漢字があるか、文字列は何バイトか、VB2.0JではLen()関数を使用すればいいことでした。それはVBの文字列の処理がUnicodeではなくANSI文字列であったからでした。 しかし、VBが4.0になり、マイクロソフトはCDK(OCXの前身VBX、つまりActiveXの2世代前のコントロール開発キット)に使用されていた文字列型をUnicodeに拡張したOLESTR(BSTR)型を作り、OCXに組み込みました。 それに伴い、文字列処理系の関数も増えました(AscW、StrConv、LenBなど)。 そこで問題が生じました。文字列型の長さは、バイト数ではなく、文字数を示すようになったのです。すなわち、"A"も"あ"も同じ長さなのです。それは文字列のバイト数を返すLenBでも同じです。 LenB()は文字列のバイト数を返すのですが、内部がUnicodeである以上返ってくる長さは2の倍数("A"も"あ"も2バイト)です。ですから、結局は文字列に漢字が含まれているかなどはわからないのです。 |
3.本当の文字列の長さは? |
では、一体本当の文字列のバイト数はいくつなのでしょう? 方法は2つ(かな?)あります。 1、APIのlstrlenA()を使う方法。 これは、ANSIAPIに文字列を渡す時文字列がANSI文字列になるのを利用する方法です。 Dim l As Long l = lstrlen("abcあいう") この関数でバイト数が返ってくるのです。 2、VB内部関数を使う方法。 こちらは、バイト配列の特性を生かす方法です。APIは嫌だ、という方に。 Dim l As Long, b() As Byte b = StrConv("abcあいう", vbFromUnicode) l = Len(b()) Len()関数は、C/C++でのsizeof()の代わりにもなります。余談ですが、宣言されたユーザー定義型変数(構造体)の長さを測ると、全体のサイズがわかり、一部のAPIで必要になる構造体の長さを得ることができます。 またこの場合、Ubound()を使用しても同じです。 |
1.文字列をバイト配列に移す |
メモリを扱う第一ステップとして、文字列をバイト配列に移すということをやってみます。 Dim s As String, b() As Byte s = "たとえばこんなの" ReDim b(lstrlen(s)) MoveMemory b(0), ByVal s, Len(b()) これは、 b = StrConv(s, vbFromUnicode) と同じ事です。 ここで、"ByVal"の重要性について少しばかり書きます。このMoveMemoryは、"As Any"で宣言された関数です。この型で宣言された場合、どんな型であろうとも関係無しに渡すことができる代わり、参照渡しとして処理されます。 ですから、文字列を渡す場合や、「ポインタ」を値として渡す場合、"ByVal"が必要になります。 |
2.バイト配列の中身は? |
さて、メモリを移したバイト配列の中身ですが、もちろんUnicodeではなく、Ascii/SJISで構成された文字コードの配列です。 |
3.元に戻して表示する |
バイト配列を元(VB文字列)に戻すのですが、ここでは単純にStrConv()関数を使用します。 s = StrConv(b, vbUnicode) これで普通の文字列に戻ります。 もっと複雑な方法としては、OLE文字列操作関数を使用して文字列を確保し(その場合解放という作業が必要です。)、MultiByteToWideChar()という関数でUnicodeに変換するという方法もあります。 |
4.なぜこんなことを? |
こんなことをするにも、単に Dim Bytes() As Byte, StrVal As String, StrVal2 As String Bytes = Strconv(StrVal, vbFromUnicode) StrVal2 = Strconv(Cstr(Bytes), vbUnicode) とやってしまえばいいだけなのですが...ね。 |