First: If the first instance want to know the second instance is running. In most case, the first instance should be actived if the second one launch. In the article use "mutex", the second instance find the first one by special window class name, then actived the first one. But the first instance do NOT know another instance has been run, and it have no choice but been actived by second instance.
Second: If the second instance should send some data to first one. Understand it this way: "Winword" is running, then you double click another .doc file in "Explore". Second instance of "Winword" should be terminated, but before it end, it must send the filename to the first instance.
Now, if you find this useful, and try to use DDE, let's step by step to finish.
First: As you see, we use DDE, so you should add ddeml.h into your project, and I recommend add it in your "Stdafx.h", instead of add this line here and there. add the following line into your "Stdafx.h"
#include "ddeml.h"
Second: handle the "InitInstance" and "ExitInstance" of you class base on CWinApp, if you have not do it yet. In the sample code the class name is "CTestApp", it is declare in "Test.h", and define in "Test.Cpp". you may have different name in different project.Third: Add the following into your CTestApp class, in "test.h"
private: bool isRun; DWORD idInst; HSZ hszCppServer, hszCppTopic; HCONV hconvCppServer; DWORD dderesult; public: HDDEDATA AppDdeCallback(WORD, WORD, HCONV, HSZ, HSZ, HDDEDATA, DWORD, DWORD); and add the following in "test.h" but out of any class, for it is CALLBACK HDDEDATA CALLBACK DdeCallback(WORD, WORD, HCONV, HSZ, HSZ, HDDEDATA, DWORD, DWORD);
Fourth: define your DDE server name and topic name Add the following into your "test.cpp"
#define CppServerName "TEST_SERVER" #define CppTopicName "TEST_TOPIC"
different project should have different server name
Fifth: add the following just after the #define
CTestApp* p_OneApp;
Sixth: add the following into your consturction
CTestApp::CTestApp() { p_OneApp = this; isRun = false; }
Seventh: Add the following at the begining of your InitInstance
BOOL CTestApp::InitInstance() { if (DdeInitialize(&idInst, (PFNCALLBACK)DdeCallback, 0, 0)) return false; hszCppServer = DdeCreateStringHandle ( idInst, CppServerName, CP_WINANSI ); hszCppTopic = DdeCreateStringHandle ( idInst, CppTopicName, CP_WINANSI ); // try to find the first instance hconvCppServer = DdeConnect ( idInst, hszCppServer, hszCppTopic, NULL ); if ( hconvCppServer ) { isRun = true; // have connection, this is the second instance, you can send data to first instance // DdeClientTransaction((unsigned char*)data, strlen(data)+1, // hconvCppServer, 0, CF_TEXT, XTYP_EXECUTE, 0, &dderesult ); return false; } DdeNameService ( idInst, hszCppServer, 0, DNS_REGISTER ); . . }
Eighth: Add the following into your ExitInstance
int CTestApp::ExitInstance() { DdeFreeStringHandle ( idInst, hszCppServer ); DdeFreeStringHandle ( idInst, hszCppTopic ); // only unregister the DDE server for first instance if ( !isRun ) if ( !DdeNameService ( idInst, hszCppServer, 0, DNS_UNREGISTER ) ) ::MessageBox ( 0, "Error", "in ServiceUnRegister", MB_OK ); DdeUninitialize(idInst); . . }
Nineth: Add the following into your "Test.Cpp"
HDDEDATA CALLBACK DdeCallback(WORD wType, WORD wFmt, HCONV hConv, HSZ hszTopic, HSZ hszItem, HDDEDATA hData, DWORD lData1, DWORD lData2) { return ( p_OneApp -> AppDdeCallback( wType, wFmt, hConv, hszTopic, hszItem, hData, lData1, lData2 ) ); } HDDEDATA CTestApp::AppDdeCallback(WORD wType, WORD wFmt, HCONV hConv, HSZ hszTopic, HSZ hszItem, HDDEDATA hData, DWORD lData1, DWORD lData2) { int icount; char* buffers; HDDEDATA ret = (HDDEDATA) NULL; CWnd* p_Wnd; switch ( wType ) { case XTYP_CONNECT: icount = DdeQueryString ( idInst, hszTopic, NULL, 0, CP_WINANSI ); buffers = (char*)malloc ( icount+1 ); DdeQueryString ( idInst, hszTopic, buffers, icount+1, CP_WINANSI ); if ( !strcmp ( buffers, CppTopicName ) ) { p_Wnd = AfxGetMainWnd(); p_Wnd -> ShowWindow(SW_RESTORE); p_Wnd -> BringWindowToTop( ); p_Wnd -> SetForegroundWindow( ); // add any code for the first instance have found the second one is launch ret = (HDDEDATA) DDE_FACK; } free ( buffers ); return ret; case XTYP_EXECUTE: // if the second instance send data, the first one received here. // icount = DdeGetData ( hData, NULL, 0, 0 ); // buffers = (char*)malloc ( icount+1 ); // DdeGetData ( hData, (unsigned char*)buffers, icount, 0 ); // free ( buffers ); return ret; default: return ret; } }
When second instance launch, it calls DdeConnect(), then the DDEML call the CALLBACK function for first instance with XTYP_CONNECT type. The first instance can determine do what by itself. If connection, second instance can send data by DdeClientTransaction() with XTYP_EXECUTE type. and the first one can received it at the CALLBACK function with XTYP_EXECUTE type.
Great Article!!Posted by sabapathy on 03/23/2006 11:20am
Hi This is Great Article!! I got enabled double click open of my project files, in vc .net 2003 Thanks & Regards, sabapathy sabapathy_80@yahoo.comReply
Why make it hard on yourself...Posted by Legacy on 03/25/2002 08:00am
Originally posted by: Mouse
ExcellentPosted by Legacy on 02/12/2002 08:00am
Originally posted by: Aries
Thanks Raymon, saved my nutsack. Great code, easy to add.
Reply
Problems with DdeConnectPosted by Legacy on 10/25/2000 07:00am
Originally posted by: Jose Luis Briones
Im trying to make an application who has to establice conversation using DDE with other application. My problem come in the next line:Reply
hconv = DdeConnect(idInst,hservname,htopicname,NULL);
All the arguments in my function are well initializated, but my program breaks: why?
Sometimes Hangs Program....Posted by Legacy on 08/17/2000 07:00am
Originally posted by: Brent
This is a great piece of code and most of the time works perfectly. However it seems that on very rare occasions, I will get stuck in the function:
hconvCppServer = DdeConnect ( idInst, hszCppServer, hszCppTopic, NULL );
for up to a minute. Do you know if there is anything I can do to avoid this?
Thanks!
Another method that works well, easy to use.Posted by Legacy on 05/24/2000 07:00am
Originally posted by: Todd Osborne
The free VWCL class library () has a class called VSingleInstanceAppRestriction. It uses Win32 named mutexes to achieve similar results. One nice thing about it is that it can communicate with the original instance of a running application using simpler WM_COPYDATA messages. That way the 1st instance can receive information about the second instance, such as command line parameters or passed in file names. It can then act on it.
ToddReply
Bravo!Posted by Legacy on 07/01/1999 07:00am
Originally posted by: Chuck Messenger
Great stuff -- thanks for sharing it! Having tried various methods in the past, and having studied the other methods presented on the CodeGuru site, I was very pleased to learn about the DDE method you describe. It is easily the most elegant solution, and works as well with a dialog-based app as with a "regular" app.Reply