Win32 API をMindから使う（ワイド文字のケースも含め）

# はじめに

　Mindから Win32 API を呼び出す方法を一つのサンプルを例としてお見せします。
　通常のDLL内関数呼び出しのほか、ワイド文字版の関数呼び出しも行ってみます。後者では引数または戻値がUnicodeになるのでその為の注意点も書きます。

# 通常のDLL内関数呼び出し

　Win32 API と言うものの、実態はDLL（kernel32.dll）に含まれる関数の呼び出しなので、この記事は Win32 API に限らず、一般的なDLL呼び出しの方法としてお読みいただくこともできます。

　サンプルとして使ったのは GetModuleFileName という関数の呼び出しです。プログラム自身のプログラム名（プロセス名）を得ることができます。
　プログラム自身の名前なぞプログラマが分かっているはずなのに、こんな機能の何が嬉しいかと言うと、ほとんど同じソースプログラムなのにexeファイルをあえて別名にして起動するケースや、ほんのわずかソースコードを変更した複数のプログラムにおいて、起動直後にUsageを表示するなどの個所で、それらのソースコードを１つ１つ変更する手間が省けます。（開発途中で気が変わってプログラム名を変えてもソース変更が不要という利点もあります）

　　　記：実は GetModuleFileName の機能はMindの標準ライブラリで
　　　　　既に使っており、「私のプログラム名」という処理単語と
　　　　　して提供されています。

　ソースコードは次のようになります。

```mind:GetModuleFileName.src
自身のモジュール名は　文字列実体　長さ　ファイル名長さ。　　　※SJIS

自身のモジュール名を得るとは　（・　→　SJISモジュール名）
                結果は　　　変数
	自身のモジュール名を　クリアしておく

	０をつみ
	　自身のモジュール名の　アドレスを得て
	　　自身のモジュール名の　長さをつみ、
	　　　"kernel32"と　"GetModuleFileNameA"で
        　　　　ＡＰＩ呼出し３し　　　　　　　　　　※APIの引数が３個なので
		　結果に　入れ　　　　　　　　　　　※「ＡＰＩ呼出し３」を使う
        エラー？
                ならば　終り
			※API呼び出しそのものが失敗した
			※エラー文字列は登録済み
                つぎに

        結果が　０より　大きい
                ならば　結果を
			自身のモジュール名に　文字列実体カウントを設定し
			自身のモジュール名を　返す
                さもなければ
			「取得失敗」を　エラー登録し
                        空列を　返し
                つぎに。

メインとは
		モジュール名は　文字列

	自身のモジュール名を得て　モジュール名に　入れ
	エラー？
		ならば　「エラー：」を　表示し　エラー文字列を　一行表示し
		さもなければ
			「モジュール名=」を　表示し　モジュール名を　一行表示し
		つぎに

```

　上記を説明します。
　まず「ＡＰＩ呼出し３」という語を呼んでいます。MindからDLL内関数を呼ぶものです。
　MindではC言語などと違って関数名を直接にソースコード内に書いて呼び出すことはできず、DLL名と関数名を文字列として渡すことで間接的に実行する方法をとります。引数の数によって、

　　　　ＡＰＩ呼出し０
　　　　ＡＰＩ呼出し１
　　　　ＡＰＩ呼出し２
　　　　・・・・・・・

のように呼び出し用の処理単語を使い分けます。

　　　　"kernel32"と　"GetModuleFileNameA"で

の部分は、DLL名と関数名をスタックに積むものです。
　"kernel32.dll" と書いても良いのですが .dll は省略できます。
　関数名 "GetModuleFileNameA" ですが、シフトJISで文字列データをやりとりする通常関数の場合は末尾に"A"の付いたものが正式名となります。（末尾"W"のものもありますが後述します）

　このAPI関数は２番目の引数で得られた文字列データを受け取るためのバッファ領域を指し示す仕様になっています。Mind側は「自身のモジュール名」という文字列実体変数をそのための領域として用意しており、その変数アドレスを受け取るべき領域アドレスとしてスタックに積みます。
　ただこのとき注意するのは、Mindでの文字列実体変数はその先頭部にヘッダがあるため、ヘッダの先にある本来の文字列収容域のアドレスを指し示す必要がある点です。それは難しいものではなく、

　　　　自身のモジュール名の　アドレスを得て

で文字列収容域のアドレスを積むことができます。文字列実体変数をその少し前にクリアしているため、単に変数参照をおこなうと空の文字列が得られきますが、そのアドレス部こそが文字列収容域のアドレスとなります。「アドレスを得る」はMindの文字列データのうちアドレス部を得る機能です。

　さて、「ＡＰＩ呼出し３」は戻ってきたとき、スタックに他の言語で言うところの関数の戻値を積んできます。上記例ではそれを「結果に　入れ」と書くことて局所変数に格納しています。
　このあとエラー検査をおこなっていますが、ここでのエラーは、DLL内関数を呼び出すこと自体に失敗したというケースになります。

