#include <windows.h>
#include <Psapi.h>
#include <stdio.h>

#define ID_LISTBOX 100
#define MY_TIMER     102
#define TIMER_PERIOD 750

HWND hListbox, hwnd;
bool bFirstInstance = true;

HANDLE myEvent;

LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);


char szClassName[] = "CodeBlocksWindowsApp";
char szTitle[] = "Code::Blocks Template Windows App";


BOOL InitWndClass(HINSTANCE hThisInstance)
{
    WNDCLASSEX wincl;

    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;
    wincl.style = CS_DBLCLKS;
    wincl.cbSize = sizeof (WNDCLASSEX);

    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;
    wincl.cbClsExtra = 0;
    wincl.cbWndExtra = 0;
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    // Неудача при регистрации класса окна
    if (!RegisterClassEx (&wincl)) {
        return FALSE;
    }

    return TRUE;
}

bool IsFirstInstance()
{
    HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS, 0, "VolvoMutex1.0");
    if (!hMutex) {
        // если программа запускается первый раз, то создаем Mutex
        hMutex = CreateMutex(0, 0, "VolvoMutex1.0");
        return true;
    }
    return false;
}


int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance,
                    LPSTR lpszArgument, int nCmdShow)
{
    MSG messages;

    bFirstInstance = IsFirstInstance();

    // Регистрируем класс окна в любом случае
    if(!InitWndClass(hThisInstance)) {
        return 0;
    }

    // Создаем класс окна
    hwnd = CreateWindowEx(0, szClassName,
        (bFirstInstance ? szTitle : ""), // первая копия - szTitle, остальные - пусто
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 544, 375,
        HWND_DESKTOP, NULL, hThisInstance, NULL);

    if(bFirstInstance) {
        // Первая копия приложения:
        hListbox = CreateWindow("LISTBOX", NULL,
            (WS_CHILD | LBS_STANDARD | WS_VSCROLL | LBS_DISABLENOSCROLL | WS_VISIBLE) & (~LBS_SORT),
            30, 30, 400, 320, hwnd, (HMENU)ID_LISTBOX, hThisInstance, NULL
        );
        ShowWindow (hwnd, nCmdShow);

        // Информация...
        SendMessage(hListbox, LB_ADDSTRING, 0, (LPARAM)"First instance of application");

        // Создаем событие ...
        myEvent = CreateEvent(NULL, FALSE, FALSE, "VolvoTestEvent");
        // ... и устанавливаем его
        SetEvent(myEvent);
    }
    else {
        // Не первый экземпляр:

        // Находим первую копию - единственную, у которой есть окно с заголовком ...
        HWND first_inst = FindWindow(NULL, szTitle);
        if(first_inst) {
            // ... и переносим его на первый план, если нашли
            SetForegroundWindow(first_inst);

            // Находим в окне первой копии ListBox, куда будем записывать
            hListbox = FindWindowEx(first_inst, NULL, "listbox", NULL);
            if(hListbox) {
                // Информация
                SendMessage(hListbox, LB_ADDSTRING, 0, (LPARAM)"Another instance of application");
            }
            else {
                // Не нашли ListBox
                MessageBox(NULL, "Cannot find LISTBOX control", "Error", MB_OK | MB_ICONEXCLAMATION);
            }
        }
        else {
            // Не нашли окно первой копии
            MessageBox(NULL, "Cannot find first-instance window", "Error", MB_OK | MB_ICONEXCLAMATION);
        }

        // Получаем созданный ранее Event
        myEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "VolvoTestEvent");
    }

    // Все, разные действия для первой и не первой копии закончились...

    // Теперь для любой копии устанавливаем таймер
    SetTimer(hwnd, MY_TIMER, TIMER_PERIOD, NULL);

    // И запускаем основной цикл обрабртки сообщений
    while (GetMessage (&messages, NULL, 0, 0)) {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
    }
    return messages.wParam;
}

// Callback-функция, будет использоваться при закрытии первой копии приложения
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    HWND hWndFirst = (HWND)lParam;
    if(hwnd != hWndFirst) {
        char s[256] = "";
        GetClassName(hwnd, s, 256);
        if(!strcmp(s, szClassName)) {
            PostMessage(hwnd, WM_DESTROY, 0, 0);
        }
    }
    return TRUE;
}

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
        case WM_TIMER:
        {
            // Ждем наступления события
            WaitForSingleObject(myEvent, INFINITE);

            char s[50] = {0};
            sprintf(s, "Event: %d", myEvent);
            // Информация - откуда приходят данные
            SendMessage(hListbox, LB_ADDSTRING, 0, (LPARAM)s);

            for(int i = 0; i < 5; i++) {
                // считаем и показываем, что что-то происходит
                sprintf(s, "App = %d; n = %d", hwnd, i);
                SendMessage(hListbox, LB_ADDSTRING, 0, (LPARAM)s);
            }
            SendMessage(hListbox, LB_SETCARETINDEX,
                SendMessage(hListbox, LB_GETCOUNT, 0, 0), TRUE);

            SetEvent(myEvent);
            break;
        }

        case WM_DESTROY:
            if(bFirstInstance) {
                EnumWindows(&EnumWindowsProc, (LPARAM)hwnd);
            }
            CloseHandle(myEvent);
            KillTimer(hwnd, MY_TIMER);

            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}