BACKGROUND
I am building a basic MFC application which helps me to run tests. The GUI is extremely spartaic. One button to START and another button to STOP the test, plus a static text object to display a few letters and digits indicating what stage the test is performing. This has been working satisfactorily for weeks.
I would find it useful if my application also made a video screen recording while the test is performed. The big plan is to compile FFmpeg library function calls into the code to implement this capability, but this will certainly take me weeks to learn, try and complete. In the meantime, a quick but sufficient solution is to call a precompiled ffmpeg.exe from a downloaded Windows binary build.
I added code to the ::OnBnClickedButtonStarttest() method to call ffmpeg.exe via CreateProcess(). This is working fine. The FFmpeg screen recording process starts in a new console window and does its intended job very well. When I select that console window and press Ctrl+C, the recording stops and I have the desired video file. I also added code to the ::OnBnClickedButtonStoptest() method to send the required Ctrl+C to the STDIN of the ffmpeg.exe process and finish recording when the STOP button is clicked. This is also working fine. MOST OF THE TIME.
These two portions of my source code are based on the example published at https://learn.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output by Microsoft.
PROBLEM
Stopping the screen recording process MOST OF THE TIME means that IT DOES NOT ALWAYS STOP. There are times when the solution does not work. And I am unable to find any reason why the same solution that works most of the time, happens to fail on other times. Eventually I would prefer to hide the FFmpeg console window and let it work behind the scene, but that would require a reliable way to stop the child process, and my current code proves to be not working reliably. Note: there is a #define BUFSIZE 128 line at the top of the source code.
DWORD dwNumOfBytesToWrite = 7;
DWORD dwWritten = 0;
CHAR chBuf[BUFSIZE] = { 0x03, 0x03, 0x03, 0x03, 0x03, 0x0A, 0x0D, 0x03, 0x0A, 0x0D };
BOOL bSuccess = FALSE;
bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwNumOfBytesToWrite, &dwWritten, NULL);
Although sending a single Ctrl+C (0x03) should stop the ffmpeg process, I send a little more input to be sure. Still, for some reason I have to send the above WriteFile instruction twice, in order to actually see the process stop. I do not know why. This does not concern me much though, as long as the second time it does work.
What does concern me is the fact that there are times when the child process does not stop. No matter how many times I send it the above WriteFile line.
I found another promising example. This time without anonymous pipes -which I suspect to be the source of the unreliable behaviour.
https://www.techpowerup.com/forums/threads/c-c-c-console-redirection-with-sockets-win32.62350/
This appear to use a named channel of its own to access the standard input of the child process. I prefer this approach. Unfortunately, it is not only that it does not work, it also does not compile. The Socket thisSocket; line refers to a non-existing type. There is no such type as Socket, not with an uppercase S and lowercase ocket. I tried to use all uppercase SOCKET type in its place, which does compile, but an accordingly modified WriteFile((HANDLE)thisSocket, chBuf, dwNumOfBytesToWrite, &dwWritten, NULL); line always returns FAILURE, and does not stop the child process.
To make my investigation more difficult, a large number of internet search hits take me to WINSOCK related topics instead.
Did I miss something?
What would be the reliable way of sending a Ctrl+C to the ffmpeg.exe process, in order to tactfully ask it to finish recording?
Mind you, I can brutally kill the FFmpeg console window via TerminateProcess(g_ffmpeg_process_handle, 0), but that does not always allow the child to properly close the video file, resulting a damaged screen recording.
No proper solution, but I have an acceptable workaround.
I introduced a global variable ffmpChildProc_statusFlag with an initial value of ZERO.
When CreateProcess() successfully started the FFmpeg child process in its hidden Console Window, then I assign ffmpChildProc_statusFlag the value of ONE.
And I use RegisterWaitForSingleObject() to automatically launch a CALLBACK function when the FFmpeg child process actually terminates.
VOID CALLBACK onSubprocessExit(_In_ PVOID lpParameter, _In_ BOOLEAN TimerOrWaitFired) {
//// Do clean-up by unregistering this callback instance.
//// Update _ffmpChildProc_statusFlag_ to have a value of TWO.
}
These allow me to confidently know how well my efforts are working out.
I also wrote an EnumWindowsProc callback that uses GetWindowThreadProcessId() to return the process ID that belongs to the currently examined window, until I run into the process ID of my FFmpeg child process, which CreateProcess() returned in the dwProcessId member of its PROCESS_INFORMATION structure argument.
I could not find a better way to obtain the window handle of the created child process from its known process Id. So that is EnumWindowsUntilProcessHandleFound().
Then here is what I did
When the time to finish the FFmpeg child process has come, I first attempt the WriteFile(0x03) to the redirected StdIN pipe method shown in my question. I do that 1-5 times, each time checking the value of ffmpChildProc_statusFlag to see if I need to try one more time, having a few hundred milliseconds of pause after each attempt. Not closing the Write end of the pipe, so that the next write attempt can succeed.
If the child process has not yet ended, then I close the 3 handles, for
the Write-end of the pipe,
the child process,
the thread of the child process,
and again pause for some hundred milliseconds to allow time for the callback to complete (if the child process completes).
As a final attempt, if the FFmpeg process is still running, then I unhide the hidden console window of the child process, making it possible for the user to manually stop it with an actual Ctrl+C. But while at this last attempt, I try a few times to programatically input that Ctrl+C using SendInput().
CWnd* wFFmpPtr = CWnd::FromHandle(ffmpExe_window_handle);
if (!wFFmpPtr->IsWindowVisible()) {
TRACE(L"\tREVEALING FFmp window.\n");
wFFmpPtr->ShowWindow(SW_SHOWNORMAL);
} //// if not already visible
SetActiveWindow(ffmpExe_window_handle);
TRACE(L"\tSending Ctrl+C keyboard action to the frontmost window.\n");
INPUT vips; //// My temporary virtual-input structure to define a keyboard key action.
vips.type = INPUT_KEYBOARD; //// Setting up a generic keyboard event.
vips.ki.wScan = 0; //// Ignoring the scan code, because the virtual-keycode will be provided instead.
vips.ki.time = 0; //// Ignoring the timestamp, so the system will fill it for me.
vips.ki.dwExtraInfo = 0; //// I have no extra info to provide.
//// Simulating a "Ctrl" key press.
vips.ki.wVk = 0x11; //// Virtual-key code for the "Ctrl" key.
vips.ki.dwFlags = 0; //// 0 means a normal key press (key going downwards).
SendInput(1, &vips, sizeof(INPUT));
//// Simulating a "C" key press.
vips.ki.wVk = 0x43; //// Virtual-key code for the "C" key.
vips.ki.dwFlags = 0;
SendInput(1, &vips, sizeof(INPUT));
//// Simulating a "C" key release.
vips.ki.dwFlags = KEYEVENTF_KEYUP; //// KEYEVENTF_KEYUP means a key release (key going upwards).
SendInput(1, &vips, sizeof(INPUT));
//// Simulating a "Ctrl" key release.
vips.ki.wVk = 0x11; //// Virtual-key code for the "Ctrl" key.
SendInput(1, &vips, sizeof(INPUT));
TRACE(L"\tActive window should have received a Ctrl+C.\n");
By this point, the child window always closes. But, to be absolutely certain, I left there a conditional TerminateProcess() too -should that be called, it would likely end the child process with a damaged video recording.
The tried but failed approaches
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); //// Never worked.
GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0); //// Never worked.
msgResult = wFFmp_ptr->SendMessage(WM_CHAR, 0x03, 1); // Never worked.
msgResult = wFFmp_ptr->SendMessage(WM_UNICHAR, 0x03, 1); // Never worked.
msgResult = wFFmp_ptr->SendMessage(WM_KEYDOWN, 0x03, 1); // Not these two.
msgResult = wFFmp_ptr->SendMessage(WM_KEYUP, 0x03, 1);
msgResult = wFFmp_ptr->SendMessage(WM_KEYDOWN, 0x11, 1); // Or these four either.
msgResult = wFFmp_ptr->SendMessage(WM_KEYDOWN, 0x43, 1);
msgResult = wFFmp_ptr->SendMessage(WM_KEYUP, 0x43, 1);
msgResult = wFFmp_ptr->SendMessage(WM_KEYUP, 0x11, 1);
None of these have ever managed to close the child process. Not once. Even though I had high hopes with them, because these would have directed the Ctrl+C signal to my particular window. Therefore, assumed to be more reliable. :-(
While the SendInput method does seem to work all the time, I am not happy with that approach because it sends the simulated keystrokes to the frontmost window, instead of a particular window. Hence it is more prone to errors. A mouse click or a real keyboard action of the user can activate another window, which then would receive the simulated Ctrl+C keystrokes intended for the FFmpeg console window.
For now, it is acceptable.
I'm working on a program that works with the game "Microsoft Flight Simulator X: Steam Edition." One of the things I need to do with it is take a screenshot of the game. I've tried several different solutions to no avail. To take the screenshot I am trying to issue a F12 keypress to trigger Steam to take a screenshot. Here's a couple of examples of what I tried:
keybd_event(VK_F12, 0, 0, 0);
Sleep(250);
keybd_event(VK_F12, 0, KEYEVENTF_KEYUP, 0);
This second example confirmed that the keypress is being registered, but no screenshots are being taken.
INPUT input[1] = {};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = VK_F12;
UINT test = SendInput(ARRAYSIZE(input), input, sizeof(INPUT));
std::cout << test;
If anyone has any ideas on how to get this to work, or other ways to take a screenshot, please let me know!
Kindly check the version maybe by this the problem is solved.
I am trying to make a program that would simulate holding down a keyboard button.
This program is supposed to work in games, where holding down buttons such as W,A,S,D is required. I tested the program both in Grand Theft Auto V and Garry's Mod, but it didn't work in either of them.
I tried using SendInput and keybd_event functions for emulating the input with no luck.
#include <windows.h>
#define VK_W 0x57
using namespace std;
int main(){
while(1){
keybd_event(VK_W, 0, 0, 0);
Sleep(3000); //should hold W for 3 seconds
keybd_event(VK_W, 0, KEYEVENTF_KEYUP, 0);
Sleep(1000);
}
return 0;
}
I've created macros using Razer's keyboard software, which work perfectly, so there must be a way to simulate the key hold input.
I also tried creating an AutoHotkey script, which does work in Garry's Mod, but not in Grand Theft Auto V:
w::
SendInput {w down}
Sleep 3000
SendInput {w up}
This script holds W for 3 seconds when W is pressed.
I tried searching the web, but didn't find any working implementation.
EDIT: I searched around some more and found this: https://stackoverflow.com/a/3647975/1587051
It turns out I was using Unicode instead of keyboard scan codes combined with KEYEVENTF_SCANCODE flag. DirectInput games are now receiving the keystrokes correctly.
For reference sake, as it has already been answered in the comments, you have to use SendInput rather than SendMessage, when filling the INPUT structure required for the SendInput function make sure you combine your Flags with KEYEVENTF_SCANCODE to send hardware signals instead of virtual ones. These will be handled by DirectInput.
Source : Simulating Keyboard with SendInput API in DirectInput applications
I want to make a program which takes screenshot whenever it runs. For that, I thought of using virtual keys but i am not able to press two keys simultaneously. I am trying to do this in Microsoft windows 8.1 and trying to press + Print Scrn simultaneously.
You can send multiple keys to the OS with keybd_event(). The first time you call it you will send the windows key and tell it to stay down. Then you will do the same with the printscreen button. After you do that you need to call the function again to lift each key in reverse order. You should be able to use:
keybd_event(VK_LWIN, 0, KEYEVENTF_EXTENDEDKEY, 0);
keybd_event(VK_SNAPSHOT, 0, KEYEVENTF_EXTENDEDKEY, 0);
keybd_event(VK_SNAPSHOT, 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0);
I'm creating a WinApi application for my programming course. The program is supposed to show an LED clock using a separate window for each 'block'. I have figured most of it out, except for one thing: when creating the two-dimensional array of windows, the first and last window never show up. Here's the piece of code from the InitInstance function:
for (int x=0;x<8;x++)
for (int y=0;y<7;y++) {
digitWnd[x][y] = CreateWindowEx((WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE | WS_EX_STATICEDGE),
szWindowClass, szTitle, (WS_POPUP| WS_BORDER), NULL, NULL, NULL, NULL, dummyWnd, NULL, hInstance, NULL);
ShowWindow(digitWnd[x][y], nCmdShow);
UpdateWindow(digitWnd[x][y]);
}
The same loop bounds are used everytime I interact with the windows (set position and enable/disable). All the windows seem to be working fine, except for digitWnd[0][0] and digitWnd[7][6]... Any ideas as to what is happening?
Open Spy++ and check if the missing windows are really missing or just overlapped by other windows. It's possible that you have some small error in the position calculations code that puts them behind another window or outside of the screen.
To validate your creation mechanism I would check:
the array initialisation HWND digitWnd[8][7]
if the parent window dummyWnd is valid
the return value of CreateWindowEx() != NULL
Another point which comes to my mind is, that you create windows with dimension 0 - no width or height. So maybe it would be a good idea to set the size within CreateWindowEx(...)
Is this your first call to ShowWindow()? If so, according to MSDN, "nCmdShow: [in] Specifies how the window is to be shown. This parameter is ignored the first time an application calls ShowWindow". This could mean that you can fix your program by simply calling ShowWindow() twice. Give it a try and see if it works. Other than that, you'll probably have to provide more of the code for us to look at.