Solved

Single Instance Application

Posted on 1997-05-09
15
1,173 Views
Last Modified: 2008-03-10
I am writing single instance application. I would like to find out on the
start of application whether previous instance of application is running,
and if so, get the handle of its main window. Currently I am using
FindWindow to find out whether previous instance is running, but it has two
problems:

1. it is extremly ugly

2. I cannot run it from Delphi IDE, because FindWindow finds always the
design form opened in Delphi as a window.

Here is my code of main project file (.DPR):

program NTPopup;

uses
  Forms,
  {$IFOPT D+} _Assert, _Trace, {$ENDIF}
  Windows,
  SysUtils,
  ResIDs,
  Utils,
  PopForm in 'PopForm.pas' {NTPopupForm},
  Results in 'Results.pas' {ResultForm};

{$R *.RES}

var
  OsInfo: TOSVersionInfo;
  hPrevWnd: hWnd;

begin
  OsInfo.dwOSVersionInfoSize := SizeOf(OsInfo);
  {$IFOPT D+} Assert( {$ENDIF} GetVersionEx(OsInfo) {$IFOPT D+} ) {$ENDIF} ;
  if OSInfo.dwPlatformId = VER_PLATFORM_WIN32_NT then
  begin
    hPrevWnd := FindWindow('TNTPopupForm', 'NTPopup');
    if 0 = hPrevWnd then
    begin
      Application.Initialize;
      Application.Title := 'NTPopup';
      Application.CreateForm(TNTPopupForm, NTPopupForm);
      Application.CreateForm(TResultForm, ResultForm);
      NTPopupForm.InitializeResultsForm;
      Application.Run;
    end
    else
    begin
      if StartingMinimized then
        SendMessage(hPrevWnd, WM_MINIMIZE, 0, 0)
      else
        SendMessage(hPrevWnd, WM_RESTORE, 0, 0);
    end;
  end
  else
    Application.MessageBox(PChar(LoadStr(IDS_ONLY_NT)), PChar(LoadStr(IDS_ERROR)), MB_OK or MB_ICONHAND);
end.
0
Comment
Question by:lkovac
  • 7
  • 4
  • 2
  • +2
15 Comments
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
This is very easy to do . I will post the answer in 1 minutes ...
0
 

Author Comment

by:lkovac
Comment Utility
OK, I'll wait :-) .
0
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
Here is code that works, I have tested it just now, sorry for the delay ...

program Project1;

uses
  Forms,WinTypes,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.RES}

var
   hMapping      :THandle;
begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  hMapping := CreateFileMapping( THANDLE($ffffffff),
                                 NIL,
                                 PAGE_READONLY,
                                 0,
                                 32,
                                 'MyTestMap' );
   if( hMapping>0 ) then
     if( GetLastError() = ERROR_ALREADY_EXISTS ) then
      begin
         Application.Terminate;
      end
   else
     Application.run;

end.

PS. In delphi 1 you only need to check HPrevinst.

eg.

if (HPrevInst<>0) then
  Application.terminate
else
  Application.run.

PS. You didn't have to reject my first answer, You could just leave a comment, I hate these answer rejected messages !
0
 

Author Comment

by:lkovac
Comment Utility
The indentation I put in my example somehow failed, so maybe you didn't see: I need to get the main window handle of previous instance (look on else part of "if 0 = hPrevWnd then").

If I didn't reject your first answer, would you be able to re-answer it ? (that was the only reason I rejected it before)
0
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
Why do you need this handle.

On the rejection matter ... If you comment and do not accept I will receive a message tah you left a comment. I can then reply to your comment and you will be notified. In this way we can have an ongoing conversation until matters has been sorted out. Only reject if you are certain that the expert is incapable of helping you ...
0
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
Why do you need this handle.

On the rejection matter ... If you comment and do not accept I will receive a message tah you left a comment. I can then reply to your comment and you will be notified. In this way we can have an ongoing conversation until matters has been sorted out. Only reject if you are certain that the expert is incapable of helping you ...
0
 
LVL 1

Expert Comment

