WMP EventDispatcher: how to know who sent the event? - c++

I am embedding Windows Media Player in a C program. I found the WMP Host example in C++ in the WMP SDK. It contains an even dispatcher. But when I receive an event, how do I know who sent the event and how do I access that class object's variables? For example, I want to set a class member (variable) or call a method.
The CWMPHost object creates the window containing the WMP object and creates the event object. The minimal code is:
LRESULT CWMPHost::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
AtlAxWinInit();
CComPtr<IAxWinHostWindow> spHost;
CComPtr<IConnectionPointContainer> spConnectionContainer;
CComWMPEventDispatch *pEventListener = NULL;
CComPtr<IWMPEvents> spEventListener;
HRESULT hr;
RECT rcClient;
m_dwAdviseCookie = 0;
// create window
GetClientRect(&rcClient);
m_wndView.Create(m_hWnd, rcClient, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, 0);
// load OCX in window
hr = m_wndView.QueryHost(&spHost);
hr = spHost->CreateControl(CComBSTR(_T("{6BF52A52-394A-11d3-B153-00C04F79FAA6}")), m_wndView, 0);
hr = m_wndView.QueryControl(&m_spWMPPlayer);
// start listening to events
hr = CComWMPEventDispatch::CreateInstance(&pEventListener);
spEventListener = pEventListener;
hr = m_spWMPPlayer->QueryInterface(&spConnectionContainer);
// See if OCX supports the IWMPEvents interface
hr = spConnectionContainer->FindConnectionPoint(__uuidof(IWMPEvents), &m_spConnectionPoint);
if (FAILED(hr))
{
// If not, try the _WMPOCXEvents interface, which will use IDispatch
hr = spConnectionContainer->FindConnectionPoint(__uuidof(_WMPOCXEvents), &m_spConnectionPoint);
}
hr = m_spConnectionPoint->Advise(spEventListener, &m_dwAdviseCookie);
return 0;
}
The full sample code can be found at https://github.com/pauldotknopf/WindowsSDK7-Samples/tree/master/multimedia/WMP/cpp/WMPHost

EDIT: My improved solution that meets my needs:
First I untangled the include dependencies and applied include guards against circular includes. The CWMPHost class definition doesn't need to know about the CWMPEventDispatch class; however its implementation does, so the CWMPEventDispatch.h class definition is included in the CWMPHost.cpp file but not in the CWMPHost.h file.
This allows to define a member of CWMPEventDispatch to be a pointer to the owning CWMPHost object:
CWMPHost *pCWMPHost;
It is set in the CWMPHost::Create method:
hr = CComWMPEventDispatch::CreateInstance(&pEventListener);
pEventListener->pCWMPHost= this;
Now the event dispatcher can access the methods and members of the CWMPHost object that created the dispatcher.

Related

DirectX9 CreateDevice() returns D3DERR_INVALIDCALL in injected DLL for VMT hooking

