2

As an example, let's look at EnumWindowStations(), which requires the caller to pass an EnumWindowStationsProc() callback function. The callback function will be invoked once for every window station in the current terminal session. Let's look at the signature of the callback function:

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

The first parameter is a pointer to string data. Was that string buffer allocated explicitly for the callback invocation, and will it be freed immediately after the callback returns, or perhaps immediately before the enumeration function returns? Or, does the pointer point to some kind of persistent memory, such that the string buffer will remain allocated and usable afterward?

This is an important point, because if it is not persistent, then it would be incorrect to, for example, store the raw pointer in a global container to be accessed after the callback and full enumeration process have finished. Instead, it would be necessary to copy the underlying string data to a buffer controlled by the application, before the callback returns.

The official documentation does not seem to make clear what the lifetime of the string data is. There's only one line in the description of the parameter:

lpszWindowStation [in]

    The name of the window station.

And nowhere on the documentation page does it talk about the lifetime of the string data. Nor can I recall ever finding an MSDN page that answers this question "once and for all", i.e. for all uses of the callback idiom in the Windows API.

At the moment I am most interested in the EnumWindowStations()/EnumWindowStationsProc() case, but it would be best if answerers would address the general case, i.e. what to assume for all Windows API callback functions.

bgoldst
  • 34,190
  • 6
  • 38
  • 64

2 Answers2

3

Generally if the memory is allocated by the system you can rely on it being valid for the duration of the callback and no more. That is the case for lpszWindowStation in your example. You would need to access and copy the string inside your callback function and must not refer to the system supplied data outside your callback.

You can deduce this with a little thought experiment. If this string could be accessed after the callback returned when would it become invalid? When would it be deallocated? You'd have to tell the system that you were done with it. Since there is no API indicated to do that the only conclusion is that stated above.

Things are different for lParam. That value was supplied by you when you called EnumWindowStations and so you manage the lifetime of whatever it points to, if indeed you are using it as a pointer.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I didn't notice your answer until after I posted mine. Regarding lParam, it is not a pointer so there is no question of lifetime anyway. As for the pointer, there are a few cases where the WINAPI will allocate memory and expect the callback to free it (or store the pointer and free it at a later time) But these are exceptions and are clearly documented as such – P. Kouvarakis Aug 23 '16 at 21:24
  • @P. It is in effect a pointer. It is user specified. When it is anything it is likely to be a pointer. – David Heffernan Aug 23 '16 at 21:27
  • I like your thought experiment idea. But what I was thinking was that maybe the string data already existed in memory somewhere, perhaps in data structures that are automatically loaded with the application, in which case the enumeration function could just return pointers to that existing memory, and it would persist after the call. (But I suppose such a design of "automatic loading" of system-related data would be wasteful for applications that never make use of that data, so maybe that wouldn't make sense...) – bgoldst Aug 23 '16 at 21:28
  • Persist after the call until when. Forever? If the system would let you use the data indefinitely there would eventually be an out of memory condition. – David Heffernan Aug 23 '16 at 21:31
  • It wouldn't cause an out-of-memory condition, because I was thinking that this "automatic loading" of system data would only happen once (at application startup) and it would only apply to finite amounts and types of system data, so it would never grow. Although now that I think more deeply about it, the window stations that exist in a terminal session can change dynamically (e.g. see [`CreateWindowStation()`](https://msdn.microsoft.com/en-ca/library/windows/desktop/ms682496.aspx)), so that wouldn't be possible anyway. I guess it all has to be dynamic and transient. – bgoldst Aug 23 '16 at 21:39
  • 1
    Dynamic is key in this example. If the data was static then your model could work. GetCommandLine is an example of static data. I can't think of one in a callback. – David Heffernan Aug 23 '16 at 21:43
3

In general pointers passed to callback functions by WINAPI are only guaranteed to be valid for the duration of the callback (unless otherwise specified in the documentation of the specific callback). Once the callback returns, the pointer should be assumed to be invalid (it may be freed or it may point to a temporary buffer that will be overwriten in the next iteration of an enumeration etc).

The callback function is responsible for creating a copy of whatever data it may need to persist after it returns. In the case of the above example you should allocate your own bufffer and copy the string from lpszWindowStation if you need to use it after the callback returns. Of course you should also manage the lifetime of the allocated buffer(s).

P. Kouvarakis
  • 1,893
  • 12
  • 21