2

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?

user541686
  • 205,094
  • 128
  • 528
  • 886
  • Shouldn't you just set the right initial folder in the dialog when you want to display files in the system32 folder? – GolezTrol Mar 09 '11 at 06:48
  • @GolezTrol: It's for a text editor (and similar kinds of apps that I've made) where *I* don't (or rather, can't) have any preference on what the user chooses, but if he wants to choose something in `System32`, then he can't. Anyway, that's not really a solution, but just a workaround that doesn't really work. (Thanks for the suggestion though.) – user541686 Mar 09 '11 at 06:50
  • If you're using C#, why not just compile for `AnyCPU` and not worry about it? – Gabe Mar 09 '11 at 08:32
  • That's not really a solution: it wouldn't work if you *needed* to use 32-bit DLLs in your code. There's more issues with that approach, but basically the entire question is about getting it to work in a 32-bit program, not finding solutions that entirely bypass the question. :( – user541686 Mar 09 '11 at 08:36
  • If it were possible, you could expect that Raymond Chen would have mentioned it in his follow-up in http://blogs.msdn.com/b/oldnewthing/archive/2010/12/31/10110525.aspx – Gabe Mar 09 '11 at 08:47
  • FYI, the open file dialog itself runs on the same thread as your app, but it does its filesystem access from a different thread to remain responsive while doing I/O. – Gabe Mar 09 '11 at 08:55
  • @Gabe: Ah... *that* makes sense! Now, the question becomes, is there any better way to somehow intercept the calls of the other threads (by some callback maybe?) that is easier than hooking the functions? – user541686 Mar 09 '11 at 09:01
  • possible duplicate of [WOW64 Redirection and LoadLibrary](http://stackoverflow.com/questions/4721342/wow64-redirection-and-loadlibrary) – David Heffernan Mar 09 '11 at 09:06

3 Answers3

5

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.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • How is this any different from enumerating the files using the standard `FindFirstFile` API after disabling redirection? It would return the real name of the file, plain and simple... – user541686 Mar 09 '11 at 07:54
  • if it returned system32 to your 32 bit process then when you accessed the file you'd get redirected to syswow64. – David Heffernan Mar 09 '11 at 07:57
  • @David: No, I wouldn't, because I would have disabled redirection... that's the whole point of `Wow64DisableWow64FsRedirection`. – user541686 Mar 09 '11 at 07:58
  • @David: And, by the way: "Not possible" is virtually *never* an answer -- you could always theoretically hook the function calls and thread creations, for example, but that's a pain... is there a better way? – user541686 Mar 09 '11 at 08:00
  • disabling redirection doesn't work here. You've already discovered that. The solution is to stop using the emulator and write a 64 bit program just like I said in my answer. – David Heffernan Mar 09 '11 at 08:09
  • @David: Of course I know that `Wow64DisableWow64FsRedirection` doesn't work (that was in the original question), and of course I know that writing a 64-bit program solves it. But the question was whether I can get the 32-bit version to work in some **other** way, since with the language/compiler I'm using (D), there's no 64-bit compiler, so I'm stuck with 32-bit. (I'm trying to get the actual question answered, not to erase the topic entirely.) So unless it's literally impossible (in which case I'd really love to see some evidence of why it is), "migrate to 64-bit" won't be a solution for me. – user541686 Mar 09 '11 at 08:14
  • @David: Haha I realize that impossibility is the potential answer, but "I believe this is simply not possible" doesn't really convince me, because it looks like speculation (rather than being supported by evidence). Do you happen to know *why* we shouldn't be able to disable redirection like we can with `Wow64DisableWow64FsRedirection`? I mean, we could all guess that things are impossible, but unless there's a good reason why, that's not a very strong answer. – user541686 Mar 09 '11 at 08:21
  • @Mehrdad It's because the shell dialogs run in different threads, presumably so they can get their activation contexts, COM initialization etc. right. But even if you could disable redirection, you'd be left with the naming problem. In XP 64 there was no sysnative so there was simply no way to name the file reasonably. I'm making large assumptions here because I doubt very much that it is documented, but MS would be well within their rights to say that 32 bit app on 64 bit OS is an emulation, system32 is a private directory, so use 64 bit process if you must see it in shell dialog. – David Heffernan Mar 09 '11 at 08:23
  • @Mehrdad If it is indeed impossible to do this, it's very hard to come up with proof. Non-existence theorems are notorious for being harder to justify than existence theorems. An example of existence cannot be denied. The absence of counter-examples proves nothing! – David Heffernan Mar 09 '11 at 08:25
  • @David: But what I don't understand here is, if you disable redirection, there *is no* naming problem! Every file would have a unique path and all would be well (except for when loading DLLs, for which you could temporarily enable redirection again). And yeah, I'm not looking for "proof" (I realize that doesn't make much sense), but just mere support beyond speculation... so far, I haven't seen any link or anything regarding why it shouldn't be possible, so that's why your answer isn't very convincing. – user541686 Mar 09 '11 at 08:25
  • @David: Seems like there's a misunderstanding. I definitely want help, but if the answer is "it is impossible", I'm just asking if you happen to have any *support* for the answer, beyond pure speculation? Surely asking for a link or something about the reason isn't asking for too much? – user541686 Mar 09 '11 at 08:29
  • @David: Oooh ouch -- I *did* notice the DLL loading thing but I never thought about the shell extension part of the shell dialog! Yeah, 32-bit shell extensions would be a problem; that makes a lot more sense now. Feel free to add this note to your answer and I'll upvote it (since it's a really good -- and convincing -- explanation of the reason IMHO). :) – user541686 Mar 09 '11 at 09:05
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);

}
selbie
  • 100,020
  • 15
  • 103
  • 173
  • Whoa, nice! o__o So... how does this handle the case where system DLLs are delay-loaded in a different thread (which seems to happen pretty much whenever the dialog loads for the first time)? – user541686 Mar 09 '11 at 09:36
  • @Mehrdad It won't. Shell extensions will be kill this. – David Heffernan Mar 09 '11 at 09:39
  • @David: I wasn't really talking about shell extensions, I was talking about *regular* DLLs like `Ole32.dll`, which are a normal part of windows and that get delay-loaded in *every* situation. I'm curious how this gets handled, since @selbie obviously said it works. :) – user541686 Mar 09 '11 at 09:42
  • I would assume the DLL Loader does not get influenced by Wow64DisableRedirect. It would be an application bug farm if it did. YMMV. – selbie Mar 09 '11 at 09:48
  • 1
    DLL Loader is influenced by redirection disabling. That's what the documentation says. – David Heffernan Mar 09 '11 at 09:53
  • Probably things like ole32 etc. will already be in the process when you run the dialog. – David Heffernan Mar 09 '11 at 09:53
  • @David. Good call. I may down-vote myself. :) So I think the final verdict is that it is possible, but totally not recommended! – selbie Mar 09 '11 at 09:57
  • @selbie Yeah, fun to play with for education, but you wouldn't want to ship to a paying customer!! – David Heffernan Mar 09 '11 at 10:03
  • @selbie: +1 -- the idea of hooking `DLL_ATTACH_THREAD` was pretty innovative, whether or not this works. :) – user541686 Mar 09 '11 at 10:27
0

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.

Gabe
  • 84,912
  • 12
  • 139
  • 238