　エラーでない場合は後続し、APIの戻値である「結果」を見ています。GetModuleFileNameA の仕様として、成功であれば取得した文字列の文字数値が、失敗であればゼロが戻値となる・・となっているので、その通りに分岐をおこなっています。
　成功したときのパスですが、「結果」は得られた文字列の文字数なので、その値を文字列実体変数のカウント部に強制設定しています。
　APIが返して来る文字列というものはC言語などで扱う末尾がNULL終端された文字列の先頭アドレスでしかありません。一方、Mindが言う文字列とは、アドレスとカウントのパック値であるためAPIが設定してくれた文字列データだけではまだ不足であり、カウントを設定する必要があります。
　文字列実体変数の先頭にはヘッダがあると言いましたが、そのヘッダのうち、カウント部を強制設定するのが「文字列実体カウントを設定」というやや高度な用途の処理単語です。これにより、この文字列実体変数は正規の形式を持つことになります。

　このプログラムの実行結果が以下のようなものです。わざと "にほんご" というフォルダ内に潜ってから実行しています。日本語を含むパスが正しく得られるか確認するためです。

```
にほんご>..\obj\GetModuleFileName
モジュール名=C:\Mind\pmindwin\test-m\にほんご\mrunt010.exe
```

　表示されたモジュール名が GetModuleFileName.exe ではなく mrunt010.exe なのはMindのアプリケーションの起動手順特有のものです。冒頭で少し触れましたがMind標準装備の「私のプログラム名」という機能ではこの部分を補正して正しいプログラム名を返すようになています。この違いは本稿の主題ではないため気にされないでください。

# ワイド文字をやり取りするDLL内関数呼び出し

　　　記：ここから先はUnicodeライブラリを使います。
　　　　　Mindが提供するUnicodeライブラリについては別記事を
　　　　　参照願います。
　　　　　MindでUnicodeの文字列を扱う方法（2024.01.14改訂）
　　　　　https://qiita.com/killy/items/7a103e01561bf6a053d7

　Wind32 API の関数群の中にはUnicode形式で文字列をやりとりするものがあります。関数名の末尾に"W"が付くことが多いです。ここではMindからそのような関数を使う方法を解説します。
 ここでは、先に紹介した GetModuleFileNameA のワイド文字版である GetModuleFileNameW を呼び出す例をお見せします。
　ソースコードは次のようになります。

```mind:GetModuleFileNameWideChar.src
"../srclib/uniencode.src"を　コンパイル。
"../srclib/unidecode.src"を　コンパイル。

	ローカル。

Unicodeテーブルをロード処理とは　（・　→　・）
	Unicodeエンコードテーブルを読み出し　偽？
		ならば　（ErrorMessageで）　重大エラー
		つぎに
	Unicodeデコードテーブルを読み出し　偽？
		ならば　（ErrorMessageで）　重大エラー
		つぎに。

	グローバル。

自身のモジュール名は　文字列実体　長さ　ファイル名長さ。　　　※SJIS

自身のモジュール名を得るとは　（・　→　SJISモジュール名）
		Unicodeモジュール名は　文字列実体　長さ　ファイル名長さ　※Unicode
                結果は　　　変数
	※－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－※
	※　　　　　　　　　　　　　解説　　　　　　　　　　　　　　　※
	※　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　※
	※　APIで得たモジュール名は局所変数「Unicodeモジュール名」に　※
	※　一旦格納し、それをシフトSJIに変換したものを 　　　　　　　※
	※　「自身のモジュール名」という大域変数に格納しています。　　※
	※　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　※
	※　「ＵＣＳ２→ＳＪＩＳ変換」は変換先の文字列実体変数を明記　※
	※　することができ、それを利用しています。　　　　　　　　　　※
	※　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　※
	※－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－※
	Unicodeモジュール名を　クリアしておく 　　　※←これを忘れないこと
						　　※（局所変数は初期値不定）
	０をつみ
	　Unicodeモジュール名の　アドレスを得て
	　　Unicodeモジュール名の　長さをつみ、
	　　　"kernel32"と　"GetModuleFileNameW"で
        　　　　ＡＰＩ呼出し３し　　　　　　　　　　※APIの引数が３個なので
		　結果に　入れ　　　　　　　　　　　※「ＡＰＩ呼出し３」を使う
        エラー？
                ならば　終り
			※API呼び出しそのものが失敗した
			※エラー文字列は登録済み
                つぎに

        結果が　０より　大きい
                ならば　結果に　２を　掛け　　※←Wide文字数を2倍しByte単位に
			Unicodeモジュール名に　文字列実体カウントを設定し
			Unicodeモジュール名を、
			　自身のモジュール名をつかい　ＵＣＳ２→ＳＪＩＳ変換し
			　　（→SJISのモジュール名）
                さもなければ
			「取得失敗」を　エラー登録し
                        空列を　返し
                つぎに。

メインとは
		モジュール名は　文字列

	Unicodeテーブルをロード処理し

	自身のモジュール名を得て　モジュール名に　入れ
	エラー？
		ならば　「エラー：」を　表示し　エラー文字列を　一行表示し
		さもなければ
			「モジュール名=」を　表示し　モジュール名を　一行表示し
		つぎに
	。
```

