第11回  「APIを使ってみる」



APIって何?

メモリ・レジストリ書き換え関数を紹介する前に、APIについて勉強しておきましょう。
というのも、ファイルの書き換えであれば標準関数だけでもできたのですが、

メモリやレジストリを書き換えるためには、APIを使わないとどうにもならないからなんです。
(厳密にはレジストリの書き換えはある程度可能)

APIとは、Application Programming Interfaceの略でして、
簡単に説明するならば、OSの機能を呼び出して使用する為の関数群といった所です。

要するに、APIを使えばOSでできる事であれば、自作のプログラムでもできるようになるのです。(限度はありますが)
また、今回使用するOSは「Windows」なので、「WindowsAPI」を使用します。

VBちょっといい話に、APIについての注意書きがありますので、一度見ておいて下さい。


APIの使い方

では、APIを使うためにはどうすれば良いのでしょうか?
APIを使うためには、APIの宣言を行う必要があります。

APIの宣言は、基本的に標準モジュールにする事が多くなると思います。
通常は、APIを利用した命令を実行している所と同じモジュールに書いておけばよいでしょう。

では、実際に宣言の例を見てみましょう。
「EditEngine.bas」の上の方に、以下のような記述がありますよね。


Declare Function RegCloseKey Lib "ADVAPI32.DLL" (ByVal hKey As Long) As Long



これは、開いたレジストリを閉じるための関数「RegCloseKey」を宣言している部分です。
見た目は、今までの自作関数とほとんど変わっていませんね。

違っているのは、関数名の後ろに「Lib "ADVAPI32.DLL"」という記述がある事です。
これは、「RegCloseKey」という関数が「ADVAPI32.DLL」というファイルの中にあるという意味です。

WindowsAPIの実体は、DLLファイル(ダイナミック・リンク・ライブラリ)です。
これを呼び出して使用するための手続きが、APIの宣言なのです。

また、これらのDLLファイルは、通常「Windows\System」の中に存在します。
一度自分のマシンの中から、DLLファイルを探しておいて下さい。(上記のDLLもあるはずです)

次に、以下の宣言部分を見て下さい。


Declare Function RegSetValueEx Lib "ADVAPI32.DLL" Alias "RegSetValueExA" _
  (ByVal hKey As Long, ByVal lpValueName As String, ByVal Reserved As Long, _
  ByVal dwType As Long, lpData As Any, ByVal cbData As Long) As Long



少し長いですが、レジストリに値をセットする為の関数「RegSetValueEx」の宣言部分です。
「RegCloseKey」とほとんど同じ書式なのですが、「Alias "RegSetValueExA"」という部分が増えていますね。

文字列を使用するAPIを使用する場合、「ANSI」「Unicode」の文字セットのうち、
どちらを使用するかを指定する必要があります。

関数名の最後に「A」がつけば「ANSI」、「W」がつけば「Unicode」の指定となりますが、
Win95では、「Unicode」のAPI関数はサポートされていないため、通常は「ANSI」の方を指定します。

詳しくは、VBヘルプ「API 関数における ANSI と Unicode の比較」をご覧下さい。


C言語専用に作られたAPI

実は、VBはWindowsAPIの使用を正式にはサポートしていません。
これは、APIが元々C言語から使われるためのものである事が関係します。

そもそもAPI自体がC言語で作成されているために、VBから利用するためには
C言語からVBへの変換が必要となってしまうのです。(言語仕様の違いを吸収する)

例えば、C言語には文字列型の変数というものがありません。
では、どうやって文字列を格納しているのかといいますと、
文字型の変数の配列として扱われているのです。

例えば、「ABC」という文字列を変数に格納する場合を考えてみましょう。

VBでは
Dim MyString As String
MyString = "ABC"


C言語では、
Char MyString[] = "ABC";

又は、
Char MyString[4];
strcpy(MyString , "ABC");

となります。

C言語の方で2つの例を挙げましたが、これは、変数の宣言と同時に値を入れるか、
宣言した後で値を入れるかの違いです。

1つ目の例では、文字型の変数「MyString」を配列で定義し、その大きさは「"ABC"」を格納する為に
最低限必要な数だけ確保されます。

2つ目の例では、配列の大きさを「4」とし、その後で配列に「"ABC"」を代入しています。
ちなみに、配列に値を代入するためには、「strcpy」という関数を使用します。

ここで、ん?と思った方は鋭いですよ。
文字列は「"ABC"」なんだから、配列の大きさは「3」でいいんじゃないか?と思いますよね。

ところがC言語の場合、文字配列の最後には「"\0"」(ヌル文字)というものが入るため、
文字列の長さ+1の大きさが必要になるのです。
ヌル文字は、文字列の終わりを示す目印として使用されます。

さて今度は、関数を使わずに文字列を代入する方法を見てみましょう。

Char MyString[4];
MyString[0] = 'A'
MyString[1] = 'B'
MyString[2] = 'C'
MyString[3] = '\0'

関数を使わない場合は、上記のように1文字ずつ格納していく必要があります。
勿論、最後にはヌル文字も格納してやります。
・・・やっぱり面倒ですね。(汗)

余談ですが、文字列の途中にヌル文字が入ってしまったらどうなるかも話しておきましょう。
上の例の3行目を以下のように書き換えます。

MyString[1] = '\0'

こうすると、2文字目にヌル文字が入ってしまうため、3文字目に「C」が入っているにも関わらず
この文字列は「ABC」ではなく「A」と認識されてしまします。


さらに余談ですが、VBとC言語では配列の大きさの宣言方法が異なります。
VBで大きさ「5」の配列を宣言するためには、以下のようになります。

Dim Moji(4) As String
カッコの中の「4」は、配列の上限の事で、要素は0〜4の5つとなります。

一方、C言語では以下のようになります。
Char Moji[5];
カッコの中の「5」は、配列の大きさの事で、要素は0〜4の5つとなります。

つまり、カッコの中の数字はそれぞれ、「VB=要素の上限値」、「C=要素の数」を示しているのです。


まあ、それはさておき、C言語で文字列型を使いたければ、文字型の変数の配列
(私は、文字配列と略して読んでいます)を使う必要があるという事と、
最後にヌル文字が付いているという2点に注意しておいて下さい。

特に、ヌル文字が付いてくるのはやっかいな問題でして、APIを利用して文字列を取得すると、
最後にこのヌル文字が付いてきてしまいます。

このヌル文字はVBでは邪魔になりますので、なんとかして消してやる必要があります。
「EditEngine.bas」の「CutNullChar」という関数でその処理をやっています。

やっている内容は簡単なので、見ておきましょう。


Private Function CutNullChar(ByVal Str As String) As String
  Str = Str & vbNullChar
  CutNullChar = Left(Str, InStr(Str, vbNullChar) - 1)
End Function



まず、与えられた引数の最後に、わざとヌル文字を追加します。
vbNullChar」は、ヌル文字を示すVBの定数です。

こうする事によって、与えられたデータにヌルが含まれていようがいまいが、
最後にヌル文字がくっついた事になりますね。

それから、データの中にヌル文字が含まれている場所が左から何番目かを探し、
その直前まで文字列を抜き出せばOKです。

事前にわざとヌル文字を追加しているのは、もしヌル文字が含まれていないと
「InStr」関数が0を返してしまい、文字列が1文字も抜き出せなくなるのを防ぐためです。


では、今回はここまでです。
次回は、APIを使用して実際にメモリの書き換えを行う部分を解説します。



前に戻る     次に進む

講座の初めに戻る