[BlueLeaf1336]> PROBLEMS> 探求其之壱>

探求其之壱 > デバイスドライバの列挙

historyTOP

2006/02/11:作成

EnumProcesses と EnumDeviceDriversTOP

タスクマネージャとは何の関係もないんですが、Platform SDK 的には、プロセスを列挙する API である EnumProcesses のすぐ近くに、デバイスドライバを列挙する EnumDeviceDrivers という API が並んでいます。ですので、ついでにデバイスドライバの列挙とやらをやっておこうと思ったまでです。

この API は、EnumProcesses に似ているんですが、EnumProcesses よりも優秀です。というのも、EnumDeviceDrivers は 列挙に必要な配列のサイズを確認してから、必要なサイズの配列を準備して、実際の列挙を行う、という手順が踏めます。

でも、少し考えてみるとプロセスの列挙にそんな手順を踏めるわけないなぁということに気づきました。必要な配列のサイズを確定してから、実際の列挙を行うまでにプロセスの数なんて容易に変わりうるなぁと。なるほどなぁと。

それに対してデバイスドライバの数なんて変わらんだろ、ということでしょうね。

ソースコードTOP

uses
    Psapi;

procedure TForm1.Button1Click(Sender: TObject);
type
    //  Pointer の配列型
    TPointers = array[0..MaxInt div SizeOf(Pointer) - 1] of Pointer;
    //  Pointer の配列型の Pointer
    PPointers = ^TPointers;
var
    lpcbNeeded: Cardinal;
    DeviceDrivers: Pointer;
    Work: PPointers;
    i: Integer;
    Count: Integer;
    Buf: array[0..1024] of Char;
begin
    Memo1.Clear();

    //  1回目:デバイスドライバの列挙に必要な配列のサイズを求める
    if (EnumDeviceDrivers(nil, 0, lpcbNeeded)) then
    begin
        //  メモリ確保
        GetMem(DeviceDrivers, lpcbNeeded);

        try
            //  2回目:デバイスドライバを列挙する
            if (EnumDeviceDrivers(DeviceDrivers, lpcbNeeded, lpcbNeeded)) then
            begin
                //  Pointer の配列型の Pointer でキャスト(Index アクセスしたい)
                Work := DeviceDrivers;

                //  取得した個数を計算(SizeOf(Pointer) って怪しい)
                Count := lpcbNeeded div SizeOf(Pointer);

                //  表示
                Label1.Caption := Format('%d', [Count]);

                //  その個数分だけ配列としてアクセスする
                for i := 0 to Count - 1 do
                begin
                    //  基本フルパス(ファイル名だけのものもあり)
                    FillChar(Buf, SizeOf(Buf), 0);
                    if (GetDeviceDriverFileName(Work[i], Buf, 1024) > 0) then
                    begin
                        Memo1.Lines.Add(Buf);
                    end
                    else
                    begin
                        Memo1.Lines.Add(SysErrorMessage(GetLastError()));
                    end;

                    //  ファイル名だけ
                    //FillChar(Buf, SizeOf(Buf), 0);
                    //GetDeviceDriverBaseName(Work[i], Buf, 1024);
                    //Memo1.Lines.Add(Buf);
                end;
            end
            else
            begin
                Memo1.Text := SysErrorMessage(GetLastError());
            end;
        finally
            //  メモリ開放
            FreeMem(DeviceDrivers);
        end;
    end
    else
    begin
        Memo1.Text := SysErrorMessage(GetLastError());
    end;
end;

気になる点TOP

いつものことながら、動くことは動く。程度の信頼性だと思っていただく必要がありますが、今回は特に微妙です。EnumDeviceDrivers はこんな定義なんですが、

BOOL EnumDeviceDrivers(
  LPVOID* lpImageBase,
  DWORD cb,
  LPDWORD lpcbNeeded
);

で、lpImageBase にデバイスドライバの列挙結果が格納されます。Platform SDK は

[out] Pointer to an array that receives the list of load addresses for the device drivers.

だと説明してます。「the list of load addresses」って何? 「LPVOID*」って?

型だけを見ると、「LPVOID = Pointer to any type.」ですが、これって一体?「Pointer の配列の Pointer」だろうけど... というわけで、わけがわからないまま動いてるだけです。

それとは別に、中身は配列なんだけど、宣言としては Pointer というどうやって扱ってよいのかわからないデータにアクセスするための方法として、Delphi-ML の過去ログを参考にして、

type
    //  Pointer の配列型
    TPointers = array[0..MaxInt div SizeOf(Pointer) - 1] of Pointer;
    //  Pointer の配列型の Pointer
    PPointers = ^TPointers;

という、超巨大なサイズの配列を型として宣言しておいて、そういう配列の変数のつもりで読み取る形をとっています。でも、そんな巨大な配列を変数として宣言してしまうとえらいことになるので、その Pointer として宣言しておいてえらいことにならないようにしています(多分)。

ちなみに、なんでこんなことになるのかというと、「デバイスドライバの数なんて 1024 個以下に決まってるやん」と割り切ってしまえば、GetMem/FreeMem なんていうことをやらずに、最初からそういう配列を定義しておいてその配列に受け取ればよくって、そうしておけばこんなややこしいことをしなくてもよいんですが、「必要なサイズだけ確保したいよ」とかっこつけたためにこんなよくわからないことをやらなくてはいけなくなってしまってます(多分)。

いろいろと意味の取りがたい言い訳を書きましたが、一言で言うと「大丈夫か、これ?」です。

最後に、単純に列挙しても LPVOID の配列が得られるだけで面白くもなんともないので、GetDeviceDriverFileName を使って デバイスドライバのファイル名を取得しています。気持ちとしては、もっと他にも得られるものはないのか? と思わなくはないのですが、Platform SDK がどこにもリンクしてなくて手を出せない状況です。

実行結果とダウンロードTOP

デバイスドライバといってもデバイスマネージャで確認できるようなものではなくて、主に「sys」ファイルのことです。あんまり用途がわかりません。

20060211DeviceDrivers.zip(167,499Bytes) ソースコードと実行ファイルです。

EOFTOP