Σάββατο 24 Σεπτεμβρίου 2011

Μοναδικό στιγμιότυπο προγράμματος

Πολλές εφαρμογές τις θέλουμε να τρέχουν μόνο με ένα στιγμιότυπο. Σε αυτό το άρθρο θα αναλύσω έναν από τους τρόπους που μπορούμε να καταφέρουμε το παραπάνω.



Ο σκοπός


     Έχουμε μια εφαρμογή που για τον χ,ψ λόγο δεν θέλουμε να τρέχει δυο φορές ταυτόχρονα. Ή η εφαρμογή μας τρέχει στο παρασκήνιο, και με το πάτημα του exe θέλουμε να την αφυπνίσουμε.

Παραδείγματα εφαρμογών


     Εφαρμογές που εφαρμόζουν αυτή τη "τακτική" είναι τα antivirus και τα clients torrents, chat, etc.

Η υλοποίηση


     Προσωπικά θα το υλοποιήσω με global mutex. Το mutex δεν είναι για αυτό το σκοπό, αλλά μας λύνει τα χεριά. Το σκεπτικό απλό, το πρόγραμμα πριν κάνει οτι κάνει θα φτιάξει ένα mutex στο global namespace, θα κρατήσει το αποτέλεσμα απο την συνάρτηση κατασκευής mutex και αν το αποτέλεσμα είναι "το mutex υπάρχει" τότε θα τερματίζει, σε αντίθετη περίπτωση θα συνεχίσει κανονικά.
     Η συνάρτηση που θα χρησιμοποιήσουμε είναι η CreateMutex.


Η πρώτη υλοποίηση είναι σε κονσόλα

#include <windows.h>
#include <iostream>
#include <string>
#include <tchar.h>

class UniqueInstance
{
private: 
    HANDLE    hMutex;
    BOOL    bIsOpen;
public:
    UniqueInstance(LPCWSTR lpswInstanceName)
        : hMutex(NULL)
    {
        /*
            Φτιαχνουμε ενα name στο kernel
            objects namespace, δηλαδη βαζουμε
            το Global\ perfix στο lpswInstanceName
        */
        std::wstring mutexName(L"Global\\");
        mutexName+= lpswInstanceName;
        
        /*
            φτιαχνουμε το mutex
        */
        hMutex = CreateMutex(NULL,FALSE,mutexName.c_str());

        /*
            Αν το mutex υπαρχει, τοτε το lasterror
            θα ειναι ERROR_ALREADY_EXISTS
        */
        bIsOpen = ERROR_ALREADY_EXISTS == GetLastError();
        
    }
    ~UniqueInstance()
    {
        /*
            Παντα καθαριζουμε τα σκουπιδια μας
        */
        if(hMutex)
        {
            ReleaseMutex(hMutex);
            CloseHandle(hMutex);
        }
    }
    BOOL IsOpen()
    {
        return bIsOpen;
    }
    operator BOOL()
    {
        return bIsOpen;
    }
};


int main(int ,char**)
{
    /*
        Το προγραμμα
    */
    /*
        Φτιαχνουμε ενα instance της class UniqueInstance
        με ενα unique string 
    */
    UniqueInstance unique = L"MyProgramPlusSomeRandom"; 
    if(unique) // ή if(unique.IsOpen())
    {
        std::cout
            << "To programa trexei idi se alo process"
            << std::endl;
        return 0;//exit
    }
    std::cout
        << "Hello World"
        << std::endl;
    Sleep(5000); // do something..
    return 0;
}

Αρκετά μίνιμουμ :P


Και η δεύτερη σε window/systray


#include <Windows.h>
#include <string>
class UniqueInstance
{
private: 
    HANDLE    hMutex;
    BOOL    bIsOpen;
public:
    UniqueInstance(LPCWSTR lpswInstanceName)
        : hMutex(NULL)
    {
        /*
            Φτιαχνουμε ενα name στο kernel
            objects namespace, δηλαδη βαζουμε
            το Global\ perfix στο lpswInstanceName
        */
        std::wstring mutexName(L"Global\\");
        mutexName+= lpswInstanceName;
        
        /*
            φτιαχνουμε το mutex
        */
        hMutex = CreateMutex(NULL,FALSE,mutexName.c_str());

        /*
            Αν το mutex υπαρχει, τοτε το lasterror
            θα ειναι ERROR_ALREADY_EXISTS
        */
        bIsOpen = ERROR_ALREADY_EXISTS == GetLastError();
        
    }
    ~UniqueInstance()
    {
        /*
            Παντα καθαριζουμε τα σκουπιδια μας
        */
        if(hMutex)
        {
            ReleaseMutex(hMutex);
            CloseHandle(hMutex);
        }
    }
    BOOL IsOpen()
    {
        return bIsOpen;
    }
    operator BOOL()
    {
        return bIsOpen;
    }
};

LPCWSTR lpszWndClsName = L"MySuperDuperWindow";


HRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);


int WINAPI wWinMain(HINSTANCE hInst,HINSTANCE,LPWSTR,int nShow)
{
    WNDCLASS wc;
    HWND hWnd;
    NOTIFYICONDATA tray;
    MSG    msg;
    UniqueInstance unique = L"MyUniqueString";
    ZeroMemory(&wc,sizeof(wc));
    ZeroMemory(&tray,sizeof(tray));

    wc.lpfnWndProc        = WndProc;
    wc.lpszClassName    = lpszWndClsName;
    
    
    if(unique)
    {
        //υπαρχει, βρες το και στειλτου sw_show
        hWnd = FindWindow(lpszWndClsName,NULL);
        if(hWnd)
            ShowWindow(hWnd,SW_SHOW);
        return 0;//και exit !
    }

    if(!RegisterClass(&wc))
        return 1;
    hWnd = CreateWindow(lpszWndClsName,L"Hello World",WS_OVERLAPPEDWINDOW,0,0,200,200,NULL,NULL,hInst,NULL);
    ShowWindow(hWnd, nShow);
    tray.cbSize                = sizeof(tray);
    tray.uCallbackMessage    = WM_USER + 1;
    tray.hWnd                = hWnd;
    tray.uFlags                = (NIF_ICON | NIF_MESSAGE | NIF_TIP);
    tray.hIcon                = LoadIcon(NULL,IDI_APPLICATION);
    Shell_NotifyIcon(NIM_ADD,&tray);
    while(GetMessage(&msg,0,0,0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    Shell_NotifyIcon(NIM_DELETE,&tray);
    return 0;
}
HRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam)
{
    static HMENU popupmenu;
    POINT p;
    switch(msg)
    {
    case WM_USER + 1:
        switch(lParam)
        {
        case WM_RBUTTONDBLCLK:
        case WM_LBUTTONDBLCLK:
        case WM_RBUTTONDOWN:
        case WM_LBUTTONDOWN:
            GetCursorPos(&p);
            switch(TrackPopupMenu( popupmenu,0,p.x,p.y,0,hWnd,NULL))
            {
            case 1:
                PostQuitMessage(0);
                break;
            case 2:
                ShowWindow(hWnd,SW_SHOW);
                break;
            }
            break;
        }
        break;
    case WM_CREATE:
        popupmenu = CreatePopupMenu();
        AppendMenu(popupmenu,MF_STRING,1,L"Close");
        AppendMenu(popupmenu,MF_STRING,1,L"Show");
        break;
    case WM_CLOSE:
        ShowWindow(hWnd,SW_HIDE);
        break;
    default:
        return DefWindowProc(hWnd,msg,wParam,lParam);
    }
    return 0;
}

Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου