为何QueryDosDeviceW在非主线程调用时始终返回0?
这种“主线程正常、子线程失败”的差异,本质是错误的类型转换触发了未定义行为,而主线程与子线程的内存/栈布局差异让未定义行为的表现截然不同,具体原因拆解如下:
错误的本质:
QueryDosDeviceW的第一个参数要求是LPCWSTR(宽字符串指针),如果错误地将窄字符串(如const char*)强制转换为LPCWSTR,相当于把单字节ASCII字符当作双字节宽字符解析。此时只有当每个窄字符的相邻高字节恰好为0时,宽字符串才会被正确识别——这完全依赖内存中的随机数据,属于典型的未定义行为。主线程的“侥幸”工作逻辑:
主线程的栈空间通常更大,且在VS2019(v142)+Windows 10的旧环境,甚至VS2022(v143)+Windows 11的主线程中,栈初始化或编译器变量分配策略可能刚好让你传入的窄字符串后面的内存字节为0。这种情况下,错误转换后的宽字符串每个字符都是有效的(ASCII字符+0字节),QueryDosDeviceW能正常解析并返回结果。子线程的失败逻辑:
子线程的栈空间通常更小,且Windows 11+VS2022(v143)的环境中,子线程栈的初始化内容可能并非全0,或者编译器对子线程的变量分配更紧凑,导致窄字符串后面的内存字节非0。此时错误转换后的宽字符串会包含无效的双字节字符,QueryDosDeviceW无法识别目标设备名,直接返回0表示调用失败。未定义行为的不确定性:
这类错误属于C++标准中的未定义行为,编译器版本、系统版本、线程类型、编译优化等级等任何微小变化,都可能导致程序表现完全不同——今天主线程能正常工作,明天可能也会突然失败,完全不可靠。修复参数类型转换(比如使用L"COM*"这类宽字符串字面量,或通过MultiByteToWideChar执行规范的窄转宽编码转换)才是根本解决办法。
内容的提问来源于stack exchange,提问作者wsys