by:olmy
Comment Utility
try this

begin
  if hPrevInst <> 0 then
    GotoPreviousInstance
  else
  begin
    Application.Title := 'Cöööö';
    Application.CreateForm(Twindow_Contact, window_Contact);
    Application.Run;
  end;
end.
---------------------------------------------------------
unit PrevInst;

interface

uses
  WinTypes, WinProcs, SysUtils;

type
  PHWND = ^HWND;
  function EnumFunc(Wnd:HWND; TargetWindow:PHWND): bool; export;
  procedure GotoPreviousInstance;

implementation

  function EnumFunc(Wnd:HWND; TargetWindow:PHWND): bool;

  var
    ClassName : array[0..30] of char;
  begin
    Result := true;
    if GetWindowWord(Wnd,GWW_HINSTANCE) = hPrevInst then
       begin
       GetClassName(Wnd,ClassName,30);
       if StrIComp(ClassName,'TApplication') = 0 then
          begin
          TargetWindow^ := Wnd;
          Result := false;
          end;
       end;
  end;

  procedure GotoPreviousInstance;
  var
    PrevInstWnd : HWND;
  begin
    PrevInstWnd := 0;
    EnumWindows(@EnumFunc,longint(@PrevInstWnd));

    if PrevInstWnd <> 0 then
       if IsIconic(PrevInstWnd) then
          ShowWindow(PrevInstWnd,SW_RESTORE)
       else
          BringWindowToTop(PrevInstWnd);
  end;

end.
0
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 

Author Comment

by:lkovac
Comment Utility
I need the handle to get application to the foreground. If user starts the application and sees nothing happening, it is weird. Previous instance should start to the foreground. I also added my own specialization - it that second start is minimized, first instance should get minimized, too.

Regarding the rejection: if you answer as opposed to comment (as you did) I have the option of 5 radio buttons and I have to check one. If I leave "Excellent" checked question would be automatically closed as if I was happy with the solution.

BTW, my e-mail is lkovac@ottawa.com
0
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
You must be able to that somehow ! Users keep doing it to me if they need clarification

PS The proposed answer does not open another app but focusses the first instance ! You could also add a warning messagebox before quitting the second instance if you like
0
 

Author Comment

by:lkovac
Comment Utility
To olmy:

I know, but in Delphi 2.0 there is no hPrevInst available. How to get it is the question here.
0
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
No it isn't. Have a look at the win32 refence library. In Windows NT, and win95 for that matter, each application instance does not  have a unique hinstance ! This is because each instance is run in a seperate virtual adress space !

Having the instance does not help in this case ! That is why I proposed my answer. You can get the HPrevInst though if you need it for some reason. It is accessible from the DPR file directly.
0
 

Accepted Solution

by:
bdupras earned 100 total points
Comment Utility
gysbert1 is on the right track with File Mapping.  All you need to do is to save the information you need in the file map.

Here's what happening:
  CreateFileMapping with a file handle of $ffffffff creates a shared spot in the 2Gb - 4Gb range of Windows32 memory.  This range of memory is not protected like the lower 2Gb.  So, CreateFileMapping allocates a chunck of momory with a name given to it (ie "MyShareMemoryChunkForMyApplication"), and depending on the security flags you send in, you can allow any other application to access this memory if it requests it by name with CreateFileMapping.

So, use CreateFileMapping on your program launch, to allocate 4 bytes of memory (enough for a handle to be stored in it).  If GetLastError = ERROR_ALREADY_EXISTS, then you know that another instance of your program has alreay allocated this memory and saved whatever iformation you need in it.  If GetLastError = 0, then you know this is the first instance.  If that's the case, save whatever you want in it (such as the handle of your app or main form) and continue on like normal.



program NTPopup;

uses blah,blah,blah...

var
      hMap:THANDLE;
      pPrevious:^THANDLE;
      hPrevious:THANDLE;
      bFirstInstance:boolean;

