¿Cómo puedo llamar a ActivateKeyboardLayout desde Windows Vista de 64 bits con VBA?
-
19-08-2019 - |
Pregunta
Ejecutando VBA bajo XP pude llamar a ActivateKeyboardLayout para cambiar mi idioma de entrada del inglés a otro idioma. Sin embargo, esto ya no funciona en Vista64.
¿Alguna sugerencia o solución?
El código que solía funcionar en XP era similar al siguiente:
Private Declare Function ActivateKeyboardLayout Lib "user32" ( _
ByVal HKL As Long, ByVal flags As Integer) As Integer
Const aklPUNJABI As Long = &H4460446
ActivateKeyboardLayout aklPUNJABI, 0
Hubo una sugerencia para probar
Public Declare Function ActivateKeyboardLayout Lib "user32" ( _
ByVal nkl As IntPtr, ByVal Flags As uint) As Integer
Cuando intento esto recibo el mensaje de error:
La variable usa un tipo de automatización no compatible con Visual Basic
Solución
Su declaración para ActivateKeyboardLayout es realmente incorrecta. Para sistemas de 32 bits, su código debería ser algo como esto:
Private Declare Function ActivateKeyboardLayout Lib "user32" (ByVal HKL As Long, _
ByVal flags As Long) As Long
Const aklPUNJABI As Long = &H4460446
Dim oldLayout as Long
oldLayout = ActivateKeyboardLayout(aklPUNJABI, 0)
If oldLayout = 0 Then
'Oops an error'
Else
'Save old layout for later restore?'
End If
La 64 bits del sistema operativo es un poco falsa en este caso. Como está ejecutando una aplicación VBA, debe ejecutarse como una aplicación de 32 bits, independientemente del sistema operativo. Sospecho que su problema puede ser que en su sistema Vista el diseño del teclado Punjabi que desea no está cargado. ActivateKeyboardLayout solo funcionará para activar un diseño de teclado que ya está cargado. Por alguna razón, los diseñadores de esta API consideraron que la falla debido a que el diseño del teclado no existía no era un error, por lo que LastDllError no está configurado. Es posible que desee considerar el uso de LoadKeyboardLayout para este tipo de situación.
EDITAR: para verificar que la distribución del teclado que está intentando cargar esté cargada, puede usar esto:
Private Declare Function GetKeyboardLayoutList Lib "user32" (ByVal size As Long, _
ByRef layouts As Long) As Long
Dim numLayouts As Long
Dim i As Long
Dim layouts() As Long
numLayouts = GetKeyboardLayoutList(0, ByVal 0&)
ReDim layouts(numLayouts - 1)
GetKeyboardLayoutList numLayouts, layouts(0)
Dim msg As String
msg = "Loaded keyboard layouts: " & vbCrLf & vbCrLf
For i = 0 To numLayouts - 1
msg = msg & Hex(layouts(i)) & vbCrLf
Next
MsgBox msg
Otros consejos
Esto es solo una suposición ciega, pero ¿ha intentado ejecutar su aplicación como administrador elevado para ver si hace la diferencia? ¿Cuál es el código / valor de error de GetLastError?
¿Intentó con un .Net line (como en Script VB.Net o esos fragmentos ) como:
InputLanguage.CurrentInputLanguage =
InputLanguage.FromCulture(New System.Globalization.CultureInfo("ar-EG"))
InputLanguage debería ser compatible con Vista64 con un .Net3.5
Código VB.Net:
Public Sub ChangeInputLanguage(ByVal InputLang As InputLanguage)
If InputLanguage.InstalledInputLanguages.IndexOf(InputLang) = -1 Then
Throw New ArgumentOutOfRangeException()
End If
InputLanguage.CurrentInputLanguage = InputLang
End Sub
Para la portabilidad de 64 bits, es posible que deba usar IntPtr. ¿Puedes darle una oportunidad a esto?
Public Declare Function ActivateKeyboardLayout Lib "user32" (ByVal nkl As IntPtr, ByVal Flags As uint) As Integer
En las ediciones de 64 bits de las aplicaciones de Office, VBA es de hecho de 64 bits. Consulte documentación de Office 2010 para Detalles de los cambios. Para el ejemplo dado en Respuesta de Stephen Martin , deberá cambiar el código de la siguiente manera para agregar el atributo PtrSafe
y corregir los parámetros que tienen un tipo HKL
en la API Win32:
Private Declare PtrSafe Function ActivateKeyboardLayout Lib "user32" (ByVal HKL As LongPtr, _
ByVal flags As Long) As LongPtr
Const aklPUNJABI As LongPtr = &H4460446
Dim oldLayout as LongPtr
oldLayout = ActivateKeyboardLayout(aklPUNJABI, 0)
If oldLayout = 0 Then
'Oops an error'
Else
'Save old layout for later restore?'
End If
y
Private Declare PtrSafe Function GetKeyboardLayoutList Lib "user32" (ByVal size As Long, _
ByRef layouts As LongPtr) As Long
Dim numLayouts As Long
Dim i As Long
Dim layouts() As LongPtr
numLayouts = GetKeyboardLayoutList(0, ByVal 0&)
ReDim layouts(numLayouts - 1)
GetKeyboardLayoutList numLayouts, layouts(0)
Dim msg As String
msg = "Loaded keyboard layouts: " & vbCrLf & vbCrLf
For i = 0 To numLayouts - 1
msg = msg & Hex(layouts(i)) & vbCrLf
Next
MsgBox msg
Lo que todos parecen pasar por alto aquí es que estás trabajando en VBA, no en .NET. IntPtr es un tipo .NET que representa un número entero nativo de la plataforma. En una plataforma de 32 bits, tiene 32 bits, en una plataforma de 64 bits, tiene 64 bits.
Dado que un HKL es un typedef para un identificador, que es un typedef para PVOID que es un typedef para VOID *, es exactamente lo que necesita, si estaba usando .NET.
VBA no tiene nada para números de 64 bits, por lo que debe adoptar un enfoque diferente.
En una máquina de 64 bits, tendrá que hacer algo como esto:
Public Type HKL64
High As Long
Low As Long
End Type
Private Declare Function ActivateKeyboardLayout Lib "user32" ( _
Byval HklHigh As Long, Byval HklLow As Long, _
ByVal flags As Integer) As HKL64
Este debería permitirle pasar un valor de 64 bits en la pila a la función API (a través de dos variables). Sin embargo, si va a usar este código en máquinas de 64 bits y 32 bits, tendrá que hacer dos declaraciones de la API y luego determinar a cuál llamar.
Además, cualquier otro código en VBA que llame a las API que se ocupan de punteros o controladores tendrá que cambiarse adecuadamente para manejar la entrada de 64 bits (no 32).
En una nota al margen, la declaración original de ActivateKeyboardLayout es incorrecta, ya que tenía un tipo de retorno de Integer, que es un valor de 16 bits, mientras que la API devuelve un tipo de HKL, que es de 32 o 64 bits, dependiendo en la plataforma.