|
Delphi Programming / Object Pascal

[掲載 2011年01月15日] [更新 2014年05月23日] Delphi サンプルプログラム集 |
| 822_コピーフック |
| 動作確認等 |
Windows 7 U64(SP1) + Delphi XE Pro
64bit 版の DLL は Delphi XE3(UP1) Pro で作成
[6] は、Windows 7 U64(SP1) + Delphi XE3(UP1) VCL-32, VCL-64 |
 |
822_ShellCopyHook.zip [4,994 KB] 2014年05月23日版(同梱のEXEは、32bitアプリ) |
- 2013年02月22日
- Windows 7 の 64bit 版でもテスト可能なコードに修正
- コードの整備と記事の構成変更と修正
- 2013年03月02日
- アプリケーションマニフェストを追加
- 2013年07月22-24日
- Windows の 32, 64 ビット版の判定の説明に間違いがあったので、記事とコードを修正
- 2014年04月29日
- PE ヘッダを使用して、EXE (DLL) が 32 ビット版か 64 ビット版かを判定するサンプルを追加
|
シェルのコピーフックのテストプログラムです。
コピーフックと言うと、ファイルのコピー操作に関する検出のような印象を受けますが、実際には、システム上に存在するフォルダの、[コピー]、[移動]、[削除]、[名前の変更]の 4 つの操作を検出し、操作の実行継続や禁止等を行うものです。対象はファイルではなく「フォルダ」だけです。
コールバック関数の定義は次のようになっています。 |
UINT CopyCallback(
[in, optional] HWND hwnd,
UINT wFunc,
UINT wFlags,
[in] PCTSTR pszSrcFile,
DWORD dwSrcAttribs,
[in, optional] LPCTSTR pszDestFile,
DWORD dwDestAttribs
);
以下の表は、その引数の値と意味です。
確かにフォルダの削除や名前変更をされては困る場面はあるわけですが、サーバ側の設定でも可能だと思われるのですが、どうなのでしょうか。ファイルの操作もフックできるのであれば、表示するダイアログを変更する用途には利用できるとは思っています。 |
表1
コールバック関数内で取得できる wFunc の値 |
| FO_COPY |
フォルダが [コピー] の後、[貼り付け] されようとしている
pszSrcFile がコピー元フォルダ、pszDestFile が貼り付けフォルダ |
| FO_DELETE |
フォルダが削除されようとしている
pszSrcFile が削除されるフォルダ |
| FO_MOVE |
フォルダが移動されようとしている
pszSrcFile が移動元フォルダ、pszDestFile が移動先フォルダ |
| FO_RENAME |
フォルダの名前が変更されようとしている
pszSrcFile が変更前のフォルダ、pszDestFile が変更後のフォルダ |
表2
wFlags の主な値 (SHFILEOPSTRUCT Structure の fFlags 参照)
本ページのサンプルでは、これらの値の検出は行っていない |
| FOF_SILENT |
処理の進行を示す UI を表示しない |
| FOF_NOERRORUI |
エラーが発生しても UI を表示しない |
| FOF_NOCONFIRMATION |
確認のための UI を表示しない
全て YES で応答したことする |
| FOF_NOCONFIRMMKDIR |
新しいフォルダ作成時に、その確認を UI で行わない |
| FOF_NO_UI |
上記 4 つのフラグの組み合わせ |
| PO_DELETE |
プリンタが削除されようとしている
pszSrcFile は対象プリンタのフルパス |
| PO_RENAME |
プリンタ名が変更されようとしている
pszSrcFile は新しい名前、pszDestFile は前の名前
逆のような気もするが未確認 |
| IDYES |
操作を許可する |
| IDNO |
操作を許可しない |
| IDCANCEL |
操作をキャンセルする。許可しない場合との違いは未確認 |
このシェルコピーフックは、シェルのフォルダ操作を検出するものです。シェルによる操作とは、主にエクスプローラによる操作ということになります。
またファイル操作関係のシェル関数である SHFileOperation 関数等でフォルダを操作した場合も発生します。次のコードのように、一般のファイルシステム関係の関数を使用して操作しても、コピーフックのコールバック関数は呼ばれません。 |
リスト2
ファイルシステムの関数ではコピーフックは検出できない |
procedure TForm1.Button3Click(Sender: TObject);
var
SrcFile : String;
DestFile : String;
begin
SrcFile := 'D:\000';
DestFile := 'D:\001';
MoveFile(PChar(SrcFile), PChar(DestFile));
end;
procedure TForm1.Button4Click(Sender: TObject);
var
ADir : String;
begin
ADir := 'D:\001';
RemoveDir(ADir);
end;
| 01_Delphi の Demos のサンプルを使用 |
コピーフックのサンプルが Delphi 2010 の [Demos] [ActiveX] [ShellExt] 内にあります。テストにはこれを使用することにしました。DLL 内に、COM サーバ登録と解除関係のコードが記述してあり、そのまま使用できます。
このサンプルは、Delphi XE 以降にはないようですが、最新版の Delphi ユーザは、旧バージョンも入手可能です。入手すればオリジナルのデモコードを確認することができます。
まず、デモの copyhook.dpr をコンパイルして、copyhook.dll を作成します。この dll を COM サーバとしてシステムに登録し、レジストリに情報を書き込みます。この変更は、Windows を再起動すると有効となります。登録の解除も再起動が必要です。
フックした結果は、dll 内のコールバック関数が処理します。今回のテストでは、
- 表示する文字列を日本語に変更
- D:\000 内のフォルダに対する操作は一切禁止
のようにコールバック関数の処理を変更しています。
DLL は 32bit 版と 64bit 版の両方作成しました。 |
リスト3
本テストプログラムで使用したコールバック関数のコード |
function TCopyHook.CopyCallBack(Wnd: HWND; wFunc, wFlags: UINT;
pszSrcFile: PWideChar; dwSrcAttribs: DWORD; pszDestFile: PWideChar;
dwDestAttribs: DWORD): UINT;
var
Operation : String;
SrcFile : String;
TargetFile : String;
MstStr : String;
begin
TargetFile := UpperCase('D:\000');
SrcFile := UpperCase(String(pszSrcFile));
if Pos(TargetFile, SrcFile) > 0 then begin
MstStr := String(pszSrcFile) + sLineBreak +
'このフォルダに対する操作は許可されていません';
MessageBox(Wnd, PChar(MstStr), 'シェルコピーフック' , MB_OK);
Result := IDNO;
end else begin
case wFunc of
FO_COPY:
Operation := 'コピー';
FO_DELETE:
Operation := '削除';
FO_MOVE:
Operation := '移動';
FO_RENAME:
Operation := '名前の変更';
else
Operation := ''
end;
MstStr := Operation + sLineBreak +
String(pszSrcFile) + sLineBreak +
'操作を続行しますか ? ';
Result := MessageBox(Wnd, PChar(MstStr), 'シェルコピーフック' , MB_YESNOCANCEL);
end;
end;
| 02_コピーフックをシステムに登録 - DLL の登録 |
レジストリへの登録を行えば、後は DLL 内のコードが COM サーバとして登録してくれます。
登録解除も同じです。本サンプルのように、システムの基本的な動作を変更するような DLL の場合は、どちらもコンピュータの再起動が必要です。
このサンプルでは、32 ビット, 64 ビットのどちらのアプリでも動作するようにしました。添付している登録・登録解除用の実行ファイル (EXE) は 32 ビットのアプリケーションです。
まず、32 ビット版の DLL をシステムに登録または登録の解除をします。現在の Windows 7 が 64 ビット版であれば、続いて 64 ビット版のコマンドプロンプトを非表示で起動し、64 ビット版の DLL を登録または登録の解除をするようにしています。
このプログラムは、レジストリを操作します。したがって、実行には管理者権限 (管理者として実行) が必要です。そこで、アプリケーションマニフェストを使用しています。EXE 起動時に、下図のよう管理者への昇格確認のダイアログが表示されます。 |
 |
