为什么 Microsoft 错误消息使用 "Token" 一词?

Why Microsoft Error Message uses the word "Token"?

我故意为 CreateDirectory 调用设置了错误的路径,以便我的异常处理代码将执行:

我不确定这是否离题,但您可能对此有更多经验。为什么错误文本:

An attempt was made to reference a token that does not exist.

为什么他们使用 token 而不是 filefolder

如果题外话,我会关闭这个问题。

GetLastError的return值为:123

根据here

ERROR_INVALID_NAME

123 (0x7B)

The filename, directory name, or volume label syntax is incorrect.

现在 那条消息 说得通了。那么为什么我的 Windows 10 显示另一条消息?

调用 FormatMessage 没有问题。它像宣传的那样工作。但是,您没有传递值 123 (ERROR_INVALID_NAME)。您正在传递 1008(ERROR_NO_TOKEN), by accident, due to calling GetLastError at the wrong time. GetLastError 有强烈要求:

You should call the GetLastError function immediately when a function's return value indicates that such a call will return useful data. That is because some functions call SetLastError with a zero when they succeed, wiping out the error code set by the most recently failed function.

在 C 中满足这一点相当简单。对于 C++,事情变得更加复杂,因为编译器会生成所有不可见的代码。有问题的代码显然仅在进入 CWin32FileError c'tor 后才捕获调用线程的最后一个错误代码。太晚了。

基于 GetWorkingPath() returns 按值 CString 实例的假设,并且 CWin32FileError 将其参数作为 CString const&,这就是发生的情况幕后花絮:

if (!CreateDirectory(GetWorkingPath() + _T("whatever"), nullptr))
  1. GetWorkingPath() 构造一个临时 CString 实例。
  2. operator+(CString const&, LPCTSTR) 构造另一个临时 CString 实例,连接两个输入。
  3. operator LPCTSTR() 在步骤 2 中构造的临时对象上隐式调用。
  4. CreateDirectory 被调用并且 returns.
  5. 重要提示:调用步骤2中创建的临时文件的析构函数。
  6. 重要提示:调用步骤1中创建的临时文件的析构函数。

第 5 步和第 6 步已经是致命的,可能会更改调用线程的最后一个错误代码。然而,还有更多代码妨碍您:

CWin32FileError e(_T("whatever"),
                  GetWorkingPath() + _T("whatever"));
  1. 重要提示: _T("whatever") 触发 CString 的转换构造函数 (CString(LPCTSTR)),产生一个临时的。
  2. 重要提示: GetWorkingPath() 构造一个临时的,调用 CString 的复制器。
  3. 重要提示: operator+(CString const&, LPCTSTR) 构造另一个临时文件。
  4. CWin32FileError c'tor 终于运行了,大概调用了 GetLastError.

这增加了另外 3 个候选者(至少),它们可以修改调用线程的最后一个错误代码。要解决这个问题,您必须确保 绝对 在失败的 Windows API 调用和对 GetLastError.

为此,您将不得不摆脱临时文件,并将最后一个错误代码的捕获移到 CWin32FileError c'tor 之外。前者的一个简单解决方案是预先构建路径名,例如

auto path_name{ GetWorkingPath() + _T("whatever") };
auto path_name_strptr{ path_name.GetString() };
if (!CreateDirectory(path_name_strptr, nullptr))
// ...

(或者在 if statement 中使用 init 语句来限制范围,如果您使用的是 C++17)。无论哪种方式,您的 very next 调用必须是 GetLastError 才能捕获最后一个错误代码,同时它仍然有意义。但是,您将该值传递给 CWin32FileError 的 c'tor,或者它使用哪种参数类型,都取决于您。但是你不能依赖那个 c'tor 为你捕获最后的错误代码。