1 /*
   2  * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "jlong.h"
  27 #include "awtmsg.h"
  28 #include "awt_AWTEvent.h"
  29 #include "awt_Component.h"
  30 #include "awt_Toolkit.h"
  31 #include "locale_str.h"
  32 #include <sun_awt_windows_WInputMethod.h>
  33 #include <sun_awt_windows_WInputMethodDescriptor.h>
  34 #include <java_awt_event_InputMethodEvent.h>
  35 
  36 const UINT SYSCOMMAND_IMM = 0xF000 - 100;
  37 
  38 /************************************************************************
  39  * WInputMethod native methods
  40  */
  41 
  42 extern "C" {
  43 
  44 jobject CreateLocaleObject(JNIEnv *env, const char * name);
  45 HKL getDefaultKeyboardLayout();
  46 
  47 extern BOOL g_bUserHasChangedInputLang;
  48 
  49 /*
  50  * Class:     sun_awt_windows_WInputMethod
  51  * Method:    createNativeContext
  52  * Signature: ()I
  53  */
  54 JNIEXPORT jint JNICALL
  55 Java_sun_awt_windows_WInputMethod_createNativeContext(JNIEnv *env, jobject self)
  56 {
  57     TRY;
  58 
  59     // use special message to call ImmCreateContext() in main thread.
  60     return (jint)AwtToolkit::GetInstance().SendMessage(WM_AWT_CREATECONTEXT);
  61 
  62     CATCH_BAD_ALLOC_RET(0);
  63 }
  64 
  65 
  66 /*
  67  * Class:     sun_awt_windows_WInputMethod
  68  * Method:    destroyNativeContext
  69  * Signature: (I)V
  70  */
  71 JNIEXPORT void JNICALL
  72 Java_sun_awt_windows_WInputMethod_destroyNativeContext(JNIEnv *env, jobject self, jint context)
  73 {
  74     TRY_NO_VERIFY;
  75 
  76     // use special message to call ImmDestroyContext() in main thread.
  77     AwtToolkit::GetInstance().SendMessage(WM_AWT_DESTROYCONTEXT, context, 0);
  78 
  79     CATCH_BAD_ALLOC;
  80 }
  81 
  82 
  83 /*
  84  * Class:     sun_awt_windows_WInputMethod
  85  * Method:    enableNativeIME
  86  * Signature: (Lsun/awt/windows/WComponentPeer;I)V
  87  */
  88 JNIEXPORT void JNICALL
  89 Java_sun_awt_windows_WInputMethod_enableNativeIME(JNIEnv *env, jobject self, jobject peer,
  90                                                   jint context, jboolean useNativeCompWindow)
  91 {
  92     TRY;
  93 
  94     //get C++ Class of Focused Component
  95     if (peer == 0)      return;
  96     AwtComponent* p = (AwtComponent*)JNI_GET_PDATA(peer);
  97     if (p == 0)         return;
  98 
  99     p->SetInputMethod(self, useNativeCompWindow);
 100 
 101     // use special message to call ImmAssociateContext() in main thread.
 102     AwtToolkit::GetInstance().SendMessage(WM_AWT_ASSOCIATECONTEXT,
 103                                           reinterpret_cast<WPARAM>(p->GetHWnd()), context);
 104 
 105     CATCH_BAD_ALLOC;
 106 }
 107 
 108 
 109 /*
 110  * Class:     sun_awt_windows_WInputMethod
 111  * Method:    disableNativeIME
 112  * Signature: (Lsun/awt/windows/WComponentPeer;)V
 113  */
 114 JNIEXPORT void JNICALL
 115 Java_sun_awt_windows_WInputMethod_disableNativeIME(JNIEnv *env, jobject self, jobject peer)
 116 {
 117     TRY_NO_VERIFY;
 118 
 119     //get C++ Class of Focused Component
 120     if (peer == 0)      return;
 121     AwtComponent* p = (AwtComponent*)JNI_GET_PDATA(peer);
 122     if (p == 0)         return;
 123 
 124     p->SetInputMethod(NULL, TRUE);
 125 
 126     // use special message to call ImmAssociateContext() in main thread.
 127     AwtToolkit::GetInstance().SendMessage(WM_AWT_ASSOCIATECONTEXT,
 128                                           reinterpret_cast<WPARAM>(p->GetHWnd()), NULL);
 129 
 130     CATCH_BAD_ALLOC;
 131 }
 132 
 133 
 134 /*
 135  * Class:     sun_awt_windows_WComponentPeer
 136  * Method:    handleEvent
 137  * Signature: (Lsun/awt/windows/WComponentPeer;Ljava/awt/AWTEvent;)V
 138  */
 139 JNIEXPORT void JNICALL
 140 Java_sun_awt_windows_WInputMethod_handleNativeIMEEvent(JNIEnv *env, jobject self,
 141                                                        jobject peer, jobject event)
 142 {
 143     TRY;
 144 
 145     PDATA pData;
 146     JNI_CHECK_PEER_RETURN(peer);
 147     AwtComponent* p = (AwtComponent *)pData;
 148 
 149     JNI_CHECK_NULL_RETURN(event, "null AWTEvent");
 150     if (env->EnsureLocalCapacity(1) < 0) {
 151         return;
 152     }
 153     jbyteArray bdata = (jbyteArray)(env)->GetObjectField(event, AwtAWTEvent::bdataID);
 154     if (bdata == 0) {
 155         return;
 156     }
 157     MSG msg;
 158     (env)->GetByteArrayRegion(bdata, 0, sizeof(MSG), (jbyte *)&msg);
 159     (env)->DeleteLocalRef(bdata);
 160     BOOL isConsumed =
 161       (BOOL)(env)->GetBooleanField(event, AwtAWTEvent::consumedID);
 162     int id = (env)->GetIntField(event, AwtAWTEvent::idID);
 163     DASSERT(!safe_ExceptionOccurred(env));
 164 
 165     if (isConsumed || p==NULL)  return;
 166 
 167     if (id >= java_awt_event_InputMethodEvent_INPUT_METHOD_FIRST &&
 168         id <= java_awt_event_InputMethodEvent_INPUT_METHOD_LAST)
 169     {
 170         long modifiers = p->GetJavaModifiers();
 171         if (msg.message==WM_CHAR || msg.message==WM_SYSCHAR) {
 172             WCHAR unicodeChar = L'\0';
 173             unicodeChar = (WCHAR)msg.wParam;
 174             p->SendKeyEvent(java_awt_event_KeyEvent_KEY_TYPED,
 175                             0, //to be fixed nowMillis(),
 176                             java_awt_event_KeyEvent_CHAR_UNDEFINED,
 177                             unicodeChar,
 178                             modifiers,
 179                             java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN,
 180                             &msg);
 181         } else {
 182             MSG* pCopiedMsg = new MSG;
 183             *pCopiedMsg = msg;
 184             p->SendMessage(WM_AWT_HANDLE_EVENT, (WPARAM) FALSE,
 185                         (LPARAM) pCopiedMsg);
 186         }
 187         (env)->SetBooleanField(event, AwtAWTEvent::consumedID, JNI_TRUE);
 188     }
 189 
 190     CATCH_BAD_ALLOC;
 191 }
 192 
 193 /*
 194  * Class:     sun_awt_windows_WInputMethod
 195  * Method:    notifyNativeIME
 196  * Signature: (Lsun/awt/windows/WComponentPeer;I)V
 197  */
 198 JNIEXPORT void JNICALL
 199 Java_sun_awt_windows_WInputMethod_endCompositionNative(JNIEnv *env, jobject self,
 200                                                        jint context, jboolean flag)
 201 {
 202     TRY;
 203 
 204     // TODO: currently the flag parameter is ignored and the outstanding input is
 205     //       always discarded.
 206     //       If the flag value is Java_sun_awt_windows_WInputMethod_COMMIT_INPUT,
 207     //       then input text should be committed. Otherwise, should be discarded.
 208     //
 209     // 10/29/98 - Changed to commit it according to the flag.
 210 
 211     // use special message to call ImmNotifyIME() in main thread.
 212     AwtToolkit::GetInstance().SendMessage(WM_AWT_ENDCOMPOSITION, context,
 213         (LPARAM)(flag != sun_awt_windows_WInputMethod_DISCARD_INPUT));
 214 
 215     CATCH_BAD_ALLOC;
 216 }
 217 
 218 /*
 219  * Class:     sun_awt_windows_WInputMethod
 220  * Method:    setConversionStatus
 221  * Signature: (II)V
 222  */
 223 JNIEXPORT void JNICALL
 224 Java_sun_awt_windows_WInputMethod_setConversionStatus(JNIEnv *env, jobject self, jint context, jint request)
 225 {
 226     TRY;
 227 
 228     // use special message to call ImmSetConversionStatus() in main thread.
 229     AwtToolkit::GetInstance().SendMessage(WM_AWT_SETCONVERSIONSTATUS,
 230                                           context,
 231                                           MAKELPARAM((WORD)request, (WORD)0));
 232 
 233     CATCH_BAD_ALLOC;
 234 }
 235 
 236 /*
 237  * Class:     sun_awt_windows_WInputMethod
 238  * Method:    getConversionStatus
 239  * Signature: (I)I
 240  */
 241 JNIEXPORT jint JNICALL
 242 Java_sun_awt_windows_WInputMethod_getConversionStatus(JNIEnv *env, jobject self, jint context)
 243 {
 244     TRY;
 245 
 246     // use special message to call ImmSetConversionStatus() in main thread.
 247     return (jint) AwtToolkit::GetInstance().SendMessage(
 248         WM_AWT_GETCONVERSIONSTATUS, context, 0);
 249 
 250     CATCH_BAD_ALLOC_RET(0);
 251 }
 252 
 253 /*
 254  * Class:     sun_awt_windows_WInputMethod
 255  * Method:    setOpenStatus
 256  * Signature: (IZ)V
 257  */
 258 JNIEXPORT void JNICALL
 259 Java_sun_awt_windows_WInputMethod_setOpenStatus(JNIEnv *env, jobject self, jint context, jboolean flag)
 260 {
 261     TRY;
 262 
 263     // use special message to call ImmSetConversionStatus() in main thread.
 264     AwtToolkit::GetInstance().SendMessage(WM_AWT_SETOPENSTATUS,
 265                                           context, flag);
 266 
 267     CATCH_BAD_ALLOC;
 268 }
 269 
 270 /*
 271  * Class:     sun_awt_windows_WInputMethod
 272  * Method:    getConversionStatus
 273  * Signature: (I)Z
 274  */
 275 JNIEXPORT jboolean JNICALL
 276 Java_sun_awt_windows_WInputMethod_getOpenStatus(JNIEnv *env, jobject self, jint context)
 277 {
 278     TRY;
 279 
 280     // use special message to call ImmSetConversionStatus() in main thread.
 281     return (jboolean)(AwtToolkit::GetInstance().SendMessage(
 282                                                        WM_AWT_GETOPENSTATUS,
 283                                                        context, 0));
 284     CATCH_BAD_ALLOC_RET(0);
 285 }
 286 
 287 /*
 288  * Class:     sun_awt_windows_WInputMethod
 289  * Method:    getNativeLocale
 290  * Signature: ()Ljava/util/Locale;
 291  */
 292 JNIEXPORT jobject JNICALL Java_sun_awt_windows_WInputMethod_getNativeLocale
 293   (JNIEnv *env, jclass cls)
 294 {
 295     TRY;
 296 
 297     const char * javaLocaleName = getJavaIDFromLangID(AwtComponent::GetInputLanguage());
 298     if (javaLocaleName != NULL) {
 299         // Now WInputMethod.currentLocale and AwtComponent::m_idLang are get sync'ed,
 300         // so we can reset this flag.
 301         g_bUserHasChangedInputLang = FALSE;
 302 
 303         return CreateLocaleObject(env, javaLocaleName);
 304     } else {
 305         return NULL;
 306     }
 307 
 308     CATCH_BAD_ALLOC_RET(NULL);
 309 }
 310 
 311 /*
 312  * Class:     sun_awt_windows_WInputMethod
 313  * Method:    setNativeLocale
 314  * Signature: (Ljava/lang/String;Z)Z
 315  */
 316 JNIEXPORT jboolean JNICALL Java_sun_awt_windows_WInputMethod_setNativeLocale
 317   (JNIEnv *env, jclass cls, jstring localeString, jboolean onActivate)
 318 {
 319     TRY;
 320 
 321     // check if current language ID is the requested one.  Note that the
 322     // current language ID (returned from 'getJavaIDFromLangID') is in
 323     // ASCII encoding, so we use 'GetStringUTFChars' to retrieve requested
 324     // language ID from the 'localeString' object.
 325     const char * current = getJavaIDFromLangID(AwtComponent::GetInputLanguage());
 326     jboolean isCopy;
 327     const char * requested = env->GetStringUTFChars(localeString, &isCopy);
 328     if ((current != NULL) && (strcmp(current, requested) == 0)) {
 329         env->ReleaseStringUTFChars(localeString, requested);
 330         return JNI_TRUE;
 331     }
 332 
 333     // get list of available HKLs.  Adding the user's preferred layout on top of the layout
 334     // list which is returned by GetKeyboardLayoutList ensures to match first when
 335     // looking up suitable layout.
 336     int layoutCount = ::GetKeyboardLayoutList(0, NULL) + 1;  // +1 for user's preferred HKL
 337     HKL FAR * hKLList = (HKL FAR *)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, sizeof(HKL), layoutCount);
 338     DASSERT(!safe_ExceptionOccurred(env));
 339     ::GetKeyboardLayoutList(layoutCount - 1, &(hKLList[1]));
 340     hKLList[0] = getDefaultKeyboardLayout(); // put user's preferred layout on top of the list
 341 
 342     // lookup matching LangID
 343     jboolean retValue = JNI_FALSE;
 344     for (int i = 0; i < layoutCount; i++) {
 345         const char * supported = getJavaIDFromLangID(LOWORD(hKLList[i]));
 346         if ((supported != NULL) && (strcmp(supported, requested) == 0)) {
 347             // use special message to call ActivateKeyboardLayout() in main thread.
 348             if (AwtToolkit::GetInstance().SendMessage(WM_AWT_ACTIVATEKEYBOARDLAYOUT, (WPARAM)onActivate, (LPARAM)hKLList[i])) {
 349                 //also need to change the same keyboard layout for the Java AWT-EventQueue thread
 350                 AwtToolkit::activateKeyboardLayout(hKLList[i]);
 351                 retValue = JNI_TRUE;
 352             }
 353             break;
 354         }
 355     }
 356 
 357     env->ReleaseStringUTFChars(localeString, requested);
 358     free(hKLList);
 359     return retValue;
 360 
 361     CATCH_BAD_ALLOC_RET(JNI_FALSE);
 362 }
 363 
 364 /*
 365  * Class:     sun_awt_windows_WInputMethod
 366  * Method:    hideWindowsNative
 367  * Signature: (Lsun/awt/windows/WComponentPeer;Z)V
 368  */
 369 JNIEXPORT void JNICALL Java_sun_awt_windows_WInputMethod_setStatusWindowVisible
 370   (JNIEnv *env, jobject self, jobject peer, jboolean visible)
 371 {
 372     /* Retrieve the default input method Window handler from AwtToolkit.
 373        Windows system creates a default input method window for the
 374        toolkit thread.
 375     */
 376     HWND hwndIME = AwtToolkit::GetInstance().GetInputMethodWindow();
 377     if (hwndIME == NULL) {
 378         if (peer == NULL) {
 379             return;
 380         }
 381 
 382         AwtComponent* p = (AwtComponent*)JNI_GET_PDATA(peer);
 383         if (p == NULL || (hwndIME = ImmGetDefaultIMEWnd(p->GetHWnd())) == NULL) {
 384             return;
 385         }
 386 
 387         AwtToolkit::GetInstance().SetInputMethodWindow(hwndIME);
 388     }
 389 
 390     ::SendMessage(hwndIME, WM_IME_CONTROL,
 391                   visible ? IMC_OPENSTATUSWINDOW : IMC_CLOSESTATUSWINDOW, 0);
 392 }
 393 
 394 /*
 395  * Class:     sun_awt_windows_WInputMethod
 396  * Method:    openCandidateWindow
 397  * Signature: (Lsun/awt/windows/WComponentPeer;II)V
 398  */
 399 JNIEXPORT void JNICALL Java_sun_awt_windows_WInputMethod_openCandidateWindow
 400   (JNIEnv *env, jobject self, jobject peer, jint x, jint y)
 401 {
 402     TRY;
 403 
 404     PDATA pData;
 405     JNI_CHECK_PEER_RETURN(peer);
 406 
 407     jobject peerGlobalRef = env->NewGlobalRef(peer);
 408 
 409     // WARNING! MAKELONG macro treats the given values as unsigned.
 410     //   This may lead to some bugs in multiscreen configurations, as
 411     //   coordinates can be negative numbers. So, while handling
 412     //   WM_AWT_OPENCANDIDATEWINDOW message in AwtToolkit, we should
 413     //   carefully extract right x and y values using GET_X_LPARAM and
 414     //   GET_Y_LPARAM, not LOWORD and HIWORD
 415     // See CR 4805862, AwtToolkit::WndProc
 416 
 417     // use special message to open candidate window in main thread.
 418     AwtToolkit::GetInstance().SendMessage(WM_AWT_OPENCANDIDATEWINDOW,
 419                                           (WPARAM)peerGlobalRef, MAKELONG(x, y));
 420 
 421     CATCH_BAD_ALLOC;
 422 }
 423 
 424 
 425 /************************************************************************
 426  * WInputMethodDescriptor native methods
 427  */
 428 
 429 /*
 430  * Class:     sun_awt_windows_WInputMethodDescriptor
 431  * Method:    getNativeAvailableLocales
 432  * Signature: ()[Ljava/util/Locale;
 433  */
 434 JNIEXPORT jobjectArray JNICALL Java_sun_awt_windows_WInputMethodDescriptor_getNativeAvailableLocales
 435   (JNIEnv *env, jclass self)
 436 {
 437     TRY;
 438 
 439     // get list of available HKLs
 440     int layoutCount = ::GetKeyboardLayoutList(0, NULL);
 441     HKL FAR * hKLList = (HKL FAR *)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, sizeof(HKL), layoutCount);
 442     DASSERT(!safe_ExceptionOccurred(env));
 443     ::GetKeyboardLayoutList(layoutCount, hKLList);
 444 
 445     // get list of Java locale names while getting rid of duplicates
 446     int srcIndex = 0;
 447     int destIndex = 0;
 448     int javaLocaleNameCount = 0;
 449     int current = 0;
 450     const char ** javaLocaleNames = (const char **)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, sizeof(char *), layoutCount);
 451     DASSERT(!safe_ExceptionOccurred(env));
 452     for (; srcIndex < layoutCount; srcIndex++) {
 453         const char * srcLocaleName = getJavaIDFromLangID(LOWORD(hKLList[srcIndex]));
 454 
 455         if (srcLocaleName == NULL) {
 456             // could not find corresponding Java locale name for this HKL.
 457             continue;
 458         }
 459 
 460         for (current = 0; current < destIndex; current++) {
 461             if (strcmp(javaLocaleNames[current], srcLocaleName) == 0) {
 462                 // duplicated. ignore this HKL
 463                 break;
 464             }
 465         }
 466 
 467         if (current == destIndex) {
 468             javaLocaleNameCount++;
 469             destIndex++;
 470             javaLocaleNames[current] = srcLocaleName;
 471         }
 472     }
 473 
 474     // convert it to an array of Java locale objects
 475     jclass localeClass = env->FindClass("java/util/Locale");
 476     jobjectArray locales = env->NewObjectArray(javaLocaleNameCount, localeClass, NULL);
 477     for (current = 0; current < javaLocaleNameCount; current++) {
 478         env->SetObjectArrayElement(locales,
 479                                    current,
 480                                    CreateLocaleObject(env, javaLocaleNames[current]));
 481     }
 482     DASSERT(!safe_ExceptionOccurred(env));
 483 
 484     env->DeleteLocalRef(localeClass);
 485     free(hKLList);
 486     free(javaLocaleNames);
 487     return locales;
 488 
 489     CATCH_BAD_ALLOC_RET(NULL);
 490 }
 491 
 492 /**
 493  * Class:     sun_awt_windows_WInputMethod
 494  * Method:    getNativeIMMDescription
 495  * Signature: ()Ljava/lang/String;
 496  *
 497  * This method tries to get the information about the input method associated with
 498  * the current active thread.
 499  *
 500  */
 501 JNIEXPORT jstring JNICALL Java_sun_awt_windows_WInputMethod_getNativeIMMDescription
 502   (JNIEnv *env, jobject self) {
 503 
 504     TRY;
 505 
 506     // Get the keyboard layout of the active thread.
 507     HKL hkl = AwtComponent::GetKeyboardLayout();
 508     LPTSTR szImmDescription = NULL;
 509     UINT buffSize = 0;
 510     jstring infojStr = NULL;
 511 
 512     if ((buffSize = ::ImmGetDescription(hkl, szImmDescription, 0)) > 0) {
 513         szImmDescription = (LPTSTR) SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, (buffSize+1), sizeof(TCHAR));
 514 
 515         if (szImmDescription != NULL) {
 516             ImmGetDescription(hkl, szImmDescription, buffSize);
 517 
 518             infojStr = JNU_NewStringPlatform(env, szImmDescription);
 519 
 520             free(szImmDescription);
 521         }
 522     }
 523 
 524     return infojStr;
 525 
 526     CATCH_BAD_ALLOC_RET(NULL);
 527 }
 528 
 529 /*
 530  * Create a Java locale object from its name string
 531  */
 532 jobject CreateLocaleObject(JNIEnv *env, const char * name)
 533 {
 534     TRY;
 535 
 536     // get language, country, variant information
 537     char * language = (char *)safe_Malloc(strlen(name) + 1);
 538     char * country;
 539     char * variant;
 540     DASSERT(!safe_ExceptionOccurred(env));
 541     strcpy(language, name);
 542     for (country = language; *country != '_' && *country != '\0'; country++);
 543     if (*country == '_') {
 544         *country++ = '\0';
 545         for (variant = country; *variant != '_' && *variant != '\0'; variant++);
 546         if (*variant == '_') {
 547             *variant++ = '\0';
 548         }
 549     } else {
 550         variant = country;
 551     }
 552 
 553     // create Locale object
 554     jobject langObj = env->NewStringUTF(language);
 555     jobject ctryObj = env->NewStringUTF(country);
 556     jobject vrntObj = env->NewStringUTF(variant);
 557     jobject localeObj = JNU_NewObjectByName(env, "java/util/Locale",
 558                                             "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
 559                                             langObj, ctryObj, vrntObj);
 560     free(language);
 561     env->DeleteLocalRef(langObj);
 562     env->DeleteLocalRef(ctryObj);
 563     env->DeleteLocalRef(vrntObj);
 564 
 565     return localeObj;
 566 
 567     CATCH_BAD_ALLOC_RET(NULL);
 568 }
 569 
 570 
 571 /*
 572  * Gets user's preferred keyboard layout
 573  * Warning: This is version dependent code
 574  */
 575 HKL getDefaultKeyboardLayout() {
 576     LONG ret;
 577     HKL hkl = 0;
 578     HKEY hKey;
 579     BYTE szHKL[16];
 580     DWORD cbHKL = 16;
 581     LPTSTR end;
 582 
 583     if (IS_NT) {
 584         ret = ::RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Keyboard Layout\\Preload"), NULL, KEY_READ, &hKey);
 585     } else {
 586         ret = ::RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("keyboard layout\\preload\\1"), NULL, KEY_READ, &hKey);
 587     }
 588 
 589     if (ret == ERROR_SUCCESS) {
 590         if (IS_NT) {
 591             ret = ::RegQueryValueEx(hKey, TEXT("1"), 0, 0, szHKL, &cbHKL);
 592         } else {
 593             ret = ::RegQueryValueEx(hKey, NULL, 0, 0, szHKL, &cbHKL);
 594         }
 595 
 596         if (ret == ERROR_SUCCESS) {
 597             hkl = reinterpret_cast<HKL>(static_cast<INT_PTR>(
 598                 _tcstoul((LPCTSTR)szHKL, &end, 16)));
 599         }
 600 
 601         ::RegCloseKey(hKey);
 602     }
 603 
 604     return hkl;
 605 }
 606 } /* extern "C" */