Single Instance Program

There are already two article about "Single instance", both of them use a "mutex". All the code worked perfectly, and it is enough for most case. But if you want to do something more as following, you may choose this one which use "DDE" ( dynamic data exchange ).

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.

Yes, "Winword" use DDE to ensure only one instance can be run. Do a test by changed the CppServerName to "Winword" and changed CppTopicName to "System" ( the two are both const in program ). Run it, then word will active my program if you click a .doc file again. ( you should know, winword can be run with muti-instance by click the program icon. It only detect instance when you click a .doc file )

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.



Comments

  • Great Article!!

    Posted by sabapathy on 03/23/2006 03: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.com

    Reply
  • Why make it hard on yourself...

    Posted by Legacy on 03/25/2002 12:00am

    Originally posted by: Mouse

    to detect if another instance of your app is running all you have to do is:
    
    

    1) on the INIT function of your app create a mutex as so:

    ::CreateMutex(NULL, FALSE, "UniqueMutexHandleForYourApp");

    2) call ::GetLastError() and see if the mutex has allready been created (obviously it allready exists since there is allready one instance of your app running that allready created it successfully)

    if(::GetLastError() == ERROR_ALREADY_EXISTS){
    // dont load this instance.
    return FALSE;
    }

    When the instance that owns the Mutex closes, it will automatically release the mutex and close the handle to it, so you dont ever have to worry about it again.

    Reply
  • Excellent

    Posted by Legacy on 02/12/2002 12:00am

    Originally posted by: Aries

    Thanks Raymon, saved my nutsack. Great code, easy to add.

    Reply
  • Problems with DdeConnect

    Posted by Legacy on 10/25/2000 12: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:
    hconv = DdeConnect(idInst,hservname,htopicname,NULL);
    All the arguments in my function are well initializated, but my program breaks: why?

    Reply
  • Sometimes Hangs Program....

    Posted by Legacy on 08/17/2000 12: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!

    Reply
  • Another method that works well, easy to use.

    Posted by Legacy on 05/24/2000 12:00am

    Originally posted by: Todd Osborne

    The free VWCL class library (www.vwcl.org) 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.

    Todd

    Reply
  • Bravo!

    Posted by Legacy on 07/01/1999 12: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
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • When it comes to desktops – physical or virtual – it's all about the applications. Cloud-hosted virtual desktops are growing fast because you get local data center-class security and 24x7 access with the complete personalization and flexibility of your own desktop. Organizations make five common mistakes when it comes to planning and implementing their application management strategy. This eBook tells you what they are and how to avoid them, and offers real-life case studies on customers who didn't …

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds