How to send information between C++ applications
In this snippet, I am going to present you a solution on how to send information between C++ windows type applications.
We came across a situation in which we had to pass a specific information between two applications. In our case, we had to pass the command line arguments from one instance of a window application to another instance of the same application. So, the main instance of the application, the one that was opened first, would know the command line arguments of the second.
In order to send information from one instance to another, one should use the SendMessage function which requires the Windows.h header.
An example of a call is SendMessage (main_window_handle, WM_ID, 0, LPARAM (“the information you would like to send”));
Parameter | Explanation |
main_window_handle | Represents the handle to the window to which we would like to send the information. |
WM_ID | Represents a defined ID that we will later listen to in the WndProc(WndProc is a function constructed by default in a window type application and it processes messages for the main window). |
WPARAM | Can be used to send information |
LPARAM | Can be used to send information |
In order to get a handle to the window to which we want to send the information, you can use the FindWindow function.
An example of a call is FindWindow (NULL, my_window_title);
- The first parameter is NULL because we do not need to pass a class name.
- Parameter my_window_title - represents the title of the window we want to find.
HWND main_window_handle = FindWindow(NULL, my_window_title); |
If the function succeeds, the return value is a handle to the window that has the specified window name. If the function fails, the return value is NULL.
HANDLE unique_mutex; unique_mutex = CreateMutex(NULL, FALSE, L"MyTestApp"); if(unique_mutex == NULL) { std::cout<< "Mutex was not successfully created!!!"; } else if(GetLastError() == ERROR_ALREADY_EXISTS) { g_another_instance_already_running = true; } |
The CreateMutex function creates a named or unnamed mutex object and gives ownership over it to the first thread that opens it. If the mutex is a named mutex and the thread that tries to open it it’s not its owner, the return value is a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS. In that case, we are simply setting the value of a boolean to true.
We are later using this boolean to see if we are allowing our window to show, if the boolean is false we are allowing it and if it is true we are not allowing it and sending the message to the main instance.
This is an example of how to use COPYDATASTRUCT to reference information:
auto command_line_arguments = ::GetCommandLine();
//Create a COPYDATASTRUCT to send the information
COPYDATASTRUCT data_to_send = {0};
data_to_send.dwData = kCommandLineArgsMessageId;
data_to_send.cbData = (DWORD)(strlen(command_line_arguments.c_str()) + 1);
data_to_send.lpData = command_line_arguments;
|
- cbData represents the size of the information we want to send.
- lpData represents the information we want to send.
- dwData is an ID defined by us(this is a type of ID different than WM_COPYDATA). This is how you can declare it:
const UINT kCommandLineArgsMessageId = RegisterWindowMessage(L"SingletonApplication"); |
The RegisterWindowMessage function simply registers an ID for the specified string.
This is how you can send a message constructed with COPYDATASTRUCT:
SendMessage(main_window_handle, WM_COPYDATA, 0, (LPARAM)&data_to_send); |
At this point, the information was sent from the second instance of the application to the first one. Now in the first one, we have to intercept the message and take action.
The SendMessage function calls the window procedure for the specified window, so the only thing we have to do is to add a case inside the WndProc in which we verify if a message has been sent using the WM_COPYDATA id.
This is an example of how to intercept this event:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COPYDATA: { HandleCopyDataEvent(hWnd, lParam); break; } } } |
The HandleCopyDataEvent function is used to parse the information received, the body of this function looks like this:
void HandleCopyDataEvent(HWND main_window_handle, LPARAM lparam) { //Copy the information sent via lparam param into a structure COPYDATASTRUCT* copy_data_structure = {0}; copy_data_structure = (COPYDATASTRUCT*)lparam; if(copy_data_structure->dwData == kCommandLineArgsMessageId) { //Extract the information from the created structure and forward the information to UI LPCWSTR arguments = (LPCWSTR)copy_data_structure->lpData; //Set the focus on the main instance SetForegroundWindow(main_window_handle); ShowWindow(main_window_handle, SW_NORMAL); MessageBox(main_window_handle, arguments, NULL, MB_OK); } } |
This function basically extracts the information from the LPARAM for further use. It also sets the main instance of the application as the foreground window (what this does is if the window is at that time minimized or hidden will show it in front).
For the purpose of this example, we will only show a message box that contains the information received from the second instance.
If you reached this point in the article, it means that I did not bore you (that much). This functionality can be used to send information or data between different window type applications, not just between instances of the same application, using WinAPI.