Inter-Process Communication (IPC) for Windows with C

13,510

Solution 1

If your old C application has a message pump (because it has an UI) maybe the simpliest way to check if it's alive or not is IsHungAppWindow() function and Windows will do the stuff for you.

If this is not your case and you need IPC there are many options, it depends on what kind of IPC mechanism you want to use. Here I'll just list some resources.

For an overview of IPC techniques: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365574(v=vs.85).aspx

Some examples:

EDIT
I think a small example will clarify much more than tons of words. In this example I'll use shared memory but you can use whatever you prefer (and you feel more comfortable with). It's untested so please use it just as reference.

The MONITOR process, should be started first.

VOID CALLBACK CheckItIsAlive(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
    static int volatile latestMagicNumber = 0;

    int currentMagicNumber = 0;
    CopyMemory(&currentMagicNumber, lpParam, sizeof(int));

    if (currentMagicNumber != latestMagicNumber)
        latestMagicNumber = currentMagicNumber;
    else
    {
        // Do something, it's hanged
    }
}

void main()
{
    // Shared memory used to communicate with the other process
    HANDLE mappedFile = CreateFileMapping(INVALID_HANDLE_VALUE,
        NULL, PAGE_READWRITE, 0, sizeof(int), "Global\\MyActivityMonitor");

    LPVOID pSharedMemory = MapViewOfFile(mappedFile,
        FILE_MAP_READ, 0, 0, sizeof(int));

    // Thread used to check activity
    HANDLE queue = CreateTimerQueue();
    HANDLE hTimer = NULL;
    CreateTimerQueueTimer(&hTimer, queue, 
        (WAITORTIMERCALLBACK)CheckItIsAlive, pSharedMemory,
        0, 5000, WT_EXECUTEDEFAULT);

    // Do your work here...

    // Clean up
    DeleteTimerQueue(queue);

    UnmapViewOfFile(pSharedMemory);
    CloseHandle(mappedFile);
}

The MONITORED process, it'll signal its activity to the Monitor process.

VOID CALLBACK NotifyImAlive(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
    static int volatile counter = 1;

    int tick = counter++;
    CopyMemory(lpParam, &tick, sizeof(int));
}

void main()
{
    // Shared memory used to communicate with the other process
    HANDLE mappedFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE,
        "Global\\MyActivityMonitor");

    LPVOID pSharedMemory = MapViewOfFile(mappedFile,
        FILE_MAP_WRITE, 0, 0, sizeof(int));

    // Thread used to signal activity
    HANDLE queue = CreateTimerQueue();
    HANDLE hTimer = NULL;
    CreateTimerQueueTimer(&hTimer, queue, 
        (WAITORTIMERCALLBACK)NotifyImAlive, pSharedMemory,
        0, 5000, WT_EXECUTEINTIMERTHREAD);

    // Do your work here...

    // Clean up
    DeleteTimerQueue(queue);

    UnmapViewOfFile(pSharedMemory);
    CloseHandle(mappedFile);
}

