Как преобразовать std::filesystem::path в LPCSTR для использования в одном из вариантов LoadLibrary()?

В Windows я пытаюсь использовать один из вариантов LoadLibrary(), чтобы открыть dll, ранее записанный в std::filesystem::path с ofstream.

Примечание. Я знаю, что dll написана правильно, так как я могу использовать ее стандартным образом, связавшись с ней во время выполнения.

Я пытался объединить методы из двух ответов ниже.

Как преобразовать std::string в LPCSTR?

как преобразовать путь файловой системы в строку

Кажется, это должно быть довольно просто, но со всем, что я пробовал до сих пор, я получаю либо ошибку о преобразовании в LPCSTR, либо что-то вроде C2228: left of '.c_str' must have class/struct/union, что меня сбивает с толку.

Вот простой пример:

// Assuming I have 
// std::filesystem::path path1 
// correctly set, I should be able to directly access it in
// a number of ways; i.e. path1.c_str(), path1.string.c_str(), etc.
// in order to pass it the function or a temp variable.
// However direct use of it in LoadLibrary() fails with the C2228 error.

HINSTANCE hGetProcIDDLL = LoadLibrary(path1.c_str());

Я пытался избежать макроса и вызывать LoadLibraryA() напрямую, но безуспешно. Я также пробовал различные способы передачи path1 с помощью path1.string(), path1.string.c_str(), path1.wstring() и т. д. безуспешно. Я также пытался использовать временную переменную несколькими способами, чтобы избежать приведения в LoadLibrary().

LPCSTR temp_lpcstr = path1.c_str();  // Also tried things like path1.string() path1.string.c_str()

// Also tried just using a temp string...
std::string temp_string = path1.string(); // and variants.

Я готов попробовать поиграть с кодировкой (например, path1.u8string() и т. д.), но я думаю, что в этом нет необходимости при непосредственном использовании LoadLibraryA().

Я стараюсь избегать приведения C и предпочел бы C++ static_ или dynamic_, но я буду использовать все, что работает.

Любая помощь приветствуется.

Заранее спасибо.

UPDATE

Комментарий @eryk-sun и ответ @Gulrak решили эту проблему для меня. Похоже, что с моей настройкой path1.c_str() само по себе равно wchar_t, но макрос LoadLibrary() не улавливал это и не направлял в LoadLibraryW(), как следовало бы.

Примечание. Для всех, кто может наткнуться на это в будущем, вот более подробная информация о моей конкретной настройке. Я использую компилятор MSVC версии 16.1.0 (~VS2019), но он вызывается из VSCode и CMake. Я не определяю явно _UNICODE, однако intellisense VSCode определенно думает, что он где-то определен, и указывает мне на LoadLibraryA(). Однако я думаю, что компилятор на самом деле не видит это определение, поэтому он интерпретирует path1.c_str() как wchar_t.


person Morgan    schedule 28.01.2020    source источник
comment
Вы используете MSVC (Visual Studio)? Какая его версия? std::filesystem является довольно новым, и MSVC часто немного отстает в его соответствии стандарту.   -  person Some programmer dude    schedule 28.01.2020
comment
LoadLibrary(path.c_str()); работает в MSVC 2019.   -  person Ari0nhh    schedule 28.01.2020
comment
C2228: left of '.c_str' must have class/struct/union означает, что вы вызываете .c_str() для объекта, для которого компилятор не знает тип. (Он не понимает, что такое path1, и поэтому не считает, что доступ к членам является законным.) Ваша проблема не имеет ничего общего с LoadLibrary/LoadLibraryA, кодированием, приведениями и т. д.   -  person jamesdlin    schedule 28.01.2020
comment
Если вам нужна дополнительная помощь, предоставьте нам дополнительную информацию (например, версию компилятора) и покажите нам минимальный воспроизводимый пример вместе с полной копией-вставкой (в виде текста) сообщений об ошибках. Также, пожалуйста, найдите время, чтобы обновить как задавать хорошие вопросы, а также этот контрольный список вопросов.   -  person Some programmer dude    schedule 28.01.2020
comment
В Windows filesystem::path использует wchar_t строк. Это не имеет ничего общего с макросом WINAPI UNICODE (не _UNICODE, который для ЭЛТ). Итак, c_str() — это строка расширенных символов C, и вам нужно вызвать string().c_str(), чтобы получить строку байтов.   -  person Eryk Sun    schedule 29.01.2020


