finding other instance of same application... Borland C++ Builder 4

What I'm trying to do is make an application that, when launched for a second time, will detect a previous instance of that application and bring it to the front then close (since the second instance is not needed).

I have code that does this with my older compiler, but it's not working now with Borland C++ Builder 4.

Here is the previous code:
prevhwndMain = FindWindow("TApplication",NULL);

if (prevhwndMain)
{
  if (IsWindow(prevhwndMain))
  {
    if (BringWindowToTop(prevhwndMain))
     {
      ShowWindow(prevhwndMain,SW_SHOWMAXIMIZED);
     }
    else
     MessageBox (NULL, "UNABLE TO BRING TO FRONT", szAppTitle, MB_ICONEXCLAMATION | MB_OK);
  }
 else
  MessageBox (NULL, "INVALID WINDOW HANDLE", szAppTitle, MB_ICONEXCLAMATION | MB_OK);
 return(FALSE);
}


Now I'm pretty sure the other code will still work, except for what's going on with FindWindow.  That has to change.  After playing with it for a while I determined that the class name was TApplication (I could be wrong though...).  However, I can't seem to figure out the window name.  I tried Application->Name (then converted to a char *), but that always got an empty string.

Please help!

-Josh
LVL 8
jbirkAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

edeyCommented:
hmmm....

FindWindow("TApplication",NULL);

should find any window of the class name "TApplication", is this not sufficient?  anyway, the WM_GETTEXT message should retrieve the window's caption, from the win32.hlp:

"For other windows, the text is the window title."


GL
Mike
0
jbirkAuthor Commented:
The problem with finding any window with class name TApplication is that that finds any window made with Borland C++ Builder 4 and possibly even more.  When I ran it like that I think it even grabbed the desktop as that same class type, because it focused the desktop and closed...

Besides, the application is programmed by me, so shouldn't I have the application name available to me somehow?  I tried the caption (or what goes into the title bar), the name of the project, the value I actually put into the name field for the main form.  I also tried the name of the main form.  None of these worked.  So I tried grabbing that name at run time to see what it is and alerting it.  Except I'm getting an empty string.  Here's the code I used for that:

char *s = Application->Name.c_str();
char buffer[80];
sprintf(buffer,"name:%s",s);
MessageBox (NULL, buffer, szAppTitle, MB_ICONEXCLAMATION | MB_OK);

Application is my instance of the TApplication class.  It's automatically created...

Any ideas?

-Josh
0
DKostovCommented:
prevhwndMain = FindWindow(NULL,"The Caption of The Main Window of your application");
If this doesn't work you did something wrong.
0
Cloud Class® Course: Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

VendiCommented:
I'm using FindWindow in C++ Builder 4 without any problems.

HANDLE hwnd = FindWindow(NULL,"MainFormCaption");  

Ideas for finding the caption:

int length = GetWindowTextLength(hwnd);

// use this length to allocate a buffer for

GetWindowText(hwnd,buffer,length + 1);

If you used these with EnumWindows, it might help you figure out what caption you should be searching for.  
0
LischkeCommented:
I don't know why almost always FindWindow and such stuff is recommended. This is unreliable and unflexible.

You just need to create a named object which can be shared between applications. Everytime you start the application it tries to create this object and if it exists it knows that there must be another instance already running. Actually you have several objects at your disposal. One is a Mutex. But this doesn't allow to share also data between the application instances (like command line parameters which might contain names of files which must be opened in the first instance). The best solution I found so far is to use memory mapped files. They can also have a name and allow to share memory. This is also useful if you want to restore the first instance if it is minimized. Just create the file mapping on application startup and if it is the first instance then place the application's main window handle in the mapping (the mapping is of course created in the paging file, so you don't need another physical file). If the application is already the second instance then it can use the stored window handle to restore the first application.

Ciao, Mike
0
jbirkAuthor Commented:
Adjusted points to 100
0
jbirkAuthor Commented:
Thanks everyone for all the input.  I like the suggestion Lischke, but it would take me too long, and I don't have much time.  Just wondering, though, what happens if the program or the OS crashes?  Is the file still there saying that an instance is open?  I guess simply checking if the file handle is valid is enough to determine if a crash occured...  Anyway, I almost have the other solution working.

The only problem left, is that I can't figure out how to restore a minimized window properly without maximizing it.  The code I had started with is posted at the beginning.  It maximized the window when it became focused.  I would prefer to restore the window to it's previous state.  Is this possible?  Using ShowWindow with some different options I was able to get the window to go back to it's previous state, but then it didn't become focused, AND it would no longer minimize?

Thanks for all the help!
-Josh
0
VendiCommented:
use SW_RESTORE instead of SW_SHOWMAXIMIZED
0
LischkeCommented:
Yep, Vendi said it...:-)

