0

I have some code that seems to have an error. I didn't write it, in fact it is in c++ and until a few weeks ago I have never written any code in c++.

We have a keyboard for a touch screen that we bring up when keyboard entry is required and closed when it is not. Our software can be used on a kiosk without a physical keyboard using that touch screen keyboard we developed years ago (limited to a-z,0-9, enter, shift, tab).

The keyboard is "behind" the application on some computers with Windows 7 (might be others but have not see the problem on other OS's).

Worked for years. I updated other parts to finally work with Windows 7, now on 66% of our windows 7 installs the keyboard doesn't come up at all (is behind the application). On my computer at first I thought it came up 100% of the time, but continued testing found it comes up 95-99% of the time, making it difficult to trouble shoot.

In addition most of the posts about GetForegroundWindow() have to deal with sending or getting messages to and from other application, not about layering applications (like I need).

When I started the code looked like this:

  bool ShowKeyBoad(bool Show)
  {
    int WizHeight = 422;
    int KeyWidth,KeyHeight,KeyLeft, KeyTop;
    AnsiString Params;
    AnsiString ShowFile = GetFullPath(GetExeDir(), "OnscreenKeyboard.exe");
    AnsiString HideFile = GetFullPath(GetExeDir(), "bkecekeyboard.exe");

    KeyHeight = Screen->Height - WizHeight;
    KeyWidth = Screen->Width;
    if (KeyWidth > Screen->Width)
      KeyWidth = Screen->Width;

    KeyLeft   = (Screen->Width - KeyWidth) / 2;
    KeyTop    = (Screen->Height) - KeyHeight;

    Params = "x-"  + IntToStr(KeyLeft)   + " y-" + IntToStr(KeyTop) +
    " h-" + IntToStr(KeyHeight) + " w-" + IntToStr(KeyWidth);

    if (Show)
    {
      HWND Hand= GetForegroundWindow();
      ShellExecute(NULL, "open", ShowFile.c_str(), Params.c_str(), NULL, SW_SHOWNORMAL);
      Sleep(100);
      SetForegroundWindow(Hand);
      SetActiveWindow(Hand);
    }
    else
      ShellExecute(NULL, "open", HideFile.c_str(), "/stop", NULL, SW_HIDE);

    return true;
  } 

I have tried a few things.
Capturing the HWND of the ShellExecute and passing that to SetActiveWindow(ShellHand) seemed to have some effect. But still not consistent. HWND shellHand = ShellExecute(NULL, "open", ShowFile.c_str(), Params.c_str(), NULL, SW_SHOWNORMAL);

Adding Sleep(25) before HWND Hand= GetForegroundWindow(); had the best affect on the GUI - but I don't want to rely on timing it just right.

Various internet search pointed me to some code that also seemed pretty good. But it changes registry settings, and that doesn't seem to be a wise choice.

With that solution we change SetForeground to SetForegroundInternal

  void SetForegroundWindowInternal(HWND hWnd)
  {
   if(!::IsWindow(hWnd)) return;

    //relation time of SetForegroundWindow lock
    DWORD lockTimeOut = 0;
    HWND  hCurrWnd = ::GetForegroundWindow();
    DWORD dwThisTID = ::GetCurrentThreadId(),
          dwCurrTID = ::GetWindowThreadProcessId(hCurrWnd,0);

    //we need to bypass some limitations from Microsoft :)
    if(dwThisTID != dwCurrTID)
    {
      ::AttachThreadInput(dwThisTID, dwCurrTID, TRUE);

      ::SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT,0,&lockTimeOut,0);
      ::SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT,0,0,SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);

      ::AllowSetForegroundWindow(ASFW_ANY);
    }

    ::SetForegroundWindow(hWnd);

    if(dwThisTID != dwCurrTID)
    {
      ::SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT,0,(PVOID)lockTimeOut,SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);
      ::AttachThreadInput(dwThisTID, dwCurrTID, FALSE);
    }
  }

update guessing that Hand = NULL is my problem I made the code look like this:

  bool ShowKeyBoad(bool Show)
  {
    int WizHeight = 422;
    int KeyWidth,KeyHeight,KeyLeft, KeyTop;
    AnsiString Params;
    AnsiString ShowFile = GetFullPath(GetExeDir(), "OnscreenKeyboard.exe");
    AnsiString HideFile = GetFullPath(GetExeDir(), "bkecekeyboard.exe");

    KeyHeight = Screen->Height - WizHeight;
    KeyWidth = Screen->Width;
    if (KeyWidth > Screen->Width)
      KeyWidth = Screen->Width;

    KeyLeft   = (Screen->Width - KeyWidth) / 2;
    KeyTop    = (Screen->Height) - KeyHeight;

    Params = "x-"  + IntToStr(KeyLeft)   + " y-" + IntToStr(KeyTop) +
    " h-" + IntToStr(KeyHeight) + " w-" + IntToStr(KeyWidth);

    if (Show)
    {

      HWND Hand;
      int intfail=1000;  // count down seems correct in this case, usually don't
      while (Hand = NULL)
      { 
        if (intfail <=0)
          break;

        Hand = GetForegroundWindow();
        intfail--;  
      }

      if (Hand == NULL)
      {
      // Send Message to screen giving instructions to try again
      return false;
      }

      ShellExecute(NULL, "open", ShowFile.c_str(), Params.c_str(), NULL, SW_SHOWNORMAL);
      Sleep(100);
      SetForegroundWindow(Hand);
      SetActiveWindow(Hand);
    }
    else
      ShellExecute(NULL, "open", HideFile.c_str(), "/stop", NULL, SW_HIDE);

    return true;
  } 

Appears to solve the problem.

Brian Hanf
  • 544
  • 2
  • 11
  • 31
  • `ShellExecute()` does not return an `HWND`. Its return value is an integer error code type-casted as an `HINSTANCE` handle pointer (for backwards compatibility with 16-bit code). To locate the `HWND` of the spawned process, use `FindWindow()` instead (presumably you know its class name since you wrote that exe), or use `CreateProcess()` and then call `EnumWindows()' to run a `GetWindowThreadProcessId()` loop looking for `HWND`s that match the spawned process. – Remy Lebeau Oct 03 '12 at 22:26
  • If you use `CreateProcess()` or `ShellExecuteEx()`, you can gain access to the process handle, which you can pass to `WaitForInputIdle()` instead of `Sleep()`. – Remy Lebeau Oct 03 '12 at 22:27
  • I am guessing that 'Hand' = NULL in HWND Hand= GetForegroundWindow(); and that is the issue. Why it is showing up in only some Windows 7 installs is very weird. – Brian Hanf Oct 04 '12 at 14:24

0 Answers0