🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: borland.public.cppbuilder.nativeapi
Article #6017

Re: kill process?

#6017
From: JD
Date: Sat, 18 Jun 2005 19:27
192 lines
8967 bytes
"Ron Eggler @ work" <nospam@hotmail.com> wrote:
>
> [...] I moved that code in the WinMain()

The problem with this implemetation, you have to edit the
WinMain and override the main form's WndProc method everytime
you want to use it.

Below is a class that you can convert into a component that
you drop onto the main form or you can simply add the class to
your project and dynamically allocate an instance of it at run
time.

The only thing that the class needs is that your main form
has a unique name so that it doesn't get confused with other
applications so name the main form something like
CompanyNameApplicationName.

If you don't like that idea, then examine the code and use it
to figure out how to modify the WinMain:

//-------------------------------------------------------------
#ifndef SingleInstanceH
#define SingleInstanceH
//-------------------------------------------------------------
#include <Classes.hpp>
//-------------------------------------------------------------
class TSingleInstance : public TComponent
{
    private:
        bool FHooked;
        bool FMinimized;
        HANDLE hMutex;
        HHOOK WndReturnHook;
        unsigned int SI_CUSTOM_MESSAGE;
        TForm* FParentForm;
    protected:
        bool __fastcall AppWndProcHook( TMessage &Message );
        static int __stdcall CallWndRetCallBackProc( int Code, WPARAM WParam, LPARAM LParam );
    public:
        __fastcall TSingleInstance(TComponent* Owner);
        __fastcall TSingleInstance::~TSingleInstance();
    __published:
};
//-------------------------------------------------------------
#endif

//-------------------------------------------------------------
#include <vcl.h>
#include <basepch.h>
//-------------------------------------------------------------
#pragma hdrstop
//-------------------------------------------------------------
#include "SingleInstance.h"
#pragma package(smart_init)
//-------------------------------------------------------------

TSingleInstance* ThisSingleInstance = NULL;
HWND hFirstInstance, hSecondInstance;
//-------------------------------------------------------------
BOOL CALLBACK EnumWindowsCallBack( HWND hWnd, LPARAM lParam )
{
    char WindowClassName[256] = {0};
    if( ::GetClassName(hWnd, WindowClassName, 255) )
      {
          if( stricmp(WindowClassName, (char*)lParam) == 0)
            {
                if( hFirstInstance )
                  {
                      hSecondInstance = hWnd;
                      return FALSE;
                  }
                else hFirstInstance = hWnd;
            }
      }              
    return TRUE;
}
//-------------------------------------------------------------
__fastcall TSingleInstance::TSingleInstance(TComponent* Owner) : TComponent(Owner)
{
    if( ThisSingleInstance ) throw Exception("TSingleInstance : Only one instance of TSingleInstance is allowed per application.");
    if( !Owner ) throw Exception("TSingleInstance : You must assign an Owner.");
    FParentForm = dynamic_cast<TForm*>(Owner);
    if( !FParentForm ) throw Exception("TSingleInstance : The Owner must be a TForm.");
    ThisSingleInstance = this;

    FHooked = false;
    hMutex = NULL;
    WndReturnHook = NULL;

    if( !ComponentState.Contains(csDesigning) )
      {
          char ThisClassName[ 256 ] = { 0 };
          ::GetClassName( FParentForm->Handle, ThisClassName, 255 );
          SI_CUSTOM_MESSAGE = ::RegisterWindowMessage( ThisClassName );
          hMutex = ::CreateMutex( NULL, TRUE, ThisClassName );
          if( GetLastError() == ERROR_ALREADY_EXISTS )
            {
//::ShowWindow( Application->Handle, SW_HIDE );            
                Application->ShowMainForm = false;
                hFirstInstance = hSecondInstance = NULL;
                ::EnumWindows( (WNDENUMPROC)EnumWindowsCallBack, (LPARAM)ThisClassName );
                if( hFirstInstance == FParentForm->Handle ) hFirstInstance = hSecondInstance;
                ::SendMessage( hFirstInstance, SI_CUSTOM_MESSAGE, 0, 0 );
                ::SetForegroundWindow( hFirstInstance );
                Application->Terminate();
            }
          else
            {
                Application->HookMainWindow( AppWndProcHook );
                FHooked = true;
                WndReturnHook = ::SetWindowsHookEx( WH_CALLWNDPROCRET, (int (__stdcall*)())CallWndRetCallBackProc, HInstance, ::GetCurrentThreadId() );
                ::PostMessage( Application->Handle, SI_CUSTOM_MESSAGE, 0, 0 );
            }
      }
}
//-------------------------------------------------------------
__fastcall TSingleInstance::~TSingleInstance()
{
    if( FHooked ) Application->UnhookMainWindow( AppWndProcHook );
    if( WndReturnHook ) ::UnhookWindowsHookEx( WndReturnHook );
    if( hMutex ) ::CloseHandle( hMutex );
    ThisSingleInstance = NULL;
}
//-------------------------------------------------------------
bool __fastcall TSingleInstance::AppWndProcHook( TMessage &Message )
{   /*
    There is a problem with windows correctly recording the window state
    when the form's exStyle does not include WS_EX_APPWINDOW. To be more
    exact, without WS_EX_APPWINDOW, when the form is minimized, windows will
    report the last state prior to minimizing so API's such as IsIconic will
    fail to detect the correct window state. This is important because if
    one blindly restores the first instance when it is already in a restored
    state, it will exibit strange behavior when the user tries to minimize
    the first instance.

    The only way around this is to not manipulate the first instance's window
    state directly and instead monitor the application's window state so that
    the first instance can determine what it wants to do when it recieves a
    custom restore message. */

    if( Message.Msg == WM_SYSCOMMAND )
      {
          switch( Message.WParam )
            {
                case  SC_RESTORE:
                case SC_MAXIMIZE: FMinimized = false; break;
                case SC_MINIMIZE: FMinimized = true;
            }
      }
    else if( Message.Msg == SI_CUSTOM_MESSAGE )
      {
          // SI_CUSTOM_MESSAGE only reaches here once and is used to complete
          // initialization. I could have used a seperate message but there was
          // no need to register a seperate message.
          if( FParentForm != Application->MainForm )
            {
                // The application is already running at this point. I had to wait
                // to check if the object was owned by the main form and this is
                // important because this hooks into the owner and it needs to be
                // hooked into the main form.
                MessageDlg("TSingleInstance : Object can only be placed on the Main Form.", mtError, TMsgDlgButtons() << mbOK, 0);
                Application->Terminate();
            }
          else
            {
                if( Application->MainForm->WindowState == wsMinimized ) FMinimized = true;
                else FMinimized = false;
            }
      }
    return false;
}
//-------------------------------------------------------------
int __stdcall TSingleInstance::CallWndRetCallBackProc( int Code, WPARAM WParam, LPARAM LParam )
{
    if( Code == HC_ACTION )
      {
          CWPRETSTRUCT* cwp = (CWPRETSTRUCT *) LParam;
          if( cwp->message == ThisSingleInstance->SI_CUSTOM_MESSAGE )
            {
                if( ThisSingleInstance->FMinimized )
                  {
                      ::SendMessage( Application->Handle, WM_SYSCOMMAND, SC_RESTORE, 0 );
                  }
            }
      }
    return ::CallNextHookEx( ThisSingleInstance->WndReturnHook, Code, WParam, LParam );
}
//-------------------------------------------------------------

