4

I'm teaching my self WinAPI in c++, but as I progress I notice that every functions in WinAPI returns either char*, DWORD, LPCSTR and etc. My concern is I like using string, so what am I doing is after I get the return value I convert it to string. Is it good or bad? or does it make any difference if I convert it every time? or will it make the process slow or any bad stuffs. And for the I/O in c++, what is better cout or printf considering the size of exe or performance/functionality issue?

blank012
  • 49
  • 1

2 Answers2

2

Start by learning the various string types. For example char*, LPSTR, and LPCSTR are all related and store ANSI characters; the last is a pointer to a const string. Similarly wchar_t*, WCHAR*, LPWSTR, and LPCWSTR are all related and store Unicode characters; again the last is a pointer to a const string.

Then watch what you mean when you say something like "returns char*" as most Windows APIs do nothing of the sort. Instead they take a LPSTR (a pointer to a char buffer) or LPWSTR (a pointer to a WCHAR buffer), and write into the buffer. Those APIs again almost always either take a count of characters available in the buffer, or have a documented buffer size requirement such as MAX_PATH.

You can use this to your advantage. C++11 requires that a std::wstring or std::string store its characters contiguously, and all known implementations did so well before that. So given an API like GetEnvironmentVariable, you can use the string as the buffer directly and sidestep most conversions. Use code similar to this, but feel free to modify how it interacts with the rest of your code:

std::wstring GetEnvironmentVariable(const std::wstring& strName)
{
    DWORD cchValue = MAX_PATH;
    std::wstring strValue;

    // call API once or twice, to get required buffer size
    strValue.resize(cchValue);
    cchValue = GetEnvironmentVariable(strName.c_str(), &strValue[0], cchValue);
    if (cchValue > MAX_PATH)
    {
        strValue.resize(cchValue);
        cchValue = GetEnvironmentVariable(strName.c_str(), &strValue[0], cchValue);
    }

    // process errors such as ERROR_ENVVAR_NOT_FOUND
    if (0 == cchValue)
    {
        // throw exception? return empty string? (current code does latter)
    }

    // remove null character and any extra buffer
    strValue.resize(cchValue);
    return strValue;
}

There are a lot of further enhancements you can make to this approach.

  • If you need to support old Windows 9x era operating systems, you can use LPTSTR instead by creating a typedef tstring that maps to std::wstring or std::string per the definition of UNICODE (otherwise I'd suggest just doing wide strings like I showed above). You can do explicit A and W function calls that either overload in C++, or follow the Windows naming convention.
  • You might be able to refactor the double call into some helper, or at least factor out some of the steps it requires.
  • You can handle APIs that take an in-out parameter for the buffer size.
  • You can figure out a way to handle APIs such as GetWindowText that only return a success/failure but do not return their required buffer size, and hide that dance behind your new wrapper function. See the related question and answer for an example approach. (Also note that other approaches may be better for that specific case.)

As far as I/O, there isn't a clear winner. You'll find that it's fairly rare to use the console and console I/O in Windows, so perhaps the question isn't very interesting after all. Aside from that, there are significant tradeoffs when it comes to internationalization; namely it's a lot easier to translate full sentence format strings than to translate the fragments that tend to result from uses of iostream. But if translation isn't a concern for you, that won't influence your decision.

Community
  • 1
  • 1
Michael Urman
  • 15,737
  • 2
  • 28
  • 44
1

Nearly everything you asked is opinion based. Yes, converting every char* you get to an std::string will be slower, but no it won't make much of a difference. Of course that is completely up to how often you're doing that, and where you'll be running this program. There's absolutely no problem with converting things to std::string in this case, but be warned, you're not going to have a fun time fighting against any API( it's going to be a lot more tedious to convert to and from types all the time. ) In the end, it's completely up to you. Just remember, just because you converted a char* that you allocated with new or malloc into an std::string doesn't mean you don't have to free, or delete it.

I don't see a problem with either printf or std::cout. Once again that's completely opinion based. However, std::cout might be a little easier to use and is certainly more type-safe.

Ben
  • 1,816
  • 1
  • 20
  • 29