Microsoft KB Archive/189779

= BUG: Enabling Accelerator Keys in Visual Basic ActiveX Controls =

Article ID: 189779

Article Last Modified on 1/8/2003

-

APPLIES TO


 * Microsoft Visual Basic 5.0 Enterprise Edition

-



This article was previously published under Q189779



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 EnumChildWindowscall.



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:

 Place the Visual Basic ActiveX control in an MFC dialog-based application.  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; ...     }                          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; }                       </li>  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);

}              }            }

if (!trap_accel) CDialog::OnSysCommand(nID, lParam); }     }                        </li></ol>

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.

<div class="references_section">