図1
管理者として実行 |
 |
図2
登録と登録解除用プログラム
|
 |
図3
登録
- 本サンプルの DLL の場合はコンピュータの再起動が必要
|
 |
図4
登録解除
- 本サンプルの DLL の場合はコンピュータの再起動が必要
|
|
登録すると、レジストリに該当 GUID の名前のキーが作成され、その階層下に InprocServer32 というサブキーが作成されます。その {既定} 項目に CopyHook.dll のフルパスが記録されます。
登録を解除すると、これからのキーはレジストリから全て削除されます。
下図のように、レジストリの [ShellExtensions] にも履歴が追加されます。
このレジストリへの登録によって、DLL 内のコールバック関数が自動的にが呼出されて実行されるようになります。登録と登録解除を繰り返すと、同じ項目が履歴分に追加されます。気になるようでしたら、Delphi CopyHook Shell で検索して削除してください。
|
- 図5
- レジストリに追加された登録履歴
- GUID と データの文字列は、DLL 内のコードを参照
|
 |
64 ビット版の Windows 7 の場合は、32 ビット用のレジストリにも同様の項目が作成されます。
32 ビット用のレジストリエディタは、[コマンドプロンプト]、あるいは [ファイル名を指定して実行] のダイアログから以下のコマンドで起動できます。 |
| C:\Windows\SysWOW64\Regedit |
 |
