「ファイルに名前をつけて保存」と「ファイルを指定して開く」処理をさせないようにするには
ほとんどのアプリケーションは、元々あるファイルを編集し保存しなおすだけでなく、「ファイルに名前をつけて保存」することができます。
また、アプリケーションからファイルを指定して開くこともできます。
すべてのアプリケーションで、ファイルに名前をつけて保存をさせたくない場合やファイルを指定して開かせたくない場合、このウィンドウを強制的にキャンセルする必要があります。
方法としては、APIフックやインジェクション、デバイスレベルでAPIをフックするなど複数ありますが、一番簡単な方法は、ウィンドウをフックすることです。
Microsoft SPY++で見てみるとわかりますが、GetOpenFileName () APIや GetSaveFileName () APIで表示されるフォルダ一覧画面は、
OS |
WNDCLASS名 |
キャプション |
Windows 2000 |
"SysListView32" |
"" |
Windows XP |
"SysListView32" |
"FolderView" |
Windows Vista |
"SysListView32" |
"FolderView" |
|
となっています。
explorer.exe のファイル一覧表示も同じ"SysListView32"コンポーネントを使っています。
なので、GetOpenFileName () API (ファイルを指定して開く)や GetSaveFileName () API (ファイルに名前を付けて保存)をフックするときにこれだけでは判断基準となりませんが、親ウィンドウのクラス名を見ますと、
ウィンドウ |
WNDCLASS名 |
explorer.exe |
ExploreWClass |
GetOpenFileName |
#32770 (ダイアログ) |
GetSaveFileName |
#32770 (ダイアログ) |
|
となっていて区別がつきます。
これで、フックの対象を見つける準備が整いました。
「名前を付けて保存する」ウィンドウはダイアログなので、作成されたときのメッセージ、つまりWM_INITDIALOGメッセージをフックします。
さらにダイアログですので、IDCANCELを送るとEndDialog ()するように実装されているはずです。
したがって、PostMessage () API で WM_COMMAND メッセージで IDCANCEL を指定して送ると、瞬間「名前を付けて保存」ダイアログが表示され、すぐに終了します。
以上で、処理は完了です。
※ Windows Vistaでは、同一の権限なら上記フックも動作しますが、権限がより高いウィンドウをフックすることはできません。このプログラムを管理者権限で実行しない限り、権限上昇しているプロセスのウィンドウをフックすることはできません。
※ 権限上昇したい場合は、manifest ファイルを適用してください。
以下、サンプルになります。
■■■ gofnhook DLL プロジェクト
■ main.cpp
#include <windows.h>
#include <tchar.h>
// #include "gofnhook.h"
// Dialog WNDCLASS名
#define DIALOG_CLASSNAME _TEXT("#32770")
// FolderView用 SysListView32 WNDCLASS名
#define SYSLISTVIEW32_CLASSNAME _TEXT("SysListView32")
// FolderView用 SysListView32 Caption名
#define SYSLISTVIEW32_CAPTIONNAME _TEXT("FolderView")
#define MAX_CLASSNAME 4096
#define MAX_CAPTIONNAME 260
// インスタンス ハンドル
HINSTANCE g_hInstance = NULL;
// フック ハンドル
HHOOK g_hHook = NULL;
BOOL APIENTRY DllMain ( HINSTANCE hInstance,
DWORD dwReason,
void *pReserved )
{
switch ( dwReason ) {
case DLL_PROCESS_ATTACH:
g_hInstance = hInstance;
break;
}
return TRUE;
}
BOOL CALLBACK EnumChildFunc ( HWND hwnd, LPARAM lParam )
{
// WNDCLASS名がSysListView32か?
_TCHAR sz[ MAX_CLASSNAME ];
memset ( sz, 0, MAX_CLASSNAME );
GetClassName ( hwnd, sz, MAX_CLASSNAME );
if ( 0 == lstrcmp ( SYSLISTVIEW32_CLASSNAME, sz ) )
{
// 2000のときはキャプションバーに名前がないので、2000対応するには書かない
// CaptionがFolderViewか?
_TCHAR szCaption[ MAX_CAPTIONNAME ];
memset ( szCaption, 0, MAX_CAPTIONNAME );
GetWindowText ( hwnd, szCaption, MAX_CAPTIONNAME );
if ( 0 == lstrcmp ( SYSLISTVIEW32_CAPTIONNAME, szCaption ) )
{
BOOL *pb = reinterpret_cast < BOOL * > ( lParam );
*pb = TRUE;
return FALSE;
}
}
return TRUE;
}
long CALLBACK CallWndFunc ( int nCode,
WPARAM wParam,
LPARAM lParam )
{
if ( 0 == wParam )
{
CWPRETSTRUCT *pCwpStruct = reinterpret_cast < CWPRETSTRUCT * > ( lParam );
long nInstance = GetWindowLong ( pCwpStruct->hwnd, GWL_HINSTANCE );
_TCHAR sz[ MAX_CLASSNAME ];
memset ( sz, 0, MAX_CLASSNAME );
GetClassName ( pCwpStruct->hwnd, sz, MAX_CLASSNAME );
// ダイアログであるか?
if ( 0 == lstrcmp ( DIALOG_CLASSNAME, sz ) )
{
BOOL bOpenDialog = FALSE;
BOOL *pbOpenDialog = &bOpenDialog;
// SysListView32を含むか?
EnumChildWindows ( pCwpStruct->hwnd, EnumChildFunc, reinterpret_cast < LPARAM > ( pbOpenDialog ) );
// SysListView32 で FolderViewか?
if ( bOpenDialog )
{
// 表示処理のとき、終了する
if ( WM_INITDIALOG == pCwpStruct->message )
{
PostMessage ( pCwpStruct->hwnd, WM_COMMAND, MAKEWORD ( IDCANCEL, 0 ), 0 );
}
}
}
}
return CallNextHookEx ( g_hHook, nCode, wParam, lParam );
}
extern "C" void __declspec ( dllexport ) ReleaseHook ()
{
if ( NULL != g_hHook )
{
UnhookWindowsHookEx ( g_hHook );
g_hHook = NULL;
}
}
extern "C" BOOL __declspec ( dllexport ) InitializeHook ()
{
if ( NULL != g_hHook )
{
ReleaseHook ();
}
g_hHook = SetWindowsHookEx ( WH_CALLWNDPROCRET,
CallWndFunc,
g_hInstance,
0 );
if ( g_hHook == NULL )
{
return FALSE;
}
return TRUE;
}
|
■■■ gofnhookapp EXE プロジェクト
■ main.cpp
// XPでテスト済み
#include <windows.h>
#include <tchar.h>
BOOL ( * pInitializeHook ) ();
void ( * pReleaseHook ) ();
int WINAPI _tWinMain ( HINSTANCE hThisInstance,
HINSTANCE,
_TCHAR *,
int )
{
// DLL読み込み
HMODULE hHookDLL;
hHookDLL = LoadLibrary ( _TEXT ( "gofnhook.dll" ) );
if ( NULL == hHookDLL )
{
MessageBox ( NULL,
_TEXT ( "LoadLibrary failed" ),
_TEXT ( "Error" ),
MB_OK | MB_ICONSTOP );
return 0;
}
// 関数ポインタ取得
pInitializeHook = ( BOOL ( * ) () ) GetProcAddress ( hHookDLL, _TEXT ( "InitializeHook" ) );
if ( NULL == pInitializeHook )
{
MessageBox ( NULL,
_TEXT ( "GetProcAddress failed - InitializeHook" ),
_TEXT ( "Error" ),
MB_OK | MB_ICONSTOP );
return 0;
}
pReleaseHook = ( void ( * ) () ) GetProcAddress ( hHookDLL, _TEXT ( "ReleaseHook" ) );
if ( NULL == pReleaseHook )
{
MessageBox ( NULL,
_TEXT ( "GetProcAddress failed - ReleaseHook" ),
_TEXT ( "Error" ),
MB_OK | MB_ICONSTOP );
return 0;
}
// フック開始
if ( !pInitializeHook () )
{
MessageBox ( NULL,
_TEXT ( "InitializeHook failed" ),
_TEXT ( "Error" ),
MB_OK | MB_ICONSTOP );
return 0;
}
// Escキーを1度でも押すと終わります
while ( 1 )
{
Sleep ( 1000 );
if ( 0 != ( GetAsyncKeyState ( VK_ESCAPE ) & 0x01 ) )
{
break;
}
}
// フック終了
pReleaseHook ();
// DLL解放
FreeLibrary ( hHookDLL );
return 0;
}
|
両方のプロジェクトをコンパイルし、同一フォルダにEXEとDLLを配置し、EXEを実行してください。
その状態でどのアプリケーションで「名前を付けて保存」ダイアログを表示しても、瞬間的にキャンセルされ、保存できなくなります。
「ファイルを指定して開く」処理も同様です。
※ ESCキーを押すと、EXEは終了します。このGetAsyncKeyState () API 処理自体はXPでのみテスト済みです。 |