Posted at

WOW64 のファイル システム リダイレクタを制御するクラス

More than 3 years have passed since last update.


このクラスの責務


  • 32ビットアプリから、64ビットOS環境の System32や、ProgramFilesにアクセスすると、ファイルシステムにリダイレクトされます。詳細:ファイル システム リダイレクタ(MSDN)

  • リダイレクト対象となるファイルの存在チェックや、exeの実行時に影響を出さないように、ON/Offする機能をもちます。


実装その1:無効化の寿命が不定


実装コード

class Wow64FsRedirectionService

{

typedef BOOL ( WINAPI *fpWow64DisableWow64FsRedirection )( LPVOID* );
typedef BOOL ( WINAPI *fpWow64RevertWow64FsRedirection )( LPVOID );

public:
Wow64FsRedirectionService()
: m_lpOldVar( nullptr )
, m_fpWow64DisableWow64FsRedirection(nullptr)
, m_fpWow64RevertWow64FsRedirection(nullptr)
{
m_hModule = ::LoadLibrary( _T( "Kernel32.dll" ) );
if( m_hModule )
{
m_fpWow64DisableWow64FsRedirection = (fpWow64DisableWow64FsRedirection)::GetProcAddress( m_hModule, "Wow64DisableWow64FsRedirection" );
m_fpWow64RevertWow64FsRedirection = (fpWow64RevertWow64FsRedirection)::GetProcAddress( m_hModule, "Wow64RevertWow64FsRedirection" );
}
}
~Wow64FsRedirectionService()
{
if ( m_hModule ) {
// m_lpOldVar がnullptrではないとき => Wow64RevertWow64FsRedirection(リダイレクト有効化)が呼ばれていないので、もどしておく
RevertRedirection();

::FreeLibrary(m_hModule);
m_hModule = nullptr;
}
}

void DisableRedirection() {
if (m_fpWow64DisableWow64FsRedirection )
m_fpWow64DisableWow64FsRedirection( &m_lpOldVar );
}
void RevertRedirection() {
if (m_lpOldVar && m_fpWow64RevertWow64FsRedirection ) {
m_fpWow64RevertWow64FsRedirection( m_lpOldVar );
m_lpOldVar = nullptr;
}
}

private:
LPVOID m_lpOldVar;
HINSTANCE m_hModule;

fpWow64DisableWow64FsRedirection m_fpWow64DisableWow64FsRedirection;
fpWow64RevertWow64FsRedirection m_fpWow64RevertWow64FsRedirection;
};


使いかた・問題点

void do()

{
Wow64FsRedirectionService serv;
serv.DisableRedirection();

// リダイレクトされてしまうファイルパスへのアクセスはここに。

serv.RevertRedirection();
}

この例ではdoメソッドのスコープ内で無効・有効が完結するので問題は起こらないけれど、本クラスをクラスメンバとして定義された場合、もしくはアプリの寿命と同じくらいのスコープまで無効化が伸びてしまう可能性が起こりうるのでよくない。

ドキュメントにもそう書かれている。


詳細:ファイル システム リダイレクタ(MSDN)

ファイル システムのリダイレクトを長期にわたって無効にすると、32 ビット アプリケーションでシステム DLL が読み込まれなくなり、アプリケーションでエラーが発生することがあります。



実装その2:必要な処理が完了したらすぐに有効化する


実装コード

#include <functional>

class Wow64FsRedirectionService
{
typedef std::function<void()> DisableScopeFunction;

typedef BOOL ( WINAPI *fpWow64DisableWow64FsRedirection )( LPVOID* );
typedef BOOL ( WINAPI *fpWow64RevertWow64FsRedirection )( LPVOID );

public:
Wow64FsRedirectionService()
: m_lpOldVar( nullptr )
, m_fpWow64DisableWow64FsRedirection(nullptr)
, m_fpWow64RevertWow64FsRedirection(nullptr)
{
m_hModule = ::LoadLibrary( _T( "Kernel32.dll" ) );
if( m_hModule )
{
m_fpWow64DisableWow64FsRedirection = (fpWow64DisableWow64FsRedirection)::GetProcAddress( m_hModule, "Wow64DisableWow64FsRedirection" );
m_fpWow64RevertWow64FsRedirection = (fpWow64RevertWow64FsRedirection)::GetProcAddress( m_hModule, "Wow64RevertWow64FsRedirection" );
}
}
~Wow64FsRedirectionService()
{
if ( m_hModule ) {
::FreeLibrary(m_hModule);
m_hModule = nullptr;
}
}

void UsingDisable(DisableScopeFunction dosomething) {

if( DisableRedirection() )
{
dosomething();
RevertRedirection();
}
}
private:
bool DisableRedirection() {
if ( !m_fpWow64DisableWow64FsRedirection )
return false;

return TRUE == m_fpWow64DisableWow64FsRedirection( &m_lpOldVar );
}
bool RevertRedirection() {
if (!m_lpOldVar || !m_fpWow64RevertWow64FsRedirection )
return false;

const BOOL result = m_fpWow64RevertWow64FsRedirection( m_lpOldVar );
m_lpOldVar = nullptr;
return TRUE == result;
}

private:
LPVOID m_lpOldVar;
HINSTANCE m_hModule;

fpWow64DisableWow64FsRedirection m_fpWow64DisableWow64FsRedirection;
fpWow64RevertWow64FsRedirection m_fpWow64RevertWow64FsRedirection;
};


変更点



  • DisableRedirectionRevertRedirectionprivateに変更


  • publicUsingDisableを追加


  • UsingDisableで実行される関数オブジェクトtypedef std::function<void()> DisableScopeFunctionを追加


使いかた

Wow64FsRedirectionService FsRedirecta;

FsRedirecta.UsingDisable( [](){
// リダイレクトされてしまうファイルパスへのアクセスはここに。
});


なにがよくなった?


  1. Disable→Revertのスコープを一定にできた

  2. 有効化し忘れることがなくなった

  3. タイプ数が減った