図6
32bit 用レジストリエディタを起動 |
リスト4
コピーフックをシステムに登録するためのプログラム
添付の EXE は 32 ビットアプリとなっている |
unit HookDllRegist32Unit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ShlObj, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
DllPath32 : String;
DllPath64 : String;
Is64OS : Boolean;
function RegistSvr(AFileName: String; AMode: Boolean): Boolean;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//-----------------------------------------------------------------------------
// コマンドラインのコマンドを実行する関数
// 起動完了を待たずに呼出し側に処理を戻す
//-----------------------------------------------------------------------------
function RunCommmadLine(CommandLine: string): DWORD;
var
StartupInfo : TStartupInfo;
ProcessInfo : TProcessInformation;
begin
FillChar(StartupInfo, SizeOf(StartupInfo), #0);
StartupInfo.cb := sizeof(TStartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
//これがないとコマンドプロンプトのウィンドウが表示されてしまう
//この指定を有効にするためには、dwFlagsにSTARTF_USESHOWWINDOWを指定する
StartupInfo.wShowWindow := SW_HIDE;
//参照カウンタ対策
UniqueString(CommandLine);
if not CreateProcess(nil,
PChar(CommandLine),
nil,
nil,
True,
CREATE_NEW_CONSOLE,
nil,
nil,
StartupInfo,
ProcessInfo) then begin
Result := WAIT_FAILED
end else begin
GetExitCodeProcess(ProcessInfo.hProcess, Result);
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
end;
end;
//-----------------------------------------------------------------------------
// 32ビットのWindowsか64ビットのWindowsかを調べる関数
// 64ビット版のWindowsの場合はTrueを返す
//-----------------------------------------------------------------------------
function Is64bitWindows: Boolean;
var
Wow64Proc : function(hProcess: THandle; var Wow64: BOOL): BOOL stdcall;
RetFlag : LongBool;
begin
@Wow64Proc := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'IsWow64Process');
if @Wow64Proc <> nil then begin
Wow64Proc(GetCurrentProcess, RetFlag);
if SizeOf(THandle) = 4 then begin
Result := RetFlag;
end else
if SizeOf(THandle) = 8 then begin
Result := True;
end;
end else begin
//XP-64bit以上
Result := CheckWin32Version(5, 2);
end;
end;
//=============================================================================
// DLLのフルパスを取得しておく
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
//32bit版のDLLのフルパス
DllPath32 := ExtractFilePath(Application.ExeName) + 'ShellExt32';
DllPath32 := IncludeTrailingPathDelimiter(DllPath32) + 'copyhook.dll';
//64bit版のDLLのフルパス
DllPath64 := ExtractFilePath(Application.ExeName) + 'ShellExt64';
DllPath64 := IncludeTrailingPathDelimiter(DllPath64) + 'copyhook.dll';
//64bit版のWindowsかを調査
Is64OS := Is64bitWindows;
end;
//-----------------------------------------------------------------------------
// 環境変数(%で囲まれた文字列)から実際のパス名を取得する関数
//-----------------------------------------------------------------------------
function ExpandEnvironmentString(S: String): String;
var
DstChar : array [0..MAX_PATH - 1] of Char;
begin
ExpandEnvironmentStrings(PChar(S), DstChar, MAX_PATH);
Result := DstChar;
end;
//=============================================================================
// シェル・コピーフックの登録
// copyhook.dllをCOMサーバーとして登録し、レジストリに登録
// Windowsの再起動が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
ACmdStr : String;
DllPath : String;
begin
if RegistSvr(DllPath32, True) then begin
if Is64OS then begin
//64bit版のWindowsだったら64bit版DLLをコマンドプロンプトを起動して登録
DllPath := AnsiQuotedStr(DllPath64, '"');
ACmdStr := '%systemroot%\Sysnative\cmd.exe /c regsvr32.exe ';
ACmdStr := ExpandEnvironmentString(ACmdStr) + DllPath;
RunCommmadLine(ACmdStr);
end;
end else begin
Application.MessageBox('登録に失敗しました', '情報');
end;
end;
//=============================================================================
// シェル・コピーフック登録解除
// シェルの操作機能を通常に戻す
// Windowsの再起動が必要
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
ACmdStr : String;
DllPath : String;
begin
if RegistSvr(DllPath32, False) then begin
if Is64OS then begin
//64bit版のWindowsだったら64bit版DLLをコマンドプロンプトを起動して登録解除
DllPath := AnsiQuotedStr(DllPath64, '"');
ACmdStr := '%systemroot%\Sysnative\cmd.exe /c regsvr32.exe /u ';
ACmdStr := ExpandEnvironmentString(ACmdStr) + DllPath;
RunCommmadLine(ACmdStr);
end;
end else begin
Application.MessageBox('登録解除に失敗しました', '情報');
end;
end;
//=============================================================================
// regsvr32コマンドを使用してレジストリにDLLを登録または登録解除するのと同じ動
// 作を行う関数。レジストリの操作は管理者権限が必要
// DLL内にCOMサーバとしての登録と登録解除のコードが実装されている
//
// AFileName ActiveX DLLやOCXのフルパス
// AMode True 登録
// False 登録解除
//=============================================================================
function TForm1.RegistSvr(AFileName: String; AMode: Boolean): Boolean;
type
TRegProc = function : HResult; stdcall;
var
RegProc : TRegProc;
LibHandle : THandle;
AREGMODE : String;
begin
Result := False;
if AMode then begin
AREGMODE := 'DllRegisterServer';
end else begin
AREGMODE := 'DllUnregisterServer';
end;
LibHandle := LoadLibrary(PChar(AFileName));
if LibHandle <> 0 then begin
try
@RegProc := GetProcAddress(LibHandle, PChar(AREGMODE));
if (@RegProc = nil) or (RegProc <> 0) then begin
Result := False;
end else begin
Result := True;
end;
finally
FreeLibrary(LibHandle);
end;
end;
end;
end.
DLL や OCX をシステムに登録するには、regsvr32.exe を使用します。
DLL がある場所が D:\000\copyhook.dll の場合、コマンドプロンプトを管理者権限で起動して、次のコマンドを実行します。DLL や OCX のパス名に空白文字が含まれている場合は、パス名をダブルクオーテーション (") で囲みます。 |
regsvr32 D:\000\copyhook.dll //登録
regsvr32 D:\000\copyhook.dll /u //登録解除
|
コマンドプロンプトは 64 ビット版、32 ビット版のどちらであっても、DLL が 64 ビット版であっても 32 ビット版であっても、自動認識して登録あるいは登録解除をしてくれるようです。
しかし、マイクロソフトのサイトの記事や、ネット上の記事には失敗する例が掲載されています。原因については分かりません。
おそらく、以下の方法が確実と思われます。
32 ビット版の DLL や OCX の場合は、32 ビット版のコマンドプロンプトを管理者権限で起動して、32 ビット版の regsvr32.exe を使用します。
64 ビット版の DLL や OCX の場合は、64 ビット版のコマンドプロンプトを管理者権限で起動して、64 ビット版の regsvr32.exe を使用します。
コマンドラインは以下のようになりますが、実際にはリダイレクト機能が働きます。したがって、ディレクトリ部分は不要です。つまり、32 ビット版の DLL の登録も、64 ビット版の DLL の登録も、コマンドラインは同じとなります。
|
// 32ビット版のDLLの場合は,32ビット版のコマンドプロンプトを管理者権限で起動
%systemroot%\SysWoW64\regsvr32.exe D:\000\copyhook.dll //32ビット版のDLLの登録
%systemroot%\SysWoW64\regsvr32.exe D:\000\copyhook.dll /u //32ビット版のDLLの登録解除
// 64ビット版のDLLの場合は,64ビット版のコマンドプロンプトを管理者権限で起動
%systemroot%\System32\regsvr32.exe D:\000\copyhook.dll //64ビット版のDLLの登録
%systemroot%\System32\regsvr32.exe D:\000\copyhook.dll /u //64ビット版のDLLの登録解除
|
下図は 64 ビット版の Windows 7 で、32 ビット版のコマンドプロンプトを管理者権限で起動して、32 ビット版の DLL を登録した画面です。
32 ビット版のコマンドプロンプトなので、タイトルが C:\Windows\SysWOW64\cmd.exe となっています。64 ビット版のコマンドプロンプトの場合は、C:\Windows\System32\cmd.exe となります。
コマンドプロンプトの起動直後は、ディレクトリがデフォルトで C:\Windows\System32
となっています。そのままだと、System32 内の regsvr32 を探してしてしまう可能性があります。
ディレクトリを、下図のように C:\ にしてから操作した方が間違いありません。
ディレクトリを C:\ にするには、cd /d C:\ というコマンドを実行します。
|
 |
図7
コマンドプロンプトから登録
- コマンドプロンプトは管理者権限で起動する
- ディレクトリを C:\ にしてからコマンド類を実行する
|
上の操作で実際にレジストリに登録されているかを確認したのが下のリストです。
64 ビット版のレジストリには、Wow6432Node という、32 ビット版のレジストリを意味する場所にしか記録されていません。一方、32 ビット版のレジストリでも、64 ビット版のレジストリにおける記録場所が分かるようになっています。
|
// Regedit.exe(64ビット版のレジストリエディタ) でD:\000\copyhook.dllを検索した結果
[HKEY_CLASSES_ROOT]
Wow6432Node\CLSID\{29F97553-FBD6-11D1-AFC1-00A024D1875C}\InprocServer32
[HKEY_LOCAL_MACHINE]
SOFTWARE\Classes\Wow6432Node\CLSID\{29F97553-FBD6-11D1-AFC1-00A024D1875C}\InprocServer32
SOFTWARE\Wow6432Node\Classes\CLSID\{29F97553-FBD6-11D1-AFC1-00A024D1875C}\InprocServer32
// C:\Windows\SysWOW64\RegeditでD:\000\copyhook.dllを検索した結果
// C:\Windows\SysWOW64\Regeditは32ビット版のレジストリエディタ
[HKEY_CLASSES_ROOT]
CLSID\{29F97553-FBD6-11D1-AFC1-00A024D1875C}\InprocServer32
Wow6432Node\CLSID\{29F97553-FBD6-11D1-AFC1-00A024D1875C}\InprocServer32
[HKEY_LOCAL_MACHINE]
SOFTWARE\Classes\CLSID\{29F97553-FBD6-11D1-AFC1-00A024D1875C}\InprocServer32
SOFTWARE\Classes\Wow6432Node\CLSID\{29F97553-FBD6-11D1-AFC1-00A024D1875C}\InprocServer32
32 ビット版の Windows 7 では、以下の場所に記録されます。
64 ビット版の Windows 7 で、64 ビット版の DLL を登録した場合も同じ場所に記録されます。 |
[HKEY_CLASSES_ROOT]
CLSID\{29F97553-FBD6-11D1-AFC1-00A024D1875C}\InprocServer32
[HKEY_LOCAL_MACHINE]
SOFTWARE\Classes\CLSID\{29F97553-FBD6-11D1-AFC1-00A024D1875C}\InprocServer32
[備考]
64 ビット版の Windows の場合、32 ビットの EXE からは、ディレクトリに C:\Windows\System32 を指定しても、32 ビット版のプログラムが起動するようになっています。例えば 32 ビット版のコマンドプロンプトで
C:\Windows\System32\calc.exe
としても、32 ビット版の電卓が起動します ([ファイル名を指定して実行] のダイアログでは当然 64 ピッ版の電卓が起動します)。
regsvr32.exe が、DLL が 32 ビットでも 64 ビットでも登録できるというのは、regsvr32.exe 特有の機能なのかも知れませんが、少々理解しにくい仕様のような感じがします。マイクロソフトの「親切」なのかも知れませんが、一貫性があるとは思えません。 |
フォルダの操作を禁止した D:\000 内のフォルダ内のフォルダを削除しようとした場合の例です。
まず、通常の確認ダイアログが表示されます。ここで、[はい] を選択しても、コピーフック内で定義したダイアログが現れて、操作ができないようになっています。 |
 |
図8
フォルダの削除
|
 |
図9
禁止 |
その他のフォルダを [コピー] して、他のフォルダに [貼り付け] 操作を行った場合です。
[コピー] 操作ではなく、実際のコピー、つまり、[貼り付け] の時に表示されます。 |
 |
図10
[貼り付け] の時に表示される |
| 05_Windows が 32 ビット版 か 64 ビット版かの判定 |
現在のシステムの Windows が 32 ビット版か 64 ビット版かを判定するため、本サンプルでは以下のような関数を作成して使用しています。
IsWow64Process は、Windows XP(SP2) より前にはありません。そこで、CheckWin32Version で判定しています。Windows 2000 は 64 ビット版が発表はされましたが、実際には普及しなかったようですから、32 ビット版だけと考えていいでしょう。
Delphi 6 では、この CheckWin32Version 関数の戻り値が Delphi 7 以降と異なる場合があります。下記の参考リンクの ID: 17706 をダウンロードして使用してください。 |
//-----------------------------------------------------------------------------
// 32ビットのWindowsか64ビットのWindowsかを調べる関数
// 64ビット版のWindowsの場合はTrueを返す
//-----------------------------------------------------------------------------
function Is64bitWindows: Boolean;
var
Wow64Proc : function(hProcess: THandle; var Wow64: BOOL): BOOL stdcall;
RetFlag : LongBool;
begin
@Wow64Proc := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'IsWow64Process');
if @Wow64Proc <> nil then begin
Wow64Proc(GetCurrentProcess, RetFlag);
if SizeOf(THandle) = 4 then begin
Result := RetFlag;
end else
if SizeOf(THandle) = 8 then begin
Result := True;
end;
end else begin
//Windows XPの64ビット版か
Result := CheckWin32Version(5, 2);
end;
end;
//=============================================================================
// 上の関数のテスト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
if Is64bitWindows then begin
MessageBox(Handle, '64 ビット Windows です', '情報', MB_OK or MB_ICONASTERISK);
end else begin
MessageBox(Handle, '32 ビット Windows です', '情報', MB_OK or MB_ICONASTERISK);
end;
end;
現在のシステムの Windows が 32 ビット版か 64 ビット版かは、WMI でも取得できます。
Win32_OperatingSystem クラスのクエリーを実行すると、現在のオペレーティングシステムの情報が取得できます。この中の OSArchitecture がビット数を表わしています。また、CSDVersion が サービスパックの内容となっています。
下図は、その下のコードの実行結果です。結果は文字列として取得することになりますから、その文字列の中に 64 か 32 の文字例があるかで判定できます。
WMI による情報の取得は、基本的に Windows のバージョンに依存しませんが、Windows の古いバージョンでは実装されていないプロパティがあります。例えば、MSDN の Win32_OperatingSystem class の説明によると OSArchitecture は、 |
| Windows Server 2003 and Windows XP: This property is not available. |
| となっていて、Windows XP では検出できません。 |
 |
図11
OS のビット数 |
 |
図12
サービスパックの情報 |
//=============================================================================
// WMIを使用してWindowsのビットバージョンを取得
// Win32_OperatingSystemクラスでは、OSの各種情報が取得できる
//
// usesにActiveXが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
strQuery : String;
EnumCollection : IEnumVariant;
CollectionItem : OleVariant;
Value : LongWord;
ACount : Integer;
StrText : String;
begin
strQuery := 'Select * From Win32_OperatingSystem';
//クエリーを実行してプロパティセットのコレクションを取得
EnumCollection := GetPropertyEnumCollection(strQuery, ACount);
//プロパティの値を取得して表示
//Win32_OperatingSystemのプロパティセットは1組だけ
if EnumCollection.Next(1, CollectionItem, Value) = S_OK then begin
//OSのビットバージョン
StrText := String(CollectionItem.OSArchitecture);
MessageBox(Handle, PChar(StrText), '情報', MB_ICONASTERISK);
//サービスパックの種類
StrText := String(CollectionItem.CSDVersion);
MessageBox(Handle, PChar(StrText), '情報', MB_ICONASTERISK);
end;
end;
//=============================================================================
// Wbemのオブジェクトを生成して指定引数で接続してクエリーを実行
// プロパティセットのコレクションを取得して返す関数
// クエリー文は引数のstrQueryで指定する
//
// [901_WMI を使用したソフトウェア関係情報の取得]の同名関数と全く同じ
// usesにWbemScripting_TLB(タイプライブラリの取り込みで作成)が必要
//=============================================================================
function TForm1.GetPropertyEnumCollection(strQuery: String;
var Count: Integer): IEnumVariant;
var
strSever : String;
strNameSpace : String;
strUser : String;
strPassword : String;
strLocale : String;
strAuthority : String;
iSecurityFlags : Integer;
Locator : SWbemLocator;
Service : SWbemServices;
PropObjectSet : SWbemObjectSet;
begin
Result := nil;
Count := 0;
strSever := '.'; //PC名。ローカルPCの場合は'.'
strNameSpace := 'root\cimv2'; //名前空間。空文字でも可
strUser := ''; //管理権限者名。ローカルPCの場合は要空文字
strPassword := ''; //パスワード。ローカルPCの場合は要空文字
strLocale := ''; //実行地域コード(米ならMS_409)。空白は現地域
strAuthority := ''; //ドメイン名'ntlmdomain:DOMAIN'のように指定
//ドメイン無しかローカルPCの場合は要空文字
iSecurityFlags := 0; //常に0
//Wbemのオブジェクトを生成して指定引数で接続
Locator := CoSWbemLocator.Create;
try
Service := Locator.ConnectServer(strSever,
strNameSpace,
strUser,
strPassword,
strLocale,
strAuthority,
iSecurityFlags,
nil);
//プロパティのセットのオブジェクトを取得
PropObjectSet := Service.ExecQuery(strQuery,
'WQL',
wbemFlagReturnImmediately,
nil);
//プロパティのセットのコレクションを列挙
Result := PropObjectSet._NewEnum as IEnumVariant;
//ExecQueryのフラグにwbemFlagForwardOnlyを含めるとcountプロパティはエラー
//ただしクエリーの実行は速くなる
Count := PropObjectSet.count;
except
on ex: Exception do
ShowMessage(ex.Message);
end;
end;
| 06_32 ビットアプリから 64 ビット版のコマンドプロンプトを起動 |
コピーフックをシステムに登録、あるいは登録解除する際に、64 ビット版のコマンドプロンプトを非表示で起動しています。
32 ビットのアプリから 64 ビットの System32 内のファイルにアクセスする場合、
以下のように、仮想ディレクトリの Sysnative にアクセスします。あるいは、リダイレクトの機能を無効化します。そうしないと、リダイレクト機能により、32 ビット用のファイルが呼出されてしまいます。
この仮想ディレクトリ Sysnative は、64 ビットのアプリでは使用できません (ありません)。64 ビットアプリでは、実ディレクトリである System32 を使用します。
ディレクトリ System32 は 32 ビット版の Windows のシステムフォルダです。System32 は、32 という名前が付いていますが、64 ビット版の Windows では 64 ビット用のシステムフォルダです。 |
%systemroot%\Sysnative\cmd.exe または
%windir%\Sysnative\cmd.exe
逆に、64 ビットアプリケーションから、32 ビット版のコマンドプロンプトを起動するには、次のように SysWOW64 を指定します。SysWOW64 は、64 ビット版の Windows における 32 ビット版のシステムフォルダとなっています。SysWOW64 は、32 ビット版の Windows にはありません。
以下の値は、デフォルトでは、C:\Windows\SysWOW64\cmd.exe となります。 |
%systemroot%\SysWOW64\cmd.exe または
%windir%\SysWOW64\cmd.exe
| 実際のアプリケーションでは、以下のように、実行環境の Windows が 32 ビットか 64 ビットかの判定が必要です。 |
implementation
uses
ShellAPI;
{$R *.DFM}
//-----------------------------------------------------------------------------
// 32ビットのWindowsか64ビットのWindowsかを調べる関数
// 64ビット版のWindowsの場合はTrueを返す
//-----------------------------------------------------------------------------
function Is64bitWindows: Boolean;
var
Wow64Proc : function(hProcess: THandle; var Wow64: BOOL): BOOL stdcall;
RetFlag : LongBool;
begin
@Wow64Proc := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'IsWow64Process');
if @Wow64Proc <> nil then begin
Wow64Proc(GetCurrentProcess, RetFlag);
if SizeOf(THandle) = 4 then begin
Result := RetFlag;
end else
if SizeOf(THandle) = 8 then begin
Result := True;
end;
end else begin
//Windows XPの64ビット版か
Result := CheckWin32Version(5, 2);
end;
end;
//=============================================================================
// コマンドプロンプトの起動
// 64ビットWindowsでは常に64ビット版のコマンドプロンプトを起動する方法
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LCmdExe : string;
LCmdParam : string;
begin
//64ビットWindowsの場合で自分自身が32ビットEXEの時
if (Is64bitWindows) and (SizeOf(Pointer) = 4) then begin
LCmdExe := 'C:\windows\Sysnative\cmd.exe';
end else begin
LCmdExe := 'cmd.exe';
end;
LCmdParam := '/k';
ShellExecute(0, nil, PChar(LCmdExe), PChar(LCmdParam), nil, SW_SHOWNORMAL);
end;
//=============================================================================
// コマンドプロンプトの起動
// 64ビットWindowsでは常に32ビット版のコマンドプロンプトを起動する方法
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LCmdExe : string;
LCmdParam : string;
begin
//64ビットWindowsの場合で自分自身が64ビットEXEの時
if (Is64bitWindows) and (SizeOf(Pointer) = 8) then begin
LCmdExe := 'C:\windows\SysWOW64\cmd.exe';
end else begin
LCmdExe := 'cmd.exe';
end;
LCmdParam := '/k';
ShellExecute(0, nil, PChar(LCmdExe), PChar(LCmdParam), nil, SW_SHOWNORMAL);
end;
end.
| 07_アプリが 32 ビットか 64 ビット版かを判定する方法 |
サンプルの DLL 登録用のプログラムは、32 ビットのプロジェクトとして作成していますので、必要ありませんが、アプリケーションが 32 ビットか 64 ビット版かを判定する場合、次のような状況が考えられます。
- 自分自身が 32 ビットアプリか 64 ビットアプリか
- 任意の実行可能なファイルが 32 ビット版か 64 ビット版か
- 現在起動中の、自アプリ以外のアプリが 32 ビットアプリか 64 ビットアプリか
自分自身が 32 ビットアプリか 64 ビットアプリかは、NativeInt のサイズやポインタのサイズで調べることができます。32 ビットアプリのポインタのサイズは 4 バイト、64 ビットアプリのポインタのサイズは 8 バイトです。
実行形式のファイルが 32 ビット版か 64 ビット版かは、GetBinaryType 関数の引数の戻り値で判定できます。SCS_64BIT_BINARY の時が 64 ビットアプリです。この定数名は Delphi XE2 以前には定義がありません。Delphi XE3 にはあります。
EXE ファイル、または DLL ファイルが 32 ビット版か 64 ビット版かは、PE ヘッダの情報からも取得できます。
現在起動中のアプリが 32 ビット版か 64 ビット版かは、起動中のアプリのフルパスを取得すれば、実行形式のアプリの判定と同じように、GetBinaryType 関数で判定できます。
また、起動中のアプリのプロセスのハンドルを取得すれば、プロセスのハンドルから、IsWow64Process 関数を使用して判定できます。
IsWow64Process 関数は、Delphi XE2 で実装されています。それより前のバージョンにはありません。利用者が定義して使用することになります。 |
[備考]
GetBinaryType 関数で指定するパス名が、システム関係フォルダの場合、ファイルシステムのリダイレクト機能が働きます。したがって、32 ビットアプリで GetBinaryType を実行すると、結果は常に 32 ビットアプリとなります。また、64 ビットアプリでは常に 64 ビットアプリとなります。
これを回避して、正しい結果を得るには、ファイルシステムのリダイレクト機能を無効にして実行する必要があります。
DLL ファイルの場合、ファイルシステムのリダイレクトの機能を無効にしても、システム関係フォルダ内の DLL は、GetBinaryType 関数では正しく検出できないことがあります。
後述の PE ヘッダを使用するサンプルを参考にしてください。
|
 |
