There are many ways for a program to communicate with another program.

Depending on what you are trying to achieve, one or another method might be better suited.

Let's start with discussing the definition of interprocess communication:

Generally speaking, interprocess communication is: When two processes share data with each other

Technically it is not possible to use a PC without using some form of interprocess communication. This is because when the user tries to open a file, the operating systems shell has to tell an appropriate program what file the user wants to be opened.

And this is where our dive into interprocess communication begins: Process Arguments or Startup Parameters.

Process Arguments is arguably the easiest way for one process to transfer data to another process, as it can be initiated from within most programming languages. If you can start a process, you usually can also control the Startup Parameters.

We should look at a few examples!

  1. If you want Notepad to open a file for you, you could either open Notepad and navigate through the menues on the top and open the file you want to open. Or you could start notepad like follows, for example via the command line: notepad.exe file.txt. That way notepad automatically opens the file for you.

  2. A kind of wrapper for the same mechanism: If you drag one file onto an executable (or link to an executable) the process is opened with the filepath and name in the Process Arguments. Let's say you've got a document file on your desktop and a reference to an Office Application on your desktop: If you drag the document file onto the reference to your Office Application, Windows automatically displays the message "open with [your office application]". And if you let go while still hovering over the reference, the document file will be opened as if you selected it within the open-file-menu in the Office Application.

  3. If you've ever used a command line before, you should have noticed that most programs utilize Process Arguments for a simple way of configuring the way that program works. For example the CD command accepts one argument which tells the CD command to what directory it should change the current directory. For example on windows, you can go to the Windows folder from anywhere in the file system, by typing CD C:\Windows into your command line. Then you can go to the root directory of your C-drive by typing CD .. into your terminal session.

Ok, now we are ready to look into a custom example! In this simple architecture there is a producer and a consumer. The consumer starts the target process with the appropriate arguments and the consumer does some calculations with those values.

The easy part: The Consumer.

int main(int argc, char *argv[]) {
printf("%s\n", argv[1]); // Print the argument to the terminal

return (int) (argv[1][0]); // Return the first character of the Argument.
}

Note: There is a new feature of this approach to interprocess communcation: process exit values, usually an integer for historic reasons.
Note 2: I omitted the includes for clarity reasons.

The not so easy part: The Producer.

#include <windows.h>

void main(void) {
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si); // Tell the Operating System what version of the Startupinfo field we want to use

CreateProcess( NULL, // No module name (use command line)
"Consumer.exe arg1", // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ); // Pointer to PROCESS_INFORMATION structure

HANDLE processHandle = pi.hProcess;

// Wait until child process exits.
WaitForSingleObject( processHandle, INFINITE );

// Consumer has produced a new return value for us:
DWORD returnCode = 0;
GetExitCodeProcess(processHandle, &returnCode);

printf("%c\n", (char) returnCode); // Print the returned value onto the terminal
}

Note: I omitted error checking, cleanup and most of the includes in this example for clarity reasons.

Starting the Producer process should now print something like this onto the terminal:

arg1
a

We've succesfully created two programs that can communcate with each other!


There are also some drawbacks to this approach of sharing data with a process that might not be apparent right away:

  1. Depending on your operating system, you might not be able to pass arguments with a NULL-character (\0) to a new process, as most APIs only directly accept a String for process initialization, which by definition of the C-language has to be NULL-terminated.

  2. Depending on your command line interpreter, certain arguments may not be passed to your process right away, as some characters are interpreted seperatly from normal arguments, namely the pipe-symbol (|). You can circumvent this issue by putting your whole argument into quotation marks. But then what happens if the path to the program does have spaces in it, like C:\program.exe foldername\yourexecutable.exe process arguments? Depending on your operating system, only program.exe will get executed, if it even exists and even though you wanted it to be part of your arguments list, "foldername\yourexecutable.exe" will be given to the process as if it was part of the arguments list.

  3. This is a limitation of most programming languages, or rather how they are used (in some cases like C): The arguments are automatically split into multiple arguments with the space-seperator. When a process is started like this: executable.exe arg1 arg2, the process is not given one argument with the String "arg1 arg2" or something similar, but rather "arg1" and "arg2" in two seperate arguments. This can also be circumvented in multiple ways: Depending on the interpreting language, sometimes quotation marks might solve the issue, but depending on your Operating System, you might instead want to ask the Operating System directly for what the Process Arguments were. In Windows the appropriate function is: GetCommandLineA() which directly returns the whole command line string instead of the split arguements.

There are many more subtleties to the drawbacks. But since you are propably using this way of interprocess communcation only because of user interaction and not because you want to store values in a database, this reduced list of drawbacks should be fine for most usecases.


There are also many more ways for two processes to communcate with each other! We will explore them in the coming weeks, as there will be close to 10 parts. (Number 10 will surprise you!)