2009-03-19 15 views
8

OK, questa è una domanda un po 'strana.Estrazione di layout di tastiera da windows

Abbiamo un'applicazione touch-screen (vale a dire, nessuna tastiera). Quando gli utenti devono inserire del testo, l'applicazione mostra una tastiera virtuale - costruita a mano in WinForms.

Realizzare queste cose a mano per ogni nuova lingua è un lavoro da scimmia. Immagino che Windows debba avere queste informazioni sul layout della tastiera nascoste da qualche parte in qualche DLL. Ci sarebbe comunque per ottenere questa informazione da Windows?

Altre idee benvenute (immagino che almeno la generazione di una cosa da un file xml debba essere migliore di farlo manualmente in VS).

(Nota: dopo aver detto tutto quello che, faccio notare che ci sia una tastiera giapponese, macchina dello Stato e tutti ..., quindi XML potrebbe non essere sufficiente)

UPDATE: abbastanza buona serie su questo tema (credo) here

+2

Questa è una domanda davvero interessante ... Anche il porting dei layout in altri ambienti (Linux) potrebbe essere possibile, ma probabilmente è una violazione del copyright. – unwind

+0

Non sarebbe più semplice usare la tastiera integrata per tablet di Windows? (ovviamente ha bisogno delle estensioni giuste installate nel sistema operativo ma sembra che basti collegare un wacom in questi giorni, quindi potrebbero esserci più modi?). –

risposta

6

Microsoft Keyboard Layout Creator può caricare le tastiere di sistema ed esportarle come .klc files. Dal momento che è scritto in .NET, puoi usare Reflector per vedere come funziona e usare il reflection per guidarlo. Ecco un zip file of .klc files for the 187 keyboards in Windows 8 creato utilizzando il codice C# seguente. Si noti che ho originariamente scritto questo per Windows XP, e ora con Windows 8 e la tastiera su schermo, è davvero lento e sembra mandare in crash la barra delle applicazioni:/Tuttavia, funziona :)

using System; 
using System.Collections; 
using System.IO; 
using System.Reflection; 

class KeyboardExtractor { 

    static Object InvokeNonPublicStaticMethod(Type t, String name, 
      Object[] args) 
    { 
     return t.GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic) 
      .Invoke(null, args); 
    } 

    static void InvokeNonPublicInstanceMethod(Object o, String name, 
      Object[] args) 
    { 
     o.GetType().GetMethod(name, BindingFlags.Instance | 
       BindingFlags.NonPublic) .Invoke(o, args); 
    } 

    static Object GetNonPublicProperty(Object o, String propertyName) { 
     return o.GetType().GetField(propertyName, 
       BindingFlags.Instance | BindingFlags.NonPublic) 
      .GetValue(o); 
    } 

    static void SetNonPublicField(Object o, String propertyName, Object v) { 
     o.GetType().GetField(propertyName, 
       BindingFlags.Instance | BindingFlags.NonPublic) 
      .SetValue(o, v); 
    } 

    [STAThread] public static void Main() { 
     System.Console.WriteLine("Keyboard Extractor..."); 

     KeyboardExtractor ke = new KeyboardExtractor(); 
     ke.extractAll(); 

     System.Console.WriteLine("Done."); 
    } 

    Assembly msklcAssembly; 
    Type utilitiesType; 
    Type keyboardType; 
    String baseDirectory; 

    public KeyboardExtractor() { 
     msklcAssembly = Assembly.LoadFile("C:\\Program Files\\Microsoft Keyboard Layout Creator 1.4\\MSKLC.exe"); 
     utilitiesType = msklcAssembly.GetType("Microsoft.Globalization.Tools.KeyboardLayoutCreator.Utilities"); 
     keyboardType = msklcAssembly.GetType("Microsoft.Globalization.Tools.KeyboardLayoutCreator.Keyboard"); 

     baseDirectory = Directory.GetCurrentDirectory(); 
    } 

    public void extractAll() { 

     DateTime startTime = DateTime.UtcNow; 

     SortedList keyboards = (SortedList)InvokeNonPublicStaticMethod(
       utilitiesType, "KeyboardsOnMachine", new Object[] {false}); 

     DateTime loopStartTime = DateTime.UtcNow; 

     int i = 0; 
     foreach (DictionaryEntry e in keyboards) { 
      i += 1; 
      Object k = e.Value; 

      String name = (String)GetNonPublicProperty(k, "m_stLayoutName"); 
      String layoutHexString = ((UInt32)GetNonPublicProperty(k, "m_hkl")) 
       .ToString("X"); 

      TimeSpan elapsed = DateTime.UtcNow - loopStartTime; 
      Double ticksRemaining = ((Double)elapsed.Ticks * keyboards.Count) 
         /i - elapsed.Ticks; 
      TimeSpan remaining = new TimeSpan((Int64)ticksRemaining); 
      String msgTimeRemaining = ""; 
      if (i > 1) { 
       // Trim milliseconds 
       remaining = new TimeSpan(remaining.Hours, remaining.Minutes, 
         remaining.Seconds); 
       msgTimeRemaining = String.Format(", about {0} remaining", 
         remaining); 
      } 
      System.Console.WriteLine(
        "Saving {0} {1}, keyboard {2} of {3}{4}", 
        layoutHexString, name, i, keyboards.Count, 
        msgTimeRemaining); 

      SaveKeyboard(name, layoutHexString); 

     } 

     System.Console.WriteLine("{0} elapsed", DateTime.UtcNow - startTime); 

    } 

    private void SaveKeyboard(String name, String layoutHexString) { 
     Object k = keyboardType.GetConstructors(
       BindingFlags.Instance | BindingFlags.NonPublic)[0] 
      .Invoke(new Object[] { 
         new String[] {"", layoutHexString}, 
        false}); 

     SetNonPublicField(k, "m_fSeenOrHeardAboutPropertiesDialog", true); 
     SetNonPublicField(k, "m_stKeyboardTextFileName", 
       String.Format("{0}\\{1} {2}.klc", 
        baseDirectory, layoutHexString, name)); 
     InvokeNonPublicInstanceMethod(k, "mnuFileSave_Click", 
       new Object[] {new Object(), new EventArgs()}); 

     ((IDisposable)k).Dispose(); 
    } 

} 

