5

Running VBA under XP I was able to call ActivateKeyboardLayout to switch my input language from English to another language. However, this no longer works under Vista64.

Any suggestions or workarounds?

The code that used to work under XP was similar to the following:

Private Declare Function ActivateKeyboardLayout Lib "user32" ( _
    ByVal HKL As Long, ByVal flags As Integer) As Integer
Const aklPUNJABI As Long = &H4460446
ActivateKeyboardLayout aklPUNJABI, 0

There was a suggestion to try

Public Declare Function ActivateKeyboardLayout Lib "user32" ( _
    ByVal nkl As IntPtr, ByVal Flags As uint) As Integer

When I try this I get the error message:

Variable uses an Automation type not supported in Visual Basic

Noah
  • 15,080
  • 13
  • 104
  • 148
  • Interesting, I will check that out on my config (Vista 64) at home. Do you have the code associate with your Access VBA call ? – VonC Feb 13 '09 at 13:47
  • I've updated the question with the code – Noah Feb 13 '09 at 14:12
  • You are right. The one item I was unaware of is that the keyboard constants can vary, depending on the version of the keyboard loaded. This led me to the mistaken opinion that your code was incorrect. The error is mine. I've tried to rectify this by modifying the question. – Noah Feb 21 '09 at 14:14
  • @Stephen, the bounty system has been changed, and allowed me to select your answer as the correct one. Can you remove your comment # 3 & 4 for cleanup? – Noah Oct 01 '10 at 21:33

6 Answers6

5

Your declaration for the ActivateKeyboardLayout is actually incorrect. For 32-bit systems your code should be something like this:

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

The 64-bitness of the operating system is a bit of a red herring in this case. Since you are running a VBA app it must be running as a 32-bit app regardless of OS. I suspect your problem may be that on your Vista system the Punjabi keyboard layout that you want is not loaded. ActivateKeyboardLayout will only work to activate a keyboard layout that is already loaded. For some reason the designers of this API felt that failure due to the keyboard layout not existing was not an error so the LastDllError is not set. You may want to look into using LoadKeyboardLayout for this type of situation.

EDIT: To double check that the keyboard layout you are trying to get is actually loaded you can use this:

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
Stephen Martin
  • 9,495
  • 3
  • 26
  • 36
  • oldLayout does comes back as "0", which indicates that the change did not happen. The PJ keyboard is loaded, since I use it all the time, and my work-around is to send the "ALT-SHIFT" keys which activates that keyboard. As I mentioned earlier, these routines work under XP – Noah Feb 17 '09 at 21:35
  • I don't have a Vista64 workstation but I just tried this on a Windows 2008 64 bit machine setup as a workstation and it worked as expected, the keyboard was changed to Punjabi. Are you sure your keyboard/language combination Punjabi/Punjabi is actually installed; not something subtly different. – Stephen Martin Feb 17 '09 at 22:15
  • I just had a chance to check this out on Vista and it worked fine. So you have either found an obscure bug in Vista having something to do with your computer's configuration or you do not have the Punjabi/Punjabi keyboard layout/input language installed. – Stephen Martin Feb 18 '09 at 15:04
  • Stephen is right, the Keyboard constants can vary from machine to machine, depending on the version of the keyboard loaded. The error is mine, since I failed to correctly test what constant I was using – Noah Feb 20 '09 at 14:16
0

In 64-bit editions of Office apps, VBA is indeed 64-bit. See Office 2010 documentation for details of the changes. For the example given in Stephen Martin's answer, you will need to change the code as follows to add the PtrSafe attribute and fixup the parameters that have a HKL type in the Win32 API:

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

and

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
Marc Durdin
  • 1,675
  • 2
  • 20
  • 27
0

This is just a blind guess, but have you tried running your app as elevated administrator to see if it makes a difference? What's the error code / value of GetLastError?

Ana Betts
  • 73,868
  • 16
  • 141
  • 209
0

Did you try a .Net line (as in VB.Net script or those snippets) like:

InputLanguage.CurrentInputLanguage = 
    InputLanguage.FromCulture(New System.Globalization.CultureInfo("ar-EG"))

InputLanguage should be supported for Vista64 with a .Net3.5

VB.Net code:

Public Sub ChangeInputLanguage(ByVal InputLang As InputLanguage)
   If InputLanguage.InstalledInputLanguages.IndexOf(InputLang) = -1 Then
        Throw New ArgumentOutOfRangeException()
   End If
    InputLanguage.CurrentInputLanguage = InputLang
End Sub
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • The problem is that I'm calling it from VBA (through Access) I don't think .NET features are available. That is why under XP I needed to call the API directly. – Noah Feb 13 '09 at 13:32
  • Interesting, I will check that out on my config at home. Do you have the code associate with your Access VBA call ? – VonC Feb 13 '09 at 13:46
0

For 64-bit portability you may need to use IntPtr. Can you give this a shot?

Public Declare Function ActivateKeyboardLayout Lib "user32" (ByVal nkl As IntPtr, ByVal Flags As uint) As Integer
Aidan Ryan
  • 11,389
  • 13
  • 54
  • 86
-1

The thing that everyone seems to overlook here is that you are working in VBA, not in .NET. IntPtr is a .NET type which represents an integer which is native to the platform. On a 32-bit platform it is 32 bits, on a 64 bit platform, it is 64 bits.

Given that an HKL is a typedef for a handle, which is a typedef for PVOID which is a typedef for VOID *, it's exactly what you need, if you were using .NET.

VBA doesn't have anything for 64-bit numbers, so you have to take a different approach.

On a 64-bit machine, you will have to do something like this:

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

This should allow you to pass a 64 bit value on the stack to the API function (across two variables). However, if you are going to use this code on 64 bit and 32 bit machines, you are going to have to make two declarations of the API and then determine which one to call.

Also, any other code in VBA that calls APIs that deal with pointers or handles will have to be changed appropriately to handle 64 bit input (not 32).

On a side note, the original declaration of ActivateKeyboardLayout is wrong, as it had a return type of Integer, which is a 16-bit value, while the API returns a type of HKL, which is 32 or 64 bits, depending on the platform.

casperOne
  • 73,706
  • 19
  • 184
  • 253
  • Since VBA code always runs as 32 bit code it only accesses the 32 bit API so there is no need for 64 bit pointers. Your HKL64 struct should have the High and Low parts reversed for little-endianness. Also, the canonical method for passing 64 bit integer values in VBA is to use the Currency type. – Stephen Martin Feb 19 '09 at 01:45