1

作为一个例子,让我们看一下EnumWindowStations(),它需要调用者传递一个EnumWindowStationsProc()回调函数。当前终端会话中的每个窗口站都会调用一次回调函数。我们看一下回调函数的签名:

BOOL CALLBACK EnumWindowStationProc(
    _In_ LPTSTR lpszWindowStation,
    _In_ LPARAM lParam
);

第一个参数是指向字符串数据的指针。是否为回调调用显式分配了该字符串缓冲区,是否会在回调返回后立即释放,或者可能在枚举函数返回之前立即释放?或者,指针是否指向某种持久内存,这样字符串缓冲区将在之后保持分配和可用?

这一点很重要,因为如果它不是持久的,那么将原始指针存储在全局容器中以在回调和完整枚举过程完成后访问是不正确的。相反,有必要在回调返回之前将底层字符串数据复制到由应用程序控制的缓冲区。

官方文档似乎没有明确说明字符串数据的生命周期是什么。参数描述中只有一行:

lpszWindowStation [输入]

    窗口站的名称。

文档页面上没有任何地方谈论字符串数据的生命周期。我也不记得曾经找到过“一劳永逸”地回答这个问题的 MSDN 页面,即所有使用 Windows API 中的回调习语。

目前我对EnumWindowStations()/EnumWindowStationsProc()案例最感兴趣,但最好是回答者解决一般案例,即所有 Windows API 回调函数的假设。

4

2 回答 2

3

通常,如果内存是由系统分配的,您可以依赖它在回调期间有效,仅此而已。lpszWindowStation在您的示例中就是这种情况。您需要在回调函数中访问和复制字符串,并且不得在回调函数之外引用系统提供的数据。

你可以通过一些思想实验来推断这一点。如果在回调返回后可以访问此字符串,它何时会变得无效?什么时候解除分配?你必须告诉系统你已经完成了它。由于没有 API 表明可以这样做,因此唯一的结论是上述内容。

对于lParam. 该值是在您调用时由您提供的EnumWindowStations,因此您可以管理它所指向的任何内容的生命周期,如果您确实将它用作指针。

于 2016-08-23T21:15:30.190 回答
3

通常,WINAPI 传递给回调函数的指针只保证在回调期间有效(除非在特定回调的文档中另有说明)。一旦回调返回,指针应该被认为是无效的(它可能被释放或者它可能指向一个临时缓冲区,该缓冲区将在枚举的下一次迭代中被覆盖等)。

回调函数负责创建它返回后可能需要保留的任何数据的副本。在上面的例子中,lpszWindowStation如果你需要在回调返回后使用它,你应该分配你自己的缓冲区并复制字符串。当然,您还应该管理已分配缓冲区的生命周期。

于 2016-08-23T21:20:02.070 回答