In sostanza, ottiene un elenco di tutte le tastiere sul sistema, quindi per ognuna, lo carica in MSKLC, imposta il nome file "Salva con nome", giace sul fatto che sia già configurato le proprietà personalizzate della tastiera e quindi simula un clic sul File - > Salva la voce di menu.

+0

Questa è una bella risposta! Dovrò fare un tentativo ... – Benjol

+0

-1 come il tarball non può più essere trovato. Aggiornerò una volta che sarà nuovamente accessibile. – Deleted

+0

@Kent link è corretto – andrewdotn

0

prego il seguente API di Windows

[DllImport("user32.dll")] 
private static extern long LoadKeyboardLayout(string pwszKLID, uint Flags); 

check MSDN here

+0

Hm, questo carica solo un layout di tastiera nel processo/thread corrente, non mi consente di riprodurlo visivamente (cioè, quale chiave è in quale posizione). – Benjol

+0

Anche se potresti avere qualcosa qui, apparentemente MapVirtualKey può mappare un codice scancode (hardware) a un VirtualKey, e GetKeyNameText può convertire un codice scanza in una stringa ... – Benjol

2

Perché non usi la tastiera su schermo (osk.exe)? Sembra che tu abbia reinventato la ruota. E non il più facile!

+0

Una questione di lookologie, temo. Tenete a mente che questo è un progetto pre-wpf in cui hanno ricodificato tutti i controlli a mano, per renderli più belli. – Benjol

+0

È anche un po 'piccolo per un touch-screen. – ProfK

1

So dove sono percorso di questi file DLL:

nel Registro di sistema, si vede:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts 

dove ogni ramo ha un certo valore come "Layout File"="KBDSP.dll". La directory principale è

C:\Windows\System32 

e

C:\Windows\SystemWOW64 

Quelli sono tutti i layout di tastiera file si trovano. Ad esempio, KBDUS.dll significa "tastiera per gli Stati Uniti".

ho cercato di sostituire il file DLL con il mio DLL su ordine dalla MSKLC, e ho trovato che carica le immagini di mappatura di layout automaticamente nella "Lingua" - "metodo di input" - "Anteprima":

enter image description here

Quindi sappiamo che la mappatura è presente nella DLL.