Thread: Shell extensions and single application instance

  1. #1
    Registered User
    Join Date
    Mar 2005
    Location
    Juneda
    Posts
    291

    Shell extensions and single application instance

    Hello, I have recently finished a simple program that setsup several timers to help in monitoring several tasks; the task files are xml files with the description and other stuff; I can load them from the application's menu, or (once started) dragging the file inside the application area; for each task the application opens a mdi child.

    The problem: I have registered a file estension for that task files, but a new instance of my application is opened for each "doubleclick" over the task file (so the mdi is totally useless). Is there a way to prevent multiple instances of the same application and at the same time redirect the launching information (in that case the information is the path to the task file that I want to load) to the unique application?

    I know how to check for another instance of the application (to alert the user or quit) (better say, I know that I have a code snippet to check the single instance), but I'm not able to find a solution to redirect the information to the unique instance.

    I have the idea that maybe with pipes it can be solved, but I have never used them and I still haven't started to learn more about that. Is that the way to do it?

    Also I have thought: if I can communicate directly between two instances, isn't that a security hole??

    Thank's in advance

  2. #2
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    You can, for example, send a message to your unique instance's window.
    The data could be stored in shared memory or copied using some sort of WM_COPYDATA message.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  3. #3
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    IPC largely depends on the app, how critical the comms are and the target environment.

    For this task I would look at Mailsots and HWND_BRODCAST (but that is personal choice...)

    A mailslot (CreateMailSlot()) is fairly easy.
    Once started the app creates a mailslot and periodically checks it for msgs (or runs it in a thread).
    On start if the app finds another already running it writes the file name to the mailslot.

    OR...

    You could, depending on the target environment (possibly not the best solution for an app running on everybodies desktop).

    Define a 'user' msgs with RegisterWindowMessage() when the app starts.

    If you find the app is already running, send a HWND_BROADCAST msg to all apps with SendMessageTimeout() (using the params to hold the string). Read the docs as this can take a while....

    If your app gets a UM_FILEPROC msg, it should add the file to the list.

    If the file processing is critical I recommend you make the second instance of the app to keep sending the msg until the first instance responds (ack'ing the msg to process the file).
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  4. #4
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Take a close look at CreateMutex(), FindWindow() and WM_COPYDATA...

    The process I use is ...
    a) Create a mutex object immediately upon launching the file, before creating any windows.
    b) if the "already exists" mutex test comes back false, carry on, it's the first instance
    c) if it comes back true, find the previous instance and send it the filename in WM_COPYDATA
    d) exit the second instance.
    e) in the first instance process the WM_COPYDATA message just like it's a file on the command line.


    Here are some snippets that may help...
    Code:
        // Test if first copy.  If not first copy
        // send command line to first copy.
        CreateMutex(NULL,1,"MUTEX_EXAMPLE_MUTEX"); 
        if (GetLastError() == ERROR_ALREADY_EXISTS)
          { SendHandOff(cmdl);
            return 0; }
    
    
    
    
         // comandline handoffs arrive here
         case WM_COPYDATA :
              GetHandOff((PCOPYDATASTRUCT)lparm);
              return 1;
    
    
    
    // Send second copy's command line to first copy
    // The dwData member is being used as a password
    void SendHandOff(PCHAR cmdstr)
      { COPYDATASTRUCT cd;
        HWND           mw;
        mw = FindWindow("MUTEX_EXAMPLE","MutexExample");
        if (mw)
          { cd.dwData = 0xf800f800;
            cd.cbData = strlen(cmdstr)+1;
            cd.lpData = cmdstr;
            SendMessage(mw,WM_COPYDATA,0,(LPARAM)&cd); } }
    
    
    // Check dwData for the password.  If it matches
    // parse a command line from the second copy
    void GetHandOff(PCOPYDATASTRUCT cd)
      { if (cd->dwData == 0xf800f800)
          ParseCommandLine(cd->lpData); }

  5. #5
    Registered User
    Join Date
    Mar 2005
    Location
    Juneda
    Posts
    291
    Hello and thanks for all the ideas and codes, sorry if I'm delayed on responding.

    I'm thinking on sendig the HWND_BROADCAST message because it seems the easyest way; let me coding for a while, and see what it happens.

    More thanks for the help,
    Niara

  6. #6
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Sounds good... experimenting is a great way to learn.

  7. #7
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    BTW.

    UM_FILEPROC msg is a typo. It was an example of a 'User defined Message' name, the rest of which I cut from my response.

    Quote Originally Posted by CommonTater View Post
    c) if it comes back true, find the previous instance and send it the filename in WM_COPYDATA
    Many file processing apps (especially MDI ones) add the file name to the window's title, rendering the FindWindow() method useless.

    I would also not use SendMessage() [as a race condition can result in a deadlock], I much prefer the safety of SendMessageTimeout() or PostMessage(), depending on how critical the comms are.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  8. #8
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by novacain View Post
    BTW.
    Many file processing apps (especially MDI ones) add the file name to the window's title, rendering the FindWindow() method useless.
    Not at all... here's the prototype...

    HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName);

    Use the classname you registered the window under and set the window name to null. If you use a classname that's likely to be unique it will reliably find previous instances of your application. I typically use classnames that are a combination of the project name, version and my own name, and I've never had a problem finding previous instances.


    I would also not use SendMessage() [as a race condition can result in a deadlock], I much prefer the safety of SendMessageTimeout() or PostMessage(), depending on how critical the comms are.
    WM_COPYDATA passes a pointer. If the message times out or returns before the data is copied, it's lost and will cause errors in the receiver. If the app is prepared to both send and receive these messages SendMessage works just fine.

    The only way a race condition can result is if both copies are sending at once... this is a single message sent one way from a second instance to the first. The second copy exits as soon as the message handler in the first returns.

    I've never seen this fail...

  9. #9
    Registered User
    Join Date
    Mar 2005
    Location
    Juneda
    Posts
    291
    Right here's what I have used to solve that; for the moment is working in all the tests I have done, but maybe you'll find some bugs (I'm not asking to solve them ); I post the code because maybe someone else can be interested in it:

    Code:
    HWND hwnd_dbl_inst_proc;
    
    
    unsigned char CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam)
    {
    unsigned long ul,dw;
    PROCESSENTRY32 *pe32=(PROCESSENTRY32*)lParam;//see the next function
    
    GetWindowThreadProcessId(hwnd,&dw);
    if(pe32->th32ProcessID==dw)//match the window
        {
        hwnd_dbl_inst_proc=hwnd;//store on the global variable
        return 0;//and stop the EnumWindows function
        }
    
    return 1;
    }
    
    
    unsigned char instanciaUnica(char* np)//translation: instanceSingle, returns 1 for yes and 0 for no
    {
    HANDLE hProcessSnap;
    PROCESSENTRY32 pe32;//that needs to include tlhelp32.h
    int ctd=0;
    
    hProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,374837483);
    pe32.dwSize=sizeof(PROCESSENTRY32);
    if(Process32First(hProcessSnap,&pe32))//loop for all the procs
    	{
    	do
    	    {
    	    if(lstrcmpi(pe32.szExeFile,np)==0)//match program name
    		    {
    		    if(ctd==0)//match first
                            {
                            hwnd_dbl_inst_proc=NULL;
                            EnumWindows((WNDENUMPROC)EnumWindowsProc,(LPARAM)&pe32);//search the hwnd of the window
                            }
                        ctd++;
    		    }
    	    } while(Process32Next(hProcessSnap,&pe32));
    	}
    CloseHandle(hProcessSnap);
    
    return (ctd>1)?0:1;
    }

    The use is easy, just check for the existence of a previous instance of the application by:

    Code:
    int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszArgument,int ncs)
    {
    if(!instanciaUnica(applications_name_and_extension))
        {
        if(hwnd_dbl_inst_proc)
            {
            PostMessage(hwnd_dbl_inst_proc,...);//notify the situation to the first instace application
            }
        return 0;//quit the application
        }
    ...//startup the application if you reach beyond last conditional
    }

    Here on the PostMesage I'm using a WM_USER+2 message althought I have seen in CBoard the use of MAXINTATOM+index to define user messages and the RegisterWindowMessage function. Something like that:

    Code:
    #define WM_MULTIPLE_INSTANCE            WM_USER+2
    
    PostMessage(hwnd_dbl_inst_proc,WM_MULTIPLE_INSTANCE,0,(LPARAM)"Test");

    There is a note on the win32 manual about the PostMessage function:

    Code:
    If you send a message in the range below WM_USER to the asynchronous message functions (PostMessage, 
    SendNotifyMessage, and SendMessageCallback), make sure that the message parameters do not include pointers. 
    Otherwise, the functions will return before the receiving thread has had a chance to process the message and 
    the sender will free the memory before it is used.
    ... so since I want to send a char* using WM_USER+2 will work right.

    The rest is simply check for the WM_MULTIPLE_INSTANCE message and respond in consequence. In my testing I simply showed the first instance window and show a MessageBox with the text passed as LPARAM.

    Hope that can be useful for others.

    Niara
    Last edited by Niara; 10-18-2010 at 03:38 PM.

Popular pages Recent additions subscribe to a feed