How to initialize LPWSTR from a const string?
我正在尝试初始化MENUITEMINFO以调用InsertMenuItem。 尝试将const字符串分配给dwTypeData时,出现错误。 下面的代码来自MSDN示例。
两种类型的作业我都遇到错误
1 2
| mii.dwTypeData ="&Sample text";
mii.dwTypeData = L"&Sample text"; |
我正在使用Visual Studio 2019。
1 2 3 4 5 6 7 8 9 10
| MENUITEMINFO mii = { sizeof(mii) };
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE;
mii.wID = idCmdFirst + IDM_DISPLAY;
mii.fType = MFT_STRING;
mii.dwTypeData = L"&Sample Text";
mii.fState = MFS_ENABLED;
if (!InsertMenuItem(hMenu, indexMenu, TRUE, &mii))
{
return HRESULT_FROM_WIN32(GetLastError());
} |
错误是
错误(活动)E0144类型" const wchar_t *"的值不能用于初始化类型" wchar_t *"的实体
根据Microsoft文档,第二个应该起作用。
https://docs.microsoft.com/zh-cn/windows/win32/learnwin32/working-with-strings
编辑:这不能通过我无法初始化WCHAR来解决,因为我无法按照该答案中的建议更改类型。
-
问题不是char类型,问题是const和较差的Win32 API。 在这种情况下,如果添加了const cast,我认为就可以了。 mii.dwTypeData = const_cast(L"&Sample Text");。
-
我可能无法初始化WCHAR的重复项
-
是的,那篇文章有很多错误,或者至少是过时的。 字符串文字在其上已有const多年了。
-
GSerg:这个问题的答案是要更改我不能的声明。
-
这个源当然不是vs2019,而是/ Zc:strictStrings选项(/ permissive-编译器选项隐式设置了此选项,/ permissive-默认情况下在vs2019新项目设置中处于启用状态)
-
@RbMm谢谢。 我从VS 2010示例项目中提取了源代码,并假设对VS 2019中的默认设置进行了一些更改。
一些Windows结构用于"获取和设置",并且这些结构中的字符串成员指向可变的字符串。这与将字符串文字存储在只读存储器中的编译器/链接器设置直接冲突。
从理论上讲,在setter函数中使用字符串文字是不安全的,因为它可能会写入字符串(然后将其还原回其原始内容)。
唯一发生此情况的地方是CreateProcessW中的命令行参数。
在所有其他地方,您可能只丢弃const:
1 2
| MENUITEMINFO mii = { sizeof(mii) };
mii.dwTypeData = const_cast<LPTSTR>(TEXT("&Sample Text")); |
-
不管使用哪种计算机分子来存储字符串文字,修改字符串文字都具有不确定的行为
-
@LightnessRacesinOrbit是的,但是Visual C ++编译器默认将字符串文字放入读/写内存中,因此人们只是忽略了这个问题。
-
简单地说,所有这些api的设计都早于/ Zc:strictStrings选项。默认情况下,字符串文字位于只读存储器中
-
@Anders实际上,包括撰写MSDN文章的人。我的意思是,此答案中的警告/解释需要更强大的IMO。当前,听起来像是通过翻转某些链接器设置即可解决此问题,但不会:字符串文字的类型错误。
-
不幸的是,Windows API的旧缺陷。尤其是本机api。例如,在POBJECT_ATTRIBUTES作为参数的函数大家族中。尽管它仅在参数中存在并且可以在const内存中-但未声明为const指针。但是由于某种原因说LsaOpenPolicy(还存在一个已知异常)不需要const OBJECT_ATTRIBUTES指针
-
@lig:但是,没有人在修改字符串文字。这是一个事实,您不能在C或C ++中以传递方式传达pointee的const性质。在这里,丢弃const并传递字符串文字是安全的。我不确定应该更明确或强调什么警告。
-
@IInspectable"没有人正在修改字符串文字"通过剥离const,您将失去保证(尽管我确实同意,在实践中,在此特定示例中,正如Anders解释的那样,您可能会没事的) )以及与此相关的潜在问题,仅在"答案"中真正涉及到了"将字符串存储在只读存储器中的编译器/链接器设置",从而完全避开了语言本身的问题,从而仅关注低级语言。级实施细节。 IMO Adrian很好地覆盖了其余的内容。
-
在"字符串"与"字符串文字"和" const"与"不可变"之间也存在一些术语的歧义,但我认为我们可以原谅
小心!正如"轨道中的竞速比赛"所指出的那样,修改您传递的数据可能是一个难题。试试这个,代替:
1 2 3 4 5 6 7
| MENUITEMINFO mii = { sizeof(mii) };
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE;
mii.wID = idCmdFirst + IDM_DISPLAY;
mii.fType = MFT_STRING;
wchar_t text[] = L"&Sample Text";
mii.dwTypeData = text;
mii.fState = MFS_ENABLED; |
这样,您应该会更安全一些-但不是完全!!正如RbMm指出的,将dwTypeData成员指向静态字符数组作为一般规则要好得多。函数的某处(外部)…
1
| static thread_local wchar_t menuText[MAXTEXTLEN]; |
然后,用...设置mii。
1 2 3
| wcscpy(menuText, L"&Sample Text");
mii.dwTypeData = menuText;
mii.cch = MAXTEXTLEN; // But not used in this case! |
-
谢谢。对于用户代码,我将牢记此方法。我希望对于这个特定的案例,安德斯的回答就足够了。抱歉,我还不能投票。
-
但没有人尝试在此处修改mii.dwTypeData。只是winapi是在/ Zc:strictStrings编译器选项之前设计的
-
@RbMm在这种情况下绝对同意!但是,避免在诸如MENUITEMINFO之类的结构中传递字符串文字是一种很好的做法,因为这有时可能会跳起来并在后面咬你!
-
@Adrian-我可以部分同意,即使不是性能损失。在您的示例中,我们需要分配额外的存储(通常在堆栈中,因为文本将是通常的局部变量),并将数据从const文字复制到此临时存储中。同样,当然不是在此api中,而是从一般角度来看-某些api可能是异步的,需要有效数据,直到调用未完成。在这种情况下,不可能使用局部变量并在api调用结束之前从函数退出。
-
不,我不是说静态存储更好。确实,如果并发中有多个线程使用静态存储调用您的函数-将会引起竞争和错误。
-
InsertMenuItem是同步调用-因为这是完全可以的,所以有临时的本地缓冲区。只是从理论上讲,如果我们调用异步api-可能需要更多持久性存储,但在这种具体情况下不需要
-
因此,我们有选择-或正式遵循严格的c ++规则,并且不将const数据传递给声明为非const的指针,并且不使用const_cast但效率很高,或者使用const_cast,即使我们确保不会修改数据并将其用作const,声明为const
-
@RbMm-酷!参见我的编辑,在static之后添加thread_local。但是这里并没有真正的"正确答案",取决于用户/编码者来选择在每种情况下最有效的方法。在严格的现代c ++中使用WinAPI总是会出现这种难题。
-
同意。 我从自身的角度更喜欢const_cast,因为了解到使用具体的情况/ api是安全的。 仅在极少数情况下才需要使用永久性存储。.static thread_local 完全同步时,static thread_local 会产生更多开销。 所以你的第一个瓦里安就可以了
-
如果要使用LVITEMW结构则更有趣-pszText是指向包含项目文本的以空字符结尾的字符串的指针。 响应LVN_GETDISPINFO通知时,请确保该指针在收到下一个通知之前一直有效。 但是只有在我们想要将pszText的指针更改为自身字符串而不是将其复制到缓冲区的情况下
-
因此,如果我们有常量字符串文字,并希望在响应LVN_GETDISPINFO通知时将其分配给pszText,而不是将其复制到pszText,这是更有趣的选择。