標籤:
寫一個Windows上的守護進程(5)檔案系統重新導向
在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,為什麼呢?
在WindowsXP這麼流行的平台上,它們沒有啊!你要是直接調用這兩個函數,在XP上,整個程式就跑不起來。
怎麼辦?
用GetProcAddress動態載入這兩個函數,如果沒有這兩個函數,就說明不存在重新導向問題。
以下是實現:
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日星期日
寫一個Windows上的守護進程(5)檔案系統重新導向