Programming Windows Maniacs - プログラミング ウィンドウズ マニアックス ■ ご利用に際して ■ 更新履歴 ■ お問い合わせ ■ このホームページについて  
ホーム >> シェル >> 「ファイルに名前をつけて保存」と「ファイルを指定して開く」処理をさせないようにするには

「ファイルに名前をつけて保存」と「ファイルを指定して開く」処理をさせないようにするには

  ほとんどのアプリケーションは、元々あるファイルを編集し保存しなおすだけでなく、「ファイルに名前をつけて保存」することができます。
  また、アプリケーションからファイルを指定して開くこともできます。
  すべてのアプリケーションで、ファイルに名前をつけて保存をさせたくない場合やファイルを指定して開かせたくない場合、このウィンドウを強制的にキャンセルする必要があります。
  方法としては、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 () APIWM_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でのみテスト済みです。

  ■ ご利用に際して ■ 更新履歴 ■ お問い合わせ ■ このホームページについて Copyright © 2014 A.Morita