I've the following piece of code in my program which dynamically links wtsapi32.dll file for session notifications like WTS_SESSION_LOCK and WTS_SESSION_UNLOCK and runs in background. After the first lock/unlock the program hangs and not responding.
Is this a right way of doing explicit linking ?
void RegisterSession(HWND hwnd)
{
typedef DWORD (WINAPI *tWTSRegisterSessionNotification)( HWND,DWORD );
tWTSRegisterSessionNotification pWTSRegisterSessionNotification=0;
HINSTANCE handle = ::LoadLibrary("wtsapi32.dll");
pWTSRegisterSessionNotification = (tWTSRegisterSessionNotification) :: GetProcAddress(handle,"WTSRegisterSessionNotification");
if (pWTSRegisterSessionNotification)
{
pWTSRegisterSessionNotification(hwnd,NOTIFY_FOR_THIS_SESSION);
}
::FreeLibrary(handle);
handle = NULL;
}
Edited:
I have another method UnRegisterSession() function which calls WTSUnRegisterSessionNotification, I am calling the RegisterSession() in WinMain method ( removed FreeLibrary as suggested by 1800) and calling UnRegisterSession() in WM_DESTROY of CALLBACK WindowProcedure function. But still the application hangs.
I'd say you probably cannot safely call FreeLibrary like that - you will be unloading the code you want to have call you. You should probably ensure not to free the dll until after you are finished getting notifications.
MS documentation suggests that you must call WTSUnRegisterSessionNotification before re-registering the session - as it only happens on your second attempt to lock it perhaps this is your issue?
With 1800 wrt the free library - you must keep this library loaded while you use it.
According to the documentation (http://msdn.microsoft.com/en-us/library/aa383841(VS.85).aspx):
"When a window no longer requires these notifications, it must call WTSUnRegisterSessionNotification before being destroyed."
I would try unregistering the notification during WM___CLOSE instead of WM_DESTROY.
Related
I'm trying to execute a function on user logout, but the program close before function call, is there any way to wait function finish the function execution?
#include "stdafx.h"
#include <windows.h>
#include <fstream>
using namespace std;
bool done = false;
void createFile() {
ofstream outfile("test.txt");
outfile << "test" << std::endl;
outfile.close();
}
BOOL WINAPI consoleHandler(DWORD signal) {
switch (signal)
{
case CTRL_LOGOFF_EVENT:
printf("Logoff");
done = true;
Sleep(20000); // force exit after 20 seconds
return TRUE;
case CTRL_C_EVENT:
printf("Ctrl+c");
done = true;
Sleep(20000); // force exit after 20 seconds
return TRUE;
case CTRL_CLOSE_EVENT:
printf("close");
done = true;
Sleep(20000); // force exit after 20 seconds
return TRUE;
default:
// Pass signal on to the next handler
return FALSE;
}
}
int main()
{
if (!SetConsoleCtrlHandler(consoleHandler, TRUE)) {
printf("\nERROR: Could not set control handler");
return 1;
}
printf("Runing");
while (!done) {
printf(".");
Sleep(1000);
}
createFile(); //Dont called on user logout but called on close and ctrl+c
printf("\nEnding\n");
Sleep(1000);
return 0;
}
On CTRL_C_EVENT and CTRL_CLOSE_EVENT
The file test.txt is created but on
CTRL_LOGOFF_EVENT
The program close instantly, without call the function.
First, let me apologise : I'll not answer the specific 'CTRL_LOGOFF_EVENT' you are referring, but the generic feature you're trying to accomplish : 'execute a function on user logout'.
A generic way to do:
As far as I know, there is no generic way to do.
You may try various solutions :
catching events as you did
using try catch / finally
spawn windows timers and react to it
spawn threads that will wait for the main thread to end
But (as far as I know) all of them have a weakness to a way to close or another.
For instance, I recently had to struggle with a poorly coded dll I had to use. This dll was calling "exit(0)". I found no elegant way to gracefully handle this behaviour.
I had to launch it in a separated process (not thread), that I was monitoring from my main process. It's a lot of work to implement and to maintain for a simple result.
Another approach:
Depending on what you are trying to do, it may be a good practice to make your "final" operation gradually, using a format that allows you to recover partially written files.
It's what I would generally do, but it always depends on what you are trying to achieve.
For instance, if you're trying to clean your workspace, you may want to:
do it at start in the case it was not done in the previous run
work in temporary folders
Another case, if you have a very long process and want to generate summary file, you may want to:
generate a partial summary file from time to time
as you generate a new file, you move the previous one
that way, you'll have a partial summary file at every step of your process.
Since my answer has been down voted, I guess I need to be more pedantic.
first of all, since you are not getting the CTRL_LOGOFF_EVENT you may want to consult the this doc (https://learn.microsoft.com/en-us/windows/console/setconsolectrlhandler). The critical section being:
If a console application loads the gdi32.dll or user32.dll library, the
HandlerRoutine function that you specify when you call SetConsoleCtrlHandler
does not get called for the CTRL_LOGOFF_EVENT and CTRL_SHUTDOWN_EVENT events. The
operating system recognizes processes that load gdi32.dll or user32.dll as Windows
applications rather than console applications. This behavior also occurs for
console applications that do not call functions in gdi32.dll or user32.dll
directly, but do call functions such as Shell functions that do in turn call
functions in gdi32.dll or user32.dll.
To receive events when a user signs out or the device shuts down in these
circumstances, create a hidden window in your console application, and then handle
the WM_QUERYENDSESSION and WM_ENDSESSION window messages that the hidden window
receives. You can create a hidden window by calling the CreateWindowEx method with
the dwExStyle parameter set to 0.
You might also want to read ( https://msdn.microsoft.com/en-us/library/windows/desktop/aa376890(v=vs.85).aspx ).
Now, what I had previously suggested is to use the SetWindowsHookEx which should work as all windows messages pass through it (it is what Spy uses to view the message queues (https://blogs.msdn.microsoft.com/vcblog/2007/01/16/spy-internals/)). 20 years ago, you would have to do something like this for certain messages that were not passed through to your window - as in your case where the windows message is processed up stream of the current window. You may come across old code that does this, or there may be situations that you still need to do this.
I am working on writing a simple game engine and I am having trouble handling Windows console events; specifically, I cannot figure out how to pass custom data to the callback handler.
I first call this code to specify my callback function:
SetConsoleCtrlHandler((PHANDLER_ROUTINE)WindowsSystemManager::ConsoleControlHandler, true);
My static-member callback function is defined as:
bool WINAPI WindowsSystemManager::ConsoleControlHandler(DWORD controlType){
if(controlType == CTRL_CLOSE_EVENT){
MessageBox(NULL, L"Close Event Captured", L"Close Event Captured", NULL);
}
return true;
}
Everything works fine - when I click on the close button in the console, this MessageBox pops up. Only problem is, I need to call code that flushes a logging buffer to a log file on this type of shutdown (as well as other clean-up), and the Logger instance is a member in my WindowsSystemManager.
I have dealt with a similar problem of passing custom data to window handles by using SetWindowLongPtr and GetWindowLongPtr successfully, but I can't find any information on how to do this type of thing with console control handlers. Any thoughts?
EDIT: I got this functionality working based on MSalters' suggestions. The final code for the console control handler is here:
bool WINAPI WindowsSystemManager::ConsoleControlHandler(DWORD controlType){
BerserkEngine* engine = (BerserkEngine*)GetWindowLongPtr(GetConsoleWindow(), GWLP_USERDATA);
if(controlType == CTRL_CLOSE_EVENT){
engine->~BerserkEngine();
PostQuitMessage(0);
}
return true;
}
Where I set this custom data pointer in the WindowsSystemManager constructor:
SetWindowLongPtr(GetConsoleWindow(), GWL_USERDATA, (LONG_PTR)this->engine);
I'm not sure why you'd need this. You can have multiple windows, but only one console.
However, GetConsoleWindow will give you the console HWND, on which you might call SetWindowLongPtr. Not very clean (you're not supposed to do this on windows that you don't manage), but it might just work.
I have created an MFCApp using VS2008 wizard. Inside my application's "InitInstance()" I'm calling "LoadLibraryA()" method as I need to load a few dll files. But as soon as I call "LoadLibraryA()", it again calls "InitInstance()" of my application and hence it becomes a infinite recursion stuff. Is there something I'm doing wrong?
// CLoader_MFCApp initialization
BOOL CLoader_MFCApp::InitInstance()
{
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinAppEx::InitInstance();
SetRegistryKey(_T("MyApp"));
HMODULE hm = LoadLibraryA("./abc/def.dll");
// after above line InitInstance() gets called again
// more code
return FALSE;
}
Call Stack:
MyApp.exe!CLoader_MFCApp::InitInstance() C++
CORE.dll!InternalDllMain(HINSTANCE__ *, unsigned long, void *) C++
CORE.dll!__DllMainCRTStartup(void *, unsigned long, void *) C
CORE.dll!_DllMainCRTStartup(void *, unsigned long, void *) C
ntdll.dll!_LdrpCallInitRoutine#16()
ntdll.dll!_LdrpRunInitializeRoutines#4()
ntdll.dll!_LdrpLoadDll#24()
ntdll.dll!_LdrLoadDll#16()
kernel32.dll!_LoadLibraryExW#12()
kernel32.dll!_LoadLibraryExA#12()
kernel32.dll!_LoadLibraryA#4()
MyApp.exe!CLoader_MFCApp::InitInstance() C++
mfc90.dll!AfxWinMain(HINSTANCE__ *, HINSTANCE__ *, char *, int) C++
MyApp.exe!__tmainCRTStartup() C
kernel32.dll!_BaseProcessStart#4()
"Def.dll" is any other dll and completely unrelated from MyApp. In this case, I'm trying to load another dll "CORE.dll"
All I can figure out is that I'm calling LoadLibrary before InitInstance routine is over. Is there any other (overridable) method which is called after InitInstance??? If so, I can try moving LoadLibrary calls to that method...
Yes, you are doing something wrong.
You are in mfc90.dll's DllMain and it is not safe to call LoadLibrary from DllMain, says so right here:
http://msdn.microsoft.com/en-us/library/ms684175%28v=vs.85%29.aspx
This is more of a workaround than a true solution (i.e. I don't know the rules for LoadLibrary in MFC, as I've never read anything to say you can't, nor do I happen to use this technique in our MFC code).
However, Generally speaking, if windows coughs up a hairball due to order of operations, I just move the calls out to another message handler. You can even post a thread message to your application, and write a handler for that message.
Something like:
// in InitInstance - post a message to our main thread to handle after init instance...
PostMessage(NULL, WM_PostInit);
// in your message table
ON_THREAD_MESSAGE(WM_PostInit, OnPostInit)
// in your app
void MyApp::OnPostInit(WPARAM,LPARAM) // both args unused
{
// try load library now...!
}
NOTE: The above is "brain code" - untested. Details undoubtedly need to be massaged for full compilability.
References:
http://msdn.microsoft.com/en-us/library/ms644944%28v=VS.85%29.aspx
I've just had the same issue, caused by the Configuration type being incorrectly set to exe not dll for the dll to be loaded.
Fix: Project -> Configuration Properties -> General -> Configuration Type = Dynamic Library (.dll) (was incorrectly set to Application (.exe))
After switching from VS2005 to VS2008 SP1, I found an issue that I can't explain.
A program works fine under VS2005 in both release and debug mode. Under VS2008, when entering the debugger an assert is raised.
If I let the program run (in debug or release mode), no assertion at all.
I spent almost two days on this and I don't understand what I do wrong.
Description of the program:
I have a MFC dialog based program that creates a user thread (CWinThread) that creates the main dialog of the application.
A worker thread loops infinitely and posts each second a message to the dialog. The message is processed in the gui thread.
Some parts of my code:
The InitInstance of the gui thread:
BOOL CGraphicalThread::InitInstance()
{
CGUIThreadDlg* pDlg = new CGUIThreadDlg();
pDlg->Create(CGUIThreadDlg::IDD);
m_pMainWnd = pDlg;
AfxGetApp()->m_pMainWnd = pDlg;
return TRUE;
}
The worker thread:
UINT ThreadProc(LPVOID pVoid)
{
do
{
AfxGetApp()->m_pMainWnd->PostMessage(WM_APP+1, (WPARAM)new CString("Hello"), NULL);
Sleep(1000);
}
while(!bStopThread);
return 0;
}
The dialog message handler is like this:
LRESULT CGUIThreadDlg::OnMsg(WPARAM wp, LPARAM lp)
{
CListBox* pList = (CListBox*)GetDlgItem(IDC_LIST1);
CString* ps = (CString*)wp;
pList->InsertString(-1, *ps);
delete ps;
return 1L;
}
This works perfectly fine with VS2005.
But with VS2008, but as soon as a put a breakpoint and enter the debugging mode, I have an assertion raised ???
wincore.cpp line 906
CObject* p=NULL;
if(pMap)
{
ASSERT( (p = pMap->LookupPermanent(m_hWnd)) != NULL ||
(p = pMap->LookupTemporary(m_hWnd)) != NULL);
}
ASSERT((CWnd*)p == this); // must be us
// Note: if either of the above asserts fire and you are
// writing a multithreaded application, it is likely that
// you have passed a C++ object from one thread to another
// and have used that object in a way that was not intended.
// (only simple inline wrapper functions should be used)
//
// In general, CWnd objects should be passed by HWND from
// one thread to another. The receiving thread can wrap
// the HWND with a CWnd object by using CWnd::FromHandle.
//
// It is dangerous to pass C++ objects from one thread to
// another, unless the objects are designed to be used in
// such a manner.
If I remove the GUI thread and create the dialog into the CWinApp thread, the problem doesn't occur anymore.
Does anybody have any idea?
Am I doing something wrong?
Thank you
// Note: if either of the above asserts fire and you are
// writing a multithreaded application, it is likely that
// you have passed a C++ object from one thread to another
// and have used that object in a way that was not intended.
// (only simple inline wrapper functions should be used)
//
// In general, CWnd objects should be passed by HWND from
// one thread to another. The receiving thread can wrap
// the HWND with a CWnd object by using CWnd::FromHandle.
//
// It is dangerous to pass C++ objects from one thread to
// another, unless the objects are designed to be used in
// such a manner.
#Ismael: I had already tried that the assert is still fired. The only way I found to remove the assert is to create the dialog into the CWinApp thread.
But this doesn't explain what happens since there's still the worker thread that post to the dialog every second.
Anyway , thanks.
#daanish.rumani: I've checked the wincore.cpp and the CWnd::AssertValid() is exactly the same (but there's of lot of differences in the rest of the files).
I would accept that a piece of code works with VS2005 and not VS2008, but
I can't see what I do wrong.
If I do something wrong, what is the correct way to proceed?
Why the assert is only fired when a breakpoint is hit and I step over the Sleep call?
I can run the program fine, even when its compiled in debug mode, as long as I don't enter the debugger.
Could it be a bug in the debugger?
I've created a very simple one-button MFC dialog app that attempts to utilize a callback function. The app complies and runs just fine, but the callback routine never gets triggered.
What needs to be modified in order to get the callback to trigger properly?
You can download the test.zip file here (the test app is in VS 2003 to ensure more people can try it out): http://tinyurl.com/testfile-zip
The code utilizes an alarm class on CodeProject, and the callback function is suppsed to get triggered every 3 seconds (as determined by the code being passed in).
Thanks!
I've looked at your code and the I believe the Function called from the button is the problem
void CTestDlg::OnBnClickedButton1()
{
CAlarmClock clock;
REPEAT_PARMS rp;
ZeroMemory(&rp, sizeof(REPEAT_PARMS));
rp.bRepeatForever = TRUE;
rp.Type = Repeat_Interval;
rp.ss = 3;
clock.SetRepeatAlarm(0, 0, 0, rp, CallbackRtn);
}
This creates the Alarm clock on the function stack.
This CAlarmclock object is therefore destroyed at the end of the function along with its contents.
For it to be able to exist for long enough to actually do the callback
you need to add it as a member variable of your dialog class for it to exist and callback for as long as the dialog exists.
See the example code on the CAlarmclock codeproject page for how to use this class correctly.