Shared memory is a pretty lightweight resource and you can use whatever you prefer for your timers (if timing isn't a strict requirement you can do some kind of idle processing. Personally I like this 'cause you won't need to lock any thread and probably you have an idle time processing thread).

Timer functions are supported starting from Windows 2000, be sure that _WIN32_WINNT macro is defined with 0x0500 (or more).

Addendum
I didn't mentioned in the list because they exist only in newer versions of OS but you may even use condition variables. Windows 8 will support a very useful WaitOnAddress function but it's still the future so I think you can't use it.

Solution 2

From the OP and the various comments, it sounds as if the main goal is to determine if the application is hung. A couple of fairly simple ways to create some kind of "heart beat" that can be monitored by another application would be either shared memory or a named semaphore.

You could use CreateFileMapping and MapViewOfFile in one process to create shared memory and then use MapViewOfFile in the other process to obtain a pointer to it. If you created it to be the size of an integer, a simple method of keep-alive would be to have the process increment the value in memory every few seconds. The other process can read it every few seconds to verify that it is changing.

With a named semaphore (CreateSemaphore and OpenSemaphore), you could do basically the same thing. Have the monitored app signal it periodically and have the monitor wait on it to make sure it has been signaled.

Solution 3

This seems to be another case of going about something the very long way due to a lack of familiarity with the platform you're dealing with.

If all you need to know as you say in your comment is whether or not your program is alive so you can kill it, you don't even remotely need IPC.

In the beginning of the program you wish to monitor:

HANDLE hMutex = CreateMutex(NULL, FALSE, _T("MyMagicKey"));
WaitForSingleObject(hMutex, INFINITE);

In the "watchdog" program, you check if the other utility is alive like this:

HANDLE hMutex = OpenMutex(SYNCHRONIZE, FALSE, _T("MyMagicKey"));
if (hMutex == NULL && GetLastError() == ERROR_FILE_NOT_FOUND)
   //The app being watched is already dead
else
   //whatever you want

There are a half-dozen other solutions that apply equally well (or better). If your watchdog is the only one that ever creates the app that's going to be monitored, you can wait on the HANDLE from CreateProcess (or ShellExecuteEx).

Share:
13,510
Roman Rdgz
Author by

Roman Rdgz

Telecom Engineer

Updated on June 14, 2022

Comments

  • Roman Rdgz
    Roman Rdgz almost 2 years

    I have an old program written in C with Microsoft Visual C++, and I need to implement some kind of "keepalive", so I am able to receive it thought interprocess communication into a new program which will kill and re-launch the first one if no msg has been received in the last 5 seconds.

    The problem is that I have been looking for any tutorial or example of IPC for Windows in C language, but almost everything I find is for C++.

    Any help or resource?

    EDIT: As @Adriano suggested in answers, I'm trying to use Shared Memory. But the launcher program is being terminated by Windows due to some kind of exception I'm not being able to catch. Happens when calling CopyMemory.

    The code is the following:

    #include "stdafx.h"
    #include "windows.h"
    #include "iostream"
    using namespace std;
    
    int launchMyProcess();
    void killMyProcess();
    bool checkIfMyProcessIsAlive();
    
    STARTUPINFO sInfo;
    PROCESS_INFORMATION pInfo;
    HANDLE mappedFile;
    LPVOID pSharedMemory;
    long lastReceivedBeatTimeStamp;
    const int MSECONDS_WITHOUT_BEAT = 500;
    const LPTSTR lpCommandLine = "MyProcess.exe configuration.txt";
    
    
        int main(int argc, char* argv[])
        {
            mappedFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(int), "Global\\ActivityMonitor");
            LPVOID pSharedMemory = MapViewOfFile(mappedFile, FILE_MAP_READ, 0, 0, sizeof(int));
            if(!launchMyProcess()){
                cout<<"Error creating MyProcess.exe"<<endl;
                UnmapViewOfFile(pSharedMemory);
                CloseHandle(mappedFile);
                return -1;
            }
            while(true){
                Sleep(100);
    
                if(!checkIfMyProcessIsAlive()){
                    cout<<"Relaunching MyProcess...";
                    killMyProcess();
                    if(!launchMyProcess()){
                        cout<<"Error relaunching MyProcess.exe"<<endl;
                        UnmapViewOfFile(pSharedMemory);
                        CloseHandle(mappedFile);
                        return -1;
                    }
                }
            }
    
            UnmapViewOfFile(pSharedMemory);
            CloseHandle(mappedFile);
            return 0;
        }
    
    
        bool checkIfMyProcessIsAlive()
        {
            static int volatile latestMagicNumber = 0;
            int currentMagicNumber = 0;
    
            CopyMemory(&currentMagicNumber, pSharedMemory, sizeof(int));
    
            if(currentMagicNumber != latestMagicNumber){
                latestMagicNumber = currentMagicNumber;
                return true;
            }
            return false;
        }
    
        int launchMyProcess()
        {
            ZeroMemory(&sInfo, sizeof(sInfo));
            sInfo.cb = sizeof(sInfo);
            ZeroMemory(&pInfo, sizeof(pInfo));
    
            return CreateProcess(NULL, lpCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &sInfo, &pInfo);
        }
    
        void killMyProcess()
        {
            TerminateProcess(pInfo.hProcess, 0);
            CloseHandle(pInfo.hProcess);
            CloseHandle(pInfo.hThread);
            Sleep(3000);
        }
    
  • Roman Rdgz
    Roman Rdgz almost 12 years
    It's a console application... and the idea is being able to launch and kill the old program programmatically from the new program. I need something simple just to know if the program is alive so I can decide if I kill the process. Is it possible to do that?
  • Adriano Repetti
    Adriano Repetti almost 12 years
    First comment doesn't sound really polite! :) He said "kill and re-launch" then the application to monitor didn't simply dead but it may be hanged.
  • Adriano Repetti
    Adriano Repetti almost 12 years
    @RomanRdgz Yes, you can use one of the IPC listed in the overview (I guess the most simple one). You can use the technique introduced by Mahmoud too, you'll have some kind of "ping-pong" mechanism (signaling events created with CreateEventEx instead of CreateMutex).
  • harry
    harry almost 12 years
    @Adriano Yes, he did in his OP - but then in the comment to your post, he says "I need something simple just to know if the program is alive so I can decide if I kill the process." which is a completely different question....
  • Roman Rdgz
    Roman Rdgz almost 12 years
    The FileMappings sounds really easy, but MSDN says that it is intended only for desktop applications, and I've got console applications here. Maybe I could try with Semaphores... but I don't want any of the processes to block waiting for a signal, so I guess it doesn't fix either to my solution. Any other idea?
  • Roman Rdgz
    Roman Rdgz almost 12 years
    As @Adriano said, I need to know if the process is hanged, not only if it has ended. That would be easy, as you have shown in the code. I need a heart beat from the old program, in such a way that if certain amount of seconds have passed since last heart beat, then I'm going to decide it is indeed hanged.
  • Adriano Repetti
    Adriano Repetti almost 12 years
    @RomanRdgz I didn't mention but MSDN uses "desktop application" for both console and GUI applications, it's needed to distinguish web, desktop and Metro.
  • Roman Rdgz
    Roman Rdgz almost 12 years
    I still have a problem: undeclared identifier at CreateTimerQueueTimer and DeleteTimerQueueTimer. I don't know why, I have "windows.h" included
  • Adriano Repetti
    Adriano Repetti almost 12 years
    @RomanRdgz It depends the version of the compiler and the SDK you're using, these functions are present from Windows 2000. Add #define _WIN32_WINNT 0x0500 before your windows.h inclusion.
  • Roman Rdgz
    Roman Rdgz almost 12 years
    I've also tried that before without any change. I'm with Visual C++ 6 over Windows XP, and I guess the SDK should be new enough then
  • Roman Rdgz
    Roman Rdgz almost 12 years
    Ok, maybe that's the problem: I don't have WinBase.h in my drives. How is that possible?
  • Roman Rdgz
    Roman Rdgz almost 12 years
    I tried to find it with windows search function, but no, it doesn't exist in this computer
  • Roman Rdgz
    Roman Rdgz almost 12 years
    Ok, I do have it, but then is the compiler the one who is not able to find it. I tried to add it to the project inside header files folder, but still the same errors
  • Mark Wilkins
    Mark Wilkins almost 12 years
    @RomanRdgz: I believe that a console application running in Windows would be considered a desktop app; you can call Windows API functions from a console application.
  • Roman Rdgz
    Roman Rdgz almost 12 years
    How can I check the value os a define? Is there a function for that into the API? I remember something for that but I can't find it
  • Roman Rdgz
    Roman Rdgz almost 12 years
    Ok @Adriano , in order to avoid problems with the TimerQueue, I've done the waiting in a different way. It compiles, but the launcher is being terminated with typical dialog "myprogram.exe has detect a problem and is going to terminate. Send diagnostic..." or whatever says in the english version of windows. It happens when executing CopyMemory at the launcher
  • Adriano Repetti
    Adriano Repetti almost 12 years
    @RomanRdgz how do you get that pointer? Is it a valid pointer if you read it after then MapViewOfFile?
  • Roman Rdgz
    Roman Rdgz almost 12 years
    I have posted the code for the launcher at the Question. I've read the pointer after the MapViewOfFile and its value is 003A0000
  • Adriano Repetti
    Adriano Repetti almost 12 years
    @RomanRdgz you shadowed the pSharedMemory variable when you call MapViewOfFile (you saved the returned pointer to a local variable and the global one is left uninitialized so it points to garbage).