I want to modify a DirectX-Application's behavior (namely I want to implement a program similar to the Statman-Application by OrfeasZ [https://github.com/OrfeasZ/Statman/releases] as Onscreen-Info for Hitman 2) by injecting code (as DLL) into it and hooking the DirectX DeviceInterface VMT.
Since there are very limited resources on how to do this for DirectX11-Applications, I first wanted to learn how to do this in DX9 by creating a program that gets the DeviceInterface pointer of any DirectX9-Application. I wrote this code in the DllMain() function of my DLL (which is almost a 1:1 copy/paste of the third answer to this thread Hooking DirectX EndScene from an injected DLL):
HMODULE hDLL = GetModuleHandleA("d3d9");
LPDIRECT3D9(__stdcall*pDirect3DCreate9)(UINT) = (LPDIRECT3D9(__stdcall*)(UINT))GetProcAddress(hDLL, "Direct3DCreate9");
LPDIRECT3D9 pD3D = pDirect3DCreate9(D3D_SDK_VERSION);
D3DDISPLAYMODE d3ddm;
HRESULT hRes = pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = true;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = d3ddm.Format;
//WNDPROC TempWndProc;
WNDCLASSEX wc = { sizeof(WNDCLASSEX),CS_CLASSDC, WndProc,0L,0L,GetModuleHandle(NULL),NULL,NULL,NULL,NULL,TEXT("1"),NULL };
RegisterClassEx(&wc);
HWND hWnd = CreateWindow(TEXT("1"), NULL, WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, GetDesktopWindow(), NULL, wc.hInstance, NULL);
ShowWindow(hWnd, SW_SHOW);
IDirect3DDevice9 * ppReturnedDeviceInterface;
hRes = pD3D->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &ppReturnedDeviceInterface);
pD3D->Release();
DestroyWindow(hWnd);
if (pD3D == NULL) {
//printf ("WARNING: D3D FAILED");
return false;
}
unsigned long* pInterface = (unsigned long*)*((unsigned long*)ppReturnedDeviceInterface);
When I inject the DLL into a DirectX9-Application (I've tried this with Civilization V and Total War: Shogun 2), it opens a window, so it actually is able to get the Direct3DCreate9 function from the d3d9.dll within the game, but pD3D->CreateDevice() always returns `D3DERR_INVALIDCALL. I don't really get what could be the reason for this, especially since the rest of this program works flawlessly. Does anybody have any idea what is missing/wrong?
D3DERR_INVALIDCALL
The method call is invalid. For example, a method's parameter may not
be a valid pointer.
Based on the error information this issue may caused by invalid parameter of IDirect3D9::CreateDevice method. You need initialize the pointer:
IDirect3DDevice9 *pReturnedDeviceInterface = NULL;
Also check if hWnd is a valid window handle and d3ddm.Format etc.

How to get HWND of an embedded web browser control in MFC

I'm using the embedded web browser control in my dialog-based MFC window and I need to know the HWND of the web browser control in it. I was able to find the following code that claims to retrieve it:
HWND hWndWebBrowser = NULL;
LPUNKNOWN unknown = m_browser.GetControlUnknown();
IWebBrowser2* pWB = NULL;
if(SUCCEEDED(unknown->QueryInterface(IID_IWebBrowser2,(void **)&pWB)))
{
CComPtr<IServiceProvider> pServiceProvider;
if (SUCCEEDED(pWB->QueryInterface(IID_IServiceProvider, (void**)&pServiceProvider)))
{
CComPtr<IOleWindow> pWindow;
if (SUCCEEDED(pServiceProvider->QueryService(SID_SShellBrowser, IID_IOleWindow, (void**)&pWindow)))
{
SHANDLE_PTR hBrowser = 0;
if (SUCCEEDED(pWindow->GetWindow(&hBrowser)))
{
hWndWebBrowser = (HWND)hBrowser;
}
}
}
}
if(unknown)
{
unknown->Release();
}
but the problem is that when it runs, it returns a handle, but not the one I would expect. The best way to illustrate it is with this Spy++ screenshot:
I understand that I can use EnumChildWindows and look for a window with the Internet Explorer_Server class, but I'm somewhat concerned about using this undocumented class name.
Does anyone have a better way to retrieve that (web browser) window handle?
Per Obtaining the HWND for the WebBrowser control, you can use following function to retrieve HWND.
IOleWindow *pOWin;
HWND hBWnd;
HRESULT hRes = m_pBrowserApp->QueryInterface(IID_IOleWindow, (void **)&pOWin);
if (SUCCEEDED(hRes)) {
hRes = pOWin->GetWindow(&hBWnd);
if (SUCCEEDED(hRes)) {
// Place hBWnd-manipulating code here
}
pOWin->Release(); // Missing from the MS example
}
Because the class names (Shell DocObject View and Internet Explorer_Server) could change, the above code should be preferred, although it is unlikely given the fact that Internet Explorer is now discontinued.
The lexical of the question is a little tricky.
The HWND of the (Web Browser) is indeed the answer
that you posted and the answer posted by Santosh Dhanawade.
When a document is loaded, the web browser control creates a
new window or iframe, see the DWebBrowserEvents2::DocumentComplete event.
Event handler parameters:
" pDisp [in] "
A pointer to the IDispatch interface of the window or frame in which the document is loaded. This IDispatch interface can be queried for the IWebBrowser2 interface.
so, changing the question:
"Does anyone have a better way to retrieve that (web browser) window handle?"
to:
"Does anyone have a better way to retrieve that (window or iframe) window handle?"
we have that the window or iframe HWND that you are locking for,
will be abailable after that the document has been completed loaded.
Which means that we can do the follow:
Implement a DocumentComplete event handler throw a raw c or c++ implementation of
IDispatch or an ATL DispEventImpl or ATL DispEventSimpleImpl.
See Understanding COM Event Handling.
Sink our event handler into the the web browser control to get the events report.
And get the window or iframe HWND from the DocumentComplete event:
assuming a raw c++ IDispatch implementation:
IFACEMETHODIMP DWebBrowserEvents2Impl::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pDispParams, VARIANT * pVarResult, EXCEPINFO * pExcepInfo, UINT * puArgErr)
{
if (dispIdMember == DISPID_DOCUMENTCOMPLETE) {
VARIANT variantDispatch;
VariantInit(&variantDispatch);
HRESULT hr = DispGetParam(pDispParams, 0, VT_DISPATCH, &variantDispatch, NULL);
if (SUCCEEDED(hr)) {
IOleWindow* iOleWindow;
hr = variantDispatch.pdispVal->QueryInterface(IID_IOleWindow, (LPVOID*) &iOleWindow);
if (SUCCEEDED(hr)) {
HWND hwnd;
hr = iOleWindow->GetWindow(&hwnd);
iOleWindow->Release();
if (SUCCEEED(hr)){
//now the hwnd correponds to the Internet Explorer_Server window.
//Do what ever you want with the HWND handler.
}
}
}
return S_OK;
}
return E_NOTIMPL;
}
From my experience, the window we're looking for is a direct descendance of the CHtmlView derived CWnd, so I use this hack to get the window and set the focus to it:
static CWnd* findChildWebbrowser(CWnd* pWnd) {
if(pWnd == NULL) { return NULL; }
CWnd* pC = pWnd->GetWindow(GW_CHILD);
if(pC == NULL) { return NULL; };
CString buf;
::GetClassName(pC->GetSafeHwnd(), buf.GetBuffer(2048), 2047);
buf.ReleaseBuffer();
if(buf == _T("Internet Explorer_Server")) {
return pC;
}
return findChildWebbrowser(pC);
}
void CMyWebView::OnSetFocus(CWnd* pOldWnd) {
// CHtmlView::OnSetFocus(pOldWnd);
CWnd* pIE = findChildWebbrowser(this);
if(pIE!=NULL) {
// this makes cursor/page keys work
pIE->SetFocus();
// this makes the TAB key work
pIE->SendMessage(WM_LBUTTONDOWN);
pIE->SendMessage(WM_LBUTTONUP);
}
}

