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.