DOCUMENT:Q189779 09-JAN-2000 [vbwin] TITLE :BUG: Enabling Accelerator Keys in Visual Basic ActiveX Controls PRODUCT :Microsoft Visual Basic for Windows PROD/VER:WINDOWS:5.0 OPER/SYS: KEYWORDS:kbGrpDSVB ====================================================================== ------------------------------------------------------------------------------- The information in this article applies to: - Microsoft Visual Basic Enterprise Edition for Windows, version 5.0 ------------------------------------------------------------------------------- SYMPTOMS ======== Using an ActiveX control (or UserControl) that is created with Visual Basic 5.0 in an MFC container application, the accelerator keys (or access keys) defined for the child windows in the ActiveX control do not work as expected. CAUSE ===== Because both Visual Basic and MFC provide different mnemonic handling implementations, Visual Basic needs to change the UserControl's accelerator tables in order for the accelerator keys to work properly in an MFC container. For more information, see the REFERENCES section of this article. RESOLUTION ========== The workaround is to call the EnumChildWindows() API to enumerate all the child windows of the ActiveX control from the container application. Then, determine the mnemonic character key of each child window based on the HWND that is passed to the callback function in the EnumChildWindows()call. STATUS ====== Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article. We are researching this bug and will post new information here in the Microsoft Knowledge Base as it becomes available. MORE INFORMATION ================ The steps below show how to set up a callback function for the EnumChildWindows() API from the MFC container application; determine the access keycode for the child windows of the Visual Basic ActiveX control; and determine which control to set the input focus to when an accelerator key is pressed: 1. Place the Visual Basic ActiveX control in an MFC dialog-based application. 2. Add a member variable named m_XControlInfo to the CDialog-derived class as below: #include // Information about child controls in the Visual Basic // ActiveX control. typedef struct xcontrol_info { HWND hwnd; // handle of child control char mnemonic_char; // mnemonic character key for the control } XCONTROL_INFO; class CTestVBDlg : public CDialog { ... // Array of info for child controls. CArray m_XControlInfo; ... } 3. Enumerate all the child windows of the Visual Basic ActiveX control in OnInitDialog() of your CDialog-derived class. This callback function will be called for each child control in the ActiveX control. The HWND and the mnemonic character keycode of the child control will be stored in the m_XControlInfo array: // Callback function for EnumChildWindows() API BOOL CALLBACK EnumActiveXControlChildProc(HWND hwnd, LPARAM lParam) { CArray* arr = (CArray*)lParam; CString str; CWnd* control = CWnd::FromHandle(hwnd); control->GetWindowText(str); int index = str.Find('&'); // look for mnemonic // character keycode if (index > -1) { XCONTROL_INFO info; info.hwnd = hwnd; info.mnemonic_char = str[index+1]; arr->Add(info); } return TRUE; } BOOL CTestVBDlg ::OnInitDialog() { CDialog::OnInitDialog(); ... // Setup a callback function for EnumChildWindows() API to // enumerate all child windows of the ActiveX control. HWND hcontrol = GetDlgItem(IDC_VB_CONTROL1)->GetSafeHwnd(); EnumChildWindows(hcontrol, EnumActiveXControlChildProc, (LPARAM) &m_XControlInfo); return TRUE; } 4. Modify the OnSysCommand() function of your CDialog-derived class for determining which child window in your ActiveX control should receive the input focus when an accelerator key is pressed: void CTestVBDlg ::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { // If user pressed an accelerator keystroke, loop through the // array to find out if it is one of the access keys for a // child control in the Visual Basic ActiveX control. BOOL trap_accel = nID == SC_KEYMENU; if (trap_accel) { int size = m_XControlInfo.GetSize(); for (int i = 0; i < size; i++) { if (toupper(LOWORD(lParam)) == toupper(m_XControlInfo[i].mnemonic_char)) { HWND child = m_XControlInfo[i].hwnd; ::SetFocus(child); ::SendMessage(child, BM_CLICK, 0, 0L); // OPTIONAL!!! } } } if (!trap_accel) CDialog::OnSysCommand(nID, lParam); } } Steps to Reproduce Behavior --------------------------- 1. Create an ActiveX control with Visual Basic 5.0. The ActiveX control has two Command buttons as its child windows called "Button &A" and "Button &B," where A and B are the access keys for the buttons. 2. Use the control in a Visual Basic form and note the access keys work as expected. 3. Use the control in an MFC container application and note the access keys do not work. You will only get a message beep sound when the access key is pressed. REFERENCES ========== For information about defining access keys for a control, please refer to the Online Help in Visual Basic. For additional information, please see the following article in the Microsoft Knowledge Base: Q145698 PRB: Mnemonic Differences Between MFC 4.0 and Visual Basic 4.0 (c) Microsoft Corporation 1997. All Rights Reserved. Contributions by Yeong-Kah Tam, Microsoft Corporation Additional query words: ocx kbOLE kbActiveX kbCtrl kbVBp500bug kbdsd kbMFC kbDSupport ====================================================================== Keywords : kbGrpDSVB Technology : kbVBSearch kbAudDeveloper kbZNotKeyword6 kbZNotKeyword2 kbVB500Search kbVB500 Version : WINDOWS:5.0 Issue type : kbbug Solution Type : kbpending ============================================================================= THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY. Copyright Microsoft Corporation 2000.