　上のソースコードを説明します。
　冒頭でUnicodeライブラリ（副ソース提供）のコンパイルをおこなっています。
　このライブラリを使うには前もってテーブルをロードしておく必要があり、それをおこなっているのが「Unicodeテーブルをロード処理」です。「メインとは」の冒頭でこの処理単語が呼び出されます。

　「自身のモジュール名を得るとは」の定義に入ります。
　APIから戻された文字列を格納するための文字列実体変数は今度は局所定義にしています。ここに格納したあと引き続きシフトJISへの変換でさらに別の文字列実体変数に格納することから一時的な利用と言うことで局所変数としています。

　注目するのはAPIからの戻値である「結果」の処理です。通常文字版のほうでは「結果」をそのまま「文字列実体カウントを設定」としていましたが、今度は「２を　掛け」をしてからカウント設定をおこなっています。この理由は、APIから返された「文字数」が16bit固定長の文字次元での文字数だからです。Mindで扱うカウントは常にバイト数であるため、バイト数に換算するため２倍しています。

　　　　Unicodeモジュール名を、
　　　　　自身のモジュール名をつかい　ＵＣＳ２→ＳＪＩＳ変換し

の部分ですが、ここでAPIから返されたUnicode形式の文字列をシフトJISに変換しています。
　「○○をつかい」という表現は Mind 7 から導入された特殊送り仮名で、処理結果となる文字列の格納先変数を明示するものです。

　このプログラムの実行例は以下のようになります。

```
にほんご>..\obj\GetModuleFileNameWideChar
モジュール名=C:\Mind\pmindwin\test-m\にほんご\mrunt010.exe
```

　さて、このプログラム例ではUnicodeの文字列はAPI関数の戻値だけでしたが、APIによっては引数の側にUnicodeを指定するものもあります。そのときの注意を書いておきます。
　たとえばワイド文字を使う関数タイプで第１引数が文字列である場合、当然ながらUnicode化してから引数として積む必要があります。処理単語としては「ＳＪＩＳ→ＵＣＳ２変換」を使います。

　複数の引数でUnicode化の必要がある場合、「ＳＪＩＳ→ＵＣＳ２変換」は変換先の文字列実体を明記する方法で呼び出すよう注意してください。
　もし変換先の文字列実体変数を省略して単に、

　　　　○○を　ＳＪＩＳ→ＵＣＳ２変換し　　　※第一引数
　　　　　△△を　ＳＪＩＳ→ＵＣＳ２変換し　　※第二引数

のように記述してしまうと、二度目の変換で最初の変換先変数を破壊してしまいます。正しくは、

　　　　　　　　バッファ１は　文字列実体　長さ　・・
　　　　　　　　バッファ２は　文字列実体　長さ　・・

　　　　○○を　バッファ１をつかい　ＳＪＩＳ→ＵＣＳ２変換し
　　　　　△△を　バッファ２をつかい　ＳＪＩＳ→ＵＣＳ２変換し

となります。

　その引数がたとえば単純なASCII文字の場合はどうでしょうか。ASCII文字だと分かっているのだからＳＪＩＳ→ＵＣＳ２変換をかける必要は無い・・と思ってしまいがちですが、そのような場合であっても変換をおこなってください。8bitストリームから16bitストリームに変換してあげないといけないからです。

　定数としての文字列を引数として与える場合も要注意です。たとえばファイル名として、

　　　　"name1.txt"をつみ

のような記述でファイル名を積むことができそうに思いますが、これは二重の意味で誤りです。
　一つには、C型文字列とするためのNULL終端が無いことです。この文字列定数を一旦、文字列実体変数に入れてから「０を　○○に　一文字追加」でNULLを追加することもできますが、もっと簡単な方法は、

　　　　"name1.txt&00&"をつみ

と書くことです。これでNULL終端されます（&xx& は文字の十六進表記です）。しかしこれでも間違いです。これは純粋にASCII文字であり、Unicode化されていません。

　　　　"(uL+)name1.txt&00&"をつみ

上記が最終的に正しい表記になります。Mindコンパイラの特殊な機能ですが、文字列定数の先頭部として

　　　　"(uL+)"
　　　　"(uB+)"

という表現があり、これによって後続する文字列定数は16bit固定長のUnicodeとして生成されます（ただしASCII文字のみ）。Windows APIが扱うのは UTF-16 かつ Littleエンディアンであるため前者の (uL+) を文字列定数先頭に書いてください(LはLittleの略です)。

以上