EnumChildWindows never calls its callback

I'm trying to manipulate a specific Internet Explorer 11 window. Using WinSpy++ I find that
The top level window's class is an IEFrame with the title of the document as the text (as returned by GetWindowText)
The actual web view class is called "Internet Explorer_Server" and is a child of the former.
I wrote a simple test case for finding the web view of IE11 opened on "https://encrypted.google.com/" in three different ways:
HWND FindIE_A()
{
// Use FindWindow, works!
HWND hWndTop = ::FindWindowA( NULL, "Google - Internet Explorer" );
// Find the web view window, the callback (FindIEServer) is NEVER called!
HWND hWnd = NULL;
::EnumChildWindows( hWndTop, &FindIEServer, (LPARAM)&hWnd );
return hWnd;
}
HWND FindIE_B()
{
// Use EnumChildWindows with NULL as parent, works!
HWND hWndTop = NULL;
::EnumChildWindows( NULL, &FindIEMain, (LPARAM)&hWndTop );
// Find the web view window, the callback (FindIEServer) is NEVER called!
HWND hWnd = NULL;
::EnumChildWindows( hWndTop, &FindIEServer, (LPARAM)&hWnd );
return hWnd;
}
HWND FindIE_C()
{
// Simple EnumWindows, works!
HWND hWndTop = NULL;
::EnumWindows( &FindIEMain, (LPARAM)&hWndTop );
// Find the web view window, the callback (FindIEServer) is NEVER called!
HWND hWnd = NULL;
::EnumChildWindows( hWndTop, &FindIEServer, (LPARAM)&hWnd );
return hWnd;
}
The callbacks that are very simple; get a property from the window and compare against a hard-coded value:
BOOL CALLBACK FindIEServer( HWND hWnd, LPARAM lParam )
{
char className[64];
::GetClassNameA( hWnd, className, sizeof(className) );
if ( !strcmp( className, "Internet Explorer_Server" ) )
{
*(HWND*)lParam = hWnd;
return FALSE;
}
return TRUE;
}
BOOL CALLBACK FindIEMain( HWND hWnd, LPARAM lParam )
{
char text[128];
::GetWindowTextA( hWnd, text, sizeof(text) );
if ( !strcmp( text, "Google - Internet Explorer" ) )
{
*(HWND*)lParam = hWnd;
return FALSE;
}
return TRUE;
}
EnumChildWindows failed (by not calling the callback AT ALL!) every time when provided with a parent window. Why?
The problem is that when I look for the window title, I was assuming there was only one window with that title. However Internet Explorer does some shenanigans and creates multiple windows with the same title however only one of them has the class IEFrame.
It just so happens that the first window found was the wrong one, it didn't have any children (and thus EnumChildWindows doesn't have anything to iterate over). Just adding an extra check for title + class works.
However as #wakjah suggested, it is better integrate IE (or any other browser) directly into your code. With google I found lots of documentation on how to do this with both IE and Chrome.