~ JD
Technical Details
Message-ID: <42b4758c$1@newsgroups.borland.com>
Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!news.glorb.com!border1.nntp.dca.giganews.com!border2.nntp.dca.giganews.com!nntp.giganews.com!meganewsservers.com!feeder2.on.meganewsservers.com!news-out.newsgroups.com!propagator2-sterling.newsfeeds.com!in.nntp.be!caladan.landsraad.net!newsgroups.borland.com!not-for-mail
References: <42b02536$1@newsgroups.borland.com> <42b1635a@newsgroups.borland.com> <42b184e4@newsgroups.borland.com>

Show Full Headers
Complete Article Headers
X-Google-Thread: 10cc67,d9980975a6334c47
X-Google-Attributes: gid10cc67,public
X-Google-Language: ENGLISH,ASCII-7-bit
From: "JD" <nospam@nospam.com>
Sender: "JD" <nospam@nospam.com>
Reply-To: "JD" <nospam@nospam.com>
Subject: Re: kill process?
Newsgroups: borland.public.cppbuilder.nativeapi
X-User-Info: 205.188.116.135     
X-Forwarded-For: 172.135.47.49
References: <42b02536$1@newsgroups.borland.com> <42b1635a@newsgroups.borland.com> <42b184e4@newsgroups.borland.com>
NNTP-Posting-Host: 207.105.83.66
Message-ID: <42b4758c$1@newsgroups.borland.com>
Date: 18 Jun 2005 12:27:08 -0700
X-Trace: newsgroups.borland.com 1119122828 207.105.83.66 (18 Jun 2005 12:27:08 -0700)
Lines: 193
Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!news.glorb.com!border1.nntp.dca.giganews.com!border2.nntp.dca.giganews.com!nntp.giganews.com!meganewsservers.com!feeder2.on.meganewsservers.com!news-out.newsgroups.com!propagator2-sterling.newsfeeds.com!in.nntp.be!caladan.landsraad.net!newsgroups.borland.com!not-for-mail