32-bit OpenFileDialog --> 64-bit System32?

2019-07-28 05:01发布

问题:

In a 32-bit program, how can I get the Open/Save File Dialogs to show the files in the System32 folder of a 64-bit system?

(Wow64DisableWow64FsRedirection doesn't work, because for some reason it doesn't work for the dialog, I'm guessing because it's on a different thread. And of course using SysNative doesn't work because the user isn't aware of what's going on internally; he just wants to see the "actual" files on the computer.)


Here's another way to ask the question:

Does any 32-bit program browse a 64-bit System32 folder from the open file dialog?

回答1:

I believe that this is simply not possible.

Even if you could get the dialog to show the files, what would their names be when they were returned to your 32 bit process? Sysnative is a bit of a hack and in any case not available on XP 64. This is just a consequence of overloading the system32 name.

Another thought experiment. If it were to be possible, you'd need the thread that does the enumeration to disable redirection. Since that thread is out of your control then there would have to be a published option to disable it. There isn't. It would be no good allowing you to disable redirection from the outside because that would result in DLL load failures when the 32 bit process tried to load shell extensions - you can't disable redirection if you are going to load DLLs because you'll get the wrong ones

I imagine that you are expected to write a 64 bit program if you want to get around this limitation.



回答2:

I have a working solution for this. It's kind of a hack, but it works.

Brief disclaimer before I show the solution. I mostly agree with Hefferman. It's not meant to be. I don't actually recommend doing this for shipping code. It's something that no 32-bit text editor, word processor (including 32-bit Office), or normal app supports. Normal users on 64-bit systems don't open or save files directly in the system directories. And most non-admin users don't have appropriate permission anyway to be touching files in there anyway. Microsoft redirects the file-system with very good reason for 32-bit apps. Don't try to fight it.

Now on to the solution.

The trick is to have a DLL call Wow64DisableWow64FsRedirection in DllMain for every DLL_THREAD_ATTACH callback.

First create a simple DLL that only has a DllMain and exports a couple of functions: "StartDisableRedirect" and "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;
}

Have your binary (EXE or DLL) link directly with this DLL. And then right before you call GetOpenFileName, call StartDisableRedirect (such that subsequent threads are not redirected) and DisableRedirect (for the current thread).

I deliberately made a "Start" function such that all DLLs (including system DLLs) were loaded before the hook would actually start processing threads. I didn't want to assume that the DLL implementing Wow64Disable would be loaded before my DLL. You have to be very careful (read: shouldn't) when calling code from 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);

}


回答3:

This is a common problem with installers. People want to ship a single installer executable for both 32-bit and 64-bit systems, meaning it has to be 32-bit. Yet a 32-bit installer can't put 64-bit executables in the right place. The solution, as described by Raymond Chen is to have a separate 64-bit installer that gets invoked by the 32-bit version on 64-bit machines.

You would create a 64-bit program whose job is to open a common dialog with your app window as owner. On a 64-bit system, you just create the process that opens the dialog, passing it the parameters that you would pass to GetOpenFileName or whatever. You could listen for the filenames on stdout or use some other IPC mechanism. Remember to use Wow64DisableWow64FsRedirection when opening the returned files! It may seem clunky to have UI running in another process, but it would be seamless to the users and many web browsers run different tabs or plug-ins in different processes.

If you're fine with using Vista or higher, you can use the IFileDialog interface which would allow you to add a "Place" for the SysNative directory. That way your users could still access the files if they need to. There might even be a simple way to redirect things so that when somebody clicks the System32 directory you take them to SysNative instead.