how to create a new window using the CWindowImpl class

im trying to build a new window using the following class.
however im having some serious problems while trying to get the new generated window's name using the CWindow::m_hwnd variable. my guess is that i should pass a variable to the CWindowImpl::Create() function that holds my parent's HWND but i dont know how to get it.
STDMETHODIMP CVMNExporter::SetSite(IUnknown *pUnkSite) {
HRESULT hr;
if(FAILED(hr = ATL::IObjectWithSiteImpl<CVMNExporter>::SetSite(pUnkSite)))
return hr;
// We are rnning from a web browser
CONNECTION_CALLBACK = JSCRIPT_DISPID;
EXPORTING_CALLBACK = JSCRIPT_DISPID;
MERGING_CALLBACK = JSCRIPT_DISPID;
ABORTING_CALLBACK = JSCRIPT_DISPID;
AddRef();
if(NULL == Create(0)) {// Creates the hidden window
Release();
return HRESULT_FROM_WIN32(GetLastError());
}
ATLTRACE("TEST - SetSite(): this: %d, window: %d", this, m_hWnd);
m_dwApartmentMode = HWND_APARTMENT;
SendMessage(WM_EXPORTER_APARTMENT_SETUP, 0, 0);
return S_OK; }
CVMNExporter implements the following interfaces:
public ATL::IObjectSafetyImpl<CVMNExporter, INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA>,
public ATL::IObjectWithSiteImpl<CVMNExporter>,
public ATL::CWindowImpl<CVMNExporter, ATL::CWindow, ATL::CFrameWinTraits>,
thanks!
Well it took me some time, but managed to solve it by using GetActiveWindow()

OpenOffice Automation Issue when Launched from Separate Thread

