- Home /
Question by
bird1000000000 · Aug 12, 2019 at 08:33 AM ·
standalonec++command-lineembedembedding
How do you get an application using parentHWND to detect keyboard input?
I've now solved my resizing and mouse input problems. I'm left with 1 last problem: Keyboard input does not work. Looking at the example WM_PAINT and WM_SIZING should use activateUnityWindow(); but using it makes all input break.
Here's my C++ code
#include "pch.h"
// Specifies Windows Class Name - The one that shows up in processes on task manager.
static TCHAR szWindowClass[] = _T("ContainerTest");
// Title of the window
static TCHAR szTitle[] = _T("ContainerTest");
HWND unityHwnd = NULL;
void activateUnityWindow() {
SendMessage(unityHwnd, WM_ACTIVATE, WA_ACTIVE, NULL);
}
void deactivateUnityWindow() {
SendMessage(unityHwnd, WM_ACTIVATE, WA_INACTIVE, NULL);
}
BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam)
{
LPRECT rcParent;
// Size and position the child window.
rcParent = (LPRECT)lParam;
MoveWindow(hwndChild,
0,
0,
rcParent->right - rcParent->left,
rcParent->bottom - rcParent->top,
TRUE);
// Make sure the child window is visible.
return TRUE;
}
// This is called when there's events (Procedures) from windows
// https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures
LRESULT CALLBACK WinProcs(
_In_ HWND hMainWindow,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
switch (uMsg) {
case WM_SIZING: { // On dragging the resize
RECT winSize;
GetWindowRect(hMainWindow, &winSize);
EnumChildWindows(hMainWindow, EnumChildProc, (LPARAM)& winSize);
//activateUnityWindow();
break;
}
//Called when the GUI updates in some way, Must be redrawn.
case WM_PAINT: {
RECT winSize;
GetWindowRect(hMainWindow, &winSize);
EnumChildWindows(hMainWindow, EnumChildProc, (LPARAM)& winSize);
//activateUnityWindow();
break;
}
case WM_CREATE: {
activateUnityWindow();
break;
}
case WM_DESTROY: {
PostQuitMessage(0);
}
default: {
return DefWindowProc(hMainWindow, uMsg, wParam, lParam);
break;
}
}
return 0;
}
BOOL CALLBACK EnumWindowsProcMy(HWND hwnd, LPARAM lParam)
{
DWORD lpdwProcessId;
GetWindowThreadProcessId(hwnd, &lpdwProcessId);
if (lpdwProcessId == lParam)
{
unityHwnd = hwnd;
return FALSE; //Stops the method
}
return TRUE;
}
// This is called when the window is opened.
int CALLBACK WinMain(
_In_ HINSTANCE hInstance, // Handle to windows instance
_In_opt_ HINSTANCE hPrevInstance, // This paremeter is always null
_In_ LPSTR lpCmdLine, // The command line for this application
_In_ int nCmdShow) // "TBD" thankyou windows documentation, very cool
{
//
// First we've got to check if the game is already running.
//
// When we create a mutex, it will return ERROR_ALREADY_EXISTS i
HANDLE MutexHandle =
CreateMutex(
FALSE, // This only needs to be set if we want child proccesses to inherit it.
TRUE, // This makes us the ownder of the mutex
"BTD6-MODDED-ISRUNNING"
);
if (MutexHandle == NULL) {
BOOL lastError = GetLastError();
// Game is already running
if (lastError == ERROR_ALREADY_EXISTS) {
TEMPBP
return 1;
}
// Limmited access right, could not create mutex.
else if (ERROR_ACCESS_DENIED) {
TEMPBP
return 1;
}
return 1;
}
//
// Second, we've got to create the window information.
//
WNDCLASSEX wcex;
// Sizeof this structure.
wcex.cbSize = sizeof(WNDCLASSEX);
// Class Styles.
// https://msdn.microsoft.com/en-us/library/ms633574(v=VS.85).aspx
wcex.style = CS_HREDRAW | CS_VREDRAW;
// The procedures handling method.
//wcex.lpfnWndProc = WinProcs;
wcex.lpfnWndProc = WinProcs;
// Number of extra bytes to allocate after this structure.
wcex.cbClsExtra = 0;
// Number of extra bytes to allocate before this structure
wcex.cbWndExtra = 0;
// The windows instance, provided by winmain.
wcex.hInstance = hInstance;
// Handle to the Icon reso urce
wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
// Handle to the cursor resource.
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
// A windows colour.
// See https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-tagwndclassexa
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
// A string that represents the name of the resource of the menu class.
wcex.lpszMenuName = NULL;
//
wcex.lpszClassName = szWindowClass;
// Small Icon, if set to null, looks for hIcon
wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wcex)) {
return GetLastError();
}
//
// Third, we have to make the window
//
HWND hMainWindow = CreateWindowEx(
//WS_EX_OVERLAPPEDWINDOW, // Extended window sytle
NULL,
szWindowClass, // Window class
szTitle, // Window name
//WS_OVERLAPPEDWINDOW, // Window Style
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, // Initial X position of the window
CW_USEDEFAULT, // Initial Y position of the window
//TODO: Save resolution to a file.
1920, // Initial width of the window
1080, // Initial height of the window
NULL, // HWND to a parent window. This is the main window.
NULL, // A HWND to a menu
hInstance, // Handle tot he instance of the module to be associated with the window
NULL
);
if (hMainWindow == NULL) {
int xd = GetLastError();
return 1;
}
//
// Now we need to load BTD6 as a child proccess
//
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
si.hStdOutput= GetStdHandle(STD_OUTPUT_HANDLE);
ZeroMemory(&pi, sizeof(pi));
std::string scmdLine = std::string(std::string(R"(Raw/Path/To/Game)")
+ std::to_string((uint64_t)hMainWindow).c_str());
lpCmdLine = (char*)(scmdLine.c_str());
if (!CreateProcess(
NULL, // Application path, we're not using this, we're just going to use the command line.
lpCmdLine, // Use this programs command line as the childs, dont plan on doing any command line shit with the container anyway.
NULL, // Process security attributes
NULL, // Thread security attributes
true, // If the child will inherit this applications handle.
CREATE_NO_WINDOW, // Dont make a new window unity, it crashes :(
// See: https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
NULL, // Use parents environment
NULL, // Use parents current directory.
&si,
&pi
)) {
return GetLastError();
}
WaitForInputIdle(pi.hProcess, INFINITE);
// Only needs ~25 ms, give it some extra time just in case of potato.
Sleep(1000);
EnumWindows(EnumWindowsProcMy, pi.dwProcessId);
if (unityHwnd == NULL) {
return GetLastError();
}
if (!SetParent(unityHwnd, hMainWindow)) {
return GetLastError();
}
//CloseHandle(pi.hProcess);
//CloseHandle(pi.hThread);
//
// Finally, We have to make the window actually visible.
//
ShowWindow(
hMainWindow,
nCmdShow
);
//
// Now we need to start reciving messages.
//
MSG msg;
while (GetMessage(
&msg, // Pointers to the message struct that recieves the information.
NULL, // Null retrieves any windows on the current thread.
0, // Lowest message to be retrieved
0 // Highest message to be retrieved
// If the lowest and the highest value are 0, then all messages are retrieved.
)) {
Sleep(1);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
Not sure why activating the windows would break all input, probably need to find a way for it to not break all input.
Comment