|
ou might want to look at the
WTSRegisterSessionNotification() API to receive session switch notification. (consult msdn) | ||
| Comment from PeterLarsen Date: 02/25/2003 12:58AM PST | Author Comment | |
But WTSRegisterSessionNotification/WM_WTSSES
| ||
| Comment from smot Date: 02/25/2003 11:44AM PST | Comment | |
yes, right.
| ||
| Comment from PeterLarsen Date: 02/26/2003 01:10AM PST | Author Comment | |
I dont get it.
Is it really necessary to use 3 different methods to detect system login - one for Win 95/98/NT4, one for Win2000 and one for XP ??? | ||
| Comment from Fallen_Knight Date: 02/26/2003 01:33AM PST | Comment | |
http: this guy did it somehow, the source for the program he wrote is availble to download. not sure how much this will help but its worth a shot. (its in C++ but i followed your link in the c++ section to here) | ||
| Assisted Answer from Salte Date: 02/26/2003 01:36AM PST Grade: A | Assisted Answer | |
Peter,
Don't you just love Microsoft? Afraid you have to use 3 methods to detect it. You should probably pack that in a streamlined interface. You could try to use the other 2 methods (Win2K and WinXP methods), detect that user has changed and instead of handling it immediately you send a WM_USERCHANGED message to yourself. That way you get the message also for Win2000 and WinXP and you can use common code to handle the situation. If you need to do different things in those 3 types of systems you probably should do the handling in each of the functions anyway. In that case you might want to make that a separate DLL and create 3 DLLs, one for Win95/98 etc and one for Win2000 and one for WinXP and at the beginning of your program test what system you are running on and load the correct DLL and call the DLL to handle the system specific stuff. Alf | ||
| Accepted Answer from jkr Date: 02/26/2003 05:47AM PST Grade: A | Accepted Answer | |
What about using Winlogon Notification Packages? You'd basically build a Dll that exports
//Event handler for the Winlogon Logon event VOID WLEventLogon (PWLX_NOTIFICATION_INFO pInfo) { //Print the name of the handler to debug output. //You can replace this with more useful functionality. OutputDebugString (TEXT("NOTIFY: Entering WLEventLogon./r/n")); } //Event handler for the Winlogon Logoff event. VOID WLEventLogoff (PWLX_NOTIFICATION_INFO pInfo) { //Print the name of the handler to debug output. //You can replace this with more useful functionality. OutputDebugString (TEXT("NOTIFY: Entering WLEventLogff./r/n")); } and register it under build that dll and register it under HKEY_LOCAL_MACHINE/Software/Microsoft/Wi DllName = mynotify.dll Logon = "WLEventLogon" Logoff = "WLEventLogoff" See also http: | ||
| Comment from Salte Date: 02/26/2003 06:40AM PST | Comment | |
Think jkr's solution might work.
You might even have those functions send a WM_USERCHANGED event to yourself - i.e. to your server's message queue, that way you can have the same interface as in Win95 etc. If WM_USERCHANGED isn't defined when building your service just #define (or use an enum) and use a value that isn't used by windows or you already. Probably not WM_USER since WM_USER area is used by windows own controls. It is ok if you are sure that none of those windows ever get the WM_USERCHANGED but if they do and WM_USERCHANGE is defined equal to WM_USER or something like that they are likely to misunderstand the message. Alf | ||
| Comment from PeterLarsen Date: 02/26/2003 12:13PM PST | Author Comment | |
Thank you for your comments.
There are several ideas here and right now i'm working on a solution where i am using WM_USERCHANGED for Win95/98/ME and for WinNT/2000/XP i'm trying Winlogon Notification Packages. I dont know whether Winlogin notification will work with XP Fast-User-Switching or not - i really hope i does. Best regards Peter | ||
| Assisted Answer from smot Date: 02/26/2003 12:18PM PST Grade: A | Assisted Answer | |
For XP, you can use this code I wrote some time ago:
{ Typically, an application does not need to be notified when a session switch occurs. However, if the application needs to be aware when its desktop is current, it can register for session switch notifications. Applications that access the serial port or another shared resource on the computer should check for this. To register for a notification, use the following function: } function WTSRegisterSessionNotification( hWnd: HWND , // Window handle dwFlags: DWORD // Flags ): Bool; // Return value { The registered HWND receives the message WM_WTSSESSION_CHANGE through its WindowProc function. In dwFlags you can specify: a) NOTIFY_FOR_THIS_SESSION. A window is notified only about the session change events that affect the session to which window belongs. b) NOTIFY_FOR_ALL_SESSIONS. A window is notified for all session change events. The action happening on the session can be found in wParam code, which may contain one of the following flags. WTS_CONSOLE_CONNECT: A session was connected to the console session. WTS_CONSOLE_DISCONNECT: A session was disconnected from the console session. WTS_REMOTE_CONNECT: A session was connected to the remote session. WTS_REMOTE_DISCONNECT: A session was disconnected from the remote session. WTS_SESSION_LOGON: A user has logged on to the session. WTS_SESSION_LOGOFF: A user has logged off the session. WTS_SESSION_LOCK: A session has been locked. WTS_SESSION_UNLOCK: A session has been unlocked. WTS_SESSION_REMOTE_CONTROL: A session has changed its remote controlled status. lParam contains the sessionId for the session affected. When your process no longer requires these notifications or is terminating, it should call the following to unregister its notification. } function WTSUnRegisterSesssionNotification( hWnd: HWND // window handle. ): Boolean; // Result { The HWND values passed to WTSRegisterSessionNotification are reference counted, so you must call WTSUnRegisterSessionNotification exactly the same number of times that you call WTSRegisterSessionNotification. Applications can use the WTS_CONSOLE_CONNECT, WTS_CONSOLE_DISCONNECT, WTS_REMOTE_CONNECT, WTS_REMOTE_DISCONNECT messages to track their state, as well as to release and acquire console specific resources. } unit Wtsapi; interface { (c) By Thomas Stutz 10. April 02 } uses Windows; const // The WM_WTSSESSION_CHANGE message notifies applications of changes in session state. WM_WTSSESSION_CHANGE = $2B1; // wParam values: WTS_CONSOLE_CONNECT = 1; WTS_CONSOLE_DISCONNECT = 2; WTS_REMOTE_CONNECT = 3; WTS_REMOTE_DISCONNECT = 4; WTS_SESSION_LOGON = 5; WTS_SESSION_LOGOFF = 6; WTS_SESSION_LOCK = 7; WTS_SESSION_UNLOCK = 8; WTS_SESSION_REMOTE_CONTROL = 9; // Only session notifications involving the session attached to by the window // identified by the hWnd parameter value are to be received. NOTIFY_FOR_THIS_SESSION = 0; // All session notifications are to be received. NOTIFY_FOR_ALL_SESSIONS = 1; function RegisterSessionNotification(Wnd: HWND; dwFlags: DWORD): Boolean; function UnRegisterSessionNotification(Wnd: HWND): Boolean; function GetCurrentSessionID: Integer; implementation function RegisterSessionNotification(Wnd: HWND; dwFlags: DWORD): Boolean; // The RegisterSessionNotification function registers the specified window // to receive session change notifications. // Parameters: // hWnd: Handle of the window to receive session change notifications. // dwFlags: Specifies which session notifications are to be received: // (NOTIFY_FOR_THIS_SESSION, NOTIFY_FOR_ALL_SESSIONS) type TWTSRegisterSessionNotification = function(Wnd: HWND; dwFlags: DWORD): BOOL; stdcall; var hWTSapi32dll: THandle; WTSRegisterSessionNotification: TWTSRegisterSessionNotification; begin Result := False; hWTSAPI32DLL := LoadLibrary('Wtsapi32.dll'); if (hWTSAPI32DLL > 0) then begin try @WTSRegisterSessionNotification := GetProcAddress(hWTSAPI32DLL, 'WTSRegisterSessionNotification'); if Assigned(WTSRegisterSessionNotification) then begin Result:= WTSRegisterSessionNotification(Wnd, dwFlags); end; finally if hWTSAPI32DLL > 0 then FreeLibrary(hWTSAPI32DLL); end; end; end; function UnRegisterSessionNotification(Wnd: HWND): Boolean; // The RegisterSessionNotification function unregisters the specified window // Parameters: // hWnd: Handle to the window type TWTSUnRegisterSessionNotification = function(Wnd: HWND): BOOL; stdcall; var hWTSapi32dll: THandle; WTSUnRegisterSessionNotification: TWTSUnRegisterSessionNotification; begin Result := False; hWTSAPI32DLL := LoadLibrary('Wtsapi32.dll'); if (hWTSAPI32DLL > 0) then begin try @WTSUnRegisterSessionNotification := GetProcAddress(hWTSAPI32DLL, 'WTSUnRegisterSessionNotification'); if Assigned(WTSUnRegisterSessionNotificatio begin Result:= WTSUnRegisterSessionNotification(Wnd); end; finally if hWTSAPI32DLL > 0 then FreeLibrary(hWTSAPI32DLL); end; end; end; function GetCurrentSessionID: Integer; // Getting the session id from the current process type TProcessIdToSessionId = function(dwProcessId: DWORD; pSessionId: DWORD): BOOL; stdcall; var ProcessIdToSessionId: TProcessIdToSessionId; hWTSapi32dll: THandle; Lib : THandle; pSessionId : DWord; begin Result := 0; Lib := GetModuleHandle('kernel32'); if Lib <> 0 then begin ProcessIdToSessionId := GetProcAddress(Lib, '1ProcessIdToSessionId'); if Assigned(ProcessIdToSessionId) then begin ProcessIdToSessionId(GetCurrentProcessId Result:= pSessionId; end; end; end; end. // Example: unit Unit1; interface uses Windows, Messages, {...}, Wtsapi; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } FRegisteredSessionNotification : Boolean; procedure AppMessage(var Msg: TMSG; var HAndled: Boolean); end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.AppMessage(var Msg: TMSG; var Handled: Boolean); var strReason: string; begin Handled := False; // Check for WM_WTSSESSION_CHANGE message if Msg.Message = WM_WTSSESSION_CHANGE then begin case Msg.wParam of WTS_CONSOLE_CONNECT: strReason := 'WTS_CONSOLE_CONNECT'; WTS_CONSOLE_DISCONNECT: strReason := 'WTS_CONSOLE_DISCONNECT'; WTS_REMOTE_CONNECT: strReason := 'WTS_REMOTE_CONNECT'; WTS_REMOTE_DISCONNECT: strReason := 'WTS_REMOTE_DISCONNECT'; WTS_SESSION_LOGON: strReason := 'WTS_SESSION_LOGON'; WTS_SESSION_LOGOFF: strReason := 'WTS_SESSION_LOGOFF'; WTS_SESSION_LOCK: strReason := 'WTS_SESSION_LOCK'; WTS_SESSION_UNLOCK: strReason := 'WTS_SESSION_UNLOCK'; WTS_SESSION_REMOTE_CONTROL: begin strReason := 'WTS_SESSION_REMOTE_CONTROL'; // GetSystemMetrics(SM_REMOTECONTROL); end; else strReason := 'WTS_Unknown'; end; // Write strReason to a Memo Memo1.Lines.Add(strReason + ' ' + IntToStr(msg.Lparam)); end; end; procedure TForm1.FormCreate(Sender: TObject); begin // register the window to receive session change notifications. FRegisteredSessionNotification := RegisterSessionNotification(Handle, NOTIFY_FOR_THIS_SESSION); Application.OnMessage := AppMessage; end; procedure TForm1.FormDestroy(Sender: TObject); begin // unregister session change notifications. if FRegisteredSessionNotification then UnRegisterSessionNotification(Handle); end; procedure TForm1.Button1Click(Sender: TObject); begin // retrieve current session ID ShowMessage(Inttostr(GetCurrentSessionID end; | ||
| Comment from PeterLarsen Date: 02/26/2003 12:50PM PST | Author Comment | |
Thanks smot,
I dont know yet, whether i have to use WTSRegisterSessionNotification/WM_WTSSES From what i hear, i must - because i have to recreate handles to the taskbar (and desktop) when fast-switching - and fast-switching dont act like a normal login/logout. Peter | ||
| Comment from PeterLarsen Date: 02/28/2003 12:13PM PST | Author Comment | |
Hi all,
If i'm using Winlogon Notification Packages, the package must notify my running NT-service on login/logout. How can it do that ?? Something like this : Sendmessage(aHandle, MessageIdentifier, xxxx, xxxx); ?? Regards Peter | ||
| Comment from jkr Date: 02/28/2003 12:20PM PST | Comment | |
>>Something like this : Sendmessage(aHandle, MessageIdentifier, xxxx, xxxx); ??
'SendMessage()' will only work for GUI apps - and since most services lack a GUI... :o) I'd use either a simple event or a service control request using 'ControlService()' with a user-defined control code. | ||
| Comment from dave_p_r_b Date: 03/21/2003 02:04AM PST | Comment | |
Hi,
I may have misunderstood, but have you looked into GINA? Its a huge topic, but search on msdn. Be prepared for a few remote network registry adjustments though! D. | ||
| Comment from Salte Date: 03/21/2003 03:48AM PST | Comment | |
SendMessage()' will only work for GUI apps - and since most services lack a GUI... :o)
This is actually wrong. SendMessage() works for any process that has a message queue attached to it. A process gets a message queue if it ever does a PeekMessage() or GetMessage call. First time this happens windows will create a message queue for the process. If a server does GetMessage() you can do SendMessage() to send messages to it. Of course, one problem is that SendMessage() takes a windows handle as argument and that means you must have a 'window'. This is actually quite silly and is the reason why full screen DirectX games have to create (an invisible) window at startup for the sole purpose so that they can receive windows messages. A server can of course do the same thing. But most services do not and as such you can't use SendMessage() to send messages to a random server. But if you write your own server you can easily do so. Just have the server create an invisible window and do GetMessage() or PeekMessage() on that window at some early stage and there you got your message queue. However, the regular way to interact with a service is to use ControlService() which is essentially exactly the same as SendMessage() wouldn't surprise me if it under the hood used exactly the same mechanism as SendMesage() and uses exactly the same message queue mechanism as SendMessage does. There is one important difference though, it won't use a window handle to identify the message queue but will instead use a handle returned by OpenService() to identify the message queue. Apart from that there's no reason why they should have a completely different type of message queue for servers compared to windows GUI programs. In fact I believe it is a bad idea that they did it this way, since the difference between a service and a GUI program is - on the whole - very small, they're both event driven or request/response driven (some event or request comes and they need to respond to it), so I believe it is bad OS design to have two different worlds for something that is - on the whole - very much the same but then nobody ever claimed that Windows was good OS design? Alf | ||
| Comment from PeterLarsen Date: 03/22/2003 02:49AM PST | Author Comment | |
Are we talking about MSGINA.DLL - and what can it do ??
salte : Thanks for your comment. Actually, i do have a question related to what you are writing about. Not that i dont know how to solve it, but i dont understand why it works | ||
| Comment from PeterLarsen Date: 03/27/2003 01:31AM PST | Author Comment | |
Hi Salte,
You are talking about handles and the need of a window in order to obtain a handle. In this NT-Service i have, i use the message "TaskBarCreated" - so i know when the taskbar is ready. But it's not possible to receive messages within the Service - just like you said. To get the message, i interrupt (override) a function. And this is what i dont understand : OldWinProc := TFNWndProc(SetWindowLong(Forms.Applicati How can this be a valid handle : Forms.Application.Handle ?? I dont have any forms yet and "Forms" is only in the uses clause. Regards Peter | ||
| Comment from Salte Date: 03/27/2003 01:57AM PST | Comment | |
well, for one thing this looks like Delphi code and not C++. In any case, let me see if I can decode it
In C++ the equivalen code would be something like: OldWinProc = TFNWndProc(SetWindowLong(Forms::Applicat Well, the crucial point here is the 'Handle' and what value it return. Check your startup code where the application object is initialized. There's one such object per application so you should have only one such object and there should be a static pointer pointing to it. This object is created during VCL startup. I am not sure if it sets a handle value there or what, if you - as you say - never create a window then I would believe the Handle returned from this should be some form of a NULL handle to no window. Not sure how that works to get messages sent to such a handle though. It is also possible that it is the application startup code that creates a window if you don't make one yourself. Handle is most likely a property and so there is a read function attached to it. If that function checks for a NULL handle and creates a window if it is NULL and then return the handle of the created window then the service will create a (presumably invisible) window that you can use to receive messages. Alf | ||
| Comment from PeterLarsen Date: 03/27/2003 12:26PM PST | Author Comment | |
Hi salte,
Thanks for your comment - it didn't answer my question, but sometimes it helps to hear what others have to say about it - and this is what i found : Delphi create a instance of TAppilation (Application) on program start. This is done automatically. In the constructor of TApplication a window is created : "FHandle := CreateWindow(WindowClass.lpszClassName, PChar(FTitle), WS_POPUP or WS_CAPTION or WS_CLIPSIBLINGS or WS_SYSMENU or WS_MINIMIZEBOX, GetSystemMetrics(SM_CXSCREEN) div 2, GetSystemMetrics(SM_CYSCREEN) div 2, 0, 0, 0, 0, HInstance, nil);" but only if the program isn't a DLL or a Console. So you was right about this >>It is also possible that it is the application startup code that creates a window if you don't make one yourself. Handle is most likely a property and so there is a read function attached to it. If that function checks for a NULL handle and creates a window if it is NULL and then return the handle of the created window then the service will create a (presumably invisible) window that you can use to receive messages. TApplication is located in unit Forms. Not the entire unit is created, only a instance of TApplication (and probably other stuff). Thats why i could type Forms.Application.Handle. Application.handle is exactly the same. >>Not sure how that works to get messages sent to such a handle though. All messages are sent to the hidden window through a function called WndProc "procedure TApplication.WndProc(var Message: TMessage);" By calling SetWindowLong the address to WndProc (in the hidden window) is changed so it point to my function (called NewWndProc) in the Service. I guess you are an experienced programmer in C++ and since this is Delphi, you'll probably dont understand much about TApplication and similar. How does it feel not to understand anything and still be able to help ?? Best regards Peter | ||
| Comment from Salte Date: 03/28/2003 02:34AM PST | Comment | |
Well, I do know about TApplication - it is used in C++ builder also and I must confess I have some but not detailed knowledge of object pascal or delphi. A couple of years ago I had a version of C++ builder installed on my computer with the source of the VCL library included and that source was all in pascal and was rather interesting reading
Must admit that it is many years ago now though and I it is not my strongest field. Alf | ||
WTSRegisterSessionNotification() API to receive session switch notification
最新推荐文章于 2021-08-06 07:11:55 发布
本文讨论了在不同操作系统上实现登录状态检测的方法,包括使用WTSRegisterSessionNotification API、Winlogon通知包等技术,并探讨了这些方法在Windows XP快速用户切换特性下的适用性。
914

被折叠的 条评论
为什么被折叠?