I have a C++ application that sometimes needs to export information to a spreadsheet. It is designed to do so using COM and ActiveX integration with Microsoft Excel and OpenOffice Calc.
I noticed with one of the newer versions of OpenOffice that my program would timeout and fail any time I tried doing the export.
I did quite a bit of research before figuring out that the failure required the following two events:
1.) Creation of a simple UI window with a custom procedure (even if that procedure did not do anything more than pass everything on to the default procedure)
2.) Creation of a separate thread in which the code to launch OpenOffice (via COM and ActiveX) is executed
I should note that any given time, there is only ONE thread doing OpenOffice integration. It just happens to be a different thread from the one handling the UI.
I also noticed some other oddities.
If the window class does NOT involve a custom procedure, no error occurs. However, if ANY custom procedure is involved it does occur. Even if the custom window procedure does absolutely nothing but pass all messages to the default window procedure, the error occurs.
If no UI window is made, the code in the separate thread executes flawlessly.
If the integration code is launched from the same thread as the UI, no error occurs. If the integration is first carried out within the same thread as the UI, subsequent creation and execution of a separate thread runs without error.
And this is the weirdest observation: I'm using Visual Studio 2005 for debugging. If I set a breakpoint just prior to the invocation of "loadComponentFromURL", the hang will NOT occur. However, if I do NOT set a break point, when the hang occurs I can break execution and I'll find that the call stack indicates that it is stuck somewhere within the process of RPC invocation awaiting a return from WaitForMultipleObjectsEx(...).
Below is a complete code example. If you compile and run this on a machine with the newest version of OpenOffice, it will hang. Within the WinMain(...) function, there is a call to TestOOCalc that is commented out. If you uncomment it, you'll find the program now launches OpenOffice Calc perfectly.
Given that there are NOT multiple threads attempting to access OpenOffice at the same time, this doesn't seem like it should be a threading issue at all.
I can't find anything anywhere about this phenomenon or what the root cause is. I really don't want to resort to putting all of the work in the same thread as the UI as this would make the UI unresponsive during lengthy operations.
Thoughts? Ideas?
#include <windows.h>
#include <atlbase.h>
#include <process.h>
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hwnd, message, wParam, lParam);
}
BOOL MakeUIWindow(HINSTANCE hInstance)
{
// Class definition for Main Window
WNDCLASS wndclass;
ZeroMemory(&wndclass, sizeof(wndclass));
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.hInstance = hInstance;
wndclass.lpszClassName = TEXT("Problem Window Class");
// Register the Main Window class
if (!RegisterClass(&wndclass))
return FALSE;
HWND hwnd = CreateWindowEx(0, TEXT("Problem Window Class"),
TEXT("Problem"), WS_OVERLAPPEDWINDOW,
10, 10, 500, 500,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, SW_NORMAL);
return TRUE;
}
BOOL ActiveX_MethodCall(CComPtr<IDispatch> &rcpPropInterface, const WCHAR *wszMethod, const UINT uiArgs, VARIANTARG *pArgs, CComPtr<IDispatch> &rcpResult)
{
DISPID dispid;
HRESULT hr = rcpPropInterface.GetIDOfName(wszMethod, &dispid);
if (FAILED(hr))
return FALSE;
DISPPARAMS dp;
EXCEPINFO ei;
VARIANT varReturn;
ZeroMemory(&varReturn, sizeof(varReturn));
ZeroMemory(&dp, sizeof(dp));
ZeroMemory(&ei, sizeof(ei));
varReturn.vt = VT_EMPTY;
dp.cArgs = uiArgs;
dp.rgvarg = pArgs;
hr = rcpPropInterface->Invoke(dispid, IID_NULL, NULL, DISPATCH_METHOD, &dp, &varReturn, NULL, NULL);
if (FAILED(hr))
return FALSE;
rcpResult.Attach(varReturn.pdispVal);
return TRUE;
}
// Performs an initialization of OpenOffice
BOOL TestOOCalc()
{
if (FAILED(CoInitialize(NULL)))
return FALSE;
// Get class IDs for the ActiveX object specified
CLSID clsid;
if (FAILED(CLSIDFromProgID(L"com.sun.star.ServiceManager", &clsid)))
return FALSE;
CComPtr<IDispatch> cpSvcMgr;
if (FAILED(cpSvcMgr.CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER)))
return FALSE;
CComPtr<IDispatch> cpDesktop;
{ // context change for local variants
VARIANTARG varArg;
ZeroMemory(&varArg, sizeof(varArg));
varArg.scode = DISP_E_PARAMNOTFOUND;
varArg.vt = VT_BSTR;
varArg.bstrVal = SysAllocString(L"com.sun.star.frame.Desktop");
if (!ActiveX_MethodCall(cpSvcMgr, L"createInstance", 1, &varArg, cpDesktop))
{
VariantClear(&varArg);
return FALSE;
}
VariantClear(&varArg);
}
// Call Desktop.loadComponentFromURL Method
CComPtr<IDispatch> cpWorkbook;
{ // context change for local variants
VARIANTARG pvarArgs[4];
ZeroMemory(&pvarArgs, sizeof(pvarArgs));
pvarArgs[3].scode = DISP_E_PARAMNOTFOUND;
pvarArgs[3].vt = VT_BSTR;
pvarArgs[3].bstrVal = SysAllocString(L"private:factory/scalc");
pvarArgs[2].scode = DISP_E_PARAMNOTFOUND;
pvarArgs[2].vt = VT_BSTR;
pvarArgs[2].bstrVal = SysAllocString(L"_blank");
pvarArgs[1].scode = DISP_E_PARAMNOTFOUND;
pvarArgs[1].vt = VT_I4;
pvarArgs[1].lVal = 0;
SAFEARRAYBOUND saBound;
saBound.lLbound = 0;
saBound.cElements = 0;
SAFEARRAY *psaArgs = SafeArrayCreate(VT_VARIANT, 1, &saBound);
pvarArgs[0].scode = DISP_E_PARAMNOTFOUND;
pvarArgs[0].vt = VT_ARRAY | VT_VARIANT;
pvarArgs[0].parray = psaArgs;
if (!ActiveX_MethodCall(cpDesktop, L"loadComponentFromURL", 4, pvarArgs, cpWorkbook))
{
SafeArrayDestroy(psaArgs);
VariantClear(&pvarArgs[3]);
VariantClear(&pvarArgs[2]);
VariantClear(&pvarArgs[1]);
VariantClear(&pvarArgs[0]);
return FALSE;
}
SafeArrayDestroy(psaArgs);
VariantClear(&pvarArgs[3]);
VariantClear(&pvarArgs[2]);
VariantClear(&pvarArgs[1]);
VariantClear(&pvarArgs[0]);
}
return TRUE;
}
unsigned int __stdcall thrTestOOCalc(void *vShare)
{
TestOOCalc();
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
if (!MakeUIWindow(hInstance))
return 0;
//TestOOCalc();
HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, thrTestOOCalc, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
return 0;
}
It has been a long time since a worked in a daily basis with COM, but to me this looks like the classic failure of pumping messages in an APARTMENT thread.
Check the following:
Are OpenOffice component declared as apartment threaded ?
If not, try to initialize your thread in MTA using CoInitializeEx.
If OO components are declared as apartment thread, you'll need to pump messages on your newly created thread.
Hope this helps.