我有一个可行的解决方案。这是一种 hack,但它确实有效。
在我展示解决方案之前,请先声明一下。我基本上同意赫弗曼的观点。这不是应该的。我实际上不建议为运输代码这样做。这是 32 位文本编辑器、文字处理器(包括 32 位 Office)或普通应用程序不支持的东西。 64 位系统上的普通用户不会直接在系统目录中打开或保存文件。而且大多数非管理员用户无论如何都没有适当的权限来触摸那里的文件。 Microsoft 为 32 位应用程序重定向文件系统有很好的理由。不要试图与之抗争。
现在开始解决问题。
诀窍是在 DllMain 中为每个 DLL_THREAD_ATTACH 回调设置一个 DLL 调用 Wow64DisableWow64FsRedirection。
首先创建一个只有 DllMain 并导出几个函数的简单 DLL:“StartDisableRedirect”和“DisableRedirection”。
bool g_fDisableRedirect = false;
__declspec(dllexport)
int DisableRedirection()
{
void* pVoid = NULL;
Wow64DisableWow64FsRedirection(&pVoid);
return 0;
}
__declspec(dllexport)
int StartDisableRedirect()
{
g_fDisableRedirect = true;
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
{
void* pVoid = NULL;
if (g_fDisableRedirect)
{
DisableRedirection();
}
break;
}
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
让您的二进制文件(EXE 或 DLL)直接与此 DLL 链接。然后在调用 GetOpenFileName 之前,调用 StartDisableRedirect(这样后续线程不会被重定向)和 DisableRedirect(针对当前线程)。
我特意制作了一个“启动”函数,以便在钩子实际开始处理线程之前加载所有 DLL(包括系统 DLL)。我不想假设实现 Wow64Disable 的 DLL 会在我的 DLL 之前加载。从 DllMain 调用代码时必须非常小心(阅读:不应该)。
extern int StartDisableRedirect();
extern int DisableRedirection();
void OnFile(HWND hwndParent)
{
StartDisableRedirect();
DisableRedirection();
OPENFILENAME ofn = {};
WCHAR szFile[MAX_PATH*2] = {};
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwndParent;
ofn.lpstrFilter = L"All Files\0*.*\0\0";
ofn.nFilterIndex = 1;
ofn.lpstrFile = szFile;
ofn.nMaxFile = ARRAYSIZE(szFile);
ofn.Flags = OFN_DONTADDTORECENT|OFN_ENABLESIZING|OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST;
::GetOpenFileName(&ofn);
}