在Windows上經常操作文件或注冊表的同學可能知道,有“文件系統/注冊表重定向”這麼一回事。大致來說就是32位程序在64位的Windows上運行時,操作系統會把對System32文件夾的訪問重定向到SysWow64下,把對HKEY_LOCAL_MACHINE\SOFTWARE的訪問重定向到HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node下。當然不止這些路徑和注冊表。詳情請查看MSDN:https://msdn.microsoft.com/en-us/library/aa384187.aspx 和https://msdn.microsoft.com/en-us/library/aa384232(v=vs.85).aspx 。
我們通常為了方便發布,都只會編譯一份32位的程序,不會編譯64的程序。如果代碼中涉及到了訪問文件和注冊表,那就要考慮這個問題了。
Windows提供了兩個API(准確的說是3個,但其中一個已不推薦使用了)來禁用文件系統/注冊表重定向:Wow64DisableWow64FsRedirection,Wow64RevertWow64FsRedirection。顧名思義,前者用於禁用,後者用於恢復。
有的同學可能就說了:我一直禁用就行了,干嘛還要恢復?這你就想少了,有的代碼段可能並不關心是否有重定向,所以並沒有考慮這個問題,假若這個代碼段的結果會影響多個線程,而你剛好又把包含這個代碼段的某一線程的重定向禁用了,那就成了有的線程禁用了重定向,有的線程沒有禁用,獲取的結果就不一致了。
一般我們在會有重定向問題的函數調用前禁用重定向,調用完畢後,恢復重定向。這很容易讓人想到使用RAII手法:在類構造函數中禁用,析構函數中恢復:
class scoped_disable_wow64_fsredirection : public boost::noncopyable { public: scoped_disable_wow64_fsredirection(); ~scoped_disable_wow64_fsredirection(); private: static bool disable(void **ppOldValue); static bool revert(void *pOldValue); private: void *_pOldValue; };
構造函數的實現中調用了disable,析構函數的實現中調用了revert。
其中disable就是調用了Wow64DisableWow64FsRedirection,revert就是調用了Wow64RevertWow64FsRedirection。
注:類名字這麼長是為了能夠達到“顧名思義”的程度。還沒想到什麼更好的名字。哎,起名真是個頭疼的事。
但是我們不能直接調用這兩個Windows API,為什麼呢?
static boost::once_flag once_;
typedef int (__stdcall *fnWow64DisableWow64FsRedirection)(void *);
typedef int (__stdcall *fnWow64RevertWow64FsRedirection)(void *);
static fnWow64DisableWow64FsRedirection g_fnWow64DisableWow64FsRedirection = NULL;
static fnWow64RevertWow64FsRedirection g_fnWow64RevertWow64FsRedirection = NULL;
static void load_wow64_funcs()
{
g_fnWow64DisableWow64FsRedirection = reinterpret_cast<fnWow64DisableWow64FsRedirection>
(WindowsUtil::load_function("Kernel32.dll", "Wow64DisableWow64FsRedirection"));
g_fnWow64RevertWow64FsRedirection = reinterpret_cast<fnWow64RevertWow64FsRedirection>
(WindowsUtil::load_function("Kernel32.dll", "Wow64RevertWow64FsRedirection"));
}
scoped_disable_wow64_fsredirection::scoped_disable_wow64_fsredirection()
: _pOldValue(NULL)
{
boost::call_once(once_, load_wow64_funcs);
disable(&_pOldValue);
}
scoped_disable_wow64_fsredirection::~scoped_disable_wow64_fsredirection()
{
revert(_pOldValue);
}
bool scoped_disable_wow64_fsredirection::disable(void **ppOldValue)
{
bool ret = true;
if (g_fnWow64DisableWow64FsRedirection)
{
if (!g_fnWow64DisableWow64FsRedirection(ppOldValue))
{
ErrorLogLastErr("Wow64DisableWow64FsRedirection fail");
ret = false;
}
}
return ret;
}
bool scoped_disable_wow64_fsredirection::revert(void *pOldValue)
{
bool ret = true;
if (g_fnWow64RevertWow64FsRedirection)
{
if (!g_fnWow64RevertWow64FsRedirection(pOldValue))
{
ErrorLogLastErr("Wow64RevertWow64FsRedirection fail");
ret = false;
}
}
return ret;
}
這裡用了前面文章講到的call_once去加載這兩個函數。
load_function封裝了GetModuleHandleA- GetProcAddress兩個函數的調用,詳情請參看源碼。
使用時,僅需定義一個類實例就可以了。切記,要盡量縮小作用域,以免影響其他代碼段。
源碼:https://git.oschina.net/mkdym/DaemonSvc.git (主)&& https://github.com/mkdym/DaemonSvc.git (提升逼格用的)。
2015年11月1日星期日