none
Problema con tastiera virtuale osk.exe - windows 7

    General discussion

  • Buongiorno a tutti,
    sto cercando di aggiornare un programma che avevo realizzato un po' di tempo fa.
    Funziona correttamente con windows xp mentre non funziona con windows 7.
    Faccio uso della tastiera virtuale osk.exe
    Premetto che prima di eseguire il programma ho disabilitato l'UAC e lo eseguo con un account administrator.

    In sintesi il punto è questo: all'avvio del programma eseguo il processo osk.exe per visualizzare la tastiera a video.
    Quando il sistema operativo restituisce l'handle del processo, nascondo la tastiera e faccio altre operazioni (elimino la barra del titolo e la posiziono in un punto preciso del video).

    Il problema è che Windows 7 non restituisce l'handle del processo !!
    Ho cercato con google ed ho applicato questo work around:

          /* work around to keep handle process*/
          while (keyboardProcess.MainWindowHandle == IntPtr.Zero)
          {
              Thread.Sleep(2000);
              keyboardProcess.Refresh();
          }

    In pratica aspetto un po' di tempo e per magia l'handle viene restituito.
    Dopo varie prove ho calcolato il "po' di tempo" in 2 secondi !!

    Fin qui tutto bene. Aspettare fino a due secondi può starci dato che non ho trovato altre soluzioni.
    Quest'operazione la faccio una sola volta all'avvio del programma: in questo modo ho la tastiera in "background" e la posso visualizzare quando voglio.

    La cosa "tragica" è che questo work around funziona sempre tranne che.... la prima volta!!
    La prima volta che eseguo l'applicazione il programma va in eccezione poiché non viene restituito l'handle.
    Chiudo l'applicazione, la rieseguo e funziona sempre.

    Ho scoperto anche questo: se prima di eseguire il mio programma, lancio notepad.exe o un'altra applicazione tutto funziona correttamente: 

    l'applicazione non va in eccezione.

    (Notepad.exe deve però essere presente nella stessa cartella del mio applicativo o in c:\ 
    Se lo lancio da c:\windows\system non funziona!)

    Quando ho scoperto questa cosa ho provato a mettere notepad.exe in esecuzione automatica.
    All'avvio del programma si apre correttamente e poi lo chiudo a mano. Lancio il mio applicativo e va in eccezione
    Stessa cosa per un'operazione pianificata. Pensavo di avviare una qualsiasi applicazione in automatico per poi killarla da codice.
    Non funziona. Solo se avviata a mano e chiusa a mano funziona !!

    Uso il framework .NET 3.5, visual studio 2010 e windows 7 a 32 bit.

    Ho realizzato un semplice programma di test che genera il problema:

    L'eseguibile compilato è reperibile qui:  Release.rar
    La soluzione è reperibile qui: TestOSK.zip 

    Posto cmq parte del codice:

    namespace TestOSK
    {
      public partial class Form1 : Form
      {
    
        [DllImport("user32.dll")]
        public static extern int GetWindowLong(IntPtr currentWindow, int a);
        [DllImport("user32.dll")]
        static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
        [DllImport("user32.dll")]
        static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
        [DllImport("user32.dll")]
        static extern IntPtr GetMenu(IntPtr hWnd);
        [DllImport("user32.dll")]
        static extern bool DeleteMenu(IntPtr hMenu, uint uPosition, uint uFlags);
        [DllImport("user32.dll")]
        static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
        [DllImport("user32.dll")]
        static extern int GetMenuItemCount(IntPtr hMenu);
        [DllImport("user32.dll")]
        static extern bool DrawMenuBar(IntPtr hWnd);
        [DllImport("user32.dll")]
        static extern bool RemoveMenu(IntPtr hMenu, uint uPosition, uint uFlags);
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, uint flags);
        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool Wow64RevertWow64FsRedirection(IntPtr ptr);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SendMessage(IntPtr hWnd,
            UInt32 Msg,
            IntPtr wParam,
            IntPtr lParam);    private const UInt32 WM_SYSCOMMAND = 0x112;
        private const UInt32 SC_RESTORE = 0xf120;
    
        private const string OnScreenKeyboardExe = "osk.exe";
    
    
        //[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
        //static extern System.IntPtr FindWindowByCaption(int ZeroOnly, string lpWindowName);
    
    
    
        static readonly IntPtr HWND_TOPMOST = new IntPtr(0);
        const UInt32 SWP_NOSIZE = 0x0001;
        const UInt32 SWP_NOACTIVATE = 0x0010;
        private const int SWP_HIDEWINDOW = 0x80;
        private const int SWP_SHOWWINDOW = 0x40;
        private const int WS_DLGFRAME = 0x400000;
        private const int MF_BYPOSITION = 0x400;
        private const Int32 MF_REMOVE = 0x1000;
    
    
        public Process keyboardProcess = null;
        string basePath = Properties.Settings.Default.ProcessPath;
        string prcName = Properties.Settings.Default.ProcessName;
    
        string exePath = "";
    
        public Form1()
        {
          InitializeComponent();
          exePath = Path.Combine(basePath, prcName + ".exe");
          label1.Text = exePath;
          keyboardProcess = new Process();
          keyboardProcess.StartInfo.FileName = exePath;
    
          InitializeKeyboard();
    
        }
    
        private void InitializeKeyboard()
        {
    
    
          foreach (Process proc in Process.GetProcessesByName(prcName))
          {
            //SetWindowPos(proc.MainWindowHandle, HWND_TOPMOST, 25, 380, 775, 210, SWP_SHOWWINDOW);
            proc.Kill();
          }
    
          // ALTEZZA TASTIERA SENZA MENU CIRCA 150 PIXEL
    
          keyboardProcess.Start();
    
          // ASPETTO CHE IL PROCESSO SIA CREATO
          keyboardProcess.WaitForInputIdle();
    
          /* work around on 7 embedded to keep handle process*/
          while (keyboardProcess.MainWindowHandle == IntPtr.Zero)
          {
              Thread.Sleep(2000);
              keyboardProcess.Refresh();
          }
    
          //Nasconde la finestra
    
          ShowWindow(keyboardProcess.MainWindowHandle, 0);
          RemoveMenu(keyboardProcess.MainWindowHandle);
    
          // ELIMINO LA BARRA DEL TITOLO
          SetWindowLong(keyboardProcess.MainWindowHandle, -16, GetWindowLong(keyboardProcess.Handle, -16) + WS_DLGFRAME);
          
          // POSIZIONO LA VISUALIZZAZIONE DELLA TASTIERA
          SetWindowPos(keyboardProcess.MainWindowHandle, HWND_TOPMOST, 25, 380, 775, 210, SWP_HIDEWINDOW);
          //Ridisegno la finestra 
          RedrawWindow(keyboardProcess.MainWindowHandle, IntPtr.Zero, IntPtr.Zero, 0x0400 | 0x0100 | 0x0001);
    
                  
        }
    
        private void btn_showKbd_Click(object sender, EventArgs e)
        {
          ShowWindowsKeyboard(0, 300);
        }
    
        private void btn_hideKbd_Click(object sender, EventArgs e)
        {
          CloseWindowsKeyboard();
        }
    
    
        public void ShowWindowsKeyboard(int x, int width)
        {
          SetWindowPos(keyboardProcess.MainWindowHandle, HWND_TOPMOST, x, 390, width, 210, SWP_SHOWWINDOW);
          ShowWindow(keyboardProcess.MainWindowHandle, 5);
        }
    
        public void CloseWindowsKeyboard()
        {
          ShowWindow(keyboardProcess.MainWindowHandle, 0);
        }
    
    
        private void RemoveMenu(IntPtr windowHandle)
        {
          //Ottiene e rimuove le voci di menu (Di finestra e di sistema)
          IntPtr hMenu = GetMenu(windowHandle);
          IntPtr hSysMenu = GetSystemMenu(windowHandle, false);
    
          if (hMenu != IntPtr.Zero) RemoveMenuCore(windowHandle, hMenu);
          if (hSysMenu != IntPtr.Zero) RemoveMenuCore(windowHandle, hSysMenu);
        }
    
        private void RemoveMenuCore(IntPtr windowHandle, IntPtr menuHandle)
        {
          int n = 0;
          n = GetMenuItemCount(menuHandle);
          if (n > 0)
          {
            for (int i = n - 1; i >= 0; i--)
            {
              RemoveMenu(menuHandle, (uint)i, MF_BYPOSITION | MF_REMOVE);
              DrawMenuBar(windowHandle);
            }
          }
        }
    
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
          keyboardProcess.CloseMainWindow();
        }
    
        private void label1_Click(object sender, EventArgs e)
        {
            
        }
      }
    }

    Monday, April 08, 2013 1:20 PM