Kann ein konsolenprogramm ein WM_CLose auf seine Shell abfangen?



  • Hallo,

    ich habe ein reines C Konsolenprogramm nach Windows portiert. Es wird von einer anderen Applikation im Hintergrund gestartet und soll von dieser Applikation auch wieder geschlossen werden.

    Diese Applikation startet einfach C:\foobar.exe worauf ein CMD-Fenster aufgeht und in dem läuft mein C-Programm. Alles fein. Jetzt will die Applikation das Programm irgendwann wieder schließen.

    Allerdings wird das Programm bzw die Shell in der es läuft, nicht geschlossen. Ich habe daher einen Signalhandler auf so ziemlich alle POSIX-Signale installiert und empfange kein einziges. Eine Nachfrage beim Hersteller der Applikation förderte nun zutage, dass sie eine "WM_Close" message sendet.

    Da das wohl eine Windows-message ist, kann ich sie in einem reinen Konsolenprogramm wohl nicht abfangen.

    Mein Frage ist daher: Kann ich irgendein minimales, möglichst noch nicht-sichtbares Fenster erzeugen (vielleicht eine Message-Box), das auf die WM-Close message hört, so dass ich dann mein Konsolenprogramm kontrolliert herunterfahren kann?

    Gruß,
    Phil


  • Mod

    Siehe SetConsoleCtrlHandler Function
    http://msdn.microsoft.com/en-us/library/ms686016(VS.85).aspx

    Ansonsten:
    - Erzeuge eine UI Programm (kein Consolen Programm),
    - Erzeuge ein verstecktes Fenster mit einem Dir bekannten Fensterklssennamen
    - Sende an dieses Fenster, dass Du mit FindWindow findet WM_CLOSE
    - In diesem Fenster löst Du das schließen der Anwendung aus.



  • Martin Richter schrieb:

    Siehe SetConsoleCtrlHandler Function
    http://msdn.microsoft.com/en-us/library/ms686016(VS.85).aspx

    Probiert, geht nicht. Scheinbar löst das WM_CLOSE kein Ctrl-Break oder Ctrl-C Event aus.

    Ansonsten:
    - Erzeuge eine UI Programm (kein Consolen Programm),
    - Erzeuge ein verstecktes Fenster mit einem Dir bekannten Fensterklssennamen
    - Sende an dieses Fenster, dass Du mit FindWindow findet WM_CLOSE
    - In diesem Fenster löst Du das schließen der Anwendung aus.

    Gibts dafür eine fertige Lösung? Wieviel Zeilen sind das etwa?
    Ich habe noch nie WinAPI programmiert, ich bin Unixxer ...


  • Mod

    Aber sicher wird der aufgerufen. Lies doch die Doku:
    http://msdn.microsoft.com/en-us/library/ms683242

    CTRL_CLOSE_EVENT
    2 A signal that the system sends to all processes attached to a console when the user closes the console (either by clicking Close on the console window's window menu, or by clicking the End Task button command from Task Manager).

    Du kannst mit dem VS Wizard Dir den Rahmen für solch ein Gui Programm erzeugen lassen. Dann musst Du höchstens 10 Zeilen ändern...



  • Martin Richter schrieb:

    Aber sicher wird der aufgerufen. Lies doch die Doku:
    http://msdn.microsoft.com/en-us/library/ms683242

    Genau das habe ich gemacht. Und ja, der Cntrl-Close handler wird auch aufgerufen, wenn ich das "X" am consolenfenster anklicke.
    Nur leider scheint die Applikation, die das Konsolenfenster schließen soll, dieses nicht schließen zu können, weil sie keinen Window-handle darauf findet (ist meine Vermutung, siehe hier: http://support.microsoft.com/kb/124103/en-us)

    Du kannst mit dem VS Wizard Dir den Rahmen für solch ein Gui Programm erzeugen lassen. Dann musst Du höchstens 10 Zeilen ändern...

    Ach du schei....
    Ich habe mir jetzt mal von VC++ 2010 den Boilerplate für eine Win32 Applikation erzeugen lassen. Heilige Mutter Gottes, das sind zusammen fast 400 Zeilen !!!! Geht's noch?

    Ich habe das ganze jetzt mal für mein Programm auf folgenden Code reduziert:

    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    
    #define MAX_LOADSTRING 100
    #define IDI_TEST			107
    #define IDI_SMALL			108
    #define IDC_TEST			109
    HINSTANCE hInst;
    TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
    TCHAR szWindowClass[MAX_LOADSTRING];
    
    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
       HWND hWnd;
    
       hInst = hInstance; // Store instance handle in our global variable
    
       hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
          CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
    
       if (!hWnd)
       {
          return FALSE;
       }
    
       ShowWindow(hWnd, nCmdShow);
       UpdateWindow(hWnd);
    
       return TRUE;
    }
    
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
        case WM_CLOSE:
            Log() << Log::Info << "Closing" << Log::endl;
            // Hier das tun, was ich eigentlich tun will beim schließen
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    
    ATOM MyRegisterClass(HINSTANCE hInstance)
    {
        WNDCLASSEX wcex;
    
        wcex.cbSize = sizeof(WNDCLASSEX);
    
        wcex.style			= CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc	= WndProc;
        wcex.cbClsExtra		= 0;
        wcex.cbWndExtra		= 0;
        wcex.hInstance		= hInstance;
        wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TEST));
        wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
        wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
        wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_TEST);
        wcex.lpszClassName	= szWindowClass;
        wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
        return RegisterClassEx(&wcex);
    }
    
    int APIENTRY _tWinMain(HINSTANCE hInstance,
                         HINSTANCE hPrevInstance,
                         LPTSTR    lpCmdLine,
                         int       nCmdShow)
    {
        MSG msg;
        MyRegisterClass(hInstance);
    
        // Perform application initialization:
        if (!InitInstance (hInstance, nCmdShow))
        {
            return FALSE;
        }
    
        // Main message loop:
        while (GetMessage(&msg, NULL, 0, 0))
        {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
                // hierdrin meine eigentliche Main-Funktion aufrufen...
        }
    
        return (int) msg.wParam;
    }
    

    Das kompiliert zwar, gibt aber folgenden Linker error:

    /mingw/lib/libmingw32.a(main.o)(.text+0x106):main.c: undefined reference to `WinMain@16'
    collect2: ld returned 1 exit status
    

    Boah... Was eine gequirlte Kacke um das zu tun, was unter Unix die Zeile

    signal(SIGTERM, &my_signal_handler);
    

    macht. 👎



  • Okay, es ist wohl keine so gute Idee, von Visual Studio erzeugten Code mit minGW zu kompilieren.

    Ich habe jetzt stattdessen diesen Code hier benutzt : http://osix.net/modules/article/?id=670

    Damit habe ich das Problem, dass er bei allen String-Konstanten folgenden Fehler ausgibt:

    error: cannot convert `const char*' to `WCHAR*' in initialization
    

    EDIT: Gelöst. Man muss L"Hello World" statt "Hello World" schreiben.


Anmelden zum Antworten