【问题标题】:Converting string to LPWSTR [duplicate]将字符串转换为 LPWSTR [重复]
【发布时间】:2014-07-31 08:29:15
【问题描述】:

我很难将字符串转换为LPWSTR,因此我可以使用PathStripToRoot() 函数。

首先,MSDN 文档说我需要 LPTSTR 变量 (http://msdn.microsoft.com/en-us/library/windows/desktop/bb773757(v=vs.85).aspx),但 Visual Studio 2013 说我需要 LPWSTR

这是我的函数的代码sn-p:

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;
}

很多人说要使用MultiByteToWideChar() 函数,但我查看了 MSDN 文档,不知道它是如何工作的。有没有比使用MultiByteToWideChar() 更简单的方法?

【问题讨论】:

  • 文档说LPTSTR,注意额外的T。这意味着它是否宽取决于构建配置。
  • 在旁注中,我有一个从“std::streamoff 转换为 int,可能丢失数据”的警告,这是由我要存储的 int a = in.tellg() 函数引起的文件大小(以字节为单位)。
  • 这里有std::wstring_convert
  • @dspaces1: 我刚刚删除了 [visual-studio-2013] 标签,因为虽然您肯定使用 VS2013 来开发您的代码,但可能这不是问题的关键(例如,这不是关于VS2013 IDE的一个问题......)。为了完整起见,我在您的问题文本中添加了“2013”​​VS 版本号。当然,如果您认为您最初的选择更好,请随时撤消我的更改。
  • @AdrianMcCarthy 我看了那篇文章,这只有助于转换为不适用于 PathStripToRoot() 的 LPCWSTR

标签: c++ string winapi unicode


【解决方案1】:

在处理 Win32 API 时,您可能希望在现代 Windows 应用程序中使用 Unicode UTF-16 字符串:std::wstring 类(基于 wchar_t)适用于 Visual C++。

然后,您可以在一些 C++ 代码中封装 Win32 C API PathStripToRoot(),使用方便的字符串类,而不是原始的类 C 字符串缓冲区。

以下面的注释代码为例:

// 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;
    }
}

从命令行编译:

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

输出:

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

关于您问题的特定点:

MSDN 文档说我需要LPTSTR 变量,但是 Visual Studios 说我需要LPWSTR

LPTSTR 是一个 typedef 等价于 TCHAR*
LPWSTR 是一个 typedef 等价于 WCHAR*,即wchar_t*

TCHAR 是字符类型的占位符,可以扩展为 charwchar_t,具体取决于您处于 ANSI/MBCS 还是 Unicode 构建模式。

自 VS2005 以来,Visual Studio 一直使用 Unicode 作为默认构建。

因此,除非您要维护必须使用 ANSI/MBCS 的旧旧应用程序,否则只需在现代 Win32 应用程序中使用 Unicode。在这种情况下,您可以直接将基于 wchar_t 的字符串与 Win32 API 一起使用,而无需使用旧的过时 TCHAR 模型。

请注意,您的代码中仍然可以包含 std::strings(基于 char),例如表示 Unicode UTF-8 文本。并且您可以在 Win32 API 边界处在 UTF-8 (char/std::string) 和 UTF-16 (wchar_t/std::wstring) 之间进行转换。

为此,您可以使用some convenient RAII wrappers to raw Win32 MultiByteToWideChar() and WideCharToMultiByte() APIs

【讨论】:

  • 链接已损坏。这正是您应该在此处包含链接内容的原因。
【解决方案2】:

考虑构建 Windows 应用程序的正确方法是假装不存在 8 位字符串。否则,您的字符串编码将根据用户的语言设置而有所不同,并且您的应用程序将无法“全局就绪”,因为总会有一些字符无法由用户的当前设置表示。 Win32 中的 8 位字符串是 1990 年代遗留下来的,一个好的 Win32 应用程序在任何地方都使用 PWSTR 请注意,例如,在 Windows CE 或 WinRT 上甚至不存在“A 函数”,即应该会给你一些关于微软对这个问题的看法的提示。

现在,实际上,您可能正在与使用 8 位字符串的非 Windows 特定代码进行交互。 IMO 最好的使用方法是按照惯例,所有此类字符串都是 UTF-8,并使用MultiByteToWideCharWideCharToMultiBytePWSTR 相互转换。请务必使用CP_UTF8。但是对于 Windows 特定的代码,请务必定义 UNICODE_UNICODE 宏,忘记 TCHARTSTR*A 函数和其他此类历史事故存在并在任何地方使用 PWSTRWCHAR .你的代码会更理智。

【讨论】:

  • 所以如果我使用这个 MultiByteToWideChar() 我发现: wstring fileInfo::strChange(const string& s) { //int len; slength = (int)s.length() + 1; len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); wchar_t* buf = 新 wchar_t[len]; MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len); wstring r(buf);删除[] 缓冲区;返回 r;我可以用 wstring 做什么来将其转换为 LPWSTR?
  • @dspaces1 在 Windows 上 wchar_t*PWSTR 最终是同一个东西。 (指向 utf-16 字符串的指针。)
  • 如果我尝试将 wchar_t* 传递到 PathStripToRoot() 我得到 2 个错误:未解析的外部符号和未解析的外部符号
【解决方案3】:

您可以使用 ATL 转换宏:

cout<<"\n\n"<<PathStripToRoot(CA2T(targetFile.c_str()))<<"\n\n";

【讨论】:

  • 如果他的项目已经在使用 ATL 库,那么使用转换宏就可以了。否则仅用于字符串转换,不应该使用 ATL 宏转换。
【解决方案4】:

如果 targetFile 是 ASCII 字符串,使用 PathStripToRootA。在这里你不需要任何转换,targetFile.c_str() 就可以了。 如果 targetFile 是 UTF8 字符串,则使用 MultiByteToWideChar 将其转换为 WideChar。然后使用 PathStripToRoot。 否则将 targetFile 设为 wstring,将其传递给 API,无需任何转换。

【讨论】:

    【解决方案5】:

    请通过 Unicode Programming Summary

    有几种方法可以解决这个问题。

    正确的方法是用 _T() 将字符串定义括起来。 PathStripToRoot 定义为

    #ifdef _UNICODE
    #define PathStripToRoot PathStripToRootA
    #else
    #define PathStripToRoot PathStripToRootW
    #endif
    

    _T 和 Windows API 遵循您在项目设置下为项目定义的字符集支持。如果您将文件名作为单字节字符串(string 包含 ANSI 字符串)使用PathStripToRootA。您可以避免将两者之间的文件名转换为 UNICODE 字符串。

    【讨论】:

      猜你喜欢
      • 2012-09-09
      • 2010-11-14
      • 1970-01-01
      • 2016-07-20
      • 1970-01-01
      • 1970-01-01
      • 2019-10-27
      • 2016-09-06
      相关资源
      最近更新 更多