Vamos a programar #109 - KeyRemap M 1.0
Hola de nuevo a todos, el día de hoy vamos a ver una aplicación que hice desde hace ya tiempo pero que por alguna razón no terminé hasta hace poco.
Para comprender mejor el post, te recomiendo revisar los post referentes al tema:
- Vamos a programar #86 - Reasignando teclas del teclado.
- Vamos a programar #87 - Camino a KeyRemap: MessageTextBox.
- Vamos a programar #88 - KeyRemap M (beta)
Pero por si no quieres leer. KeyRemap es una aplicación que sirve para reasignar las algunas de las teclas del teclado. Es importante mencionar que las resignaciones pueden no funcionar en algunas aplicaciones o juegos, pero para la mayoría de las aplicaciones de windows esta aplicación podría servirnos.
El Código
El Código de la aplicación es el siguiente:
using System;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Collections.Generic;
using System.IO;
using System.Security;
using System.Security.AccessControl;
namespace KeyRemap
{
public partial class FrmMain : Form
{
private byte[] Buff = { };
public FrmMain()
{
InitializeComponent();
}
#region "Funciones y procedimientos"
/// <summary>
/// Escribe una secuencia de bytes en el registro de windows
/// </summary>
/// <param name="Data">Secuencia de bytes a escribir</param>
private void WriteHexRegKey(byte[] Data)
{
string user = Environment.UserDomainName + "\\" + Environment.UserName;
RegistrySecurity rs = new RegistrySecurity();
rs.AddAccessRule(new RegistryAccessRule(user, RegistryRights.ReadKey | RegistryRights.Delete, InheritanceFlags.None, PropagationFlags.None, AccessControlType.Allow));
RegistryKey RegKey = Registry.LocalMachine;
RegKey = RegKey.CreateSubKey(@"SYSTEM\CurrentControlSet\Control\Keyboard Layout",RegistryKeyPermissionCheck.Default, rs);
RegKey.SetValue("ScanCode Map", Data, RegistryValueKind.Binary);
RegKey.Close();
}
/// <summary>
/// Elimina la entrada "ScanCode Map" Del registro de windows
/// </summary>
private void DeleteRegKey()
{
try
{
RegistryKey RegKey = Registry.LocalMachine;
RegKey = RegKey.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Keyboard Layout", true);
RegKey.DeleteValue("ScanCode Map");
}
catch(Exception Err)
{
MessageBox.Show("Un error ocurrio, prueba iniciar la aplicacion en modo de administrador\n" + Err.StackTrace.ToString() + "","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
}
/// <summary>
/// Leee los datos de la llave del registro "ScanCode Map"
/// </summary>
/// <returns>Una secuencia de bytes con el contenido de la llave </returns>
private byte[] ReadRegKey()
{
byte[] Data = { };
try
{
RegistryKey RegKey = Registry.LocalMachine;
RegKey = RegKey.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Keyboard Layout");
byte[] KeyVal = (byte[])RegKey.GetValue("Scancode Map");
RegKey.Close();
Data = KeyVal;
return Data;
}
catch
{
return Data;
}
}
/// <summary>
/// Guarda las reasignaciones de las teclas en arhivo ".bat"
/// </summary>
/// <param name="Data">Los datos que se van a escribir</param>
private void SaveRegFile(byte[] Data)
{
using (SaveFileDialog SFD = new SaveFileDialog())
{
SFD.Filter = "Archivo de registro|*.reg";
SFD.AddExtension = true;
if (SFD.ShowDialog() == DialogResult.OK)
{
using (StreamWriter SW = new StreamWriter(SFD.OpenFile()))
{
SW.AutoFlush = true;
SW.WriteLine(@"Windows Registry Editor Version 5.00");
SW.WriteLine();
SW.WriteLine(@"[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout]");
SW.Write(string.Concat("\"", "Scancode Map", "\"", "=hex:"));
for (int i = 0; i < Data.Length; i++)
{
if (i < Data.Length - 1)
SW.Write(string.Concat(Data[i].ToString("X2"), ","));
else
SW.Write(Data[i].ToString("X2"));
}
}
}
}
}
/// <summary>
/// Determina si un número es par
/// </summary>
/// <param name="Number">Número que se quiere saber si es par</param>
/// <returns>Regresa true si es par; false en caso contrario</returns>
private bool IsEven(int Number)
{
if ((Number % 2) == 0)
return true;
else
return false;
}
/// <summary>
/// Llena el ListView con los datos aplicando el formato correcto
/// </summary>
/// <param name="Data">DAtos a formatear</param>
private void FillListView(byte[] Data)
{
int Entries = Data[8] - 1;
if (Data.Length < 20 && (Data.Length % 4) != 0)
MessageBox.Show("La configuracion no parece estar correcta\nQuieres eliminarla?", "Error", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
else
{
//e020
//57376
//224
//32
for (int i = 0; i < Entries; i++)
{
int MainItem = Data[12 + (i * 4)] | (Data[13+ (i * 4)] << 8);
int SubItem = Data[14 + (i * 4)] | (Data[15 + (i * 4)] << 8);
ListViewItem LV = new ListViewItem(new string[] { MainItem.ToString(), SubItem.ToString() });
LvReassignedKeys.Items.Add(LV);
}
}
}
/// <summary>
/// Escribe o guarda los datos de las reasignaciones
/// </summary>
/// <param name="SaveFile">SI es true, genera un arhivo .bat para guardar. Si es false, escribe la llave en el registro</param>
private void BuildAndSaveData(bool SaveFile)
{
List<string> Items = new List<string>();
for (int i = 0; i < LvReassignedKeys.Items.Count; i++)
{
Items.AddRange(new string[] { LvReassignedKeys.Items[i].Text, LvReassignedKeys.Items[i].SubItems[1].Text });
}
if (SaveFile)
{
if (Items.Count > 0)
SaveRegFile(RebuildRegistryEntry(Items));
}
else
{
WriteHexRegKey(RebuildRegistryEntry(Items));
}
}
/// <summary>
/// Formatea el número para usarse en las reasignaciones
/// </summary>
/// <param name="Number">Número que se va a formatear</param>
/// <returns>Regresa el número con el formato adecuado</returns>
private byte GetBytes(int Number)
{
return (byte)(Number % 0xE0000);
}
/// <summary>
/// Lee los datos del registro y los agrega al ListView
/// </summary>
private void LoadData()
{
Buff = ReadRegKey();
try
{
if (!(Buff == null))
{
if ((Buff.Length % 4) != 0 || Buff.Length < 20)
{
throw new InvalidDataException();
}
TSlblCount.Text = Convert.ToString(Buff[8] - 1);
FillListView(Buff);
}
}
catch (InvalidDataException)
{
MessageBox.Show("Se encontraron datos no válidos en el registro", "Error" , MessageBoxButtons.OK , MessageBoxIcon.Error);
TSlblCount.Text = "0";
}
}
/// <summary>
/// Crea los datos para agregarse al registro con el formato adecuado
/// </summary>
/// <param name="Items">Lista del tipo string con los elementos</param>
/// <returns>Regresa un arreglo de bytes con los datos</returns>
private byte[] RebuildRegistryEntry(List<string> Items)
{
byte[] OutBuff = new byte[16 + ((Items.Count/2)*4)]; //Encabezado y flags
OutBuff[0] = 0; //Encabezado y flags
OutBuff[1] = 0; //Encabezado y flags
OutBuff[2] = 0; //Encabezado y flags
OutBuff[3] = 0; //Encabezado y flags
OutBuff[4] = 0; //Encabezado y flags
OutBuff[5] = 0; //Encabezado y flags
OutBuff[6] = 0; //Encabezado y flags
OutBuff[7] = 0; //Encabezado y flags
OutBuff[8] = (byte)((Items.Count / 2) + 1); //Numero de entradas
OutBuff[9] = 0; //Numero de entradas
OutBuff[10] = 0;//Numero de entradas
OutBuff[11] = 0;//Numero de entradas
for (int i = 0; i < Items.Count; i++)
{
if (Convert.ToInt32((Items[i])) > 255)
{
OutBuff[12 + (i * 2)] = GetBytes(Convert.ToInt32(Items[i]));
OutBuff[13 + (i * 2)] = 0xe0;
}
else
{
OutBuff[12 + (i * 2)] = Convert.ToByte( Items[i]);
OutBuff[13 + (i * 2)] = 0x00;
}
}
OutBuff[OutBuff.Length - 4] = 0;
OutBuff[OutBuff.Length - 3] = 0;
OutBuff[OutBuff.Length - 2] = 0;
OutBuff[OutBuff.Length - 1] = 0;
return OutBuff;
}
#endregion
private void FrmMain_Load(object sender, EventArgs e)
{
LoadData();
}
private void BtnAdd_Click(object sender, EventArgs e)
{
if (!string.IsNullOrWhiteSpace(TxtKeyIn.Text) && !string.IsNullOrWhiteSpace(TxtKeyOut.Text))
{
ListViewItem LV = new ListViewItem(TxtKeyOut.Text);
LV.SubItems.Add(TxtKeyIn.Text);
LvReassignedKeys.Items.Add(LV);
}
else
MessageBox.Show("Revisa los valores de las teclas", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = true;
}
private void TSMIExport_Click(object sender, EventArgs e)
{
BuildAndSaveData(SaveFile:true);
}
private void BtnApply_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Realmente quires reasignar estas teclas?", "Eliminar", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
BuildAndSaveData(false);
}
private void BtnDelete_Click(object sender, EventArgs e)
{
try
{
DeleteRegKey();
}
catch
{
MessageBox.Show( e.ToString());
}
if (LvReassignedKeys.SelectedItems.Count > 0)
LvReassignedKeys.Items.Remove(LvReassignedKeys.SelectedItems[0]);
}
private void TSMIUpdate_Click(object sender, EventArgs e)
{
LvReassignedKeys.Items.Clear();
LoadData();
}
private void salirToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void TSMIReset_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Realmente quieres eliminar todo", "Eliminar", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
DeleteRegKey();
LvReassignedKeys.Items.Clear();
LoadData();
}
private void BtnRemoveKey_Click(object sender, EventArgs e)
{
foreach (ListViewItem LV in LvReassignedKeys.CheckedItems)
{
LvReassignedKeys.Items.Remove(LV);
}
}
}
}
El procedimiento "WriteHexRegKey()". Este procedimiento recibe un arreglo del tipo "byte", en esta función es donde se escribe la entrada en el registro. Pero entes de hacerlo, primero se crea una directiva en las cual se nos permite escribir en el registro usando "RegistrySecurity()". Luego se crea la entrada en el registro.
El siguiente procedimiento es: "DeleteRegKey()". Cómo su nombre lo sugiere, este procedimiento se encarga de borrar la entrada del registro donde se guardan las resignaciones.
La siguiente función es: "ReadRegKey()". Está función lee los datos de la clave en el registro y regresa un arreglo de bytes con los valores.
El siguiente procedimiento es: "SaveRegFile()". Este procedimiento recibe un arreglo de bytes con los datos de las resignaciones y guarda los datos en archivo .reg (Entrada del registro).
La siguiente función es: "IsEven()". Esta función recibe un valor del tipo "int", si el número es par regresa true, en caso contrario regresa false.
El siguiente procedimiento es "FillListView()". Este recibe un arreglo del tipo "byte" con las resignaciones, luego las agrega a un "listview" en donde la primer columna es el valor asignado a la tecla y la segunda es el valor real de la tecla.
El siguiente procedimiento es "BuildAndSaveData()". Recibe un valor del tipo "bool", si el valor es "true", guarda el archivo ".reg", en caso contrario escribe los valores en el registro.
La siguiente función es: "GetBytes()", recibe un valor del tipo "int", le aplica la división con el valor "0xE0000" y regresa el valor.
El siguiente procedimiento es "LoadData()". Este Procedimiento lee los datos del registro y los agrega al "listview". Antes hace unas comprobaciones para que los datos desplegados sean validos.
La última funcion es "RebuidRegistryEntry()" esta función recibe una lista del tipo "string" con los pares de las asignaciones y les da el formato adecuado. Devuelve un arreglos bytes con las resignaciones.
¿Cómo se usa?
Para usarlo, simplemente debes de hacer clic en la caja de texto que dice "Tecla a reprogramar" y presionar la tecla que quieres reasignar en mi caso, da el valor de 57361 y en la segunda caja de texto debes escribir el valor de la tecla de función, en este caso quiero que la tecla haga la función de silenciar y su valor es de 57376. Cuando termines con la asignaciones basta con hacer clic en el botón aplicar y se guardaran los valores en el registro. Si no quieres hacer los cambios en el registro aun, puedes guardar los datos en un archivo REG para poder usarlos después, para eso debes de hacer clic en el menú archivo y despues seleccionar "Exportar REG" con eso se generará el archivo que se puede integrar después.
Si en algun momento quieres restablecer las teclas, bastará con hacer clic en el menú archivo y después "Eliminar TODO". Recuerda que puedes revisar algunos valores de las teclas en esta página. También recuerda que hay que reiniciar la computadora para que los cambios surtan efecto.
El código fuente los puedes descargar de mi DropBox cómo de costumbre.
Por el momento es todo. Los leo luego.





No hay comentarios. :
Publicar un comentario