Thread View: borland.public.cppbuilder.nativeapi
23 messages
23 total messages
Started by Nate Lockwood
Sun, 26 Feb 2006 21:11
Continuing problem with SendMessage and WM_CCOPYDATA
Author: Nate Lockwood
Date: Sun, 26 Feb 2006 21:11
Date: Sun, 26 Feb 2006 21:11
108 lines
4794 bytes
4794 bytes
I've tried this a couple of ways and have now have the most basic code
for testing. I'm trying to use SendMessage from an initializing
application when it discovers that there is another instance already
running. SendMessage returns 0 (I think that means success) but the
function in my main form is never called.
Nate
Here is some code
UINT WM_MYMSG; Global
HANDLE CheckForInstance(const UINT WM_MYMSG, const String sTitle,
TStringList *);
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
HANDLE mutex;
TStringList * FileList = NULL;
try
{
String sTitle = "Google Earth (C) Tool";
String sMessage =
"USDA.FS.FireScience.GooleEarthTool.Awake";
Application->Title = sTitle + " Temporary";
WM_MYMSG = RegisterWindowMessage(sMessage.c_str());
if((mutex = CheckForInstance(WM_MYMSG, sTitle,
FileList)) == NULL)
return 0 ; // second instance
Application->Title = sTitle;
Application->Initialize();
Application->CreateForm(__classid(TFormMain), &FormMain);
Application->CreateForm(__classid(TAboutBox), &AboutBox);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
catch (...)
{
try
{
throw Exception("");
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
}
if (mutex != NULL)
::ReleaseMutex(mutex);
return 0;
//---------------------------------------------------------------------------
HANDLE CheckForInstance(const UINT WM_MYMSG, // unique
const String sTitle, // of running ap
TStringList * FileList)
{
SetLastError(0) ;
char * m_Name = "USDA.FS.FireScience.GooleEarthTool.mutex";
HANDLE m_Handle = ::CreateMutex(NULL, true, m_Name);
if(m_Handle == NULL) // Didn't exist and couldn't create
return m_Handle;
// got a mutex -
if(::GetLastError() != ERROR_ALREADY_EXISTS) // already set?
return m_Handle; // no, first instance
// code for the second instance
HWND hReceiver = ::FindWindow("TApplication", sTitle.c_str());
if( hReceiver ) // send a message to other
{
WPARAM wparam;
char * pBuf = "Second instance";
COPYDATASTRUCT MyCDS;
MyCDS.dwData = WM_MYMSG; // unique identifier
MyCDS.cbData = strlen(pBuf) + 1; // buffer size 1 -> no data
MyCDS.lpData = &pBuf; // address of the data
HANDLE hThisAp = Application->Handle;
LRESULT result = ::SendMessage(hReceiver , WM_COPYDATA,
(WPARAM)hThisAp, (LPARAM)&MyCDS);
int SyntheticBreak;
SyntheticBreak = 0;
//::PostMessage(hWnd, WM_MYMSG, wparam, NULL);
// cleanup
}
return NULL; // NULL handle
}
// in my main form .h
public: // public user declarations
void __fastcall WMCopyData(TWMCopyData &message);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_COPYDATA, TWMCopyData, WMCopyData)
END_MESSAGE_MAP(TForm)
// in my main form .cpp
extern const UINT WM_MYMSG;
// This function is never called...
void __fastcall TFormMain::WMCopyData(TWMCopyData &message)
{
if(message.CopyDataStruct->dwData == WM_MYMSG)
{
Application->Restore();
Show();
}
}
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Dennis Jones
Date: Mon, 27 Feb 2006 00:04
Date: Mon, 27 Feb 2006 00:04
63 lines
3929 bytes
3929 bytes
"Nate Lockwood" <RNLockwoodREMOVE@attglobal.net> wrote in message
news:44021981$1@newsgroups.borland.com...
> I've tried this a couple of ways and have now have the most basic code
> for testing. I'm trying to use SendMessage from an initializing
> application when it discovers that there is another instance already
> running. SendMessage returns 0 (I think that means success) but the
> function in my main form is never called.
First of all, the return value of SendMessages depends on the message type
that gets sent. A return value of 0 may be success, or it may be failure --
it all depends on how the message is processed by the receiver. SendMessage
works kind of like a function call...when the receiver finishes processing
the message, the value it returns becomes SendMessage's return value.
Now, forgive me if I've missed something, but if all you want to do is
prevent a second instance from running and instead just bring the first
instance to the foreground, you don't need to use SendMessage to do that.
The way I do it is use a mutex (like you're doing) and then tell the first
application to display itself using ShowWindow (no need to send a custom
message):
// create an application-specific, named mutex
hMutex = CreateMutex( NULL, FALSE, "MyApplicationMutex" );
// if the mutex already exists, it means that another instance
// is already running -- display it
if ( GetLastError() == ERROR_ALREADY_EXISTS )
{
// The main "form" in a C++Builder/Delphi application is not the
// application's main window (as far as Windows is concerned).
// TApplication creates a hidden window that acts as the true main
// window for the application. Thus, whenever we want to refer to
the
// application's main window, we must use the handle of the window
// created by TApplication, not the handle of the main "form". The
// TApplication's window handle is the parent window handle for the
// main form, so it is easy to find:
// get the window handle of the other instance's main form
HWND MainFormHWnd = FindWindow( "TMyApplicationMainForm", NULL );
// get the main form's parent window handle (we'll assume
MainFormHWnd
// is not NULL, since we wouldn't even be here unless another
instance
// of the application were actually running)
HWND hAppWnd = (HWND)GetWindowLong( MainFormHWnd, GWL_HWNDPARENT );
if ( IsIconic( hAppWnd ) )
{
ShowWindow( hAppWnd, SW_RESTORE );
}
SetForegroundWindow( hAppWnd );
return 0;
}
Does this help at all? Now, if you still want to send a message to the
other instance, *maybe* (I don't know for sure -- I've never done it myself)
you need to send the message to the application's main window as opposed to
the main form (see the comments in my code above).
- Dennis
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Dennis Jones
Date: Mon, 27 Feb 2006 00:41
Date: Mon, 27 Feb 2006 00:41
26 lines
2153 bytes
2153 bytes
"Dennis Jones" <nospam@nospam.com> wrote in message news:44024219$1@newsgroups.borland.com... > > Now, if you still want to send a message to the > other instance, *maybe* (I don't know for sure -- I've never done it myself) > you need to send the message to the application's main window as opposed to > the main form... Forget that -- it isn't necessary. The main form handle is the one that receives the message. I just created a sample application, and it does indeed receive the WM_COPYDATA message, and the SendMessage call does not return until processing completes on the receiver. Here's one thing you might not be aware of -- if you are doing this in the IDE and trying to step through the code of the sending application, if you have a design-time form/unit open in the IDE that matches the criteria of the FindWindow call (your main form in this case), the design-time window will receive the message instead of the other executing instance of your application! So, when you are testing from the IDE, close the design-time main form and its associated CPP file. Other than that, your current code should work fine. - Dennis
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Nate Lockwood
Date: Mon, 27 Feb 2006 01:43
Date: Mon, 27 Feb 2006 01:43
23 lines
1945 bytes
1945 bytes
Dennis Jones wrote: > Now, forgive me if I've missed something, but if all you want to do is > prevent a second instance from running and instead ... No, I want to do more than that, and I able to use PostMessage just to restore the window and bring it to the front. The way my ap is used is that the user will probably run it, then minimize it while other work is done, then a folder will be checked, new input files will be chosen, and they will be opened with my ap, GoogleEarthTool which produces GoogleEarth KML files. I want to get any filenames from the command line and pass them to the running instance as well as restore, etc. But first things first, If I can use SendMessage with a trivial message then I know I can use it the filenames in a string (or better yet, a StringList). Thanks for your kind help, Dennis. I got so frustrated with this I started to learn Python! Nate
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Nate Lockwood
Date: Mon, 27 Feb 2006 01:48
Date: Mon, 27 Feb 2006 01:48
17 lines
1803 bytes
1803 bytes
Dennis Jones wrote: > Here's one thing you might not be aware of -- if you are doing this in the > IDE and trying to step through the code of the sending application, if you > have a design-time form/unit open in the IDE that matches the criteria of > the FindWindow call (your main form in this case), the design-time window > will receive the message instead of the other executing instance of your > application! So, when you are testing from the IDE, close the design-time > main form and its associated CPP file. Other than that, your current code > should work fine. > Ah ha! That might be it, I'll give it a try. The method of bringing the first window in front is lots better/easier than PostMessage! Thanks again.
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Dennis Jones
Date: Mon, 27 Feb 2006 03:20
Date: Mon, 27 Feb 2006 03:20
18 lines
1699 bytes
1699 bytes
"Nate Lockwood" <RNLockwoodREMOVE@attglobal.net> wrote in message news:44025954@newsgroups.borland.com... > Thanks for your kind help, Dennis. I got so frustrated with this I > started to learn Python! No! Don't do that!!! :-) j/k I don't know anything about Python, but I don't think your problems are language-specific. I think you just need a little more experience with the Win32 API. Switching to Python might simplify some programming tasks, but you will still (presumably) be using Windows, which means you will always need a good grasp of the Win32 API. Don't get me wrong though...I'm not saying you shouldn't learn Python...knowing several languages can only help you in the long run. - Dennis
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Nate Lockwood
Date: Mon, 27 Feb 2006 04:19
Date: Mon, 27 Feb 2006 04:19
34 lines
3038 bytes
3038 bytes
Dennis Jones wrote: > I don't know anything about Python, but I don't think your problems are > language-specific. I think you just need a little more experience with the > Win32 API. Switching to Python might simplify some programming tasks, but > you will still (presumably) be using Windows, which means you will always > need a good grasp of the Win32 API. Don't get me wrong though...I'm not > saying you shouldn't learn Python...knowing several languages can only help > you in the long run. Well, I need to do some server side scripts and I *think* python is more C/C++ like than PHP or perl. In the last couple of hours I wrote a script that enumerates all the .txt files in a folder and saves the names, sizes, and creation dates as tuples in a specialized python file - my first python script. It's the basis that I'll build on for a script that I need that will compare the files in the folder now to those there the last time it was run and generate a web page to inform the user that a new file(s) is available. But I think I *really* need to read the tutorial before I do any more! :-) I do need more experience with the Win32 API and I do have the reference text - at my office. I started in 1975 with FORTRAN, then basic, 6502 assembly, Pascal, PLI, C, C++, and a tiny bit of Delphi on CDC, IBM, DG, Sun UNIX, and Windows PCs. I've always used Borland compilers on my own computers and now my work Windows machines. My programming is to support the research group that I'm in so the code is specialized and has few users, sometimes only one. I'm an ecologist (officially) and aircraft manager, too... So I don't work anywhere near full time programming, some of my knowledge is shallow, but I'm always learning. I'd rather be programming than doing some of my other duties. At least I'm allowed to work at home when I want to. Our beta site: http://www.fireimaging.com
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Bob Gonder
Date: Mon, 27 Feb 2006 14:57
Date: Mon, 27 Feb 2006 14:57
43 lines
2305 bytes
2305 bytes
Nate Lockwood wrote:
> String sTitle = "Google Earth (C) Tool";
> String sMessage = "USDA.FS.FireScience.GooleEarthTool.Awake";
> Application->Title = sTitle + " Temporary";
> WM_MYMSG = RegisterWindowMessage(sMessage.c_str());
Why do you insist on using String for what should be a simple c-string
(char array) ?
char sMessage[] = "USDA.FS.FireScience.GooleEarthTool.Awake";
WM_MYMSG = RegisterWindowMessage(sMessage);
Or, simpler:
WM_MYMSG=RegisterWindowMessage("USDA.FS.FireScience.GooleEarthTool.Awake");
> char * pBuf = "Second instance";
>
> COPYDATASTRUCT MyCDS;
> MyCDS.dwData = WM_MYMSG; // unique identifier
> MyCDS.cbData = strlen(pBuf) + 1; // buffer size 1 -> no data
> MyCDS.lpData = &pBuf; // address of the data
You want a pointer to the data, not a pointer to a pointer to the
data.
MyCDS.lpData = pBuf; // address of the data
>// in my main form .h
> // This function is never called...
>void __fastcall TFormMain::WMCopyData(TWMCopyData &message)
>{
> if(message.CopyDataStruct->dwData == WM_MYMSG)
> {
> Application->Restore();
> Show();
Verify that it is never called, and not just called with "incorrect"
data:
}else{ ShowMessage("WM_MYMSG is not correct");
> }
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Nate
Date: Mon, 27 Feb 2006 19:18
Date: Mon, 27 Feb 2006 19:18
47 lines
2203 bytes
2203 bytes
I tried posting a reply using the web page inferface but don't
see it so I'll try again.
Bob Gonder <notbg@notmindspring.invalid> wrote:
>WM_MYMSG=RegisterWindowMessage("USDA.FS.FireScience.GooleEarthTool.Awake");
Made the change.
>> MyCDS.lpData = &pBuf; // address of the data
>
>You want a pointer to the data, not a pointer to a pointer to
the data.
> MyCDS.lpData = pBuf; // address of the data
Found the first instance in some posted code, tried it, and
forgot to change it back. It's been corrected.
>>// in my main form .h
>
>> // This function is never called...
>>void __fastcall TFormMain::WMCopyData(TWMCopyData &message)
>>{
>> if(message.CopyDataStruct->dwData == WM_MYMSG)
>> {
>> Application->Restore();
>> Show();
>
>Verify that it is never called, and not just called with
"incorrect"
>data:
> }else{ ShowMessage("WM_MYMSG is not correct");
>
OK, I should have done that rather than watch it in the
debugger. I made the change and tested it. ShowMessage is not
called.
The receiver addess is the handle to the application but the
receiver code is in the main form - does this matter?
What should SendMessage return on success?
Thanks
Nate
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Dennis Jones
Date: Mon, 27 Feb 2006 20:29
Date: Mon, 27 Feb 2006 20:29
14 lines
1499 bytes
1499 bytes
"Nate" <RNLockwood@attglobal.net> wrote in message news:44035e7c$1@newsgroups.borland.com... > > The receiver addess is the handle to the application but the > receiver code is in the main form - does this matter? > What should SendMessage return on success? Nate, did you read my other post? First, SendMessage returns whatever the receiver sends back. Second, I've tested a simple app, and it works just fine. I'll post my test project in the binaries group if you want it. - Dennis
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Remy Lebeau (TeamB)
Date: Mon, 27 Feb 2006 21:11
Date: Mon, 27 Feb 2006 21:11
140 lines
4766 bytes
4766 bytes
"Nate Lockwood" <RNLockwoodREMOVE@attglobal.net> wrote in message
news:44021981$1@newsgroups.borland.com...
> Here is some code
Your code is still incorrect for several reasons:
1) you are accessing a NULL TStringList pointer when constructing the
message to be sent
2) you are never checking the WM_MYMSG global for 0 before using it, in case
RegisterWindowMessage() fails
3) you are still not freeing the mutex properly.
4) you are still catching the message in the completely wrong place (I
explained that to you earlier).
Use this code instead:
UINT WM_MYMSG = 0;
HANDLE CheckForInstance(const AnsiString &sTitle, TStrings *FileList)
{
::SetLastError(0);
HANDLE hMutex ::CreateMutex(NULL, TRUE,
"USDA.FS.FireScience.GooleEarthTool.mutex");
if( hMutex != NULL ) // exists or created
{
if( ::GetLastError() != ERROR_ALREADY_EXISTS )
return hMutex;
if( (WM_MYMSG != 0) && (FileList != NULL) )
{
HWND hReceiver = ::FindWindow("TApplication",
sTitle.c_str());
if( hReceiver != NULL )
{
AnsiString sBuf = FileList->Text;
COPYDATASTRUCT cds = {0};
cds.dwData = WM_MYMSG;
cds.cbData = sBuf.Length();
cds.lpData = pBuf.c_str();
::SendMessage(hReceiver, WM_COPYDATA,
reinterpret_cast<WPARAM>(Application->Handle),
reinterpret_cast<LPARAM>(&cds));
}
}
::CloseHandle(hMutex);
}
return NULL;
}
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
const AnsiString sTitle = "Google Earth (C) Tool";
WM_MYMSG =
RegisterWindowMessage("USDA.FS.FireScience.GooleEarthTool.Awake");
TStringList * FileList = NULL;
try
{
FileList = new TStringList;
// fill FileList as needed ...
HANDLE mutex = CheckForInstance(sTitle, FileList);
if( mutex != NULL )
{
Application->Title = """;
Application->Title = sTitle;
Application->Initialize();
Application->CreateForm(__classid(TFormMain), &FormMain);
Application->CreateForm(__classid(TAboutBox), &AboutBox);
Application->Run();
}
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
catch (...)
{
try
{
throw Exception("");
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
}
if (mutex != NULL)
::CloseHandle(mutex);
return 0;
}
extern UINT WM_MYMSG;
__TFormMain::TFormMain(TComponent *Owner)
: TForm(Owner)
{
Application->HookMainWindow(AppMessageHook);
}
__TFormMain::~TFormMain()
{
Application->UnhookMainWindow(AppMessageHook);
}
bool __fastcall TFormMain::AppMessageHook(TMessage &Message)
{
if( Message.Msg == WM_COPYDATA )
{
LPCOPYDATASTRUCT lpcds =
reinterpret_cast<LPCOPYDATASTRUCT>(Message.LParam);
if( (lpcds->dwData == WM_MYMSG) && (WM_MYMSG != 0) )
{
AnsiString sData(static_cast<char*>(lpcds->lpData),
lpcds->cbData);
// use sData as needed ...
Application->Restore();
Show();
return true;
}
}
return false;
}
Gambit
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Remy Lebeau (TeamB)
Date: Mon, 27 Feb 2006 21:17
Date: Mon, 27 Feb 2006 21:17
41 lines
2215 bytes
2215 bytes
"Nate" <RNLockwood@attglobal.net> wrote in message
news:44035e7c$1@newsgroups.borland.com...
> The receiver addess is the handle to the application but
> the receiver code is in the main form - does this matter?
Yes! That is what I keep explaining to you. You are not handling the
message from the context of the window that actually receives it.
> What should SendMessage return on success?
Whatever the receiver of the message returns. Messages have a return value
of 0 by default. You should have your message handler explicitally set the
result to TRUE. The Win32 API documentation even says as much:
"If the receiving application processes this message, it should return
TRUE; otherwise, it should return FALSE. "
Then, in the process that is sending the message, you can check the message
result to make sure that it was actually processed. For example:
if( Message.Msg == WM_COPYDATA )
{
//...
if( ... is my message ... )
{
//...
Message.Result = TRUE;
}
//...
}
if( ::SendMessage(hReceiver, WM_COPYDATA, ...) == TRUE )
// message was processed ...
else
// message was not processed ...
Gambit
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: JD
Date: Tue, 28 Feb 2006 14:35
Date: Tue, 28 Feb 2006 14:35
154 lines
7086 bytes
7086 bytes
"Dennis Jones" <nospam@nospam.com> wrote:
>
> [...] if all you want to do is prevent a second instance
> from running and instead just bring the first instance to
> the foreground, you don't need to use SendMessage to do that.
Where were you when I was working on my TSingleInstance? I
posted many times because I couldn't correctly detect the main
form's window state because all API's incorrectly reported the
state (unless it's ExStyle had WS_APP_WINDOW applied) when it
was minimized.
The only solution was to have the component hook into the main
form's WndProc and internally keep track of the window state
and then have the second instance send a custom message because
only the component knew the real state so it had to decide to
restore or not prior to bringing it to front.
> HWND MainFormHWnd = FindWindow( "TMyApplicationMainForm", NULL );
I have a problem with this line because you could get the
wrong instance.
> HWND hAppWnd = (HWND)GetWindowLong( MainFormHWnd, GWL_HWNDPARENT );
With this line, I was able to solve my problem with knowing
the state and reduce the component code and complexity:
//-------------------------------------------------------------
#ifndef Unit2H
#define Unit2H
//-------------------------------------------------------------
#include <Classes.hpp>
//-------------------------------------------------------------
#define UWM_INITIALIZE (WM_USER + 100)
//-------------------------------------------------------------
class TSingleInstance : public TComponent
{
private:
HANDLE hMutex;
HWND hFirstInstance;
HWND hSecondInstance;
HWND FHandle;
TForm *FParentForm;
protected:
virtual void __fastcall WndProc( TMessage &Message );
static bool __stdcall EnumWindowsCallBack( HWND hWnd, LPARAM lParam );
public:
__fastcall TSingleInstance(TComponent* Owner);
__fastcall ~TSingleInstance();
};
//-------------------------------------------------------------
#endif
//-------------------------------------------------------------
#include <vcl.h>
#include <basepch.h>
//-------------------------------------------------------------
#pragma hdrstop
//-------------------------------------------------------------
#include "Unit2.h"
#pragma package(smart_init)
//-------------------------------------------------------------
TSingleInstance *ThisSingleInstance = NULL;
//-------------------------------------------------------------
__fastcall TSingleInstance::TSingleInstance(TComponent* Owner) : TComponent(Owner)
{
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.");
if( ThisSingleInstance ) throw Exception("TSingleInstance : Only one instance of TSingleInstance is allowed per application.");
hMutex = NULL;
FHandle = NULL;
ThisSingleInstance = this;
if( !ComponentState.Contains(csDesigning) )
{
char ThisClassName[ 256 ] = { 0 };
::GetClassName( FParentForm->Handle, ThisClassName, 255 );
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;
HWND hWnd = reinterpret_cast<HWND>( ::GetWindowLong(hFirstInstance, GWL_HWNDPARENT) );
if( IsIconic(hWnd) ) ::ShowWindow( hWnd, SW_RESTORE );
::SetForegroundWindow( hWnd );
Application->Terminate();
}
else
{
// Still need some final initialization but it needs to be delayed until Application::MainForm is valid.
FHandle = AllocateHWnd( WndProc );
if( !FHandle )
{
throw Exception("TSingleInstance : AllocateHWnd failed.");
}
::PostMessage( FHandle, UWM_INITIALIZE, 0, 0 );
}
}
}
//-------------------------------------------------------------
__fastcall TSingleInstance::~TSingleInstance()
{
if( FHandle ) DeallocateHWnd( FHandle );
if( hMutex ) ::CloseHandle( hMutex );
ThisSingleInstance = NULL;
}
//-------------------------------------------------------------
void __fastcall TSingleInstance::WndProc( TMessage &Message )
{
if( Message.Msg == UWM_INITIALIZE )
{
if( FParentForm != Application->MainForm )
{
MessageDlg("TSingleInstance : Object can only be placed on the Main Form.", mtError, TMsgDlgButtons() << mbOK, 0);
Application->Terminate();
}
}
Message.Result = DefWindowProc( FHandle, Message.Msg, Message.WParam, Message.LParam );
}
//-------------------------------------------------------------
bool __stdcall TSingleInstance::EnumWindowsCallBack( HWND hWnd, LPARAM lParam )
{
char WindowClassName[256] = { 0 };
if( ::GetClassName(hWnd, WindowClassName, 255) )
{
if( stricmp(WindowClassName, (char*)lParam) == 0 )
{
if( ThisSingleInstance->hFirstInstance )
{
ThisSingleInstance->hSecondInstance = hWnd;
return FALSE;
}
else ThisSingleInstance->hFirstInstance = hWnd;
}
}
return TRUE;
}
//-------------------------------------------------------------
Alot of people looked at this, including Gambit and at least
2 other TeamB members commented and even though we all knew
that TApplication has a hidden window, none of us thought of
it being important.
~ JD
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Dennis Jones
Date: Tue, 28 Feb 2006 16:07
Date: Tue, 28 Feb 2006 16:07
32 lines
1990 bytes
1990 bytes
"JD" <nospam@nospam.com> wrote in message news:44046dac$1@newsgroups.borland.com... > > "Dennis Jones" <nospam@nospam.com> wrote: > > > > [...] if all you want to do is prevent a second instance > > from running and instead just bring the first instance to > > the foreground, you don't need to use SendMessage to do that. > > Where were you when I was working on my TSingleInstance? > > HWND MainFormHWnd = FindWindow( "TMyApplicationMainForm", NULL ); > > I have a problem with this line because you could get the > wrong instance. Wait...if you can only have one instance running, then how could you possibly get the wrong instance? > Alot of people looked at this, including Gambit and at least > 2 other TeamB members commented and even though we all knew > that TApplication has a hidden window, none of us thought of > it being important. That's funny, because I am almost positive that I found that little tidbit in one of Remy's (Gambit) posts! - Dennis
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Dennis Jones
Date: Tue, 28 Feb 2006 16:09
Date: Tue, 28 Feb 2006 16:09
10 lines
1115 bytes
1115 bytes
"JD" <nospam@nospam.com> wrote in message news:44046dac$1@newsgroups.borland.com... > > Where were you when I was working on my TSingleInstance? I was hiding from you! :-) - Dennis
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: JD
Date: Tue, 28 Feb 2006 16:43
Date: Tue, 28 Feb 2006 16:43
22 lines
1563 bytes
1563 bytes
"Dennis Jones" <nospam@nospam.com> wrote:
>
>> I have a problem with this line because you could get the
>> wrong instance.
>
> Wait...if you can only have one instance running, then how
> could you possibly get the wrong instance?
That block of code only executes if another instance of the
application is already running:
if ( GetLastError() == ERROR_ALREADY_EXISTS )
{
HWND MainFormHWnd = FindWindow( "TMyApplicationMainForm", NULL );
....
}
How can you be sure that FindWindow returns the first instance
v/s the instance in which that block is executing?
~ JD
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Alan Bellingham
Date: Tue, 28 Feb 2006 18:00
Date: Tue, 28 Feb 2006 18:00
23 lines
1804 bytes
1804 bytes
"JD" <nospam@nospam.com> wrote:
>That block of code only executes if another instance of the
>application is already running:
>
> if ( GetLastError() == ERROR_ALREADY_EXISTS )
> {
> HWND MainFormHWnd = FindWindow( "TMyApplicationMainForm", NULL );
> ....
> }
>
>How can you be sure that FindWindow returns the first instance
>v/s the instance in which that block is executing?
Very easily - after all, the second and subsequent instances know that
there should only be one instance. Therefore, when they detect the first
instance already exists, they don't create the window.
I assume you know how not to create a window?
Alan Bellingham
--
ACCU Conference 2006 - 19-22 April, Randolph Hotel, Oxford, UK
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Dennis Jones
Date: Tue, 28 Feb 2006 18:14
Date: Tue, 28 Feb 2006 18:14
29 lines
1998 bytes
1998 bytes
"JD" <nospam@nospam.com> wrote in message
news:44048bdc$1@newsgroups.borland.com...
>
> "Dennis Jones" <nospam@nospam.com> wrote:
> >
> >> I have a problem with this line because you could get the
> >> wrong instance.
> >
> > Wait...if you can only have one instance running, then how
> > could you possibly get the wrong instance?
>
> That block of code only executes if another instance of the
> application is already running:
>
> if ( GetLastError() == ERROR_ALREADY_EXISTS )
> {
> HWND MainFormHWnd = FindWindow( "TMyApplicationMainForm", NULL );
> ....
> }
>
> How can you be sure that FindWindow returns the first instance
> v/s the instance in which that block is executing?
Well, along the same lines of what Alan said, this block of code is supposed
to exist in WinMain before Application->Run() is called, therefore, the
window has not been created yet.
- Dennis
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: JD
Date: Tue, 28 Feb 2006 19:15
Date: Tue, 28 Feb 2006 19:15
10 lines
1251 bytes
1251 bytes
"Dennis Jones" <nospam@nospam.com> wrote: > > [...] this block of code is supposed to exist in WinMain > before Application->Run() is called, therefore, the window > has not been created yet. That makes all the difference in the world. ~ JD
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Remy Lebeau (TeamB)
Date: Tue, 28 Feb 2006 19:17
Date: Tue, 28 Feb 2006 19:17
94 lines
4592 bytes
4592 bytes
"JD" <nospam@nospam.com> wrote in message
news:44046dac$1@newsgroups.borland.com...
> > HWND hAppWnd = (HWND)GetWindowLong( MainFormHWnd,
GWL_HWNDPARENT );
>
> With this line, I was able to solve my problem with knowing
> the state and reduce the component code and complexity:
The HWND parent of a TForm is the TApplication::Handle (unless the form
overrides its CreateParams() method to specify something else)
The application can subclass its TApplication window by using the
TApplication::HookMainWindow() method.
> ::GetClassName( FParentForm->Handle, ThisClassName, 255 );
> hMutex = ::CreateMutex( NULL, TRUE, ThisClassName );
That will only be reliable if the TForm class name is unique across all
applications. A better approach would be to append some kind of
process-specific data to the mutex name to ensure its uniqueness, such as
the process ID. Then the TForm class name becomes irrelevant and is not
needed at all:
char MutexName[32] = {0};
::wsprintf(MutexName, "TSingleInstance.%0.10u", GetCurrentProcessId());
hMutex = ::CreateMutex(NULL, TRUE, MutexName);
> bool __stdcall TSingleInstance::EnumWindowsCallBack( HWND hWnd, LPARAM
lParam )
> {
> char WindowClassName[256] = { 0 };
> if( ::GetClassName(hWnd, WindowClassName, 255) )
> {
> if( stricmp(WindowClassName, (char*)lParam) == 0 )
> {
> if( ThisSingleInstance->hFirstInstance )
> {
> ThisSingleInstance->hSecondInstance = hWnd;
> return FALSE;
> }
> else ThisSingleInstance->hFirstInstance = hWnd;
> }
> }
> return TRUE;
> }
That is not a very accurate way to find previous instances. The order in
which the enumeration can return windows is not predictable ahead of time.
The new application instance could be found before the previous instance, in
which case your HWND variables will be swapped incorrectly - hFirstInstace
will point to the second instance, and hSecondInstace will point to the
first instace. I do notice that you are swapping the variables after
EnumWindows() returns, but a better approach would be to not track two
variables at all. You can check the HWND provided by the callback to see if
it matches the TForm::Handle before assuming whether it is the first or
second instance. Then you only have to to use one variable instead of two:
private:
HWND hPreviousInstance;
bool __stdcall TSingleInstance::EnumWindowsCallBack( HWND hWnd, LPARAM
lParam )
{
char WindowClassName[256] = { 0 };
if( ::GetClassName(hWnd, WindowClassName, 255) )
{
if( stricmp(WindowClassName, reinterpret_cast<char*>(lParam)) ==
0 )
{
if( hWnd != ThisSingleInstance->FParentForm->Handle )
{
ThisSingleInstance->hPreviousInstance = hWnd;
return FALSE;
}
}
}
return TRUE;
}
> Alot of people looked at this, including Gambit and at least
> 2 other TeamB members commented and even though we all
> knew that TApplication has a hidden window, none of us thought of
> it being important.
When did I ever tell you that the TApplication window was not important? If
you hook the TApplication window, it could greatly simplify your code, since
you won't have to hook into any particular TForm objects anymore.
Gambit
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Remy Lebeau (TeamB)
Date: Tue, 28 Feb 2006 19:19
Date: Tue, 28 Feb 2006 19:19
15 lines
1610 bytes
1610 bytes
"JD" <nospam@nospam.com> wrote in message news:44048bdc$1@newsgroups.borland.com... > How can you be sure that FindWindow returns the first > instance v/s the instance in which that block is executing? By not specifying only the class name when calling FindWindow(). Specify the window title as well, and then change the window title of the current process before enumerating the windows. All the more reason to look for the TApplication window instead of a TForm window, since you have access to the TApplication::Title property. Gambit
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: JD
Date: Tue, 28 Feb 2006 19:34
Date: Tue, 28 Feb 2006 19:34
44 lines
2550 bytes
2550 bytes
"Remy Lebeau \(TeamB\)" <no.spam@no.spam.com> wrote:
>
>> ::GetClassName( FParentForm->Handle, ThisClassName, 255 );
>> hMutex = ::CreateMutex( NULL, TRUE, ThisClassName );
>
> That will only be reliable if the TForm class name is unique
> across all applications.
Yeah. That's not too bad though. We've been living with it.
> A better approach would be to append some kind of process-
> specific data to the mutex name to ensure its uniqueness,
> such as the process ID. Then the TForm class name becomes
> irrelevant and is not needed at all:
>
> char MutexName[32] = {0};
> ::wsprintf(MutexName, "TSingleInstance.%0.10u", GetCurrentProcessId());
> hMutex = ::CreateMutex(NULL, TRUE, MutexName);
But the point is to have the same mutex name for both
processes. Won't that break it?
> [...] but a better approach would be to not track two
> variables at all. You can check the HWND provided by the
> callback to see if it matches the TForm::Handle before
> assuming whether it is the first or second instance.
Good point.
> When did I ever tell you that the TApplication window was
> not important?
Never. My point was that no one thought to use it to get the
window state of the main form.
> If you hook the TApplication window, it could greatly
> simplify your code, since you won't have to hook into any
> particular TForm objects anymore.
I was originally hooking into that but that one line of code
from Dennis means that the component doesn't need to anymore.
~ JD
Re: Continuing problem with SendMessage and WM_CCOPYDATA
Author: Remy Lebeau (TeamB)
Date: Wed, 01 Mar 2006 02:38
Date: Wed, 01 Mar 2006 02:38
12 lines
1365 bytes
1365 bytes
"JD" <nospam@nospam.com> wrote in message news:4404b3dc$1@newsgroups.borland.com... > But the point is to have the same mutex name for both > processes. Won't that break it? Ok, then use the process's filename instead. That will be the same across processes of the same executable. Gambit
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads