I have developed a console ATL application and want to trap the close?, exit?, terminate? event so that I can close log files and perform a general clean-up on exit.
How can I trap the 'terminate' event that would result from someone ending the .exe in the task manager?
EDIT:
It's a console application, I've attached the main class. Could you possibly point to where and how I should use SetConsoleCtrlHandler?
// Override CAtlExeModuleT members
int WinMain(int nShowCmd) throw()
{
if (CAtlBaseModule::m_bInitFailed) //ctor failed somewhere
{
ATLASSERT(0);
return -1;
}
HRESULT hr = S_OK;
LPTSTR lpCmdLine = GetCommandLine(); //this line necessary for _ATL_MIN_CRT
if( ParseCommandLine( lpCmdLine, &hr ) )
{
if( SUCCEEDED( RegisterClassObjects( CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE ) ) )
{
CComPtr<IRORCAdapterMain> pAdapter;
if( SUCCEEDED( pAdapter.CoCreateInstance( L"RORCAdapter.RORCAdapterMain" ) ) )
{
if( SUCCEEDED( pAdapter->StartAdapter() ) )
{
MSG msg;
while( GetMessage( &msg, 0, 0, 0 ) )
DispatchMessage( &msg );
}
}
RevokeClassObjects();
}
}
You can't trap "End Process" from the Processes tab in Task Manager. If a program could trap it, how would you kill it?
To respond to "End Process" on the Applications tab in Task Manager, handle the WM_CLOSE message in your main window.
This assumes that your ATL app is a Windows GUI application. If it's a console application, you need to look at SetConsoleCtrlHandler.
Catching Ctrl-C / Ctrl-Break is not to hard.
Just call SetConsoleCtrlHandler to specify which call-back function should handle it.
(Hopefully) illustrating example:
#include <wincon.h>
bool g_terminate = false;
int main(void)
{
SetConsoleCtrlHandler( control_handler, TRUE );
while ( !g_terminate )
{
doWork();
}
}
int WINAPI control_handler ( DWORD dwCtrlType )
{
switch( dwCtrlType )
{
case CTRL_BREAK_EVENT:
case CTRL_C_EVENT:
g_terminate = true;
return 1;
default:
return 0;
}
}
/L
Off course, to have you example terminating, in control_handler replace
g_terminate = true;
with
PostMessage(HWND_BROADCAST, WM_CLOSE, 0, 0);
Related
I am launching an windows desktop application by
CATStartProcess (const char *comPath,
char *const argv[],
int wait, int *pid,
int *exitStatus);
The arguments are passed to it.
If the application is already running I don't need to create a new instance for this. How can I check if this application is already running in background or not?
int wait = 0;
int pid;
int exitStatus;
char *CommandArgs[9] = { 0 };
CommandArgs[0] = (char*)usComposerExePath.ConvertToChar();
CommandArgs[1] = (char*)usOpen.ConvertToChar();
CommandArgs[2] = (char*)usComposerProjectDocPath.ConvertToChar();
CommandArgs[3] = (char*)strInfiniteTicket.ConvertToChar();
CommandArgs[4] = (char*)strDocName.ConvertToChar();
CommandArgs[5] = (char*)strSecurityContext.ConvertToChar();
CommandArgs[6] = (char*)usBusID.ConvertToChar();
CommandArgs[7] = (char*)usUserID.ConvertToChar();
CommandArgs[8] = NULL;
CATLibStatus startComposerBatchStatus = CATStartProcess((char*)usComposerExePath.ConvertToChar(), CommandArgs, wait, &pid, &exitStatus);
There's a few ways, but I'll admit, neither of my two solutions are portable/standard C++, but you tagged Windows, so I'll give a Windows method.
The below code actually performs both checks. The first method is to use a named mutex. Windows has a "Global" mutex, which checks for running processes by any user. If the mutex already exists, then its running. If it doesn't exist, then it's not running. There's some states where things can't be easily determined, so it checks the running process list. This part is less accurate, since different permissions affects the list.
The part with mutexes will only work if you can modify the application you are trying to launch so that it creates a mutex.
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <psapi.h>
#include <TlHelp32.h>
#include <shellapi.h>
#include <advpub.h>
enum class ProcessRunningState {
YES,
NO,
ERR
};
ProcessRunningState CheckIfProcessIsAlreadyRunning( DWORD currentProcessId, const wchar_t *programName, const wchar_t *programGUID, HANDLE *mutex_handle ) noexcept {
{ // Check the mutexes first
wchar_t global_prog_name[1024] = L"Global\\";
wcsncat_s( global_prog_name, programName, wcslen( programGUID ) );
if ( mutex_handle ) {
*mutex_handle = CreateMutex( NULL, TRUE, global_prog_name );
if ( !( *mutex_handle ) ) {
const DWORD dw = GetLastError();
if ( dw == ERROR_ALREADY_EXISTS )
return ProcessRunningState::YES;
} else {
return ProcessRunningState::NO;
}
} else {
HANDLE h = OpenMutex( SYNCHRONIZE, FALSE, global_prog_name );
if ( h ) {
CloseHandle( h );
return ProcessRunningState::YES;
} else if ( GetLastError() == ERROR_FILE_NOT_FOUND ) {
return ProcessRunningState::NO;
}
}
}
{ // At this point, the state is unknown, so try running through the process list
DWORD aProcesses[2048], cProcesses;
if ( !EnumProcesses( aProcesses, sizeof( aProcesses ), &cProcesses ) ) {
return ProcessRunningState::ERR;
}
// Calculate how many process identifiers were returned.
cProcesses = cProcesses / sizeof( DWORD );
for ( unsigned int i = 0; i < cProcesses; i++ ) {
if ( aProcesses[i] != 0 && aProcesses[i] != currentProcessId ) {
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, aProcesses[i] );
WCHAR szProcessName[MAX_PATH] = { 0 };
if ( hProcess ) {
HMODULE hMod;
DWORD cbNeeded;
if ( EnumProcessModules( hProcess, &hMod, sizeof( hMod ), &cbNeeded ) ) {
GetModuleBaseName( hProcess, hMod, szProcessName, sizeof( szProcessName ) / sizeof( TCHAR ) ); // Can't error here, since this function "errors" on access
}/* else {
return ProcessRunningState::ERR;
}*/
CloseHandle( hProcess );
}
if ( _wcsicmp( szProcessName, programName ) == 0 ) {
return ProcessRunningState::YES;
}
}
}
}
return ProcessRunningState::NO;
}
Calling it like so will create the mutex if possible, and basically says that "I want to run, can I?"
HANDLE mutex_handle;
const ProcessRunningState cur_state = CheckIfProcessIsAlreadyRunning( GetCurrentProcessId(), L"PROGRAM_NAME", programGUID, &mutex_handle );
switch ( cur_state ) {
case ProcessRunningState::ERR:
case ProcessRunningState::YES:
return ERROR_ALREADY_EXISTS;
default:
break;
}
Calling it like so, simply checks if its already running, and launches the application if not.
if ( CheckIfProcessIsAlreadyRunning( GetCurrentProcessId(), L"PROGRAM_NAME", programGUID, nullptr ) == ProcessRunningState::NO ) {
std::wstring programInstallLocation = L"path";
std::wstring programName = programInstallLocation + L"\\PROGRAM_NAME";
ShellExecute( NULL, L"open", programName.c_str(), NULL, NULL, SW_SHOW );
}
And somewhere in your code, you would specify what programGUID is. For example:
WCHAR programGUID[] = L"ba2e95a0-9168-4b6e-bcd6-57309748df21";
I am hoping someone might be able to help me out. My objective is to open a math input panel using C++ but the panel only opens for a split second before the program exits. Here is what I have tried.
cin.get();
system("PAUSE");
getchar();
All of the aforementioned attempts to keep the program open have resulted in the math input control window popping up but staying blank.
The code that I am using is directly from here.
https://msdn.microsoft.com/en-us/library/windows/desktop/dd317324(v=vs.85).aspx
I am using Visual Studio Community Edition 2017 with Windows 10.
Any and all help is appreciated.
#include "stdafx.h"
#include "micaut.h"
#include "micaut_i.c"
#include "atlcomcli.h"
using namespace std;
int main()
{
CComPtr<IMathInputControl> g_spMIC; // Math Input Control
HRESULT hr = CoInitialize(NULL);
hr = g_spMIC.CoCreateInstance(CLSID_MathInputControl);
hr = g_spMIC->EnableExtendedButtons(VARIANT_TRUE);
hr = g_spMIC->Show();
return 0;
}
Your program shows the window but then terminates immediately. You will need to run a message loop to service the GUI.
Synchronization in a single-threaded apartment (STA) is implemented through window messages (see Understanding and Using COM Threading Models). This requires that you run a message loop in every STA thread.
#include <windows.h>
#include <micaut.h>
#include <micaut_i.c>
#include <atlcomcli.h>
int main() {
HRESULT hr = ::CoInitialize(NULL);
CComPtr<IMathInputControl> g_spMIC; // Math Input Control
if ( SUCCEEDED( hr ) ) {
hr = g_spMIC.CoCreateInstance(CLSID_MathInputControl);
}
if ( SUCCEEDED( hr ) ) {
hr = g_spMIC->EnableExtendedButtons(VARIANT_TRUE);
}
if ( SUCCEEDED( hr ) ) {
hr = g_spMIC->Show();
}
if ( SUCCEEDED( hr ) ) {
// Message loop for STA
MSG msg{ 0 };
while ( ::GetMessageW( &msg, nullptr, 0, 0 ) ) {
// MathInputControl sends WM_USER + 2 when it should close
if ( msg.message == ( WM_USER + 2 ) )
break;
::TranslateMessage( &msg );
::DispatchMessageW( &msg );
}
::CoUninitialize();
}
}
The Math Input Control sending a WM_USER + 2 message in response to clicking the Close or Cancel buttons is not part of the documented contract. A proper implementation would implement an _IMathInputControlEvents, and respond to its Close event instead.
I'm developing an application that checks open windows on a user computer on Windows (just like the Task Manager)
I used EnumWindows to list all the active window and it works, now i want to create a function that write a message on the console when a windows has been closed. Is possible or i have to check an array of WindowHandler in a separate thread and how do I check their status?
Thank for the help.
The easiest solution is to use WinEvents, by registering for EVENT_OBJECT_DESTROY events. The code is fairly straight forward:
#include <windows.h>
namespace {
HWINEVENTHOOK g_WindowDestructionHook = NULL;
}
inline void CALLBACK WinEventProc( HWINEVENTHOOK hWinEventHook,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime ) {
// Filter interesting events only:
if ( idObject == OBJID_WINDOW && idChild == CHILDID_SELF ) {
wprintf( L"Window destroyed: HWND = %08X\n", hwnd );
}
}
inline void RegisterWindowDestructionHook() {
g_WindowDestructionHook = ::SetWinEventHook( EVENT_OBJECT_DESTROY,
EVENT_OBJECT_DESTROY,
NULL,
WinEventProc,
0, 0,
WINEVENT_OUTOFCONTEXT );
}
inline void UnregisterHook() {
::UnhookWinEvent( g_WindowDestructionHook );
}
Using this is equally simple:
::CoInitialize( NULL );
RegisterWindowDestructionHook();
MSG msg = {};
while ( ::GetMessageW( &msg, nullptr, 0, 0 ) > 0 ) {
::TranslateMessage( &msg );
::DispatchMessageW( &msg );
}
UnregisterHook();
::CoUninitialize();
I am running a Win32 application in debug mode using Visual Studio. After switching to Visual Studio to debug my application does not continue its message loop...no messages are being sent...How do I handle focus/activation messages in order to resume the message loop?
EDIT:
This application uses DirectX9. I have 2 viewports( device swapchains ) to which I render geometry. If, during runtime, I click on a different window and then later try to return to my application both viewports are black( not being rendered to ). I set breakpoints in the message loop and am finding that one message( WM_PAINT ) is constantly being translated and dispatched, thus preventing my application from rendering. Below is message loop. 'bGotMsg' is always true after returning focus to my application...
EDIT: The problem seems to be an internal WM_PAINT message that is not being handled...I've checked the message that is continually being dispatched and the window handle is not one of my application...could it be some internal handle? MSDN indicates here that a WM_PAINT message will be sent while no other messages are in the queue or until the internal paint message is handled...How do I handle this message?
HRESULT WINAPI GDEMainLoop( HACCEL hAccel )
{
HRESULT hr =S_OK;
HWND hWnd = DXUTGetHWND();
// Now we're ready to receive and process Windows messages.
bool bGotMsg;
MSG msg;
msg.message = WM_NULL;
PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );
while( WM_QUIT != msg.message )
{
// Use PeekMessage() so we can use idle time to render the scene.
bGotMsg = ( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) != 0 );
if( bGotMsg )
{
// Translate and dispatch the message
if( /*!TranslateAccelerator( hWnd, hAccel, &msg )*/
!IsDialogMessage( wnd.GetWindow( CWindowManager::EXPLORERBAR ), &msg )
&& !IsDialogMessage( wnd.GetSceneOutliner()->GetHWND(), &msg )
&& !IsDialogMessage( wnd.GetResourceOutliner()->GetHWND(), &msg )
&& !IsDialogMessage( wnd.GetProjectOutliner()->GetHWND(), &msg )
&& !IsDialogMessage( wnd.GetProjectDialog()->GetHWND(), &msg )
&& !IsDialogMessage( wnd.GetSceneDialog()->GetHWND(), &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
else
{
if( FAILED( hr = GDERender3DEnvironment() ) )
{
DebugStringDX( MainClassName, "Failed to GDERender3DEnvironment() at GDEMainLoop()", __LINE__, hr );
Alert( NULL,
L"A failure occured while trying to render the 3D environment!",
L"Render Failure" );
PostQuitMessage( 0 );
}
}
}
return S_OK;
}
I will describe my problem as below
I have two applications mainApp and supervisorApp.
The supervisorApp will run along with mainApp to supervise mainApp.
supervisorApp will check status of mainApp each 30s by sending KEEP_ALIVE message to mainApp.
And supervisorApp will restart mainApp if mainApp is not responding that KEEP_ALIVE message.
And more one rule, mainApp will not allow more than two processes "mainApp.exe" run together,
that mean when started, mainApp will close immediately if there is other process "mainApp.exe"is running.
And then I will posted some main code and show my problem.
//Code for supervisorApp - Begin
void Main()
{
while (true)
{
Sleep(30000);
if(SendKeepAliveMessage() == false) //MainApp is not responding -> Restart mainApp
{
TerminateMainApp();
Sleep(2000);
StartMainApp();
}
}
}
void TerminateMainApp()
{
TerminateProcess(MainAppProcessInfo.hProcess, 0);
CloseHandle( MainAppProcessInfo.hProcess );
CloseHandle( MainAppProcessInfo.hThread );
}
bool StartMainApp()
{
ZeroMemory( &MainAppStartUpInfo, sizeof(MainAppStartUpInfo) );
MainAppStartUpInfo.cb = sizeof(MainAppStartUpInfo);
ZeroMemory( &MainAppProcessInfo, sizeof(MainAppProcessInfo) );
char commandLine[STR_LENGTH_256];
sprintf(commandLine, "mainApp.exe");
LPSTR cmd = _T(commandLine);
//Start the child process
if( !CreateProcess
(NULL, // No module name (use command line)
cmd, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&MainAppStartUpInfo, // Pointer to STARTUPINFO structure
&MainAppProcessInfo // Pointer to PROCESS_INFORMATION structure
)
)
{
return false;
}
return true;
}
//Code for supervisorApp - End
//Code for mainApp - Begin
void Main()
{
if (IsThisProcessHasRun())
{
MessageBox.Show("The mainApp is running!");
return;
}
//Do something
}
bool IsThisProcessHasRun()
{
HANDLE SnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if( SnapShot == INVALID_HANDLE_VALUE )
return false;
PROCESSENTRY32 procEntry;
procEntry.dwSize = sizeof( PROCESSENTRY32 );
if( !Process32First( SnapShot, &procEntry ) )
{
CloseHandle(SnapShot);
return false;
}
int countProcess = 0;
do
{
if( strcmp( procEntry.szExeFile, "mainApp.exe" ) == 0 )
{
countProcess++;
if(countProcess == 2)
{
CloseHandle(SnapShot);
return true;
}
}
}
while( Process32Next( SnapShot, &procEntry ) );
CloseHandle(SnapShot);
return false;
}
//Code for mainApp - End
Everything is ok on windows 7, but on windows 8 I have encountered the following problem.
When mainApp.exe is crashed (Window will show the dialog with the message "the mainApp has stopped working" -> do not close it),
the supervisorMainApp send KEEP_ALIVE message and mainApp does not respond this message,
Then supervisorMainApp will call TerminateMainApp() and StartMainApp() to restart mainApp.
But when mainApp start, it will show the dialog "The mainApp is running!"...
I debug and see that, function TerminateMainApp() and StartMainApp() work fine.
When TerminateMainApp() called, in Task Manager's proceess list will remove "mainApp.exe" => it's ok
But when mainApp start, the function IsThisProcessHasRun() return true with countProcess=2, so it can not start.
And then I close Dialog's Crashed message of mainApp, IsThisProcessHasRun() false with countProcess=1, so it can start successfully!
I don't understand why? And I think if I can Dialog's Crashed message of mainApp automatically, then my problem can solve.
But maybe it is not good solution!
Someone can show me how to kill the process mainApp.exe completely?
Many thanks!
T&T