Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting string to LPWSTR [duplicate]

I'm having a hard time converting a string into LPWSTR so I can use the PathStripToRoot() function.

Well for one the MSDN documation says I need LPTSTR variable (http://msdn.microsoft.com/en-us/library/windows/desktop/bb773757(v=vs.85).aspx), but Visual Studio 2013 says I need LPWSTR.

Here is a code snippet of my function:

fileStat fileCreate(const string& targetFile)
{
    fileStat filez;

    fstream file(targetFile.c_str());
    if (!file)
    {
        cout << "File does not exist" << endl;
    }

    std::ifstream in(targetFile, ios::binary | ios::ate);
    int a = in.tellg();
    cout << "File size(bytes): " << in.tellg() << endl << endl;
    file.close();


    wstring stemp = strChange(targetFile);
    LPCWSTR result = stemp.c_str();

    /* Tried the below code but that did not work
    LPWSTR ws = new wchar_t[targetFile.size() + 1]; 
    copy(targetFile.begin(), targetFile.end(), ws);
    ws[targetFile.size()] = 0;
        */

    cout<<"\n\n"<<PathStripToRoot(ws)<<"\n\n";

 ...
    filez.fileSize = a;
    return filez;
}

A lot of people have said to use MultiByteToWideChar() function but I looked at the MSDN documation and have no idea how it works. Is there an easier way than using MultiByteToWideChar() ?

like image 871
dspaces1 Avatar asked Sep 30 '22 14:09

dspaces1


2 Answers

You may want to use Unicode UTF-16 strings in modern Windows applications when dealing with Win32 APIs: the std::wstring class (based on wchar_t) is OK for that with Visual C++.

Then, you can wrap the Win32 C API PathStripToRoot() in some C++ code, using convenient string classes instead of raw C-like string buffers.

Consider the following commented code as an example:

// Set Unicode mode
#define UNICODE
#define _UNICODE

// Windows SDK Headers
#include <Windows.h>    // Win32 Platform SDK
#include <Shlwapi.h>    // For PathStripToRoot()
#include <Strsafe.h>    // For StringCchCopy()

// Standard C++ Headers
#include <exception>    // For std::exception
#include <iostream>     // For console output
#include <stdexcept>    // For std::invalid_argument, std::runtime_error
#include <string>       // For std::wstring

// For using PathStripToRoot()
#pragma comment(lib, "Shlwapi.lib")


// C++ wrapper around PathStripToRoot() Win32 API
std::wstring RootFromPath(const std::wstring& path) 
{   
    // Buffer for PathStripToRoot()
    wchar_t pathBuffer[MAX_PATH];

    // Copy the input string into the buffer.
    // Beware of buffer overruns!
    HRESULT hr = ::StringCchCopy(pathBuffer,            // dest
                                 _countof(pathBuffer),  // dest size
                                 path.c_str());         // source
    if (hr == STRSAFE_E_INSUFFICIENT_BUFFER)
    {
        // Copy failed due to insufficient buffer space.
        // May accept this case or throw an exception
        // based on the context...
        // In this case, I just throw here.
        throw std::invalid_argument("RootFromPath() - Path string too long.");
    }
    if (hr != S_OK)
    {
        throw std::runtime_error("RootFromPath() - StringCchCopy failed.");
    }


    // Call the Win32 C API using the raw C buffer
    if (! ::PathStripToRoot(pathBuffer))
    {
        // No valid drive letter was found.
        // Return an empty string
        return std::wstring();
    }

    // Return a std::wstring with the buffer content
    return std::wstring(pathBuffer);
}


// Test
int main() 
{
    try
    {
        const std::wstring path = L"C:\\Path1\\Path2";
        const std::wstring root = RootFromPath(path);

        std::wcout << "The content of the path before is:\t" << path << std::endl;
        std::wcout << "RootFromPath() returned:          \t" << root << std::endl;        
    }
    catch(const std::exception& ex)
    {
        std::cerr << "\n*** ERROR: " << ex.what() << std::endl;
    }
}

Compiled from command line:

C:\Temp\CppTests>cl /EHsc /W4 /nologo TestPathStripToRoot.cpp

Output:

C:\Temp\CppTests>TestPathStripToRoot.exe
The content of the path before is:      C:\Path1\Path2
RootFromPath() returned:                C:\

On that particular point of your question:

Well for one the MSDN documation says I need LPTSTR variable, but Visual Studios says I need LPWSTR.

LPTSTR is a typedef equivalent to TCHAR*.
LPWSTR is a typedef equivalent to WCHAR*, i.e. wchar_t*.

TCHAR is a placeholder for a character type, that can be expanded to char or wchar_t, depending if you are in ANSI/MBCS or Unicode build mode.

Since VS2005, Visual Studio has been using Unicode builds as default.

So, unless you are maintaining an old legacy app that must use ANSI/MBCS, just use Unicode in modern Win32 applications. In this case, you can directly use wchar_t-based strings with Win32 APIs, without bothering with the old obsolete TCHAR-model.

Note that you can still have std::strings (which are char-based) in your code, e.g. to represent Unicode UTF-8 text. And you can convert between UTF-8 (char/std::string) and UTF-16 (wchar_t/std::wstring) at the Win32 API boundaries.

For that purpose, you can use some convenient RAII wrappers to raw Win32 MultiByteToWideChar() and WideCharToMultiByte() APIs.

like image 139
Mr.C64 Avatar answered Oct 20 '22 08:10

Mr.C64


The right way to think about building a Windows application is to pretend that 8-bit strings do not exist. Otherwise, the encoding of your string will vary based on the user's language settings, and your app will not be able "global ready" because there will always be some characters not representable by the user's current settings. 8-bit strings in Win32 are legacy from the 1990s and a good Win32 app uses PWSTR everywhere. Notice for instance that on Windows CE or WinRT the "A functions" don't even exist, that should give you some hint about how Microsoft feels about the issue.

Now, in practical terms, you may be interacting with non-Windows specific code that uses 8-bit strings. IMO the best approach to use for that is to say by convention that all such strings are UTF-8, and use MultiByteToWideChar and WideCharToMultiByte to convert to and from PWSTR. Be sure to use CP_UTF8. But for Windows specific code, please do define the UNICODE and _UNICODE macros, forget that TCHAR, TSTR, *A functions and other such accidents of history exist and use PWSTR and WCHAR everywhere. Your code will be saner for it.

like image 34
asveikau Avatar answered Oct 20 '22 09:10

asveikau