******************************************************************************** * Program : INSTANCE * Launch INSTANCE.EXE * Aim : Show how to program Applications using Windows traditional manner, * ..........: with NTKCORE GUI (low level) set of functions for X/HARBOUR. * ..........: Demonstrates how to allow only ONE INSTANCE of an application * Make......: Juste type MK INSTANCE * Date : 27/04/06 * Author(s) : Jn DECHEREUX * Copyright : (c) 2002-2006 - Jn DECHEREUX. Tous droits r‚serv‚s/All Rights Reserved. ******************************************************************************** * Remark: * I have to confess that English is not mother tongue. So, in case of * typo or misspelling, don't be too angry - just bear with me. * You have to know that i'll do consider any comment, correction, suggestion * with great pleasure and high kindliness. Just be polite ! ;-) * This remark is also available for bug notifications or any other kind of * contributions that can improve my product... ******************************************************************************** #include "windows.ch" #include "ntkdll.ch" #include "ntkgdi.ch" #include "ntkmsg.ch" #include "ntkacc.ch" #include "wNtk.ch" STATIC aDemoFont1 STATIC hDemoFont1 FUNCTION MAIN() LOCAL cMsg LOCAL NTK_aMSG := { 0,0,0,0,0,0,0 } // 7 elements, see NTKMSG.CH LOCAL hInst := NTK_GetInstance() LOCAL hIcon := NTK_LoadIcon( Nil , IDI_APPLICATION) LOCAL hCurs := NTK_LoadCursor( Nil , IDC_ARROW) LOCAL hBrush := NTK_GetStockObject(WHITE_BRUSH) //LTGRAY_BRUSH) LOCAL cWinTitle := "Single INSTANCE demonstration with NTKCore for X/HRB !" LOCAL hWndDemo, hWndPrevInst := Nil IF !NTK_RegisterClassEx( CS_HREDRAW + CS_VREDRAW,; hInst,; hIcon,; hCurs,; hBrush,; "NTKFEN",; { |hWnd, message, nwParam, nlParam|; MAINWNDPROC(hWnd, message, nwParam, nlParam) } ) NTK_MsgBox( , "Can't register NTKFEN class..." ) RETURN Nil ENDIF hWndDemo = NTK_CreateWindowEx( WS_EX_WINDOWEDGE, "NTKFEN", cWinTitle, ; WS_SYSMENU, ; CW_USEDEFAULT, CW_USEDEFAULT,; 640, 480 ) IF hWndDemo == 0 NTK_MsgBox( , "Can not create MAIN Window..." ) RETURN Nil ENDIF IF !IsSingleInstance( cWinTitle, hWndDemo ) NTK_MsgBox( , "Sorry, but another Instance of " +chr(13)+ UPPER(cWinTitle) +CHR(13)+ "is already running !",; "ALERT",; MB_OK+MB_ICONEXCLAMATION ) NTK_DestroyWindow( hWndDemo ) If ( hWndPrevInst := NTK_FindWindow( , cWinTitle) ) != 0 NTK_SetForegroundWindow(hWndPrevInst) // Bring back the original instance to the top (when possible)! *NTK_SelectWindow(hWndPrevInst) *NTK_SetFocus(hWndPrevInst) EndIf RETURN Nil ENDIF aDemoFont1 := { -48,12,0,0,700, .F., .F., .F., 1, 0, 0, 0, 0, "ARIAL" } hDemoFont1 := NTK_CreateFont( aDemoFont1 ) IF hDemoFont1 == 0 NTK_MsgBox( , "Can not create DemoFont1..." ) RETURN Nil ENDIF NTK_ShowWindow( hWndDemo, SW_SHOW ) NTK_UpdateWindow( hWndDemo ) // ---------------------------------- NTK's Main Events (Windows Msg) Loop DO WHILE NTK_GetMessage( NTK_aMSG, 0 ) //,hWndDemo,0,0 ) NTK_TranslateMessage( NTK_aMSG ) NTK_DispatchMessage( NTK_aMSG ) ENDDO NTK_DeleteObject( hDemoFont1 ) NTK_SelectWindow( hWndDemo ) NTK_DestroyWindow( hWndDemo ) NTK_UnregisterClass( "NTKFEN", hInst ) RETURN ****** ****** ****** FUNCTION MAINWNDPROC( hWnd, message, nwParam, nlParam) LOCAL aPS := { 0,.T.,0,0,0,0,.T.,.T.,nil } // 9 elements, see NTKGDI.CH LOCAL hDC := 0 LOCAL nRed := NTK_RGB(255,0,0) LOCAL hOldFont DO CASE CASE message == WM_CHAR IF nwParam==27 // K_ESC IF NTK_MsgBox( hWnd,; "Do you really want to quit ?",; "Your Attention Please !",; MB_OKCANCEL+MB_ICONQUESTION ) == IDOK NTK_PostQuitMessage(0) ENDIF ENDIF CASE message == WM_RBUTTONDOWN IF NTK_MsgBox( hWnd,; "Do you really want to quit ?",; "Your Attention Please !",; MB_OKCANCEL+MB_ICONQUESTION ) == IDOK NTK_PostQuitMessage(0) ENDIF CASE message == WM_SYSCOMMAND IF nwParam == SC_CLOSE // system menu double click, or Alt-F4 IF NTK_MsgBox( hWnd,; "Do you really want to quit ?",; "Your Attention Please !",; MB_OKCANCEL+MB_ICONQUESTION ) == IDOK NTK_PostQuitMessage(0) ENDIF RETURN(0) // According to M$ Win32 SDK Doc. We tell Windows we've processed the message ENDIF CASE message == WM_PAINT hDC := NTK_BEGINPAINT( hWnd, aPS ) NTK_TextOut( hDC, 100, 400, "Press Esc or hit right mouse button to quit..." ) NTK_DrawText( hDC,; "The main goal of this proggy is to demonstrate how to "+Chr(13)+; "avoid an application to be run twice."+Chr(13)+Chr(13)+; "Feel free to use it in both your NTK Core and RAD developements!",; {0,50,200,350}, DT_CENTER+DT_VCENTER ) hOldFont := NTK_SelectObject( hDC, hDemoFont1 ) NTK_SetTextColor( hDC, nRed ) NTK_SetBkMode( hDC, TRANSPARENT ) NTK_TextOut( hDC, 300, 100, "HEY, my little boy !" ) NTK_SelectObject( hDC, hOldFont ) NTK_DrawText( hDC,; "Just try (if you can) to launch another instance of this program "+; "while this one is already running ...",; {300,160,500,230}, DT_CENTER+DT_VCENTER ) NTK_ENDPAINT( hWnd, aPS ) RETURN(0) CASE message == WM_DESTROY NTK_PostQuitMessage(0) RETURN(0) ENDCASE RETURN( NTK_DEFWNDPROC(hWnd, message, nwParam, nlParam) ) //RETURN -1 // OR NTK_DEFWNDPROC( ... ) is the same ! ****** ****************************************************************************************** ****** ****** ****** ****** ****************************************************************************************** ****** This function allows to detect if there is already a previous running instance of the app. ****** As you can see, there is no mystery about how things are done... ****** ****** Note: Some of the needed functions are not include yet in NTKCORE.LIB, So, they have been ****** imported via NTK's DLL Wrapper command : Declare function... or _DLL function... ****** ****** ****************************************************************************************** FUNCTION IsSingleInstance( cWinTitle, hMainWnd ) LOCAL nAt, cTextBuf, nTxtLen LOCAL hWnd := GetTopWindow(Nil) LOCAL lSingleInst := .T. IF !VALTYPE(hMainWnd)=="N" hMainWnd := 0 ENDIF DO WHILE( hWnd != 0 ) IF ( hWnd != hMainWnd .AND. NTK_GetWindow(hWnd, GW_OWNER) == 0 .AND. ; ( nTxtLen := IIF(NTK_IsWindow(hWnd), GetWindowTextLength(hWnd), 0) ) > 0 ) cTextBuf := SPACE(nTxtLen+1) GetWindowText(hWnd, @cTextBuf, nTxtLen+1) nAt := AT(cWinTitle, cTextBuf) If ( nAt > 0 ) lSingleInst := .F. // There is an already existing instance. EXIT // So, inform the caller this is not the first instance... We're not alone! ;-) EndIf ENDIF hWnd := NTK_GetWindow(hWnd, GW_HWNDNEXT) ENDDO RETURN( lSingleInst ) ****** ****** ****** ****** GetWindowTextLength() WRAPPER. ****** Usage : GetWindowTextLength( hWnd ) -> nTextLen ****** Included: In User32.DLL ****** // --------- NTK declaration style... Declare function GetWindowTextLength Lib User32.GetWindowTextLengthA ( hWnd As HWND ) AS int // The folowing syntaxe works same as the above. Do not hesitate to try it ! // It has been created for NTK's backward compatibility with 16 bit Clip4Win apps. // Just Look at NTKDLL.CH for further details... // --------- C4W like declaration style... // _DLL function GetWindowTextLength(hWnd AS HWND) AS int Pascal:User32.GetWindowTextLengthA ****** ****** ****** ****** GetWindowText() WRAPPER. ****** Usage : GetWindowText( hWnd, cTxtBuf, nMaxChar ) -> nTxtBufLen ****** Included: In User32.DLL ****** ****** Extra Doc : ****** HWND hWnd // handle of window or control with text ****** LPTSTR cTxtBuf // address of buffer for text ****** int nMaxChar // maximum number of characters to copy // --------- NTK declaration style... Declare function GetWindowText Lib User32.GetWindowTextA ; ( hWnd As HWND, cTxtBuf As STRING, nMaxChar As INT ) AS int // Ok, Ok, i know, you should say that 'usage of NTK_GetWindowText(hWnd)' is a better and faster way ! // I have to admit I wrapped it first for fun, but also to check if NTK's DLL wrapper is correctly working... ****** ****** ****** ****** GetTopWindow() WRAPPER. ****** Usage : GetTopWindow( hWndParent ) -> hWndChild ****** Included: In User32.DLL ****** // --------- NTK declaration style... Declare function GetTopWindow Lib User32.GetTopWindow ( hWnd As HWND ) AS hwnd // The folowing syntaxe works same as the above. Do not hesitate to try it ! // It has been created for NTK's backward compatibility with 16 bit Clip4Win apps. // Just Look at NTKDLL.CH for further details... // --------- C4W like declaration style... // _DLL function GetTopWindow(hWnd AS HWND) AS int Pascal:User32.GetTopWindowA ****** ****** ******