And for a crashing application: Windows will close all handles of such an application including those for memory mapped files.

Ciao, Mike
0
DKostovCommented:
sorry for that post. It's only to exclude myself from the automated mail system because something is broken there
and I received 100+ e-mails for this Q
0
MadshiCommented:
Josh, Mike's suggestion does not create a real file. If you use CreateFileMapping(-1, ...), you get a named file mapping object without a "real" file being created. Windows uses the swap file.
0
mikeblasCommented:
PLease don't create a file--even if it's a memory-mapped one.  Lishke didn't even suggest that, so I don't know where it came from.

Just create a lightweight named object, like an event or a mutex.

See the ONLYONE sample in my book for a very complete example.

..B ekiM

0
mikeblasCommented:
Oh, Lishke _did_ suggest that! I confused his comment with someone elses'.

Look: a memory mapped file for this use is a waste of resources.  Please, use something more efficient, unless you really _need_ to exchange some data. (Even then, I think you're far better off creating the file only in the event you really need to.)

..B ekiM
0
jbirkAuthor Commented:
Wow, the response on this question has been overwhelming!  I hardly have time to even read all the comments, let alone try them all.

First thing is first.  The SW_RESTORE option for ShowWindow is doing something funky.  It's restoring it back to it's previous state before being minimized, BUT it somehow disables the window's ability to then minimize again.  If I click the minimize button it animates (the button depresses), but nothing happens, same when clicking on the icon on the left and choosing minimize from the menu.  Here is the exact code I'm using right now (this is at the beginning of WinMain):

      prevhwndMain = FindWindow(NULL,"Hydroworks 3.0");

      if (prevhwndMain)
      {
            if (IsWindow(prevhwndMain))
            {
                ShowWindow(prevhwndMain,SW_RESTORE);
            SetForegroundWindow(prevhwndMain);
            }
            else
                   MessageBox (NULL, "INVALID WINDOW HANDLE", szAppTitle, MB_ICONEXCLAMATION | MB_OK);

            return(FALSE);
      }


Now, the other idea about using an object or mutex sounds great, but I have no idea how to do it.  I'm new to Windows programming, in case no one's noticed yet.  I'm familiar with mutual exculsion and shared memory as a theory (learned it in a class for Unix...), but have never covered it for the windows operating system.  Can someone supply some code for an example?  All I want it to do is somehow set this variable (with the window handle) so any new process starting up will see it and know to focus it and quit.

I will raise the points again for a more complete response.

Thanks again for all the help everyone!

-Josh
0
MadshiCommented:
MikeBlas, of course a mutex or event consumes less resources than a file mapping object, but how can the second app instance know the window handle of the first app instance when using e.g. a mutex? The 2. instance then only knows THAT there is another instance, but it doesn't know how to bring it to front!

Regards, Madshi.
0
MadshiCommented:
Hmmm... I'm even not sure if a memory mapped file really consumes so much more resources than a mutex, see, it's only one handle and 4 bytes in the swap file.

Josh, two examples from me. They are in Delphi, but I guess, that's no problem for you...

(1) This one uses a mutex and is VERY simple. It doesn't bring the first instance's window to the front, though:

  if (CreateMutex(nil, false, 'JoshRunOnceMutex') = 0) or (GetLastError = ERROR_ALREADY_EXISTS) then
    ExitProcess(0);

(2) Here comes the more complicated one. It's using a memory mapped file and does bring the original instance to the front:

procedure RunOnce(thisInstancesMainWindowHandle: dword);
var mapHandle : dword;
    window    : ^dword;
begin
  mapHandle := CreateFileMapping(dword(-1), nil, PAGE_READWRITE, 0, 4, 'JoshsRunOnceMap');
  if GetLastError = ERROR_ALREADY_EXISTS then begin
    window := MapViewOfFile(mapHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
    ShowWindow(window^, SW_RESTORE);
    SetForegroundWindow(window^);
    ExitProcess(0);
  end else begin
    window := MapViewOfFile(mapHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
    window^ := thisInstancesMainWindowHandle;
    UnmapViewOfFile(window);
  end;
end;

Regards, Madshi.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
LischkeCommented:
Yep, Madshi took a great deal of the stuff to explain. Mikeblas, I think you see problems where no are. We are talking about a few bytes which are queried very seldom (just when an application starts). Even the frequent timer event to create a blinking caret in an edit control consumes more resources than the small piece of a memory mapped file we are talking about here.

Josh, I'm also one of the Delphi fraction :-) I can give you a complete solution (230 lines of code including description), but it is written in Delphi. Are you able to translate that?

Ciao, Mike
0
jbirkAuthor Commented:
Well, I've never even seen Delphi code before in my life (though I've heard about it).  It looks kind of like pascal.  I should be able to figure out what's going on.  The only problem is that I don't know what functions in C++ correspond to these Delphi function since I'm new to windows programming.  I'll try and look them up in the help documents of BCB and microsoft visual C, but they never give any examples...

Amyway, for now, I'm going to try Madshi's example.  It'll take a day or two...

And what about the minimize problem?  Has anyone seen that before?  Why is it happening?  How do I fix it?

Thanks,
Josh
0
MadshiCommented:
Yep, Delphi is something like object pascal ++ for Windows.
You should not have any problems with the functions, since these are no Delphi specific functions, but win32 APIs. They work *exactly* the same way in C++, so you only need to change the syntax from pascal to C++. That's all...

Don't know about the minimize problem. Normally ShowWindow(window, SW_RESTORE) should work alright...

Regards, Madshi.
0
mikeblasCommented:

 > but how can the second app instance know
 > the window handle of the first app instance when using e.g. a mutex?
 > The 2. instance then only knows THAT there is another instance,
 > but it doesn't know how to bring it to front!

It doesn't need to!  The second instance can just signal that mutex so the first instance can do something appropriate in repsonse _when_ it's appropriate.

 > Even the frequent timer event to create a blinking caret in an edit
 > control consumes more resources than the small piece of a memory
 > mapped file we are talking about here.

If you didn't really need that edit control, the blinking cursor is a waste, too.

..B ekiM
0
MadshiCommented:
>> It doesn't need to!  The second instance can just signal that mutex so the first instance can do something appropriate in repsonse _when_ it's appropriate.

Well, then the first instance needs to create a thread that waits for that mutex. Right?
So what consumes more resources? A thread or a memory mapped file?

Regards, Madshi.
0
mikeblasCommented:
> Well, then the first instance needs to create a thread that waits for that mutex.

It can, if it wants to.  Or, it can simply check with MsgWaitForSingleObject() during its main window message loop.

..B ekiM
0
MadshiCommented:
Well, yes, that would be possible. But what if an event handler that was fired by the main message loop starts another message loop?
I think a thread (that waits for a mutex or event) or the memory mapped file solution would be more reliable. And I don't think that a memory mapped file consumes more ressources than a thread.

Regards, Madshi.
0
MadshiCommented:
Ehmmm... And a thread PLUS a mutex consumes two handles, while a memory mapped file only consumes 1 handle.
0
VendiCommented:
Sorry for this comment, I just want off the e-mail notification.
0
jbirkAuthor Commented:
Sorry I'm taking so long to look into this guys.  I've been really busy at work (and this is for work, not at home).  I'll get to it as soon as possible, and get back to you on how it works for me.

I plan to try the memory mapped file solution shown by madshi.

In the meantime, does ANYONE have an idea about why I can't minimize after using SW_RESTORE, or should I ask a seperate question for that one?

Thanks!
-Josh
0
mikeblasCommented:
> Sorry for this comment, I just want off the e-mail notification.

Just check the box and leave the comments blank.  No need for a phony comment.  (But, you're probably gone.)

 > while a memory mapped file only consumes 1 handle.

A memory mapped file, even with only 4 bytes in it, takes a handle, a page in memory (unless it's swapped out) and a page on disk.  It's an actual I/O hit!

..B ekiM
0
jbirkAuthor Commented:
Sorry for taking so dog gone long.  I had another project which took priority over this one.  That's finally finished so I'm back to working on this.

I tried Madshi's suggestion, and it's acknowledging that the mapped file is there properly, but it's not getting the window handle properly, because the showwindow command is doing nothing.  Here is my code in WinMain: (keep in mind it's running on Borland C++ Builder 4 which is a little different...)

    HANDLE hMap;
    hMap = CreateFileMapping((HANDLE) 0xFFFFFFFF,NULL,PAGE_READWRITE,0,4,"appName");
    if (hMap != NULL && GetLastError() == ERROR_ALREADY_EXISTS)
      { // bring the window to the front if it's already opened
//        MessageBox(NULL, "Already opened the mapped file", "Title", MB_ICONEXCLAMATION | MB_OK);
       (HANDLE)prevhwndMain = MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,0);
        ShowWindow(*prevhwndMain,SW_RESTORE);
        SetForegroundWindow(*prevhwndMain);
        UnmapViewOfFile(hMap);
        CloseHandle(hMap);
        hMap = NULL;
        return 0; //it already existed so close me down!
      }
    else if (hMap == NULL)
     {  int retval;
        char buffer[20];
        retval = (int)GetLastError();
        sprintf(buffer,"Error: %i",retval);
        MessageBox(NULL,buffer, "Title", MB_ICONEXCLAMATION | MB_OK);
     }

    try
    {
        Application->Initialize();
        Application->CreateForm(__classid(THW3), &HW3);

    // need to write our info to the file
   (HANDLE)prevhwndMain = MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,0);
    *prevhwndMain = HW3;
    UnmapViewOfFile(hMap);

        Application->Run();

    CloseHandle(hMap);
    hMap = NULL;

    }
    catch (Exception &exception)
    {
        Application->ShowException(&exception);
    }
    return 0;


Please help!
-Josh
0
MadshiCommented:
Is HW3 really a window handle? BCB is quite near to Delphi, and in Delphi you would fill in the pointer to the BCB object, not the window handle. Shouldn't it be something like this?

  *prevhwndMain = HW3->handle;

Regards, Madshi.
0
jbirkAuthor Commented:
Woops!  You were right. HW3->Handle (it's capitalized with BCB 4).  Also I had to call it from the form create handler since Handle isn't defined until then, but making that change got it to work!  Thanks a lot!

I'm still having the problem though where if the window is minimized when I show it, it restores it properly, but the window no longer has the ability to minimize.  Wierd error!

Thanks everyone for your help, but since I used Madshi's code, he gets the points.

-Josh
0
MadshiCommented:
Try this:

  PostMessage(*prevhwndMain, WM_SYSCOMMAND, SC_RESTORE, 0);
0
jbirkAuthor Commented:
Hmm...  it seems to send the message ok, but nothing then happens.  The receiving window doesn't seem to be doing anything with the message.  Am I supposed to program that handler myself, or let it get handled automatically?

I know how to process a message using the WndProc, but BCB 4  does all the event handling behind the scenes...  Do I need to override the WNDProc in this case to process this one message?

-Josh
0
MadshiCommented:
Hmm... If it does nothing, then forget about this idea, it was just an idea... Sorry. If I have another idea, I'll tell you.
0
MadshiCommented:
Another thought: Please try Application->Handle instead of HW3->Handle.
0
jbirkAuthor Commented:
YES!!  It worked!  I'm going to give you another 50 points for helping out with this additional problem.  Look for "For Madshi".  Thanks a lot!
-Josh
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Microsoft Development

From novice to tech pro — start learning today.