図13
設計時画面 |
 |
図14
32 ビット版のアプリの場合
|
 |
図15
64 ビット版のアプリの場合
|
リスト5
アプリケーションが 32 ビット版か 64 ビット版かを判定する
Windows Vista 以降専用 (正確には Windows XP(SP2) 以降専用) |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
OpenDialog1: TOpenDialog;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private 宣言 }
InitDir : String;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// 自分自身が32bit版か64bit版かを判定
// NativeIntのサイズで判定
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
ByteSize : Integer;
begin
ByteSize := SizeOf(NativeInt);
if ByteSize = 4 then begin
MessageBox(Handle, '32 ビットアプリです', '情報', MB_OK or MB_ICONASTERISK);
end else
if ByteSize = 8 then begin
MessageBox(Handle, '64 ビットアプリです', '情報', MB_OK or MB_ICONASTERISK);
end else begin
MessageBox(Handle, '特定できません', '情報', MB_OK or MB_ICONASTERISK);
end;
end;
//=============================================================================
// 自分自身が32bit版か64bit版かを判定
// Pointerのサイズで判定
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
ByteSize : Integer;
begin
ByteSize := SizeOf(Pointer);
if ByteSize = 4 then begin
MessageBox(Handle, '32 ビットアプリです', '情報', MB_OK or MB_ICONASTERISK);
end else
if ByteSize = 8 then begin
MessageBox(Handle, '64 ビットアプリです', '情報', MB_OK or MB_ICONASTERISK);
end else begin
MessageBox(Handle, '特定できません', '情報', MB_OK or MB_ICONASTERISK);
end;
end;
//=============================================================================
// 実行形式(EXE)が32bit版か64bit版かを判定
// GetBinaryType関数を利用
//
// SCS_32BIT_BINARY等の定数名は、Delphi XE3では実装されている
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
const
SCS_32BIT_BINARY = 0;
SCS_64BIT_BINARY = 6;
var
FileFullPath : String;
RetFlag : LongBool;
BinType : Cardinal;
begin
if InitDir = EmptyStr then begin
InitDir := ExtractFileDir(Application.ExeName);
end;
OpenDialog1.InitialDir := InitDir;
OpenDialog1.Filter := '実行形式ファイル|*.exe';
if not OpenDialog1.Execute then exit;
FileFullPath := OpenDialog1.FileName;
InitDir := ExtractFileDir(FileFullPath);
RetFlag := GetBinaryType(PChar(FileFullPath), BinType);
if RetFlag then begin
if BinType = SCS_32BIT_BINARY then begin
MessageBox(Handle, '32 ビットアプリです', '情報', MB_OK or MB_ICONASTERISK);
end else
if BinType = SCS_64BIT_BINARY then begin
MessageBox(Handle, '64 ビットアプリです', '情報', MB_OK or MB_ICONASTERISK);
end else begin
MessageBox(Handle, '特定できません', '情報', MB_OK or MB_ICONASTERISK);
end;
end else begin
MessageBox(Handle, '特定できません', '情報', MB_OK or MB_ICONASTERISK);
end;
end;
//=============================================================================
// 起動中のプロセスが32bit版か64bit版かを判定
// IsWow64Process関数を利用
// Windows XP(SP2)以上専用
// ここでは、自分自身のプロセスのハンドルで、自分自身を判定
//
// IsWow64Process関数は、Delphi XE2には実装されている
//=============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
IsWow64Process : function(hProcess: THandle; var Wow64Process: BOOL): BOOL stdcall;
RetFlag : LongBool;
begin
@IsWow64Process := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'IsWow64Process');
if @IsWow64Process = nil then begin
MessageBox(Handle, '特定できません', '情報', MB_OK or MB_ICONASTERISK);
end else begin
IsWow64Process(GetCurrentProcess, RetFlag);
if RetFlag then begin
MessageBox(Handle, '32 ビットアプリです', '情報', MB_OK or MB_ICONASTERISK);
end else begin
MessageBox(Handle, '64 ビットアプリです', '情報', MB_OK or MB_ICONASTERISK);
end;
end;
end;
end.
| 08_EXE ( DLL ) の 32 ビットと 64 ビットの判定 - PE ヘッダを使用 |
実行形式ファイルである EXE が 32 ビットか 64 ビットのアプリであるかは、PE ヘッダからも取得できます。PE ヘッダからは DLL が 32 ビット版か 64 ビット版かも取得できます。ファイルシステムのリダイレクト機能を無効にすれば、システムフォルダ内の EXE, DLL の判定も可能です。
IMAGE_OPTIONAL_HEADER 構造体の Subsystemメンバの値を調べると、バイナリファイルが、デバイスドライバであるか、コンソールアプリケーションであるかどうかも知ることができます。
PE ヘッダ (Portable Executable Header) は、EXE や DLL のヘッダ部に書き込まれているファイルの情報で、複数の構造体 (レコード型) で構成されています。本サンプルは、この構造体の一部のメンバの値を取得するサンプルとなっています。他のメンバの値を取得するテストも追加できるように、結果は TMemo に表示します。 |
[備考]
DLL が 32 ビット版か 64 ビット版を判定する方法として、LoadLibrary を実行して、DLL のハンドルを取得する方法があります。32 ビットのアプリでは 64 ビット版の DLL のハンドルの取得に失敗します。また、64 ビットのアプリでは 32 ビット版の DLL のハンドルの取得に失敗します。このことを利用することができます。
ただし、32 ビットアプリの場合、リダイレクトの機能を無効にしても、System32 と SysWOW64 の両方に同じ名前の DLL がある場合、LoadLibrary は、SysWOW64 内の DLL をロードします。
また、LoadLibrary は、実際に DLL のロードに失敗した場合もハンドルの取得に失敗します。したがって、DLL の存在確認が別に必要となるでしょう。 |
下図が、設計時の画面と実行結果の例です。
2 番目の値は CPU の特性値ではありません。ファイルイメージの特性としての値です。 |
 |