begin
      Application.Initialize;
      Application.CreateForm(TForm1, Form1);

      hMap:=CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, sizeof(HANDLE), 'MyApp_CheckPrevious');
      if (hMap<>0) then begin
            bFirstInstance:=GetLastError<>ERROR_ALREADY_EXISTS;
            pPrevious:=MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0);
            if bFirstInstance
                  then pPrevious^:=Form1.Handle
                  else hPrevious:=pPrevious^;
            UnMapViewOfFile(pPrevious);
            pPrevious:=nil;
      end else begin
            //error detecting previous instance, run anyway?  Sure - why not...
            bFirstInstance:=true;
      end;

      if bFirstInstance then begin
            Application.Run;
            CloseHandle(hMap);
      end else begin
            SendMessage(hPrevious, WM_RESTORE, 0, 0)
            CloseHandle(hMap);
            Application.Terminate;
      end;
end.
0
 
LVL 3

Expert Comment

by:sperling
Comment Utility
lkovac:
If you look in comp.lang.pascal.delphi.misc, I've just posted an unit there using the file mapping approach, which also passes commandline parameters to the primary instance. Posting title is "How to limit one instance of Delphi app from being run?"
The solution is rather similar to the one given by bdupros, but it does not require you to edit the project source and it'll switch focus somewhat quicker. I also suspect bdupros solution might cause synchronization trouble in between the Application and the MainForm, as Delphi progs don't handle restoring, minimizing and maximizing like they should.

gysbert1:
You have a very annoying habit of rushing to give a bad answer, telling the one who asked that he/she should not reject. Then, while the question is locked, you works out an answer and gives this as a comment. This might be a nice approach to getting a lot of points, but it is *not* for the best of those asking the questions, as they have to try out your (often incorrect) answers, discuss for some days and then reject the answer before any of the experts who knows a simple solution to the problem can provide it.

Also, there is something called a "Previously Asked Question" on experts exchange, which contains the question and the accepted answer but not the comments. People spend points to see these questions, and therefore the accepted answer should contain as much relevant information as possible.

Your first answer to this question is just plain stupid. Who are you to reserve all questions for yourself? Are you maybe not skilled enough to compete with the rest of us without dirty tricks?

"Only reject if you are certain that the expert is incapable of helping you ... "

That's not correct. If you've bothered to read the FAQ, you'll see that a rejection should be given if an answer is incorrect. A rejection is not given as an evaluation of the *expert*, but as an evaluation of the experts *answer*

If the answer needs more clarification or small modifications, the commenting approach should be used.

I'd suggest you started to
*first* read the question properly.
*second* work out an answer
*third* test the answer as far as your hardware/software allows
*finally* post the complete answer

If you'd like to discuss this any further, let me know and I'll post a question which you answers, and after grading it we'll be quite private. Or, you could email me at erik@info-pro.no


Erik.

0
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
Sperling:
I'm sorry I resorted to this, but I don't see why you say its a habit since its the first time I did this. I've noticed Javiertb and Heapster doing it, and they were both logged on when this question was posted. I knew how to answer it, so I "booked" it. I won't do it again, I realise it is childish and I apologise for my bad behaviour. I have however given a suggestion to experts-exchange that they should think about changing the system so that there isn't an answer as such, but that everybody can comment, and the person who asked the question can pick the comment that has the best answer. A couple of time it has happened that someone posts an incorrect answer, and I've commented to give the correct or a better answer, but the original guy still gets the points.... What is your opinion on this?

0
 
LVL 3

Expert Comment

by:sperling
Comment Utility
gysbert1:

oops.. I'm truely sorry. I've gotten more and more irritated with this way of answering questions, and when I finally blew, I hit the wrong person. I've looked a bit around, and it was in fact javiertb I was irritated with... Again, I'm sorry.

I tend to agree that the method currently used to answer and evaluate is not good enough. But it is hard to find a solution that fits all needs, and picking a comment would probably lead to a lot of actually answered but still ungraded questions floating around. AFAIK experts exchange is planning to grade answered questions which have been silent for a couple of weeks automatically, and if there are multiple answers to choose from, automation won't do. I can't come up with some better scheme, though.

Regards,

Erik.



0

Featured Post

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now