Как запретить GetOpenFileName изменять текущий каталог, пока отображается диалоговое окно? - PullRequest
0 голосов
/ 22 мая 2018

GetOpenFileName (по сомнительным причинам) изменяет текущий каталог приложения, пока отображается диалоговое окно.Это можно сбросить при закрытии диалога, указав OFN_NOCHANGEDIR в качестве флага инициализации диалога:

OFN_NOCHANGEDIR Восстанавливает текущий каталог в его первоначальное значение, если пользователь изменил каталог во время поискафайлы.

Однако установка этого флага не мешает функции изменять текущий каталог , пока отображается диалоговое окно проводника .

Этопроблема в многопоточных средах, где другие потоки полагаются на текущий каталог, оставаясь в качестве пути к исполняемому файлу.

Есть ли способ предотвратить GetOpenFileName от изменения текущего каталога приложения во время диалога обозревателяпоказывается и пользователь просматривает папки?

Ответы [ 3 ]

0 голосов
/ 22 мая 2018

Как и другие в упомянутых комментариях, не стоит полагаться на то, что текущий каталог всегда остается неизменным.Я бы изменил его, если бы не требовалось переписывать фабрику загрузки ресурсов (не мой код).

Однако одно, по общему признанию, дешевое и грязное решение этой проблемы - перехватить оконную процедуру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;
}

Кажется, что сама логика диалога не зависит от текущего каталога в любомпуть, форма или форма.Он отлично работает, даже когда текущий каталог принудительно возвращается к изменениям папки.

Переходит в категорию "Делает трюк" и уступает использованию абсолютных путей.

0 голосов
/ 27 мая 2018

Я решил опубликовать то, что должно быть работоспособным решением для этого.Это включает исправление кода «правильно», чтобы использовать абсолютные пути, но терпите меня.Это не так сложно.Ответ, опубликованный ФП, просто ужасен.Это совсем не «дешево».Наоборот, в долгосрочной перспективе это может оказаться очень дорогостоящим.

Теперь я предполагаю, что в коде разбросаны вызовы функций / методов, которые обращаются к файлам или папкам через относительные имена путей (скорее всего, неквалифицированные имена файлов),Их вполне может быть много, но, вероятно, все они сводятся к следующему:

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 .

Я оставлю деталипреобразования относительных путей в абсолютные пути к ОП.Все, что вам нужно, это глобальная переменная, содержащая текущий каталог, действующий при запуске программы.Или я сильно подозреваю, что это должен быть каталог, содержащий исполняемый файл.

0 голосов
/ 22 мая 2018

У вас есть несколько вариантов:

  1. Используйте обходные пути или MinHook, чтобы перехватить SetCurrentDirectory и заставить его ничего не делать для вашего процесса (безобразно)
  2. Использовать выборщик пользовательских файлов, который не меняет текущий каталог.(лучше)
  3. Удалите зависимости от текущего каталога в вашем коде, так как вы можете столкнуться с другими ошибками, связанными с этим, особенно в многопоточной среде.(лучший)
...