Windows API 应用程序定义的回调函数中,指针参数数据的生命周期是多少?回调后是否持续returns?
In Windows API application-defined callback functions, what is the lifetime of pointer parameter data? Does it persist after the callback returns?
举个例子,让我们看看EnumWindowStations()
, which requires the caller to pass an EnumWindowStationsProc()
回调函数。当前终端会话中每 window 个站将调用一次回调函数。我们看一下回调函数的签名:
BOOL CALLBACK EnumWindowStationProc(
_In_ LPTSTR lpszWindowStation,
_In_ LPARAM lParam
);
第一个参数是指向字符串数据的指针。该字符串缓冲区是否为回调调用显式分配,它会在回调 returns 之后立即释放,还是可能在枚举函数 returns 之前立即释放?或者,指针是否指向某种持久内存,这样字符串缓冲区将保持分配状态并在之后可用?
这一点很重要,因为如果它不是持久化的,那么将原始指针存储在全局容器中以在回调和完整枚举过程完成后访问是不正确的。相反,有必要将底层字符串数据复制到应用程序控制的缓冲区,在 回调 returns.
之前
官方文档似乎并没有说清楚字符串数据的生命周期是多少。参数说明只有一行:
lpszWindowStation [in]
The name of the window station.
文档页面上没有任何地方讨论字符串数据的生命周期。我也不记得曾经找到一个 MSDN 页面来回答这个问题 "once and for all",即在 Windows API.
中所有回调习语的使用
目前我最感兴趣的是 EnumWindowStations()
/EnumWindowStationsProc()
案例,但如果回答者能解决一般案例,即假设所有 Windows API 回调函数。
通常,如果内存是由系统分配的,您可以相信它在回调期间有效,仅此而已。在您的示例中, lpszWindowStation
就是这种情况。您需要在回调函数中访问和复制字符串,并且不得在回调之外引用系统提供的数据。
你可以通过一个小的思想实验来推断这一点。如果这个字符串在回调返回后可以访问,它什么时候会失效?什么时候取消分配?你必须告诉系统你已经完成了它。由于没有 API 指示这样做,唯一的结论就是上面所述。
lParam
的情况有所不同。该值是在您调用 EnumWindowStations
时由您提供的,因此如果您确实将其用作指针,则您可以管理它指向的任何内容的生命周期。
一般情况下,由 WINAPI 传递给回调函数的指针只保证在回调期间有效(除非在特定回调的文档中另有说明)。一旦回调 returns,指针应该被假定为无效(它可能被释放或者它可能指向一个临时缓冲区,该缓冲区将在下一次枚举迭代中被覆盖等)。
回调函数负责创建它可能需要保留的任何数据的副本 returns。在上面的例子中,如果你需要在回调 returns 之后使用它,你应该分配你自己的缓冲区并从 lpszWindowStation
复制字符串。当然,您还应该管理分配的缓冲区的生命周期。
举个例子,让我们看看EnumWindowStations()
, which requires the caller to pass an EnumWindowStationsProc()
回调函数。当前终端会话中每 window 个站将调用一次回调函数。我们看一下回调函数的签名:
BOOL CALLBACK EnumWindowStationProc(
_In_ LPTSTR lpszWindowStation,
_In_ LPARAM lParam
);
第一个参数是指向字符串数据的指针。该字符串缓冲区是否为回调调用显式分配,它会在回调 returns 之后立即释放,还是可能在枚举函数 returns 之前立即释放?或者,指针是否指向某种持久内存,这样字符串缓冲区将保持分配状态并在之后可用?
这一点很重要,因为如果它不是持久化的,那么将原始指针存储在全局容器中以在回调和完整枚举过程完成后访问是不正确的。相反,有必要将底层字符串数据复制到应用程序控制的缓冲区,在 回调 returns.
之前官方文档似乎并没有说清楚字符串数据的生命周期是多少。参数说明只有一行:
lpszWindowStation [in]
The name of the window station.
文档页面上没有任何地方讨论字符串数据的生命周期。我也不记得曾经找到一个 MSDN 页面来回答这个问题 "once and for all",即在 Windows API.
中所有回调习语的使用目前我最感兴趣的是 EnumWindowStations()
/EnumWindowStationsProc()
案例,但如果回答者能解决一般案例,即假设所有 Windows API 回调函数。
通常,如果内存是由系统分配的,您可以相信它在回调期间有效,仅此而已。在您的示例中, lpszWindowStation
就是这种情况。您需要在回调函数中访问和复制字符串,并且不得在回调之外引用系统提供的数据。
你可以通过一个小的思想实验来推断这一点。如果这个字符串在回调返回后可以访问,它什么时候会失效?什么时候取消分配?你必须告诉系统你已经完成了它。由于没有 API 指示这样做,唯一的结论就是上面所述。
lParam
的情况有所不同。该值是在您调用 EnumWindowStations
时由您提供的,因此如果您确实将其用作指针,则您可以管理它指向的任何内容的生命周期。
一般情况下,由 WINAPI 传递给回调函数的指针只保证在回调期间有效(除非在特定回调的文档中另有说明)。一旦回调 returns,指针应该被假定为无效(它可能被释放或者它可能指向一个临时缓冲区,该缓冲区将在下一次枚举迭代中被覆盖等)。
回调函数负责创建它可能需要保留的任何数据的副本 returns。在上面的例子中,如果你需要在回调 returns 之后使用它,你应该分配你自己的缓冲区并从 lpszWindowStation
复制字符串。当然,您还应该管理分配的缓冲区的生命周期。