1

GetOpenFileName(出于可疑原因)在显示对话框时更改应用程序的当前目录。这可以通过指定OFN_NOCHANGEDIR为对话框初始化标志在对话框关闭时重置:

OFN_NOCHANGEDIR如果用户在搜索文件时更改了目录,则将当前目录恢复为其原始值。

但是,设置此标志不会阻止函数在显示资源管理器对话框时更改当前目录。

这是多线程环境中的一个问题,其中其他线程依赖当前目录来保持可执行文件的路径。

有没有办法阻止GetOpenFileName在显示资源管理器对话框并且用户浏览文件夹时更改应用程序的当前目录?

4

3 回答 3

4

您在这里有几个选择:

  1. 使用 detours 或 MinHook 挂钩 SetCurrentDirectory 并使其对您的流程无能为力(丑陋)
  2. 使用不会更改当前目录的自定义文件选择器。(更好的)
  3. 删除代码中当前目录的依赖项,因为您可能会遇到其他相关的错误,尤其是在多线程环境中。(最好的)
于 2018-05-22T14:01:09.563 回答
0

我决定发布应该是可行的解决方案。它涉及“正确”修复代码以使用绝对路径,但请耐心等待。这并不难。OP发布的答案太危险了。它一点也不“便宜”。相反,从长远来看,它可能会非常昂贵。

现在我假设在代码中散布着函数/方法调用,它们通过相对路径名(很可能是不合格的文件名)访问文件或文件夹。可能有很多这些,但它们可能都归结为:

do_something_with_this_file ("somefile.foo");

现在,如果do_something_with_this_file是在应用程序中定义的函数,则解决方案很明显:更改该函数的实现以将传入的参数转换为绝对路径名(如有必要),然后再对其进行任何操作。

但生活不是那么容易,如果do_something_with_this_file是一个电话CreateFile或也许fopen。然后我们被该函数的现有行为所困扰。还是我们?实际上,没有。有一个简单的解决方案,只涉及一个宏和少量的实现工作。

是时候举个例子了。我会使用fopen它,因为它有一个很好的简单 API,但它同样适用于CreateFile其他任何东西。

首先,创建一个头文件,让我们调用它AbsolutePathHelpers.h,它重新定义fopen,以及您需要的任何其他内容,如下所示:

// AbsolutePathHelpers.h

FILE *APHfopen (const char *filename, const char *mode);
#define fopen APHfopen
// ...

确保这个头文件被包含在你的所有编译单元中,可能是#include通过将它放在他们都使用的一些通用头文件中。

APHfopen现在在一个新的 .cpp 文件(我们称之为)中编写实现AbsolutePathHelpers.cpp,您需要将其与您的项目链接:

// AbsolutePathHelpers.cpp
#include <stdio.h>

#undef fopen
FILE *APHfopen (const char *filename, const char *mode)
{
    printf ("Opening %s\n", filename);      // diagnostic message for testing
    // Convert filename to absolute path here (if necessary) before calling the 'real' fopen
    return fopen (filename, mode);
}

// ...

最后是一个简单的测试程序:

// Test program
#include <stdio.h>

int main ()
{
    FILE *f = fopen ("myfile", "r");
    if (f)
        fclose (f);

    printf ("Finished.  Press any key...");
    getchar ();
    return 0;
}

输出:

Opening myfile
Finished.  Press any key...

在Wandbox运行它。

我将把将相对路径转换为绝对路径的细节留给 OP。您所需要的只是一个包含程序启动时生效的当前目录的全局变量。或者,我强烈怀疑,这实际上应该是包含可执行文件的目录。

于 2018-05-27T09:12:24.870 回答
-1

正如评论中的其他人所提到的,依赖当前目录始终保持不变并不理想。如果不需要重写资源加载工厂(不是我的代码),我会更改它。

然而,一个公认的廉价和混乱的解决方案是挂钩 OpenFileDialog 的窗口过程并对文件夹更改通知做出反应。从那里,以前备份的目录路径可以“强制”作为当前目录。这就是我所做的:

备份:

// Well outside of the OpenFileDialog logic
static TCHAR g_BackupDir[MAX_PATH];
...
GetCurrentDirectory(ARRAYSIZE(g_BackupDir), g_BackupDir);

钩:

// Hooking procedure specified as lpfnHook parameter to the GetOpenFileName function
// Requires the flags OFN_EXPLORER and OFN_ENABLEHOOK to be set as well
UINT_PTR CALLBACK OFNHookProc(_In_ HWND hdlg, _In_ UINT uiMsg, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
    switch (uiMsg)
    {
        case WM_NOTIFY:
        {
            LPNMHDR pNotify = reinterpret_cast<LPNMHDR>(lParam);
            switch (pNotify->code)
            {
                case CDN_FOLDERCHANGE:
                    // Force back initial current dir
                    SetCurrentDirectory(g_BackupDir);

                    // No further processing on dialog
                    return 1;
            }
        }
        break;
    }

    // Default processing
    return 0;
}

对话逻辑本身似乎不以任何方式、形状或形式依赖于当前目录。即使当前目录被强制返回文件夹更改,它也能完美运行。

进入“有效”类别,不如使用绝对路径。

于 2018-05-22T14:15:23.090 回答