To solve this, the easiest thing I found was to use the OnFrameWindowActivate and OnDocWindowActivate methods of the IOleInPlaceActiveObject interface. I posted another article about how to use a custom TActiveXControlClass, and that code already uses a method from IOleInPlaceActiveObject. This means that all we need to do is add these 2 methods to that existing class and implement them. The code looks like this:
type TTranslateAcceleratorFormControl = class(TActiveFormControl, IOleInPlaceActiveObject) private FActiveHWND: HWND; procedure HandleActiveHWND(Activate: boolean); protected function TranslateAccelerator(var msg: TMsg): HResult; stdcall; function OnFrameWindowActivate(fActivate: BOOL): HResult; stdcall; function OnDocWindowActivate(fActivate: BOOL): HResult; stdcall; end; procedure TTranslateAcceleratorFormControl.HandleActiveHWND(Activate: boolean); begin if Activate then begin if FActiveHWND <> 0 then SetFocus(FActiveHWND); end else FActiveHWND := GetFocus; end; function TTranslateAcceleratorFormControl.OnDocWindowActivate(fActivate: BOOL): HResult; begin Result := inherited OnDocWindowActivate(fActivate); HandleActiveHWND(fActivate); end; function TTranslateAcceleratorFormControl.OnFrameWindowActivate(fActivate: BOOL): HResult; begin Result := inherited OnFrameWindowActivate(fActivate); HandleActiveHWND(fActivate); end; function TTranslateAcceleratorFormControl.TranslateAccelerator(var msg: TMsg): HResult; begin Result := inherited TranslateAccelerator(msg); if Result = S_OK then begin if (msg.message = WM_KEYDOWN) and (GetKeyState(VK_CONTROL) < 0) and (msg.wParam = 67) then Result := S_FALSE; end; end;
If you test the ActiveForm at this point, you'll find that things work pretty well. The one remaining problem is that these methods don't fire when getting activated, so I handled that by adding another PostMessage after the PostMessage(WM_ACTIVATE) call. By doing this, we're letting the DAX framework set things up properly, and during that time, InPlaceActivate is called which finishes proper initialization. The code in the ActiveForm class looks similar to this:
type TMyActiveForm = class(TActiveForm, IMyActiveForm) procedure UMACtivate(var Message: TMessage); message UM_ACTIVATE; // const UM_ACTIVATE = WM_USER + 123; // Other methods declared here procedure TMyActiveForm.UMActivate(var Message: TMessage); begin (ComObject as IOleInPlaceActiveObject).OnFrameWindowActivate(true); end;
Be sure to call
PostMessage(Handle, UM_ACTIVATE, 0, 0);
Let me know if you run into any problems with this approach.
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.