将LPCTSTR转换为LPWSTR

时间:2016-02-25 07:52:49

标签: visual-c++ unicode mfc

使用MFC和Unicode-Build

我想更改listctrl的colum-header-text并且这样做我必须将LPCTSTR转换为LPWSTR。我现在做的是

void CSPCListViewCtrl::SetHeaderText(long lCol, LPCTSTR lColText)
{
    CListCtrl& ListCtrl = GetListCtrl();

    LV_COLUMN lvc;
    ::ZeroMemory((void *)&lvc, sizeof(LVCOLUMN));
    lvc.mask |= LVCF_TEXT;

    ListCtrl.GetColumn(lCol, &lvc);
    lvc.pszText = const_cast<LPWSTR>(lColText);

    ListCtrl.SetColumn(lCol, &lvc);
}

它似乎工作,但const_cast看起来有些奇怪和错误,所以我尝试了类似

的东西
USES_CONVERSION;
lvc.pszText = CT2W(lColText);

这似乎在release-build中工作,但在debug-build中产生垃圾,所以我想知道这样做的正确方法是什么?

2 个答案:

答案 0 :(得分:2)

TL;DR:调用CListCtrl::SetColumn时,使用const_cast<LPTSTR>(lColText)是安全的。

但是为什么LVCOLUMN structure pszText 成员声明为非const? LVCOLUMN结构用于设置和检索信息。检索列的文本时,需要传入可修改的缓冲区(和长度参数)。另一方面,在设置列文本时,系统使用 pszText 成员并在内部存储副本。它不会尝试写入它。这也是documented,即使非常巧妙:

  

cchTextMax定义
   pszText 成员指向的缓冲区的 TCHAR 中的大小。 如果结构未接收有关列的信息,则忽略此成员。

这是Windows API中的常见模式,其中相同的结构用作输入和输出参数。解决这个问题的唯一选择是为这些结构引入const - ified版本。当Windows API在30年前发明时,这被认为是不必要或有用的。此外,它会使共同模式(读取 - 更新 - 写入)更加繁琐且容易出错,因为必须在不相关的类型之间手动复制数据。


现在您知道,在您描述的场景中使用const_cast是安全的,您可能想知道,如果使用它是个好主意。这取决于许多因素,例如

  • 你能否提出一个有用的评论来简明扼要地解释,为什么这样可以? // Totally safe, it's just that M$ sucks的某些内容可能不会削减它。
  • 您团队中的所有成员是否会理解其含义,或者这会在堆损坏调试会话中呈现红色鲱鱼?
  • 您是否有允许使用const_cast s?
  • 的编码指南
  • 您使用的静态代码分析工具是否会产生const_cast的误报?

如果您发现无法满意地回答所有这些问题,您可以考虑实施(技术上不必要的)手动复制(并交换const_cast进行异常处理):

void CSPCListViewCtrl::SetHeaderText(long lCol, LPCTSTR lColText) {
    CListCtrl& ListCtrl = GetListCtrl();

    LVCOLUMN lvc = {0};
    lvc.mask |= LVCF_TEXT;

    // Create modifiable copy of lColText
    size_t colTextLength = _tcslen(lColText);
    std::vector<TCHAR> buffer(colTextLength + 1);
    std::copy(lColText, lColText + colTextLength + 1, buffer.data());
    lvc.pszText = buffer.data();

    ListCtrl.SetColumn(lCol, &lvc);
}


关于使用字符串编码的另一个注意事项:您正在混合使用Generic-Text Mappings和显式Unicode(UTF-16LE)编码。要添加一致性,您应该更改 lColText 参数以键入LPCWSTR,并使用LVCOLUMNW结构(如果您决定使用const_cast<LPWSTR> [{"id":"1","title":"Test Event 1","description":"This is the first test event","location":"Acme Hall, Room 101","contact":"John Smith","url":"http:\/\/www.example.com","start":"2016-02-29 13:00:00","end":"2016-02-29 14:00:00"}, {"id":"2","title":"Test Event 2","description":"This is the second test event","location":"Venable Hall, Room 101","contact":"Jane Smith","url":"http:\/\/www.example.com","start":"2016-03-08 09:00:00","end":"2016-03-10 10:45:00"}, {"id":"3","title":"Test Event 3","description":"This is the third test event","location":"Sitterson Hall, Room 200","contact":"Jane Smith","url":"http:\/\/www.example.com","start":"2016-03-18 15:00:00","end":"2016-02-22 16:30:00"}] 路线)。不幸的是,在使用MFC时,在调用任何类成员时,您将不得不求助于使用泛型文本映射。但至少在字符编码不匹配的情况下会出现编译器错误。

答案 1 :(得分:1)

您可以使用CString::GetBuffer()

void SetHeaderText(long lCol, LPCTSTR lColText)
{
    LV_COLUMN lvc;
    ::ZeroMemory((void *)&lvc, sizeof(LVCOLUMN));
    lvc.mask |= LVCF_TEXT;

    list.GetColumn(lCol, &lvc);

    CString str = lColText;
    lvc.pszText = str.GetBuffer();

    list.SetColumn(lCol, &lvc);

    str.ReleaseBuffer();
    //ReleaseBuffer is option in this case because 
    //"str" is local variable and is destroyed before being used again*
}


SetHeaderText(0, L"text");

在UNICODE中,LPTSTR只是LPWSTR(或只是wchar_t*

如果由于某种原因您有ANSI文本,那么您可以使用CString进行转换

CStringA ansi = "Text";
CStringW wide = CStringW(ansi);

SetHeaderText(0, wide);

另见
CString::GetBuffer()