1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 extern int64 getMouseEventTime();
30 
31 JUCE_DECLARE_UUID_GETTER (IOleObject,       "00000112-0000-0000-C000-000000000046")
32 JUCE_DECLARE_UUID_GETTER (IOleWindow,       "00000114-0000-0000-C000-000000000046")
33 JUCE_DECLARE_UUID_GETTER (IOleInPlaceSite,  "00000119-0000-0000-C000-000000000046")
34 
35 namespace ActiveXHelpers
36 {
37     //==============================================================================
38     struct JuceIStorage   : public ComBaseClassHelper<IStorage>
39     {
JuceIStoragejuce::ActiveXHelpers::JuceIStorage40         JuceIStorage() {}
41 
CreateStreamjuce::ActiveXHelpers::JuceIStorage42         JUCE_COMRESULT CreateStream (const WCHAR*, DWORD, DWORD, DWORD, IStream**)           { return E_NOTIMPL; }
OpenStreamjuce::ActiveXHelpers::JuceIStorage43         JUCE_COMRESULT OpenStream (const WCHAR*, void*, DWORD, DWORD, IStream**)             { return E_NOTIMPL; }
CreateStoragejuce::ActiveXHelpers::JuceIStorage44         JUCE_COMRESULT CreateStorage (const WCHAR*, DWORD, DWORD, DWORD, IStorage**)         { return E_NOTIMPL; }
OpenStoragejuce::ActiveXHelpers::JuceIStorage45         JUCE_COMRESULT OpenStorage (const WCHAR*, IStorage*, DWORD, SNB, DWORD, IStorage**)  { return E_NOTIMPL; }
CopyTojuce::ActiveXHelpers::JuceIStorage46         JUCE_COMRESULT CopyTo (DWORD, IID const*, SNB, IStorage*)                            { return E_NOTIMPL; }
MoveElementTojuce::ActiveXHelpers::JuceIStorage47         JUCE_COMRESULT MoveElementTo (const OLECHAR*,IStorage*, const OLECHAR*, DWORD)       { return E_NOTIMPL; }
Commitjuce::ActiveXHelpers::JuceIStorage48         JUCE_COMRESULT Commit (DWORD)                                                        { return E_NOTIMPL; }
Revertjuce::ActiveXHelpers::JuceIStorage49         JUCE_COMRESULT Revert()                                                              { return E_NOTIMPL; }
EnumElementsjuce::ActiveXHelpers::JuceIStorage50         JUCE_COMRESULT EnumElements (DWORD, void*, DWORD, IEnumSTATSTG**)                    { return E_NOTIMPL; }
DestroyElementjuce::ActiveXHelpers::JuceIStorage51         JUCE_COMRESULT DestroyElement (const OLECHAR*)                                       { return E_NOTIMPL; }
RenameElementjuce::ActiveXHelpers::JuceIStorage52         JUCE_COMRESULT RenameElement (const WCHAR*, const WCHAR*)                            { return E_NOTIMPL; }
SetElementTimesjuce::ActiveXHelpers::JuceIStorage53         JUCE_COMRESULT SetElementTimes (const WCHAR*, FILETIME const*, FILETIME const*, FILETIME const*)    { return E_NOTIMPL; }
SetClassjuce::ActiveXHelpers::JuceIStorage54         JUCE_COMRESULT SetClass (REFCLSID)                                                   { return S_OK; }
SetStateBitsjuce::ActiveXHelpers::JuceIStorage55         JUCE_COMRESULT SetStateBits (DWORD, DWORD)                                           { return E_NOTIMPL; }
Statjuce::ActiveXHelpers::JuceIStorage56         JUCE_COMRESULT Stat (STATSTG*, DWORD)                                                { return E_NOTIMPL; }
57     };
58 
59     //==============================================================================
60     struct JuceOleInPlaceFrame   : public ComBaseClassHelper<IOleInPlaceFrame>
61     {
JuceOleInPlaceFramejuce::ActiveXHelpers::JuceOleInPlaceFrame62         JuceOleInPlaceFrame (HWND hwnd)   : window (hwnd) {}
63 
GetWindowjuce::ActiveXHelpers::JuceOleInPlaceFrame64         JUCE_COMRESULT GetWindow (HWND* lphwnd)                                 { *lphwnd = window; return S_OK; }
ContextSensitiveHelpjuce::ActiveXHelpers::JuceOleInPlaceFrame65         JUCE_COMRESULT ContextSensitiveHelp (BOOL)                              { return E_NOTIMPL; }
GetBorderjuce::ActiveXHelpers::JuceOleInPlaceFrame66         JUCE_COMRESULT GetBorder (LPRECT)                                       { return E_NOTIMPL; }
RequestBorderSpacejuce::ActiveXHelpers::JuceOleInPlaceFrame67         JUCE_COMRESULT RequestBorderSpace (LPCBORDERWIDTHS)                     { return E_NOTIMPL; }
SetBorderSpacejuce::ActiveXHelpers::JuceOleInPlaceFrame68         JUCE_COMRESULT SetBorderSpace (LPCBORDERWIDTHS)                         { return E_NOTIMPL; }
SetActiveObjectjuce::ActiveXHelpers::JuceOleInPlaceFrame69         JUCE_COMRESULT SetActiveObject (IOleInPlaceActiveObject* a, LPCOLESTR)  { activeObject = a; return S_OK; }
InsertMenusjuce::ActiveXHelpers::JuceOleInPlaceFrame70         JUCE_COMRESULT InsertMenus (HMENU, LPOLEMENUGROUPWIDTHS)                { return E_NOTIMPL; }
SetMenujuce::ActiveXHelpers::JuceOleInPlaceFrame71         JUCE_COMRESULT SetMenu (HMENU, HOLEMENU, HWND)                          { return S_OK; }
RemoveMenusjuce::ActiveXHelpers::JuceOleInPlaceFrame72         JUCE_COMRESULT RemoveMenus (HMENU)                                      { return E_NOTIMPL; }
SetStatusTextjuce::ActiveXHelpers::JuceOleInPlaceFrame73         JUCE_COMRESULT SetStatusText (LPCOLESTR)                                { return S_OK; }
EnableModelessjuce::ActiveXHelpers::JuceOleInPlaceFrame74         JUCE_COMRESULT EnableModeless (BOOL)                                    { return S_OK; }
TranslateAcceleratorjuce::ActiveXHelpers::JuceOleInPlaceFrame75         JUCE_COMRESULT TranslateAccelerator (LPMSG, WORD)                       { return E_NOTIMPL; }
76 
OfferKeyTranslationjuce::ActiveXHelpers::JuceOleInPlaceFrame77         HRESULT OfferKeyTranslation (LPMSG lpmsg)
78         {
79             if (activeObject != nullptr)
80                 return activeObject->TranslateAcceleratorW (lpmsg);
81 
82             return S_FALSE;
83         }
84 
85         HWND window;
86         ComSmartPtr<IOleInPlaceActiveObject> activeObject;
87     };
88 
89     //==============================================================================
90     struct JuceIOleInPlaceSite   : public ComBaseClassHelper<IOleInPlaceSite>
91     {
JuceIOleInPlaceSitejuce::ActiveXHelpers::JuceIOleInPlaceSite92         JuceIOleInPlaceSite (HWND hwnd)
93             : window (hwnd),
94               frame (new JuceOleInPlaceFrame (window))
95         {}
96 
~JuceIOleInPlaceSitejuce::ActiveXHelpers::JuceIOleInPlaceSite97         ~JuceIOleInPlaceSite()
98         {
99             frame->Release();
100         }
101 
GetWindowjuce::ActiveXHelpers::JuceIOleInPlaceSite102         JUCE_COMRESULT GetWindow (HWND* lphwnd)      { *lphwnd = window; return S_OK; }
ContextSensitiveHelpjuce::ActiveXHelpers::JuceIOleInPlaceSite103         JUCE_COMRESULT ContextSensitiveHelp (BOOL)   { return E_NOTIMPL; }
CanInPlaceActivatejuce::ActiveXHelpers::JuceIOleInPlaceSite104         JUCE_COMRESULT CanInPlaceActivate()          { return S_OK; }
OnInPlaceActivatejuce::ActiveXHelpers::JuceIOleInPlaceSite105         JUCE_COMRESULT OnInPlaceActivate()           { return S_OK; }
OnUIActivatejuce::ActiveXHelpers::JuceIOleInPlaceSite106         JUCE_COMRESULT OnUIActivate()                { return S_OK; }
107 
GetWindowContextjuce::ActiveXHelpers::JuceIOleInPlaceSite108         JUCE_COMRESULT GetWindowContext (LPOLEINPLACEFRAME* lplpFrame, LPOLEINPLACEUIWINDOW* lplpDoc, LPRECT, LPRECT, LPOLEINPLACEFRAMEINFO lpFrameInfo)
109         {
110             /* Note: If you call AddRef on the frame here, then some types of object (e.g. web browser control) cause leaks..
111                If you don't call AddRef then others crash (e.g. QuickTime).. Bit of a catch-22, so letting it leak is probably preferable.
112             */
113             if (lplpFrame != nullptr) { frame->AddRef(); *lplpFrame = frame; }
114             if (lplpDoc != nullptr)   *lplpDoc = nullptr;
115             lpFrameInfo->fMDIApp = FALSE;
116             lpFrameInfo->hwndFrame = window;
117             lpFrameInfo->haccel = nullptr;
118             lpFrameInfo->cAccelEntries = 0;
119             return S_OK;
120         }
121 
Scrolljuce::ActiveXHelpers::JuceIOleInPlaceSite122         JUCE_COMRESULT Scroll (SIZE)                 { return E_NOTIMPL; }
OnUIDeactivatejuce::ActiveXHelpers::JuceIOleInPlaceSite123         JUCE_COMRESULT OnUIDeactivate (BOOL)         { return S_OK; }
OnInPlaceDeactivatejuce::ActiveXHelpers::JuceIOleInPlaceSite124         JUCE_COMRESULT OnInPlaceDeactivate()         { return S_OK; }
DiscardUndoStatejuce::ActiveXHelpers::JuceIOleInPlaceSite125         JUCE_COMRESULT DiscardUndoState()            { return E_NOTIMPL; }
DeactivateAndUndojuce::ActiveXHelpers::JuceIOleInPlaceSite126         JUCE_COMRESULT DeactivateAndUndo()           { return E_NOTIMPL; }
OnPosRectChangejuce::ActiveXHelpers::JuceIOleInPlaceSite127         JUCE_COMRESULT OnPosRectChange (LPCRECT)     { return S_OK; }
128 
offerEventToActiveXControljuce::ActiveXHelpers::JuceIOleInPlaceSite129         LRESULT offerEventToActiveXControl (::MSG& msg)
130         {
131             if (frame != nullptr)
132                 return frame->OfferKeyTranslation (&msg);
133 
134             return S_FALSE;
135         }
136 
137         HWND window;
138         JuceOleInPlaceFrame* frame;
139     };
140 
141     //==============================================================================
142     struct JuceIOleClientSite  : public ComBaseClassHelper<IOleClientSite>
143     {
JuceIOleClientSitejuce::ActiveXHelpers::JuceIOleClientSite144         JuceIOleClientSite (HWND window)  : inplaceSite (new JuceIOleInPlaceSite (window))
145         {}
146 
~JuceIOleClientSitejuce::ActiveXHelpers::JuceIOleClientSite147         ~JuceIOleClientSite()
148         {
149             inplaceSite->Release();
150         }
151 
QueryInterfacejuce::ActiveXHelpers::JuceIOleClientSite152         JUCE_COMRESULT QueryInterface (REFIID type, void** result)
153         {
154             if (type == __uuidof (IOleInPlaceSite))
155             {
156                 inplaceSite->AddRef();
157                 *result = static_cast<IOleInPlaceSite*> (inplaceSite);
158                 return S_OK;
159             }
160 
161             return ComBaseClassHelper <IOleClientSite>::QueryInterface (type, result);
162         }
163 
SaveObjectjuce::ActiveXHelpers::JuceIOleClientSite164         JUCE_COMRESULT SaveObject()                                  { return E_NOTIMPL; }
GetMonikerjuce::ActiveXHelpers::JuceIOleClientSite165         JUCE_COMRESULT GetMoniker (DWORD, DWORD, IMoniker**)         { return E_NOTIMPL; }
GetContainerjuce::ActiveXHelpers::JuceIOleClientSite166         JUCE_COMRESULT GetContainer (LPOLECONTAINER* ppContainer)    { *ppContainer = nullptr; return E_NOINTERFACE; }
ShowObjectjuce::ActiveXHelpers::JuceIOleClientSite167         JUCE_COMRESULT ShowObject()                                  { return S_OK; }
OnShowWindowjuce::ActiveXHelpers::JuceIOleClientSite168         JUCE_COMRESULT OnShowWindow (BOOL)                           { return E_NOTIMPL; }
RequestNewObjectLayoutjuce::ActiveXHelpers::JuceIOleClientSite169         JUCE_COMRESULT RequestNewObjectLayout()                      { return E_NOTIMPL; }
170 
offerEventToActiveXControljuce::ActiveXHelpers::JuceIOleClientSite171         LRESULT offerEventToActiveXControl (::MSG& msg)
172         {
173             if (inplaceSite != nullptr)
174                 return inplaceSite->offerEventToActiveXControl (msg);
175 
176             return S_FALSE;
177         }
178 
179         JuceIOleInPlaceSite* inplaceSite;
180     };
181 
182     //==============================================================================
183     static Array<ActiveXControlComponent*> activeXComps;
184 
getHWND(const ActiveXControlComponent * const component)185     static HWND getHWND (const ActiveXControlComponent* const component)
186     {
187         HWND hwnd = {};
188         const IID iid = __uuidof (IOleWindow);
189 
190         if (auto* window = (IOleWindow*) component->queryInterface (&iid))
191         {
192             window->GetWindow (&hwnd);
193             window->Release();
194         }
195 
196         return hwnd;
197     }
198 
offerActiveXMouseEventToPeer(ComponentPeer * peer,HWND hwnd,UINT message,LPARAM lParam)199     static void offerActiveXMouseEventToPeer (ComponentPeer* peer, HWND hwnd, UINT message, LPARAM lParam)
200     {
201         switch (message)
202         {
203             case WM_MOUSEMOVE:
204             case WM_LBUTTONDOWN:
205             case WM_MBUTTONDOWN:
206             case WM_RBUTTONDOWN:
207             case WM_LBUTTONUP:
208             case WM_MBUTTONUP:
209             case WM_RBUTTONUP:
210             {
211                 RECT activeXRect, peerRect;
212                 GetWindowRect (hwnd, &activeXRect);
213                 GetWindowRect ((HWND) peer->getNativeHandle(), &peerRect);
214 
215                 peer->handleMouseEvent (MouseInputSource::InputSourceType::mouse,
216                                         { (float) (GET_X_LPARAM (lParam) + activeXRect.left - peerRect.left),
217                                           (float) (GET_Y_LPARAM (lParam) + activeXRect.top  - peerRect.top) },
218                                         ComponentPeer::getCurrentModifiersRealtime(),
219                                         MouseInputSource::invalidPressure,
220                                         MouseInputSource::invalidOrientation,
221                                         getMouseEventTime());
222                 break;
223             }
224 
225             default:
226                 break;
227         }
228     }
229 }
230 
231 //==============================================================================
232 class ActiveXControlComponent::Pimpl  : public ComponentMovementWatcher
233                                      #if JUCE_WIN_PER_MONITOR_DPI_AWARE
234                                       , public ComponentPeer::ScaleFactorListener
235                                      #endif
236 {
237 public:
Pimpl(HWND hwnd,ActiveXControlComponent & activeXComp)238     Pimpl (HWND hwnd, ActiveXControlComponent& activeXComp)
239         : ComponentMovementWatcher (&activeXComp),
240           owner (activeXComp),
241           storage (new ActiveXHelpers::JuceIStorage()),
242           clientSite (new ActiveXHelpers::JuceIOleClientSite (hwnd))
243     {
244     }
245 
~Pimpl()246     ~Pimpl()
247     {
248         if (control != nullptr)
249         {
250             control->Close (OLECLOSE_NOSAVE);
251             control->Release();
252         }
253 
254         clientSite->Release();
255         storage->Release();
256 
257        #if JUCE_WIN_PER_MONITOR_DPI_AWARE
258         for (int i = 0; i < ComponentPeer::getNumPeers(); ++i)
259             if (auto* peer = ComponentPeer::getPeer (i))
260                 peer->removeScaleFactorListener (this);
261         #endif
262     }
263 
setControlBounds(Rectangle<int> newBounds) const264     void setControlBounds (Rectangle<int> newBounds) const
265     {
266         if (controlHWND != nullptr)
267         {
268            #if JUCE_WIN_PER_MONITOR_DPI_AWARE
269             if (auto* peer = owner.getTopLevelComponent()->getPeer())
270                 newBounds = (newBounds.toDouble() * peer->getPlatformScaleFactor()).toNearestInt();
271            #endif
272 
273             MoveWindow (controlHWND, newBounds.getX(), newBounds.getY(), newBounds.getWidth(), newBounds.getHeight(), TRUE);
274         }
275 
276     }
277 
setControlVisible(bool shouldBeVisible) const278     void setControlVisible (bool shouldBeVisible) const
279     {
280         if (controlHWND != nullptr)
281             ShowWindow (controlHWND, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
282     }
283 
284     //==============================================================================
285     using ComponentMovementWatcher::componentMovedOrResized;
286 
componentMovedOrResized(bool,bool)287     void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
288     {
289         if (auto* peer = owner.getTopLevelComponent()->getPeer())
290             setControlBounds (peer->getAreaCoveredBy (owner));
291     }
292 
componentPeerChanged()293     void componentPeerChanged() override
294     {
295         componentMovedOrResized (true, true);
296 
297        #if JUCE_WIN_PER_MONITOR_DPI_AWARE
298         if (auto* peer = owner.getTopLevelComponent()->getPeer())
299             peer->addScaleFactorListener (this);
300        #endif
301     }
302 
303     using ComponentMovementWatcher::componentVisibilityChanged;
304 
componentVisibilityChanged()305     void componentVisibilityChanged() override
306     {
307         setControlVisible (owner.isShowing());
308         componentPeerChanged();
309     }
310 
311    #if JUCE_WIN_PER_MONITOR_DPI_AWARE
nativeScaleFactorChanged(double)312     void nativeScaleFactorChanged (double /*newScaleFactor*/) override
313     {
314         componentMovedOrResized (true, true);
315     }
316    #endif
317 
318     // intercepts events going to an activeX control, so we can sneakily use the mouse events
activeXHookWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)319     static LRESULT CALLBACK activeXHookWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
320     {
321         for (auto* ax : ActiveXHelpers::activeXComps)
322         {
323             if (ax->control != nullptr && ax->control->controlHWND == hwnd)
324             {
325                 switch (message)
326                 {
327                     case WM_MOUSEMOVE:
328                     case WM_LBUTTONDOWN:
329                     case WM_MBUTTONDOWN:
330                     case WM_RBUTTONDOWN:
331                     case WM_LBUTTONUP:
332                     case WM_MBUTTONUP:
333                     case WM_RBUTTONUP:
334                     case WM_LBUTTONDBLCLK:
335                     case WM_MBUTTONDBLCLK:
336                     case WM_RBUTTONDBLCLK:
337                         if (ax->isShowing())
338                         {
339                             if (auto* peer = ax->getPeer())
340                             {
341                                 ActiveXHelpers::offerActiveXMouseEventToPeer (peer, hwnd, message, lParam);
342 
343                                 if (! ax->areMouseEventsAllowed())
344                                     return 0;
345                             }
346                         }
347                         break;
348 
349                     default:
350                         break;
351                 }
352 
353                 return CallWindowProc (ax->control->originalWndProc, hwnd, message, wParam, lParam);
354             }
355         }
356 
357         return DefWindowProc (hwnd, message, wParam, lParam);
358     }
359 
360     ActiveXControlComponent& owner;
361     HWND controlHWND = {};
362     IStorage* storage = nullptr;
363     ActiveXHelpers::JuceIOleClientSite* clientSite = nullptr;
364     IOleObject* control = nullptr;
365     WNDPROC originalWndProc = nullptr;
366 };
367 
368 //==============================================================================
ActiveXControlComponent()369 ActiveXControlComponent::ActiveXControlComponent()
370 {
371     ActiveXHelpers::activeXComps.add (this);
372 }
373 
~ActiveXControlComponent()374 ActiveXControlComponent::~ActiveXControlComponent()
375 {
376     deleteControl();
377     ActiveXHelpers::activeXComps.removeFirstMatchingValue (this);
378 }
379 
paint(Graphics & g)380 void ActiveXControlComponent::paint (Graphics& g)
381 {
382     if (control == nullptr)
383         g.fillAll (Colours::lightgrey);
384 }
385 
createControl(const void * controlIID)386 bool ActiveXControlComponent::createControl (const void* controlIID)
387 {
388     deleteControl();
389 
390     if (auto* peer = getPeer())
391     {
392         auto controlBounds = peer->getAreaCoveredBy (*this);
393         auto hwnd = (HWND) peer->getNativeHandle();
394 
395         std::unique_ptr<Pimpl> newControl (new Pimpl (hwnd, *this));
396 
397         HRESULT hr = OleCreate (*(const IID*) controlIID, __uuidof (IOleObject), 1 /*OLERENDER_DRAW*/, nullptr,
398                                 newControl->clientSite, newControl->storage,
399                                 (void**) &(newControl->control));
400 
401         if (hr == S_OK)
402         {
403             newControl->control->SetHostNames (L"JUCE", nullptr);
404 
405             if (OleSetContainedObject (newControl->control, TRUE) == S_OK)
406             {
407                 RECT rect;
408                 rect.left   = controlBounds.getX();
409                 rect.top    = controlBounds.getY();
410                 rect.right  = controlBounds.getRight();
411                 rect.bottom = controlBounds.getBottom();
412 
413                 if (newControl->control->DoVerb (OLEIVERB_SHOW, nullptr, newControl->clientSite, 0, hwnd, &rect) == S_OK)
414                 {
415                     control.reset (newControl.release());
416                     control->controlHWND = ActiveXHelpers::getHWND (this);
417 
418                     if (control->controlHWND != nullptr)
419                     {
420                         control->setControlBounds (controlBounds);
421 
422                         control->originalWndProc = (WNDPROC) GetWindowLongPtr ((HWND) control->controlHWND, GWLP_WNDPROC);
423                         SetWindowLongPtr ((HWND) control->controlHWND, GWLP_WNDPROC, (LONG_PTR) Pimpl::activeXHookWndProc);
424                     }
425 
426                     return true;
427                 }
428             }
429         }
430     }
431     else
432     {
433         // the component must have already been added to a real window when you call this!
434         jassertfalse;
435     }
436 
437     return false;
438 }
439 
deleteControl()440 void ActiveXControlComponent::deleteControl()
441 {
442     control = nullptr;
443 }
444 
queryInterface(const void * iid) const445 void* ActiveXControlComponent::queryInterface (const void* iid) const
446 {
447     void* result = nullptr;
448 
449     if (control != nullptr && control->control != nullptr
450          && SUCCEEDED (control->control->QueryInterface (*(const IID*) iid, &result)))
451         return result;
452 
453     return nullptr;
454 }
455 
setMouseEventsAllowed(const bool eventsCanReachControl)456 void ActiveXControlComponent::setMouseEventsAllowed (const bool eventsCanReachControl)
457 {
458     mouseEventsAllowed = eventsCanReachControl;
459 }
460 
offerEventToActiveXControl(void * ptr)461 intptr_t ActiveXControlComponent::offerEventToActiveXControl (void* ptr)
462 {
463     if (control != nullptr && control->clientSite != nullptr)
464         return (intptr_t) control->clientSite->offerEventToActiveXControl (*reinterpret_cast<::MSG*> (ptr));
465 
466     return S_FALSE;
467 }
468 
offerEventToActiveXControlStatic(void * ptr)469 intptr_t ActiveXControlComponent::offerEventToActiveXControlStatic (void* ptr)
470 {
471     for (auto* ax : ActiveXHelpers::activeXComps)
472     {
473         auto result = ax->offerEventToActiveXControl (ptr);
474 
475         if (result != S_FALSE)
476             return result;
477     }
478 
479     return S_FALSE;
480 }
481 
juce_offerEventToActiveXControl(::MSG & msg)482 LRESULT juce_offerEventToActiveXControl (::MSG& msg)
483 {
484     if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST)
485         return ActiveXControlComponent::offerEventToActiveXControlStatic (&msg);
486 
487     return S_FALSE;
488 }
489 
490 } // namespace juce
491