0

On Windows I'm trying to use one of the variants of LoadLibrary() to open a dll previously written to an std::filesystem::path with an ofstream.

Note: I know the dll is written correctly as I can use it in the standard fashion by linking to it at runtime.

I've been trying to combine the methods from the two answers below.

How to convert std::string to LPCSTR?

how to convert filesystem path to string

This seems like it should be pretty basic but with anything I've tried so far I either get an error about conversion to LPCSTR or something like C2228: left of '.c_str' must have class/struct/union which I am baffled by.

Here's a simple example:

// 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());

I've tried avoiding the macro and calling LoadLibraryA() directly with no luck. I've also tried various ways of passing path1 with path1.string(), path1.string.c_str(), path1.wstring(), etc. with no luck. I've also tried using a temp variable in a number of ways to avoid the cast within 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.

I'm willing to try playing with the encoding (like path1.u8string() etc.) but I think it shouldn't be necessary with use of LoadLibraryA() directly.

I'm trying to avoid C casts and would prefer a c++ static_ or dynamic_ but I'll use anything that works.

Any help is appreciated.

Thanks in advance.

UPDATE

@eryk-sun's comment and @Gulrak's answer solved it for me. It looks like with my setup, path1.c_str() alone is wchar_t but the LoadLibrary() macro was not picking that up and directing it to LoadLibraryW() as it should.

Note: For anyone else who might stumble onto this in the future here's more details of my specific setup. I'm using the MSVC compiler from 16.1.0 (~VS2019) but that's getting called from VSCode and CMake. I'm not explicitly defining _UNICODE however VSCode's intellisense certainly thinks it's been defined somewhere and points me to LoadLibraryA(). However, I think the compiler is not actually seeing that define so it interprets path1.c_str() as a wchar_t.

Morgan
  • 5
  • 5
  • 1
    Are you using MSVC (Visual Studio)? Which version of it? `std::filesystem` is rather new, and MSVC often lags a little in its standard compliance. – Some programmer dude Jan 28 '20 at 07:35
  • 1
    `LoadLibrary(path.c_str());` works in MSVC 2019. – Ari0nhh Jan 28 '20 at 07:39
  • `C2228: left of '.c_str' must have class/struct/union` means that you're calling `.c_str()` on an object for which the compiler does not know the type. (It doesn't understand what `path1` is and therefore doesn't think that member access is legal.) Your problem has nothing to do with `LoadLibrary`/`LoadLibraryA`, encoding, casts, etc. – jamesdlin Jan 28 '20 at 07:41
  • 1
    If you want more help, then please give us more information (like the compiler version), and please show us a [mcve] together with a full and complete copy-paste (as text) of the error messages. Also please take some time to refresh [how to ask good questions](http://stackoverflow.com/help/how-to-ask), as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). – Some programmer dude Jan 28 '20 at 08:09
  • In Windows, [`filesystem::path`](https://learn.microsoft.com/en-us/cpp/standard-library/path-class?view=vs-2019) uses `wchar_t` strings. This has nothing to do with the WINAPI `UNICODE` macro (not `_UNICODE`, which is for the CRT). So `c_str()` is a wide-character C string, and you have to call `string().c_str()` to get a byte string. – Eryk Sun Jan 29 '20 at 03:22

2 Answers2

1

You should use string member function of path class which returns std::string. Then call c_stron the returned string. std::filesystem::path path /* = initialization here */; std::string str = path.string(); /* some handle = */ LoadLibrary(str.c_str());

navyblue
  • 776
  • 4
  • 8
  • I assume `path.string()` encodes the path string using the system-locale ANSI encoding, but a path can include Unicode characters that aren't mapped in that codepage (e.g. user profile directories quite often contain character's that can't be encoded as system ANSI). It would be safer in Windows to use `path.wstring()` with `LoadLibraryW`, which uses the native `wchar_t` string type of the Windows platform. – Eryk Sun Jan 29 '20 at 00:54
  • @navyblue: I definitely tried using an extra std::string just as you mentioned but that too gave me the C2228 error. It looks like @eryk-sun and @Gulrak have nailed it. In my case I need to call `LoadLibraryW(path1.c_str())` instead of LoadLibrary() or LoadLibraryA(). – Morgan Jan 29 '20 at 02:01
  • @Morgan, `path1.wstring().c_str()` does work to get a `wchar_t *` string that `LoadLibraryW` expects. I tested this with VC++. The explicit `wstring()` call ensures it's the right string type, though in Windows (not Unix) `path1.c_str()` on its own will be a `wchar_t *` string. – Eryk Sun Jan 29 '20 at 02:56
  • @morgan, I also checked that `path1.string().c_str()` returns an encoded `char *` string, and stepped through to confirm that it's encoded via `WideCharToMultiByte` with `CP_ACP` (i.e. system ANSI), so it's the right encoding for `LoadLibraryA`. However, I advise against using `LoadLibraryA` -- and the Windows multibyte string (ANSI) API in general -- until it's commonly UTF-8. Currently it's still commonly a non-Unicode legacy encoding, such as codepage 1252. Avoid legacy encodings. – Eryk Sun Jan 29 '20 at 02:59
1

Actually on Windows you should be able to use LoadLibraryW(path1.c_str()) as on Windows the returned type of std::filesystem::path::c_str() should be a const wchar_t* so it's a good fit for the LoadLibraryW expected LPCWSTR.

As for the error of C2228 my guess is, you tried path1.string.c_str() as given by your comment, wich should have been path1.string().c_str(). That would give you a LPCSTR compatible string for LoadLibaryA, but if there is a chance of Non-ASCII in your path I would suggest using the explicit LoadLibaryW version.

In any way: When interfacing WinAPI with std::filesystem::path you should use the explicit A/W-Version to make your code safe independent of the state of _UNICODE, and I allways suggest the *W versions.

Gulrak
  • 726
  • 7
  • 10