3

A windows service creates a registry value (for an Excel add-in) under HKEY_CURRENT_USER registry key for each logged on user (by calling ImpersonateLoggedOnUser() and RegSetValueEx()). I need to delete this registry value when a user logs off, including system shutdown. If it is not deleted at log off, and the software is uninstalled by one user then the entry in the registry remains for any other user that logged on during the lifetime of the service which causes a message box error to be displayed each time Excel begins because it is attempting to load an add-in that no longer exists.

Considered but rejected the following:

  • SetConsoleCtrlHandler() because there is no indication of what user is logging off.
  • REG_OPTION_VOLATILE because it is effective only when creating keys and I am only creating a value (did not thoroughly investigate so may not have been solution even if I was creating a key).

Are there any other mechanisms that would provide a solution to this? Windows versions are XP, Vista and 7.

hmjd
  • 120,187
  • 20
  • 207
  • 252
  • How about [`WM_QUERYENDSESSION`](http://msdn.microsoft.com/en-ca/library/windows/desktop/aa376890(v=vs.85).aspx)? – chris May 08 '13 at 08:23
  • @chris, how is the user logging off identified? – hmjd May 08 '13 at 08:25
  • Do you control the service program and/or the installation of the software? If so, you could just unset the value when the software terminates (use RAII if you are indeed using C++). If you control the installation/uninstallation program, there are options to remove any such values from the registry on uninstallation. – Arne Mertz May 08 '13 at 08:26
  • @ArneMertz, I control the service which sets the registry value for any user that logs on. The problem is when the software is uninstalled it can only remove values for users that are currently logged on and cannot access the `HKEY_CURRENT_USER` key of a user logged off, and therefore cannot remove the registry value. – hmjd May 08 '13 at 08:28
  • @hmjd, If `lParam` contains `ENDSESSION_LOGOFF`, they're logging off. If that's not what you're asking, the user is still logged on when you get this message. Other programs are starting to end, but the session hasn't yet. I believe, for example, that `GetUserName` would still consistently work. – chris May 08 '13 at 08:29
  • @chris, a machine can have multiple users logged on similataneously. If the service recieves the logoff message how to know which of the multiple users is departing? – hmjd May 08 '13 at 08:30
  • @hmjd, Ah, I see. I have code that works for getting the user name from a service. Let me find it. I'm pretty sure there's something that you'll find useful in the same batch of functions. The two I used for that were [WTSGetActiveConsoleSessionId](http://msdn.microsoft.com/en-us/library/windows/desktop/aa383835(v=vs.85).aspx) and [WTSQuerySessionInformation](http://msdn.microsoft.com/en-us/library/windows/desktop/aa383838(v=vs.85).aspx). There might be something in those or the related functions that you find helpful. – chris May 08 '13 at 08:32
  • 1
    You can enumerate all the user profiles off the machine when uninstalling at SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList get the "ProfileImagePath" value and then dynamically load the "profile" and remove the key you want. – João Augusto May 08 '13 at 08:40
  • @JoãoAugusto, That's actually kind of handy. – chris May 08 '13 at 08:42
  • @JoãoAugusto, interesting. Will investigate that now. Thanks. – hmjd May 08 '13 at 08:43
  • @hmjd, no problem if you need help I can give you some code. – João Augusto May 08 '13 at 08:53
  • @JoãoAugusto, thank you but I will have a go myself first to make sure I understand everything. If it works, then I will let you know and you can post an answer. – hmjd May 08 '13 at 08:54
  • @JoãoAugusto: that won't work with roaming profiles. – Harry Johnston May 08 '13 at 23:23

2 Answers2

4

Since you are already on a service, your life (should be) is easy. In fact you can register yourself to receive the SERVICE_CONTROL_SESSIONCHANGE event. In particular, you will want to look for the WTS_SESSION_LOGOFF reason.

You have to register for these events in your service control routine, at startup, adding SERVICE_ACCEPT_SESSIONCHANGE. When the event is SERVICE_CONTROL_SESSIONCHANGE, the lpEventData parameter is a pointer to a WTSSESSION_NOTIFICATION structure, with information about the session currently terminating (thus, about the user logging off).

Check out the details on MSDN1, MSDN2, MSDN3 - the data structure that contains the dwSessionId of the interesting session

Check out this related (but not duplicate) question too

That said, I find João Augusto solution cleaner; I would use that for a similar problem; however, I wanted to add this solution for having an answer to the wider question (for future reference readers)

EDIT: Another method is to use SENS, check this MSDN article

Community
  • 1
  • 1
Lorenzo Dematté
  • 7,638
  • 3
  • 37
  • 77
  • How do I determine which user is logging out if there are mutliple users logged in? – hmjd May 08 '13 at 15:07
  • By using the dwSessionId parameter. You can then use it (I think through WTSQuerySessionInformation as chris suggested in the comments), to obtain any kind of info on the user – Lorenzo Dematté May 08 '13 at 15:22
  • Since I suppose you are then using ImpersonateLoggedOnUser to operate on the registry, see this good answer on how to obtain the user token from a session id http://stackoverflow.com/a/4534100/863564 – Lorenzo Dematté May 08 '13 at 15:33
  • Thank you for all of this (+1 for effort alone). I will try it out and let you know how it goes. – hmjd May 08 '13 at 15:38
0

An easier approach would be to put a command removing the value in question into the user's RunOnce key, e.g.,

 reg.exe delete HKCU\Software\xyzzy /v myvalue /f

so that the unwanted value will be removed when the user next logs in. Note, however, that this might interfere with the creation of the value depending on how you are handling that.

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158