- 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
              
 
               
              Your answer