図16
設計時画面 |
 |
図17
64 ビットの Shell32.dll の場合 |
 |
図18
32 ビットの Shell32.dll の場合 |
 |
図19
64 ビット版の電卓の場合 |
 |
図20
本ページのサンプルの copyhook.dll |
 |
図21
Delphi XE の bds.exe の場合 |
 |
図22
32 ビットのコンソールアプリの場合 |
リスト6
EXE (DLL) の情報取得を PE ヘッダから取得 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
OpenDialog1: TOpenDialog;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
InitDir : String;
procedure FileHeader_Read(const Header: IMAGE_FILE_HEADER; Memo: TMemo);
procedure OptionalHeader_Read(const Header: IMAGE_OPTIONAL_HEADER; Memo: TMemo);
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
// システムフォルダ内も正確にビットを取得するために、
// ファイルシステムのリダイレクト機能を無効にしておく
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
Wow64DisableProc : function(var flgRedirec: LongBool): LongBool; stdcall;
DirectFlag : LongBool;
begin
@Wow64DisableProc := GetProcAddress(GetModuleHandle('Kernel32.dll'),
'Wow64DisableWow64FsRedirection');
if @Wow64DisableProc <> nil then begin
Wow64DisableProc(DirectFlag);
end;
end;
//=============================================================================
// EXEまたはDLLが32ビットか64ビットかを判定 - PEヘッダから取得
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
FileFullPath : String;
FStream : TFilestream;
DummyData : DWORD;
HeaderInfo_DOS : IMAGE_DOS_HEADER;
HeaderInfo_FILE : IMAGE_FILE_HEADER;
HeaderInfo_OPTIONAL : IMAGE_OPTIONAL_HEADER;
begin
if InitDir = EmptyStr then begin
InitDir := ExtractFileDir(Application.ExeName);
end;
OpenDialog1.InitialDir := InitDir;
OpenDialog1.Filter := 'ファイル|*.exe;*.dll';
if OpenDialog1.Execute then begin
FileFullPath := OpenDialog1.FileName;
InitDir := ExtractFileDir(FileFullPath);
Memo1.Clear;
//ファイルストリームを生成
FStream := TFilestream.Create(FileFullPath, fmOpenread or fmShareDenyNone);
try
Memo1.Lines.Add(' ' + ExtractFileName(FileFullPath));
//IMAGE_DOS_HEADERの情報をHeaderInfo_DOSレコード型に読み込む
FStream.Read(HeaderInfo_DOS, SizeOf(HeaderInfo_DOS));
//IMAGE_FILE_HEADER部の先頭に移動するための位置移動
FStream.Seek(HeaderInfo_DOS._lfanew, soFromBeginning);
FStream.Read(DummyData, SizeOf(DummyData));
//IMAGE_FILE_HEADERの情報をHeaderInfo_FILEレコード型に読み込む
FStream.Read(HeaderInfo_FILE, SizeOf(HeaderInfo_FILE));
//その情報をTMemoに表示する
FileHeader_Read(HeaderInfo_FILE, Memo1);
//一応IMAGE_OPTIONAL_HEADERの存在を確認
if HeaderInfo_FILE.SizeOfOptionalHeader > 0 then begin
//IMAGE_OPTIONAL_HEADER部の情報をHeaderInfo_OPTIONALレコード型に読み込む
FStream.Read( HeaderInfo_OPTIONAL, SizeOf(HeaderInfo_OPTIONAL));
//その情報をTMemoに表示する
OptionalHeader_Read(HeaderInfo_OPTIONAL, Memo1);
end;
finally
FStream.Free;
end;
end;
end;
//-----------------------------------------------------------------------------
// PEのIMAGE_FILE_HEADERレコードのメンバーの値を取得してTMemoに表示
// ここでは、Machineメンバーの値とCharacteristicsメンバーの値のみ取得
//-----------------------------------------------------------------------------
procedure TForm1.FileHeader_Read(const Header: IMAGE_FILE_HEADER; Memo: TMemo);
const
//Delphi XE2以降では不要
IMAGE_FILE_MACHINE_IA64 = $0200;
IMAGE_FILE_MACHINE_AMD64 = $8664;
begin
case Header.Machine of
IMAGE_FILE_MACHINE_I386: Memo.Lines.Add( ' Intel 386 (x86) or later' );
IMAGE_FILE_MACHINE_IA64: Memo.Lines.Add( ' Intel Itanium' );
IMAGE_FILE_MACHINE_AMD64: Memo.Lines.Add( ' AMD 64 (x64)' );
end;
if (IMAGE_FILE_DLL and Header.Characteristics) <> 0 then begin
Memo.Lines.Add(' DLL file');
end else
if (IMAGE_FILE_EXECUTABLE_IMAGE and Header.Characteristics) <> 0 then begin
Memo.Lines.Add(' EXE file' );
end else begin
Memo.Lines.Add(' Unknown file');
end;
end;
//-----------------------------------------------------------------------------
// PEのIMAGE_OPTIONAL_HEADERレコードのメンバーの値を取得してTMemoに表示
// ここでは、Magicメンバーの値のみ取得
// MagicはEXEまたはDLLが32ビット版か64ビット版かの値
//-----------------------------------------------------------------------------
procedure TForm1.OptionalHeader_Read(const Header: IMAGE_OPTIONAL_HEADER;
Memo: TMemo);
const
//Delphi XE2以降では不要
IMAGE_NT_OPTIONAL_HDR32_MAGIC = $010B;
IMAGE_NT_OPTIONAL_HDR64_MAGIC = $020B;
var
MajorVer : String;
MinorVer : String;
begin
//リンカーのバージョン
MajorVer := IntToStr( Header.MajorLinkerVersion);
MinorVer := IntToStr( Header.MinorLinkerVersion);
Memo.Lines.Add(' LinkerVersion : ' + MajorVer + '.' + MinorVer);
if (Header.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC) then begin
Memo.Lines.Add(' IMAGE_NT_OPTIONAL_HDR32_MAGIC');
end else
if (Header.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC) then begin
Memo.Lines.Add(' IMAGE_NT_OPTIONAL_HDR64_MAGIC');
end;
//コンソールアプリケーションの場合
if Header.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI then begin
Memo.Lines.Add(' Subsystem : CONSOLE Application');
end;
end;
end.
| 09_EXE (DLL) の 32 ビットと 64 ビットの判定 - ImageNtHeader 関数 |
PE ヘッダ内の情報の取得には、前項と同じ方法も使えますが、Windows API には、PE ヘッダを操作するための関数類があります。それらは ImageHlp.pas 内で定義されています。この中の ImageNtHeader という関数を使用したサンプルです。
特定の構造体の、特定のメンバーの値だけを取得したい場合は、ImageNtHeader 関数を使用すると便利です。
ImageNtHeader 関数を使用すると、PE ヘッダの先頭ポインタが取得できます。そのためには、ファイルを読み込んだ時の先頭アドレスが必要です。これは、メモリマップドファイルを生成して取得しています。メモリマップドファイルは、ファイルハンドルから生成します。
TMemoryStream や TFileStream は、シーケンシャルなアクセスに向いていますが、メモリマップドファイルは、メモリにロードしたオブジェクトの構造が分かっている場合、ランダムにメモリ内をアクセスするのに適しています。
設計時の画面は前項のサンプルと同じです。実行結果も前項のサンプルと全く同じになります。
|
リスト7
EXE (DLL) の情報取得 - PE ヘッダの取得に ImageNtHeader 関数を使用する例 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
OpenDialog1: TOpenDialog;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
InitDir : String;
function GetFileHeaderInfo(FileHandle: THandle): Boolean;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses ImageHlp;
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
// システムフォルダ内も取得するために、
// ファイルシステムのリダイレクト機能を無効にしておく
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
Wow64DisableProc : function(var flgRedirec: LongBool): LongBool; stdcall;
DirectFlag : LongBool;
begin
@Wow64DisableProc := GetProcAddress(GetModuleHandle('Kernel32.dll'),
'Wow64DisableWow64FsRedirection');
if @Wow64DisableProc <> nil then begin
Wow64DisableProc(DirectFlag);
end;
end;
//=============================================================================
// EXEまたはDLLが32ビットか64ビットかを判定 - PEヘッダから取得
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
FileFullPath : String;
FileHandle : THandle;
begin
if InitDir = EmptyStr then begin
InitDir := ExtractFileDir(Application.ExeName);
end;
OpenDialog1.InitialDir := InitDir;
OpenDialog1.Filter := 'ファイル|*.exe;*.dll';
if OpenDialog1.Execute then begin
FileFullPath := OpenDialog1.FileName;
InitDir := ExtractFileDir(FileFullPath);
Memo1.Clear;
Memo1.Lines.BeginUpdate;
FileHandle := CreateFile(PChar(FileFullPath),
GENERIC_READ,
FILE_SHARE_READ,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if FileHandle = INVALID_HANDLE_VALUE then exit;
try
GetFileHeaderInfo(FileHandle);
finally
CloseHandle(FileHandle);
end;
Memo1.Lines.EndUpdate;
end;
end;
//-----------------------------------------------------------------------------
// PEヘッダからEXEやDLLが32ビット版か64ビット版かを調べる
// ImageHlp.pasのImageNtHeader関数でヘッダのポインタを取得する方法
// そのために、ファイルハンドルを引数にしている
//-----------------------------------------------------------------------------
function TForm1.GetFileHeaderInfo(FileHandle: THandle): Boolean;
const
IMAGE_FILE_MACHINE_IA64 = $0200;
IMAGE_FILE_MACHINE_AMD64 = $8664;
IMAGE_NT_OPTIONAL_HDR32_MAGIC = $010B;
IMAGE_NT_OPTIONAL_HDR64_MAGIC = $020B;
var
HeaderHandle : THandle;
HeaderPointer : Pointer;
NTHeaders : PImageNtHeaders;
Characteristics : Integer;
MajorVer : String;
MinorVer : String;
Magic : WORD;
Subsystem : WORD;
begin
Result := False;
//メモリマップドファイル生成
//ファイルハンドルからファイルのマッピングオブジェクトを取得
HeaderHandle := CreateFileMapping(FileHandle, nil, PAGE_READONLY, 0, 0, nil);
if HeaderHandle = 0 then exit;
try
//メモリマップドファイルをアドレス空間にマップ
//戻り値は開始アドレス
HeaderPointer := MapViewOfFile(HeaderHandle, FILE_MAP_READ, 0, 0, 0);
if not Assigned(HeaderPointer) then exit;
try
//ヘッダのポインタを取得
NTHeaders := ImageNtHeader(HeaderPointer);
if not Assigned(NTHeaders) then exit;
//PEヘッダ(EXE,DLLファイル)であるかを確認
if Trim(String(PAnsiChar(@NTHeaders.Signature))) <> 'PE' then exit;
case NTHeaders.FileHeader.Machine of
IMAGE_FILE_MACHINE_I386: Memo1.Lines.Add( ' Intel 386 (x86) or later' );
IMAGE_FILE_MACHINE_IA64: Memo1.Lines.Add( ' Intel Itanium' );
IMAGE_FILE_MACHINE_AMD64: Memo1.Lines.Add( ' AMD 64 (x64)' );
end;
Characteristics := NTHeaders.FileHeader.Characteristics;
if (IMAGE_FILE_DLL and Characteristics) <> 0 then begin
Memo1.Lines.Add(' DLL file');
end else
if (IMAGE_FILE_EXECUTABLE_IMAGE and Characteristics) <> 0 then begin
Memo1.Lines.Add(' EXE file' );
end else begin
Memo1.Lines.Add(' Unknown file');
end;
//リンカーのバージョン
MajorVer := IntToStr(NTHeaders.OptionalHeader.MajorLinkerVersion);
MinorVer := IntToStr(NTHeaders.OptionalHeader.MinorLinkerVersion);
Memo1.Lines.Add(' LinkerVersion : ' + MajorVer + '.' + MinorVer);
Magic := NTHeaders.OptionalHeader.Magic;
if (Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC) then begin
Memo1.Lines.Add(' IMAGE_NT_OPTIONAL_HDR32_MAGIC');
end else
if (Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC) then begin
Memo1.Lines.Add(' IMAGE_NT_OPTIONAL_HDR64_MAGIC');
end;
Subsystem := NTHeaders.OptionalHeader.Subsystem;
//コンソールアプリケーションの場合
if Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI then begin
Memo1.Lines.Add(' Subsystem : CONSOLE Application');
end;
finally
UnmapViewOfFile(HeaderPointer);
end;
finally
CloseHandle(HeaderHandle);
end;
Result := True;
end;
end.
|