Ответы (2)


На самом деле в Windows вы должны иметь возможность использовать LoadLibraryW(path1.c_str()), так как в Windows возвращаемый тип std::filesystem::path::c_str() должен быть const wchar_t*, поэтому он хорошо подходит для ожидаемого LoadLibraryW LPCWSTR.

Что касается ошибки C2228, я предполагаю, что вы попробовали path1.string.c_str(), как указано в вашем комментарии, что должно было быть path1.string().c_str(). Это дало бы вам строку, совместимую с LPCSTR, для LoadLibaryA, но если есть шанс, что в вашем пути есть не-ASCII, я бы предложил использовать явную версию LoadLibaryW.

В любом случае: при взаимодействии WinAPI с std::filesystem::path вы должны использовать явную A/W-версию, чтобы сделать ваш код безопасным независимо от состояния _UNICODE, и я всегда предлагаю версии *W.

person Gulrak    schedule 28.01.2020
comment
Вот оно. Спасибо, @Gulrak. - person Morgan; 29.01.2020
comment
Поскольку LoadLibraryW — это Windows, можно использовать c_str() напрямую и предполагать, что это wchar_t *, но я думаю, что понятнее использовать path1.wstring().c_str(). - person Eryk Sun; 29.01.2020

Вы должны использовать функцию-член string класса path, которая возвращает std::string. Затем вызовите c_str для возвращаемой строки. std::filesystem::path path /* = initialization here */; std::string str = path.string(); /* some handle = */ LoadLibrary(str.c_str());

person navyblue    schedule 28.01.2020
comment
Я предполагаю, что path.string() кодирует строку пути, используя кодировку ANSI языковой системы, но путь может включать символы Unicode, которые не отображаются в этой кодовой странице (например, каталоги профилей пользователей довольно часто содержат символы, которые не могут быть закодированы как системный ANSI). В Windows было бы безопаснее использовать path.wstring() с LoadLibraryW, который использует родной тип строки wchar_t платформы Windows. - person Eryk Sun; 29.01.2020
comment
@navyblue: я определенно пытался использовать дополнительный std::string, как вы упомянули, но это тоже дало мне ошибку C2228. Похоже, @eryk-sun и @Gulrak справились. В моем случае мне нужно вызвать LoadLibraryW(path1.c_str()) вместо LoadLibrary() или LoadLibraryA(). - person Morgan; 29.01.2020
comment
@Morgan, path1.wstring().c_str() действительно работает, чтобы получить строку wchar_t *, которую ожидает LoadLibraryW. Я тестировал это с помощью VC++. Явный вызов wstring() гарантирует, что это правильный тип строки, хотя в Windows (не Unix) path1.c_str() сам по себе будет строкой wchar_t *. - person Eryk Sun; 29.01.2020
comment
@morgan, я также проверил, что path1.string().c_str() возвращает закодированную строку char *, и прошел проверку, чтобы подтвердить, что она закодирована через WideCharToMultiByte с CP_ACP (то есть системным ANSI), поэтому это правильная кодировка для LoadLibraryA. Тем не менее, я не советую использовать LoadLibraryA — и API многобайтовой строки Windows (ANSI) в целом — до тех пор, пока обычно не будет использоваться UTF-8. В настоящее время это все еще обычно устаревшая кодировка, отличная от Unicode, например кодовая страница 1252. Избегайте устаревших кодировок. - person Eryk Sun; 29.01.2020