在Windows API应用程序定义的回调函数中,指针参数数据的生存期是多少?它在回调返回后是否持续?

问题描述:

举一个例子,我们来看看EnumWindowStations(),它需要调用者通过一个EnumWindowStationsProc()回调函数。回调函数将在当前终端会话中为每个窗口站调用一次。让我们来看看回调函数的签名:在Windows API应用程序定义的回调函数中,指针参数数据的生存期是多少?它在回调返回后是否持续?

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

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

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

官方文档似乎没有说明字符串数据的生命周期是什么。这里只有一个在参数的说明行:

lpszWindowStation [IN]

       窗口站的名称。

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

目前我最感兴趣的是EnumWindowStations()/EnumWindowStationsProc()的情况,但如果回答者会解决一般情况,即所有Windows API回调函数的假设,那么最好。

一般情况下,如果系统分配了内存,则可以依赖它在回调期间有效并且不再有效。在你的例子中,lpszWindowStation就是这种情况。您需要访问并复制回调函数中的字符串,并且不得在回调之外引用系统提供的数据。

你可以通过一点思考实验来推断这一点。如果这个字符串可以在回调之后访问,那么它何时会失效?它何时会被释放?你必须告诉系统你已经完成了它。由于没有API表示要这样做,唯一的结论就是上面所述的结论。

事情与lParam不同。这个值是你在调用EnumWindowStations时提供的,所以你可以管理它指向的任何生命周期,如果你确实将它用作指针。

+0

我没有注意到你的答案,直到我发布我的。关于lParam,它不是一个指针,所以不存在生命问题。至于指针,有一些情况下WINAPI会分配内存并期望回调释放它(或者存储指针并在以后释放它)。但是这些都是例外情况,并且明确记录为这样的情况 –

+0

@P。它实际上是一个指针。它是用户指定的。什么时候它可能是一个指针。 –

+0

我喜欢你的思想实验的想法。但是我的想法是,字符串数据可能已经存在于内存中,也许在应用程序自动加载的数据结构中,在这种情况下,枚举函数可能会返回指向该现有内存的指针,并且会在呼叫。 (但是,我认为这样的系统相关数据的“自动加载”设计对于从未使用该数据的应用程序来说是浪费的,所以也许这样做是没有意义的。) – bgoldst

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

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