IDropTarget registration not working on Windows 11 - winapi

I followed this article from Raymond Chen:
How do I accept files to be opened via IDropTarget instead of on the command line?
This article shows how to implement a drop target server which may be used to associate a file type with an application, without having to use the command-line to achieve that.
On my previous Windows 10 computer, all worked fine. However, on my current computer installed with Windows 11, nothing is working.
Obviously, there are important changes between Windows 10 and 11. Can someone explain to me what I should do to make the above demo work correctly on Windows 11? Or point me to a document showing the differences between the different versions of Windows, and what I should do to make my code compatible with them?
UPDATE on 10.08.2022
Since a reproducible example has been requested many times, below is my implementation of the Raymond Chen's example mentioned above, with which the issue occurs.
ImageVerb.cpp
// std
#include <string>
#include <sstream>
// classes
#include "ProcessReference.h"
#include "SimpleDropTarget.h"
#include "SimpleDropTargetFactory.h"
// windows
#include <windows.h>
#include <shlobj.h>
#include <shellapi.h>
/**
* Based on the following Raymond Chen article: https://devblogs.microsoft.com/oldnewthing/20100503-00/?p=14183
*
* IMPORTANT NOTE
* In order to put this application to work as expected, the following keys should be created and/or modified in the registry:
* - Computer\HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{C4A3B129-FD6D-43EB-8880-6C32E5495ACD}\LocalServer32
* => In the LocalServer32 key, set path to this exe in the Default value (e.g W:\Labo\__ImageVerb\x64\Debug\ImageVerb.exe)
* - Computer\HKEY_CURRENT_USER\SOFTWARE\Classes\heicfile\Shell\ImageVerbVerb\DropTarget
* => In the ImageVerbVerb key, set the name to show in the Shell Explorer popup menu in the Default value
* => Optionally add a new (empty) NeverDefault string value in this key to avoid that the item takes the first/Default position in the menus
* => In the DropTarget key, add a new CLSID string value, and set the {C4A3B129-FD6D-43EB-8880-6C32E5495ACD} parameter in it
* - Computer\HKEY_CURRENT_USER\SOFTWARE\Classes\.heic
* => In the .heic key, set heicfile in the Default value
*
* NOTE All the keys seems to propagate themselves through the registry (in the HKCR keys, ...)
*/
//------------------------------------------------------------------------------
#define WM_OPENFILES (WM_USER + 1)
//------------------------------------------------------------------------------
HWND g_hWnd = NULL;
SimpleDropTargetFactory g_SimpleFropTargetFactory;
//------------------------------------------------------------------------------
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
::PostQuitMessage(0);
break;
case WM_DESTROY:
return 0;
case WM_OPENFILES:
{
IDataObject* pDataObj = reinterpret_cast<IDataObject*>(lParam);
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM stgm;
if (SUCCEEDED(pDataObj->GetData(&fmte, &stgm)))
{
HDROP hdrop = reinterpret_cast<HDROP>(stgm.hGlobal);
UINT cFiles = ::DragQueryFile(hdrop, 0xFFFFFFFF, NULL, 0);
for (UINT i = 0; i < cFiles; ++i)
{
TCHAR szFile[MAX_PATH];
UINT cch = ::DragQueryFile(hdrop, i, szFile, MAX_PATH);
if (cch > 0 && cch < MAX_PATH)
{
// get the window client rect
RECT clientRect;
::GetClientRect(g_hWnd, &clientRect);
// get the window device context
HDC hDC = ::GetDC(g_hWnd);
::SetBkMode(hDC, TRANSPARENT);
::SetBkColor(hDC, 0x000000);
::SetTextColor(hDC, 0xffffff);
::DrawText(hDC, szFile, ::wcslen(szFile), &clientRect, DT_SINGLELINE | DT_CENTER | DT_BOTTOM);
::ReleaseDC(g_hWnd, hDC);
}
}
::ReleaseStgMedium(&stgm);
}
pDataObj->Release();
break;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
//------------------------------------------------------------------------------
void OpenFilesFromDataObject(IDataObject* pDataObj)
{
pDataObj->AddRef();
::PostMessage(g_hWnd, WM_OPENFILES, 0, reinterpret_cast<LPARAM>(pDataObj));
}
//------------------------------------------------------------------------------
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
if (FAILED(::CoInitialize(NULL)))
return -1;
g_SimpleFropTargetFactory.Set_OnOpenFilesFromDataObject(OpenFilesFromDataObject);
// in case we use COM
HRESULT hrRegister;
DWORD dwRegisterCookie;
MSG msg;
{
// lock the Windows Explorer and other Shell objects to prevent their host process from closing prematurely
ProcessReference ref;
g_pProcRef = &ref;
// register the drop target interface with OLE so other applications can connect to it
hrRegister = ::CoRegisterClassObject(g_CLSID_DropTarget, &g_SimpleFropTargetFactory,
CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwRegisterCookie);
WNDCLASSEX wcex = {0};
BOOL bQuit = FALSE;
// register window class
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_OWNDC;
wcex.lpfnWndProc = WindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = ::LoadIcon(nullptr, IDI_APPLICATION);
wcex.hCursor = ::LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)::GetStockObject(BLACK_BRUSH);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = L"ImageVerb";
wcex.hIconSm = ::LoadIcon(nullptr, IDI_APPLICATION);
if (!RegisterClassEx(&wcex))
return 0;
// create main window
g_hWnd = ::CreateWindowEx(0,
L"ImageVerb",
L"Image verb",
WS_DLGFRAME | WS_CAPTION | WS_SYSMENU,
CW_USEDEFAULT,
CW_USEDEFAULT,
800,
650,
nullptr,
nullptr,
hInstance,
nullptr);
::ShowWindow(g_hWnd, nCmdShow);
// get the window client rect
RECT clientRect;
::GetClientRect(g_hWnd, &clientRect);
// get the window device context
HDC hDC = ::GetDC(g_hWnd);
// please wait text background
HBRUSH hBrush = ::CreateSolidBrush(RGB(20, 30, 43));
::FillRect(hDC, &clientRect, hBrush);
::DeleteObject(hBrush);
::SetBkMode(hDC, TRANSPARENT);
::SetBkColor(hDC, 0x000000);
::SetTextColor(hDC, 0xffffff);
std::wostringstream sstr;
// is the command line containing the COM magic command indicating that the app was launched as a server?
if (::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, lpCmdLine, -1, L"-Embedding", -1) != CSTR_EQUAL &&
::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, lpCmdLine, -1, L"/Embedding", -1) != CSTR_EQUAL)
{
// no, process the command line normally
if (!::wcslen(lpCmdLine))
sstr << L"Run as normal process";
else
sstr << L"Cmd line: " << lpCmdLine;
}
else
sstr << L"Run as local server";
::DrawText(hDC, sstr.str().c_str(), (int)sstr.str().length(), &clientRect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
::ReleaseDC(g_hWnd, hDC);
// program main loop
while (!bQuit)
{
// check for messages
if (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
// handle or dispatch messages
if (msg.message == WM_QUIT)
bQuit = TRUE;
else
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
else
{}
}
// destroy the window explicitly
::DestroyWindow(g_hWnd);
}
// release the Shell object lock
g_pProcRef = nullptr;
if (SUCCEEDED(hrRegister))
{
::CoRevokeClassObject(dwRegisterCookie);
}
::CoUninitialize();
return (int)msg.wParam;
}
//------------------------------------------------------------------------------
ProcessReference.h
#pragma once
// windows
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <windows.h>
/**
* Process reference interface, used to keep track of outstanding objects and locks
*/
class ProcessReference : public IUnknown
{
public:
ProcessReference();
virtual ~ProcessReference();
/**
* Add a reference
*#returns new reference count after add
*/
STDMETHODIMP_(ULONG) AddRef();
/**
* Remove a reference
*#returns new reference count after remove
*/
STDMETHODIMP_(ULONG) Release();
/**
* Query a COM object for a pointer to its interface
*#param riid - reference to the queried interface identifier (IID)
*#param ppv - the queried interface pointer
*#returns error or success code
*/
STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
private:
LONG m_Ref = 1;
DWORD m_Thread = 0;
};
ProcessReference.cpp
#include "ProcessReference.h"
// windows
#include <Shlobj.h>
//------------------------------------------------------------------------------
ProcessReference::ProcessReference() :
m_Thread(::GetCurrentThreadId())
{
// lock hosted Shell extensions and other components (in particular Windows Explorer)
// to prevent their host process from closing prematurely
::SHSetInstanceExplorer(this);
}
//------------------------------------------------------------------------------
ProcessReference::~ProcessReference()
{
// release the hosted Shell extensions lock
::SHSetInstanceExplorer(NULL);
Release();
MSG msg;
// process the remaining messages handled by the references to release
while (m_Ref && ::GetMessage(&msg, NULL, 0, 0))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
//------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) ProcessReference::AddRef()
{
return ::InterlockedIncrement(&m_Ref);
}
//------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) ProcessReference::Release()
{
const LONG ref = ::InterlockedDecrement(&m_Ref);
if (!ref)
::PostThreadMessage(m_Thread, WM_NULL, 0, 0);
return ref;
}
//------------------------------------------------------------------------------
STDMETHODIMP ProcessReference::QueryInterface(REFIID riid, void** ppv)
{
if (riid == IID_IUnknown)
{
*ppv = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
//------------------------------------------------------------------------------
SimpleDropTarget.h
#pragma once
// classes
#include "ProcessReference.h"
// windows
#include <oleidl.h>
//------------------------------------------------------------------------------
// process reference, used to prevent Windows Explorer instances from closing prematurely
extern ProcessReference* g_pProcRef;
// drop target component class identifier {C4A3B129-FD6D-43EB-8880-6C32E5495ACD}
// NOTE a new GUID should be regenerated every time this class is used in a new project. To do that (in VS),
// open Tools->Create GUID menu and select option nb. 3, then press Copy
const CLSID g_CLSID_DropTarget = {0xc4a3b129, 0xfd6d, 0x43eb, {0x88, 0x80, 0x6c, 0x32, 0xe5, 0x49, 0x5a, 0xcd}};
//------------------------------------------------------------------------------
/**
* Simple drop target, receives the open file events from the Shell
*/
class SimpleDropTarget : public IDropTarget
{
public:
/**
* Called when files are opened from data object
*#param pDataObj - data object containing the files info
*/
typedef void (*ITfOnOpenFilesFromDataObject)(IDataObject* pDataObj);
SimpleDropTarget();
virtual ~SimpleDropTarget();
/**
* Add a reference
*#returns new reference count after add
*/
STDMETHODIMP_(ULONG) AddRef();
/**
* Remove a reference
*#returns new reference count after remove
*/
STDMETHODIMP_(ULONG) Release();
/**
* Query a COM object for a pointer to its interface
*#param riid - reference to the queried interface identifier (IID)
*#param ppv - the queried interface pointer
*#returns error or success code
*/
STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
/**
* Called when a drag operation should be accepted
*#param pDataObj - data object containing the drag info
*#param grfKeyState - modifier keys state
*#param ptl - current cursor position, in screen coordinates
*#param pdwEffect - DoDragDrop pdwEffect function parameter, drag operation result on function ends
*#returns error or success code
*/
STDMETHODIMP DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL ptl, DWORD* pdwEffect);
/**
* Called when an object is dragged over a valid target
*#param grfKeyState - modifier keys state
*#param ptl - current cursor position, in screen coordinates
*#param pdwEffect - DoDragDrop pdwEffect function parameter, drag operation result on function ends
*#returns error or success code
*/
STDMETHODIMP DragOver(DWORD grfKeyState, POINTL ptl, DWORD* pdwEffect);
/**
* Called when a drag target is leaved
*#returns error or success code
*/
STDMETHODIMP DragLeave();
/**
* Called when an object is dropped on the target
*#param pDataObj - data object containing the drag info
*#param grfKeyState - modifier keys state
*#param ptl - current cursor position, in screen coordinates
*#param pdwEffect - DoDragDrop pdwEffect function parameter, drop operation result on function ends
*#returns error or success code
*/
STDMETHODIMP Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL ptl, DWORD* pdwEffect);
/**
* Set the OnOpenFilesFromDataObject callback
*#param hCallback - callback function handler
*/
void Set_OnOpenFilesFromDataObject(ITfOnOpenFilesFromDataObject hCallback);
private:
LONG m_Ref = 1;
ITfOnOpenFilesFromDataObject m_fOnOpenFilesFromDataObject = nullptr;
};
SimpleDropTarget.cpp
#include "SimpleDropTarget.h"
//------------------------------------------------------------------------------
ProcessReference* g_pProcRef = nullptr;
//------------------------------------------------------------------------------
SimpleDropTarget::SimpleDropTarget() :
IDropTarget()
{
g_pProcRef->AddRef();
}
//------------------------------------------------------------------------------
SimpleDropTarget::~SimpleDropTarget()
{
g_pProcRef->Release();
}
//------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) SimpleDropTarget::AddRef()
{
return ::InterlockedIncrement(&m_Ref);
}
//------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) SimpleDropTarget::Release()
{
const LONG ref = ::InterlockedDecrement(&m_Ref);
if (!ref)
delete this;
return ref;
}
//------------------------------------------------------------------------------
STDMETHODIMP SimpleDropTarget::QueryInterface(REFIID riid, void** ppv)
{
if (riid == IID_IUnknown || riid == IID_IDropTarget)
{
*ppv = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
//------------------------------------------------------------------------------
STDMETHODIMP SimpleDropTarget::DragEnter(IDataObject* pdto, DWORD grfKeyState, POINTL ptl, DWORD* pdwEffect)
{
*pdwEffect &= DROPEFFECT_COPY;
return S_OK;
}
//------------------------------------------------------------------------------
STDMETHODIMP SimpleDropTarget::DragOver(DWORD grfKeyState, POINTL ptl, DWORD* pdwEffect)
{
*pdwEffect &= DROPEFFECT_COPY;
return S_OK;
}
//------------------------------------------------------------------------------
STDMETHODIMP SimpleDropTarget::DragLeave()
{
return S_OK;
}
//------------------------------------------------------------------------------
STDMETHODIMP SimpleDropTarget::Drop(IDataObject* pdto, DWORD grfKeyState, POINTL ptl, DWORD* pdwEffect)
{
if (m_fOnOpenFilesFromDataObject)
m_fOnOpenFilesFromDataObject(pdto);
*pdwEffect &= DROPEFFECT_COPY;
return S_OK;
}
//------------------------------------------------------------------------------
void SimpleDropTarget::Set_OnOpenFilesFromDataObject(ITfOnOpenFilesFromDataObject hCallback)
{
m_fOnOpenFilesFromDataObject = hCallback;
}
//------------------------------------------------------------------------------
SimpleDropTargetFactory.h
#pragma once
// classes
#include "SimpleDropTarget.h"
// windows
#include <Unknwnbase.h>
/**
* Simple drop target factory
*/
class SimpleDropTargetFactory : public IClassFactory
{
public:
// SimpleDropTarget::OnOpenFilesFromDataObject alias
typedef SimpleDropTarget::ITfOnOpenFilesFromDataObject ITfOnOpenFilesFromDataObject;
SimpleDropTargetFactory();
virtual ~SimpleDropTargetFactory();
/**
* Add a reference
*#returns new reference count after add
*/
STDMETHODIMP_(ULONG) AddRef();
/**
* Remove a reference
*#returns new reference count after remove
*/
STDMETHODIMP_(ULONG) Release();
/**
* Query a COM object for a pointer to its interface
*#param riid - reference to the queried interface identifier (IID)
*#param ppv - the queried interface pointer
*#returns error or success code
*/
STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
/**
* Create an uninitialized object
*#param pUnkOuter - pointer to the controlling IUnknown aggregate interface if being created as part of an aggregate, otherwise nullptr
*#param riid - reference to interface identifier to be used to communicate with the newly created object
*#param ppv - pointer that receives the interface requested in riid
*#returns error or success code
*/
STDMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv);
/**
* Lock an object application open in memory
*#param fLock - If TRUE increment lock count, otherwise decrement lock count
*#returns error or success code
*#note This function enable instances to be created more quickly
*/
STDMETHODIMP LockServer(BOOL fLock);
/**
* Set the OnOpenFilesFromDataObject callback
*#param hCallback - callback function handler
*/
void Set_OnOpenFilesFromDataObject(ITfOnOpenFilesFromDataObject hCallback);
private:
ITfOnOpenFilesFromDataObject m_fOnOpenFilesFromDataObject = nullptr;
};
SimpleDropTargetFactory.cpp
#include "SimpleDropTargetFactory.h"
// std
#include <new>
// classes
#include "SimpleDropTarget.h"
//------------------------------------------------------------------------------
SimpleDropTargetFactory::SimpleDropTargetFactory()
{}
//------------------------------------------------------------------------------
SimpleDropTargetFactory::~SimpleDropTargetFactory()
{}
//------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) SimpleDropTargetFactory::AddRef()
{
return 2;
}
//------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) SimpleDropTargetFactory::Release()
{
return 1;
}
//------------------------------------------------------------------------------
STDMETHODIMP SimpleDropTargetFactory::QueryInterface(REFIID riid, void** ppv)
{
if (riid == IID_IUnknown || riid == IID_IClassFactory)
{
*ppv = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
//------------------------------------------------------------------------------
STDMETHODIMP SimpleDropTargetFactory::CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv)
{
*ppv = NULL;
if (pUnkOuter)
return CLASS_E_NOAGGREGATION;
SimpleDropTarget* pDropTarget = new(std::nothrow)SimpleDropTarget();
if (!pDropTarget)
return E_OUTOFMEMORY;
pDropTarget->Set_OnOpenFilesFromDataObject(m_fOnOpenFilesFromDataObject);
HRESULT hr = pDropTarget->QueryInterface(riid, ppv);
pDropTarget->Release();
return hr;
}
//------------------------------------------------------------------------------
STDMETHODIMP SimpleDropTargetFactory::LockServer(BOOL fLock)
{
// server shutting down
if (!g_pProcRef)
return E_FAIL;
if (fLock)
g_pProcRef->AddRef();
else
g_pProcRef->Release();
return S_OK;
}
//------------------------------------------------------------------------------
void SimpleDropTargetFactory::Set_OnOpenFilesFromDataObject(ITfOnOpenFilesFromDataObject hCallback)
{
m_fOnOpenFilesFromDataObject = hCallback;
}
//------------------------------------------------------------------------------

So after many searches and headache, I found what was the issue.
In fact, the applications associated by default in Windows 11 may override the keys and values for the file type you write in the registry.
In my case I tried to associate my application with the .heic file type. On Windows 10, there was no association with this type by default. On Windows 11, the following key:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts.heic\UserChoice
was showing AppX43hnxtbyyps62jhe9sqpdzxn1790zetc in its ProgId value. This ID points to the Windows Photo Viewer application. When I replaced the default app for the .heic files by my own application, the ProgId value changed to heicfile, and my file association was working as expected.
Thank to #SimonMourier to helped me to find out what was the issue.

Related

Preview handler only works when application is invoked from Explorer

I made a simple application that creates a window and then uses preview handlers to display a preview of a file passed as an argument to the application, see code below.
This works perfectly fine when just drag'n'dropping the file to preview onto the .exe file (or alternatively just hard coding the file path instead and then launching the .exe directly from explorer), but fails for almost all preview handlers when invoking it from a terminal (powershell, cmd, whatever) with e.g. ./main.exe testfile.docx. It also fails when trying to invoke it from other processes.
The issue i described happens (among others) for the following preview handlers:
MS Office preview handler for excel files, CLSID {00020827-0000-0000-C000-000000000046}
MS Office preview handler for word files, CLSID {84F66100-FF7C-4fb4-B0C0-02CD7FB668FE}
Edge preview handler for pdf files, CLSID {3A84F9C2-6164-485C-A7D9-4B27F8AC009E}
MS Office preview handler for power point files, CLSID {65235197-874B-4A07-BDC5-E65EA825B718}. For the power point preview handler, the initialize call to the IInitializeWithFile interface fails hresult 0x86420003
It works fine for the following preview handlers:
Windows provided preview handler for text files, CLSID {1531d583-8375-4d3f-b5fb-d23bbd169f22}
Windows provided preview handler for html files, CLSID {f8b8412b-dea3-4130-b36c-5e8be73106ac}
When i say it "fails", i mean that it just doesn't display a preview, none of the function calls fail. See images:
test.xlsx excel file dragged onto .exe file, same result when hardcoding path to test.xlsx and just starting the application from explorer:
Path to test.xlsx passed as command line argument (./main.exe ./test.xlsx in powershell):
I am not sure, what would cause this or where to even start looking or what to search for, so any input on this would be appreciated.
Note: I asked a similar question a few days ago, but that was a seperate issue.
Compile with cl /std:c++20 /EHsc main.cpp, expects path to a file to preview as first command line parameter. Since i used a bit of winrt, this requires windows 10.
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "WindowsApp.lib")
#pragma comment(lib, "Ole32.lib")
#include <iostream>
#include <string>
#include <string_view>
#include <array>
#include <filesystem>
#include <Windows.h>
#include <shlwapi.h>
#include <winrt/base.h>
#include <ShObjIdl.h>
class Preview{
winrt::com_ptr<IPreviewHandler> mPreviewHandler;
bool mIsActive = false;
bool mIsInitialized = false;
std::filesystem::path mFilePath;
HWND mHwnd;
RECT mRect;
public:
Preview(const std::filesystem::path& filePath, const HWND hwnd, const RECT& rect):
mFilePath(filePath), mHwnd(hwnd), mRect(rect){
createPreviewHandler();
initialize();
};
void setRect(const RECT& rect){
mPreviewHandler->SetRect(&rect);
mRect = rect;
}
void setWindow(const HWND hwnd, const RECT& rect){
mPreviewHandler->SetWindow(hwnd, &rect);
mHwnd = hwnd;
setRect(rect);
}
void startPreview(){
if(!mIsActive){
if(!mIsInitialized){
initialize();
}
mPreviewHandler->DoPreview();
setRect(mRect);
mIsActive = true;
}
}
void stopPreview(){
if(mIsActive){
mPreviewHandler->Unload();
mIsActive = false;
mIsInitialized = false;
}
}
private:
void createPreviewHandler(){
CLSID previewHandlerId = getShellExClsidForType(mFilePath.extension());
mPreviewHandler.capture(CoCreateInstance, previewHandlerId, nullptr, CLSCTX_LOCAL_SERVER);
}
CLSID getShellExClsidForType(const std::wstring& extension){
winrt::hresult res;
DWORD size;
std::array<wchar_t, 39> interfaceIdWstr;
size = StringFromGUID2(IID_IPreviewHandler, interfaceIdWstr.data(), interfaceIdWstr.size());
if(!size){
winrt::throw_hresult(HRESULT_FROM_WIN32(GetLastError()));
}
std::array<wchar_t, 39> exIdWstr;
res = AssocQueryStringW(ASSOCF_INIT_DEFAULTTOSTAR,
ASSOCSTR_SHELLEXTENSION,
extension.c_str(),
interfaceIdWstr.data(),
exIdWstr.data(),
&size);
winrt::check_hresult(res);
CLSID exId;
res = IIDFromString(exIdWstr.data(), &exId);
winrt::check_hresult(res);
return(exId);
}
void initialize(){
initializeFromPath(mFilePath);
setWindow(mHwnd, mRect);
mIsInitialized = true;
}
void initializeFromPath(const std::filesystem::path& filePath){
if(!initWithFile(filePath) && !initWithStream(filePath)){
winrt::throw_hresult(E_NOINTERFACE);
}
}
bool initWithFile(const std::filesystem::path& filePath){
if(!mPreviewHandler.try_as<IInitializeWithFile>()){
return(false);
}
winrt::check_hresult(mPreviewHandler.as<IInitializeWithFile>()->Initialize(filePath.c_str(), STGM_READ));
return(true);
}
bool initWithStream(const std::filesystem::path& filePath){
if(!mPreviewHandler.try_as<IInitializeWithStream>()){
return(false);
}
winrt::com_ptr<IStream> stream;
stream.capture([](LPCWSTR pszFile, DWORD grfMode, REFIID riid, void **ppstm)
{return(SHCreateStreamOnFileEx(pszFile, grfMode, 0, false, nullptr, reinterpret_cast<IStream**>(ppstm)));},
filePath.c_str(), STGM_READ | STGM_SHARE_DENY_WRITE | STGM_FAILIFTHERE);
winrt::check_hresult(mPreviewHandler.as<IInitializeWithStream>()->Initialize(stream.get(), STGM_READ));
return(true);
}
};
HMODULE getCurrentModule(){
HMODULE hModule;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(LPCTSTR)getCurrentModule,
&hModule);
return(hModule);
}
LRESULT windowProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam){
LPARAM ret = 0;
switch(msg){
case WM_CLOSE:
{
PostQuitMessage(0);
ret = 0;
}break;
default:
{
ret = DefWindowProc(hwnd, msg, wParam, lParam);
}break;
}
return(ret);
}
HWND createTestWindow(){
WNDCLASSW wndClass;
wndClass.style = 0;
wndClass.lpfnWndProc = windowProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = getCurrentModule();
wndClass.hIcon = nullptr;
wndClass.hCursor = nullptr;
wndClass.hbrBackground = nullptr;
wndClass.lpszMenuName = nullptr;
wndClass.lpszClassName = L"test";
ATOM wndAtom = RegisterClassW(&wndClass);
if(!wndAtom){
winrt::throw_hresult(HRESULT_FROM_WIN32(GetLastError()));
}
HWND window = CreateWindowExW(0,
L"Test",
L"",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
wndClass.hInstance,
nullptr);
if(!window){
winrt::throw_hresult(HRESULT_FROM_WIN32(GetLastError()));
}
ShowWindow(window, SW_NORMAL);
return(window);
}
int main(int argc, char *argv[]){
try{
winrt::check_hresult(CoInitialize(nullptr));
if(!SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)){
winrt::throw_hresult(E_FAIL);
}
if(argc != 2){
return(1);
}
std::filesystem::path filePath(argv[1]);
HWND window = createTestWindow();
RECT rect;
GetClientRect(window, &rect);
Preview preview(filePath, window, rect);
preview.startPreview();
MSG msg;
while(GetMessageW(&msg, nullptr, 0, 0) != 0){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}catch(winrt::hresult_error err){
std::wcout << "0x" << std::hex << err.code() << " " << static_cast<std::wstring_view>(err.message());
}catch(...){}
}
Call GetFullPathName on argv[1]. The shell functions can't parse a relative path into a pidl.

How can I make a EDIT control in wxwidgets accepting ENTER and TAB key

Below is simple code for testing Native Win32 Edit control in wxWidgets, no matter if or not I add style WS_EX_CONTROLPARENT or not OR no matter if I add TranslateMessage function, the EDIT control simple does not accept ENTER key and TAB key. I also captured wxEVT_NAVIGATION_KEY and send it to the child window, but it's not working. This seems a basic function. Did I miss someting?
(accepting mean showing ENTER and TAB chars in the EDIT box, if you can see the GIF, the message is captured, but no ENTER or TAB chars in the EDIT box)
#include <wx/wx.h>
#include <wx/xrc/xmlres.h>
#include <wx/fs_mem.h>
#include <wx/textdlg.h>
#include <wx/sysopt.h>
#include <wx/socket.h>
#include <wx/aboutdlg.h>
#include <wx/utils.h>
#include <wx/nativewin.h>
#include <wx/process.h>
#include <wx/infobar.h>
#ifdef __WXMSW__
#include "wx/msw/private.h"
#endif
#include <wx/log.h>
//==============================================================================
class MyMSWEdit : public wxNativeWindow{
protected:
HWND m_cHWnd;
protected:
virtual WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) wxOVERRIDE {
switch (nMsg){
case WM_KEYDOWN:
case WM_KEYUP:
case WM_CHAR:
//MSG messages;
//while (PeekMessage(&messages, (HWND)0, 0, 0, PM_REMOVE))
//{
// /* Translate virtual-key messages into character messages */
// TranslateMessage(&messages);
// /* Send message to WindowProcedure */
// DispatchMessage(&messages);
//}
wxLogMessage("%d",wParam);
return wxNativeWindow::MSWWindowProc(nMsg, wParam, lParam);
}
return wxNativeWindow::MSWWindowProc(nMsg, wParam, lParam);
}
public:
explicit MyMSWEdit(wxWindow * parent) : wxNativeWindow(){
int winmode = WS_CHILD | WS_VISIBLE | ES_MULTILINE;
int exwinmode = 0;
m_cHWnd = CreateWindowExW(exwinmode, TEXT("EDIT"), TEXT("EDIT"), winmode, CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, parent->GetHWND(), NULL, NULL, NULL);
if (m_cHWnd)
(void)Create(parent, wxID_ANY, m_cHWnd);
}
HWND Gethwnd(){
return m_cHWnd;
}
virtual ~MyMSWEdit(){
Disown();
}
};
//==============================================================================
class MainFrame : public wxFrame {
protected:
HWND m_pHWnd;
HWND m_cHWnd;
MyMSWEdit * m_myedit;
public:
explicit MainFrame(const wxString& title) {
wxXmlResource::Get()->LoadFrame(this, NULL, "Frame1");
this->SetTitle(title);
#ifdef _DEBUG
auto pLog = new wxLogWindow(this, "Debug");
pLog->PassMessages(false);
wxLog::SetActiveTarget(pLog);
#else
wxLog::EnableLogging(false);
wxLog::SetActiveTarget(NULL);
#endif
auto P1 = XRCCTRL(*this, "m_panel1", wxPanel);
P1->Bind(wxEVT_NAVIGATION_KEY, [=](wxNavigationKeyEvent& event){
::SendMessage(event.GetCurrentFocus()->GetHWND(),WM_CHAR, VK_TAB, 0);
wxLogMessage("NAV");
return 0;
});
auto P2 = XRCCTRL(*this, "m_panel2", wxPanel);
m_myedit = new MyMSWEdit(P2);
wxASSERT(m_myedit);
P2->GetSizer()->Insert(0, m_myedit, 1, wxEXPAND | wxALL, 0);
P2->GetSizer()->Layout();
m_cHWnd = m_myedit->Gethwnd();
}
//--------------------------------------------------------------------------
virtual ~MainFrame(){
}
};// end class MainFrame
//=============================================================================
class CJApp : public wxApp {
protected:
MainFrame* m_pFrame;
public:
CJApp() {
m_pFrame = NULL;
}
static bool LoadFromString(const wxString & data) {
static int s_memFileIdx = 0;
// Check for memory FS. If not present, load the handler:
wxMemoryFSHandler::AddFile(wxT("XRC_resource/dummy_file"),
wxT("dummy data"));
wxFileSystem fsys;
wxFSFile *f =
fsys.OpenFile(wxT("memory:XRC_resource/dummy_file"));
wxMemoryFSHandler::RemoveFile(wxT("XRC_resource/dummy_file"));
if (f)
delete f;
else
wxFileSystem::AddHandler(new wxMemoryFSHandler);
// Now put the resource data into the memory FS
wxString filename(wxT("XRC_resource/data_string_"));
filename << s_memFileIdx;
s_memFileIdx += 1;
wxMemoryFSHandler::AddFile(filename, data);
// Load the "file" into the resource object
bool retval = wxXmlResource::Get()->Load(wxT("memory:") + filename);
return retval;
}
virtual bool OnInit() {
wxSystemOptions::SetOption("msw.remap", 2);
wxInitAllImageHandlers();
wxXmlResource::Get()->InitAllHandlers();
#ifdef USERES
TCHAR * sResName = _T("#131");
TCHAR * sRestype = _T("CUSTOMERSTRING");
HRSRC hres = FindResource(NULL, sResName, sRestype);
HGLOBAL hbytes = LoadResource(NULL, hres);
LPVOID pdata = LockResource(hbytes);
LPBYTE sData = (LPBYTE)pdata;
LPTSTR sXml = (LPTSTR)sData;
char tmp[99999];
sprintf(tmp, "%s", sXml);
tmp[99998] = 0;
std::string XRC(tmp);
if (LoadFromString(XRC)){
goto success;
}
#else
if (wxXmlResource::Get()->Load("Frame1.xrc")){
goto success;
}
#endif
return false;
success:
m_pFrame = new MainFrame("");
m_pFrame->Show(true);
return true;
}
};
//==============================================================================
DECLARE_APP(CJApp)
IMPLEMENT_APP(CJApp)
Frame1.xrc:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<resource xmlns="http://www.wxwindows.org/wxxrc" version="2.3.0.1">
<object class="wxFrame" name="Frame1">
<style>wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL</style>
<size>500,300</size>
<title></title>
<centered>1</centered>
<aui_managed>0</aui_managed>
<object class="wxPanel" name="m_panel1">
<style>wxTAB_TRAVERSAL</style>
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<option>1</option>
<flag>wxEXPAND | wxALL</flag>
<border>5</border>
<object class="wxPanel" name="m_panel2">
<style>wxTAB_TRAVERSAL</style>
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
</object>
</object>
</object>
<object class="sizeritem">
<option>0</option>
<flag>wxALL</flag>
<border>5</border>
<object class="wxButton" name="m_button1">
<label>MyButton</label>
<default>0</default>
</object>
</object>
<object class="sizeritem">
<option>0</option>
<flag>wxALL</flag>
<border>5</border>
<object class="wxButton" name="m_button2">
<label>MyButton</label>
<default>0</default>
</object>
</object>
</object>
</object>
</object>
</resource>
Inspired by this post
Win32 - Appending text to an Edit Control and some wxWidget source code src\msw\textctrl.cpp
The problem is resolved in following way
protected:
void WriteText(TCHAR *newText) {
// get the current selection
//DWORD StartPos, EndPos;
//SendMessage(m_cHWnd, EM_GETSEL, reinterpret_cast<WPARAM>(&StartPos), reinterpret_cast<WPARAM>(&EndPos));
// move the caret to the end of the text
//int outLength = GetWindowTextLength(m_cHWnd);
//SaendMessage(m_cHWnd, EM_SETSEL, outLength, outLength);
// insert the text at the new caret position
SendMessage(m_cHWnd, EM_REPLACESEL, TRUE, reinterpret_cast<LPARAM>(newText));
// restore the previous selection
//SendMessage(m_cHWnd, EM_SETSEL, StartPos, EndPos);
}
virtual WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) wxOVERRIDE {
switch (nMsg){
case WM_CHAR:
//MSG messages;
//while (PeekMessage(&messages, (HWND)0, 0, 0, PM_REMOVE))
//{
// /* Translate virtual-key messages into character messages */
// TranslateMessage(&messages);
// /* Send message to WindowProcedure */
// DispatchMessage(&messages);
//}
wxLogMessage("%d",wParam);
switch (wParam){
case VK_RETURN:
WriteText(L"\r\n");
break;
case VK_TAB:
WriteText(L"\t");
break;
}
}
return wxNativeWindow::MSWWindowProc(nMsg, wParam, lParam);
}

CDialogEventHandler_CreateInstance Identifier Not Found

I have been following several guides/examples on creating a Vista/7 open file dialog box and have now hit a dead end due to an error message stating that main.cpp(189): error C3861: 'CDialogEventHandler_CreateInstance': identifier not found.
I could find nothing about this on Google except for one page where someone rewrote the sample code to not use the CDialogEventHandler class.
Here is my code. The CDialogEventHandler class and function definitions come straight from the Windows 7 SDK sample for the CommonFileDialogApp:
#include "resource.h"
#include <windows.h> // For common windows data types and function headers
#define STRICT_TYPED_ITEMIDS
#include <objbase.h> // For COM headers
#include <shobjidl.h> // for IFileDialogEvents and IFileDialogControlEvents
#include <shlwapi.h>
#include <knownfolders.h> // for KnownFolder APIs/datatypes/function headers
#include <propvarutil.h> // for PROPVAR-related functions
#include <propkey.h> // for the Property key APIs/datatypes
#include <propidl.h> // for the Property System APIs
#include <strsafe.h> // for StringCchPrintfW
#include <shtypes.h> // for COMDLG_FILTERSPEC
#include <new>
// Controls
#define CONTROL_GROUP 2000
#define CONTROL_RADIOBUTTONLIST 2
#define CONTROL_RADIOBUTTON1 1
#define CONTROL_RADIOBUTTON2 2 // It is OK for this to have the same IDas CONTROL_RADIOBUTTONLIST,
// because it is a child control under CONTROL_RADIOBUTTONLIST
// IDs for the Task Dialog Buttons
#define IDC_BASICFILEOPEN 100
#define IDC_ADDITEMSTOCUSTOMPLACES 101
#define IDC_ADDCUSTOMCONTROLS 102
#define IDC_SETDEFAULTVALUESFORPROPERTIES 103
#define IDC_WRITEPROPERTIESUSINGHANDLERS 104
#define IDC_WRITEPROPERTIESWITHOUTUSINGHANDLERS 105
HWND ghMainWnd = 0;
HINSTANCE ghAppInst = 0;
RECT winRect;
void centerWnd(HWND parent_window);
void openDB();
class CDialogEventHandler : public IFileDialogEvents,
public IFileDialogControlEvents
{
public:
// IUnknown methods
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(CDialogEventHandler, IFileDialogEvents),
QITABENT(CDialogEventHandler, IFileDialogControlEvents),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&_cRef);
}
IFACEMETHODIMP_(ULONG) Release()
{
long cRef = InterlockedDecrement(&_cRef);
if (!cRef)
delete this;
return cRef;
}
// IFileDialogEvents methods
IFACEMETHODIMP OnFileOk(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnFolderChange(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnFolderChanging(IFileDialog *, IShellItem *) { return S_OK; };
IFACEMETHODIMP OnHelp(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnSelectionChange(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnShareViolation(IFileDialog *, IShellItem *, FDE_SHAREVIOLATION_RESPONSE *) { return S_OK; };
IFACEMETHODIMP OnTypeChange(IFileDialog *pfd);
IFACEMETHODIMP OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) { return S_OK; };
// IFileDialogControlEvents methods
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize *pfdc, DWORD dwIDCtl, DWORD dwIDItem);
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize *, DWORD) { return S_OK; };
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize *, DWORD, BOOL) { return S_OK; };
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize *, DWORD) { return S_OK; };
CDialogEventHandler() : _cRef(1) { };
private:
~CDialogEventHandler() { };
long _cRef;
};
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_FILE_OPENDB:
openDB();
break;
default:
break;
}
break;
case WM_LBUTTONDOWN:
MessageBox(0, "WM_LBUTTONDOWN message.", "Msg", MB_OK);
return 0;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
DestroyWindow(ghMainWnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR cmdLine, int showCmd)
{
ghAppInst = hInstance;
HMENU mMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = ghAppInst;
wc.hIcon = ::LoadIcon(0, IDI_APPLICATION);
wc.hCursor = ::LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH) ::GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = "MyWndClassName";
RegisterClass(&wc);
centerWnd(GetDesktopWindow());
ghMainWnd = ::CreateWindow("MyWndClassName", "Space Crusade Database Editor V1.0", WS_OVERLAPPEDWINDOW, winRect.left, winRect.top, 1280, 720, 0, mMenu, ghAppInst, 0);
if(ghMainWnd == 0)
{
::MessageBox(0, "CreateWindow - Failed", 0, 0);
return false;
}
ShowWindow(ghMainWnd, showCmd);
UpdateWindow(ghMainWnd);
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
void centerWnd(HWND parent_window)
{
GetClientRect(parent_window, &winRect);
winRect.left = (winRect.right/2) - (1280/2);
winRect.top = (winRect.bottom/2) - (720/2);
}
void openDB()
{
//Cocreate the file open dialog object
IFileDialog *pfd = NULL;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
//Stuff needed for later
const COMDLG_FILTERSPEC rgFExt[] = {{L"SQLite3 Database (*.sqlite)", L"*.sqlite"}};
WCHAR fPath[MAX_PATH] = {};
//Create event handling
IFileDialogEvents *pfde = NULL;
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
if(SUCCEEDED(hr))
{
//Hook the event handler
DWORD dwCookie;
hr = pfd->Advise(pfde, &dwCookie);
if (SUCCEEDED(hr))
{
//Set options for the dialog
DWORD dwFlags;
//Get options first so we do not override
hr = pfd->GetOptions(&dwFlags);
if (SUCCEEDED(hr))
{
//Get shell items only
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
if (SUCCEEDED(hr))
{
//Types of files to display (not default)
hr = pfd->SetFileTypes(ARRAYSIZE(rgFExt), rgFExt);
if (SUCCEEDED(hr))
{
//Set default file type to display
hr = pfd->SetDefaultExtension(L"sqlite");
if (SUCCEEDED(hr))
{
//Show dialog
hr = pfd->Show(NULL);
if (SUCCEEDED(hr))
{
//Get the result once the user clicks on open
IShellItem *result;
hr = pfd->GetResult(&result);
if (SUCCEEDED(hr))
{
//Print out the file name
PWSTR fName = NULL;
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &fName);
if (SUCCEEDED(hr))
{
TaskDialog(NULL,NULL,L"File Name",fName,NULL,TDCBF_OK_BUTTON,TD_INFORMATION_ICON,NULL);
CoTaskMemFree(fName);
}
result->Release();
}
}
}
}
}
}
}
pfd->Unadvise(dwCookie);
}
pfde->Release();
}
pfd->Release();
}
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void **ppv)
{
*ppv = NULL;
CDialogEventHandler *pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pDialogEventHandler->QueryInterface(riid, ppv);
pDialogEventHandler->Release();
}
return hr;
}
HRESULT CDialogEventHandler::OnTypeChange(IFileDialog *pfd)
{
IFileSaveDialog *pfsd;
HRESULT hr = pfd->QueryInterface(&pfsd);
if (SUCCEEDED(hr))
{
UINT uIndex;
hr = pfsd->GetFileTypeIndex(&uIndex); // index of current file-type
if (SUCCEEDED(hr))
{
IPropertyDescriptionList *pdl = NULL;
}
pfsd->Release();
}
return hr;
}
// IFileDialogControlEvents
// This method gets called when an dialog control item selection happens (radio-button selection. etc).
// For sample sake, let's react to this event by changing the dialog title.
HRESULT CDialogEventHandler::OnItemSelected(IFileDialogCustomize *pfdc, DWORD dwIDCtl, DWORD dwIDItem)
{
IFileDialog *pfd = NULL;
HRESULT hr = pfdc->QueryInterface(&pfd);
if (SUCCEEDED(hr))
{
if (dwIDCtl == CONTROL_RADIOBUTTONLIST)
{
switch (dwIDItem)
{
case CONTROL_RADIOBUTTON1:
hr = pfd->SetTitle(L"Longhorn Dialog");
break;
case CONTROL_RADIOBUTTON2:
hr = pfd->SetTitle(L"Vista Dialog");
break;
}
}
pfd->Release();
}
return hr;
}
So I managed to get the code to work. What I did was I took the solution file from the Sample mentioned in the original post and attempted to build and run it. This was successful which lead me to believe that the problem was in the project settings. I tried switching back to Unicode for the typeset but this did not fix the problem.
In the end I just co-opted the Sample project and I am now altering it to me needs. I am not sure what project options are needed to make it work but I am almost sure it has to do with with something in the linker options.
Not sure you have got the entire sample code, try github sample below with these libraries, should compile first time.
//Libraries I added to get this to work.
#pragma comment(lib,"comctl32.lib")
#pragma comment(lib,"propsys.lib")
#pragma comment(lib,"shlwapi.lib")
https://github.com/pauldotknopf/WindowsSDK7-Samples/blob/master/winui/shell/appplatform/commonfiledialog/CommonFileDialogApp.cpp
CDialogEventHandler_CreateInstance is a helper function defined in Microsoft's CommonFileDialogue example app. You can also find the GitHub page from the MSDN page.
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void **ppv)
{
*ppv = NULL;
CDialogEventHandler *pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pDialogEventHandler->QueryInterface(riid, ppv);
pDialogEventHandler->Release();
}
return hr;
}
You used function prototypes (I believe that is what they are called, forward declarations of functions before the main function) for centerWnd and openDB, but not the CDialogEventHandler_CreateInstance function. Try adding
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void **ppv);
under
void openDB();

How to inject a DLL into Adobe Reader X

I need to inject a DLL into Adobe Reader X that reads the events sent to the scrollbar (even if it is hidden). I need to do this to be able to find out what page of the document i am on.
I have tried hooking a dll using the win32 hooking API, i give a CBT hook to all processes on the desktop and listen for the creation of the Adobe Reader X window, then hooking this window with my scrollbar hook.
The problem is that i never get the scrollbar hook placed on Adobe Reader X, i dont get the create window or window activate messages for these windows when they are created. How do i get these messages and how do i hook into Adobe Reader X?
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "pdfviewlib.h"
#include <sstream>
#pragma data_seg(".PDFVIEWLIB")
PDFVIEWOBJ pdfviewobj[MAX_PDFOBJS] = {NULL};
HHOOK globalhook = NULL;
BOOL debug = TRUE;
INT sSlide = 0;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.PDFVIEWLIB,RWS")
#define DEBUG(...) if(debug) printf(__VA_ARGS__)
HINSTANCE hInstance = NULL;
static int tAttach = 0;
static int tDetach = 0;
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
hInstance = (HINSTANCE)hModule;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DEBUG("PROCESS_ATTACH\n");
break;
case DLL_THREAD_ATTACH:
DEBUG("THREAD_ATTACH %i\n",tAttach++);
break;
case DLL_THREAD_DETACH:
DEBUG("THREAD_DETACH %i\n", tDetach++);
break;
case DLL_PROCESS_DETACH:
// Clean up... hopefully there is only the one process attached?
DEBUG("PROCESS_DETACH\n");
for(int i = 0; i<MAX_PDFOBJS; i++)
ClosePDF(i);
break;
}
return TRUE;
}
DllExport void SetDebug(BOOL onoff)
{
printf("SetDebug\n");
debug = onoff;
DEBUG("enabled\n");
}
//Check if Acrobat Reader is installed
DllExport BOOL CheckInstalled()
{
DEBUG("CheckInstalled\n");
char cmdline[MAX_PATH * 2];
return GetPDFViewerPath(cmdline, sizeof(cmdline));
}
// Open the PDF
DllExport int OpenPDF(char *filename, HWND hParentWnd, int startSlide)
{
STARTUPINFO * si = (STARTUPINFO *) malloc(sizeof(STARTUPINFO));
PROCESS_INFORMATION * pi = (PROCESS_INFORMATION*) malloc(sizeof(PROCESS_INFORMATION));
char cmdline[MAX_PATH * 2];
int id;
sSlide = startSlide;
DEBUG("OpenPDF start: %u", hParentWnd);
//First check if Acrobat Reader is installed before continuing
if(GetPDFViewerPath(cmdline, sizeof(cmdline))==FALSE)
{
DEBUG("OpenPDF: GetPDFTViewerPath failed\n");
return -1;
}
id = -1;
for(int i = 0; i<MAX_PDFOBJS; i++)
{
if(pdfviewobj[i].state==PDF_CLOSED)
{
id=i;
break;
}
}
if(id<0)
{
DEBUG("OpenPDF: Too many PDFs\n");
return -1;
}
if (pdfviewobj[id].state == PDF_STARTED)
{
DEBUG("RERUN WHEN PDF_STARTED\n");
return -1;
}
memset(&pdfviewobj[id], 0, sizeof(PDFVIEWOBJ));
strcpy_s(pdfviewobj[id].filename, MAX_PATH, filename);
pdfviewobj[id].state = PDF_CLOSED;
pdfviewobj[id].currentSlide = 0;
pdfviewobj[id].hParentWnd = hParentWnd;
pdfviewobj[id].hWnd = NULL;
pdfviewobj[id].hWnd2 = NULL;
strcat_s(cmdline, MAX_PATH * 2, "\\AcroRd32.exe /n /s /o");
strcat_s(cmdline, MAX_PATH * 2, " \"");
strcat_s(cmdline, MAX_PATH * 2, filename);
strcat_s(cmdline, MAX_PATH * 2, "\"");
si = (STARTUPINFO *)memset(si, 0, sizeof(STARTUPINFO));
pi = (PROCESS_INFORMATION *)memset(pi, 0, sizeof(PROCESS_INFORMATION));
if(globalhook!=NULL){
UnhookWindowsHookEx(globalhook);
DEBUG("Global unhooked\n");
globalhook = NULL;
}
//Set the global hook listening for Window Create/Window Activate messages
globalhook = SetWindowsHookEx(WH_CBT,CbtProc,hInstance,NULL);
if(globalhook==NULL)
{
DEBUG("OpenPDF: Global SetWindowsHookEx failed\n");
DEBUG("ERROR: %X\n", GetLastError());
globalhook = NULL;
ClosePDF(id);
return -1;
}
else DEBUG("GLOBAL HOOKED %X\n", globalhook);
pdfviewobj[id].state = PDF_STARTED;
Sleep(10);
DEBUG(cmdline);
//Run Acrobat Reader, PDF STATE SET TO STARTED
if(!CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, 0, NULL, si, pi))
{
DEBUG("OpenPDF: CreateProcess failed\n");
ClosePDF(id);
return -1;
}
pdfviewobj[id].dwProcessId = pi->dwProcessId;
pdfviewobj[id].dwThreadId = pi->dwThreadId;
pdfviewobj[id].hThread = pi->hThread;
pdfviewobj[id].hProcess = pi->hProcess;
//WAIT FOR GLOBAL HOOK TO DETECT Acrobat Windows and set PDF STATE TO PDF_OPENED
//For some reason the loops exits and PDFSTATE is PDF_CLOSED...
while(pdfviewobj[id].state==PDF_STARTED)
Sleep(50);
DEBUG("PDFSTATE == CLOSED = %i \n", pdfviewobj[id].state==PDF_CLOSED);
DEBUG("PDFSTATE == STARTED = %i \n", pdfviewobj[id].state==PDF_STARTED);
DEBUG("PDFSTATE == OPENED = %i \n", pdfviewobj[id].state==PDF_OPENED);
DEBUG("PDFSTATE == LOADED = %i \n", pdfviewobj[id].state==PDF_LOADED);
if (sSlide > 0){
GotoSlide(id, sSlide+1);
}
pdfviewobj[id].state = PDF_LOADED;
DEBUG("OpenPDF Done: id=%i\n", id);
return id;
}
// Get the path of Acrobat Reader X from the registry
BOOL GetPDFViewerPath(char *pdfviewerpath, int strsize)
{
HKEY hkey;
DWORD dwtype, dwsize;
LRESULT lresult;
DEBUG("GetPDFViewerPath: start\n");
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Adobe\\Acrobat Reader\\9.0\\InstallPath", 0, KEY_READ, &hkey)!=ERROR_SUCCESS)
return FALSE;
dwtype = REG_SZ;
dwsize = (DWORD)strsize;
lresult = RegQueryValueEx(hkey, NULL, NULL, &dwtype, (LPBYTE)pdfviewerpath, &dwsize );
RegCloseKey(hkey);
if(lresult!=ERROR_SUCCESS)
return FALSE;
DEBUG("GetPDFViewerPath: exit ok \n");
return TRUE;
}
// Unhook the Windows hook
void Unhook(int id)
{
DEBUG("Unhook: start %i\n", id);
if(pdfviewobj[id].hook!=NULL)
UnhookWindowsHookEx(pdfviewobj[id].hook);
pdfviewobj[id].hook = NULL;
DEBUG("Unhook: exit ok\n");
}
// Close the Acrobat Reader, release resources
DllExport void ClosePDF(int id)
{
DEBUG("ClosePDF: start %i\n", id);
if (globalhook != NULL) {
DEBUG("GLOBAL UNHOOKED %X\n", globalhook);
UnhookWindowsHookEx(globalhook);
globalhook = NULL;
}
else DEBUG("GLOBAL NOT UNHOOKED\n");
pdfviewobj[id].state = PDF_CLOSED;
Unhook(id);
if(pdfviewobj[id].hWnd==0)
TerminateThread(pdfviewobj[id].hThread, 0);
else
PostMessage(pdfviewobj[id].hWnd, WM_CLOSE, 0, 0);
CloseHandle(pdfviewobj[id].hThread);
CloseHandle(pdfviewobj[id].hProcess);
memset(&pdfviewobj[id], 0, sizeof(PDFVIEWOBJ));
DEBUG("ClosePDF: exit ok\n");
return;
}
// Return the number of the slide currently viewing
DllExport int GetCurrentSlide(int id)
{
DEBUG("GetCurrentSlide:%d\n", id);
if(pdfviewobj[id].state==0)
return -1;
else
return pdfviewobj[id].currentSlide;
}
// Take a step forwards through the show
DllExport void NextStep(int id)
{
DEBUG("NextStep:%d\n", id);
SetForegroundWindow(pdfviewobj[id].hWnd);
SetFocus(pdfviewobj[id].hWnd);
PostMessage(pdfviewobj[id].hWnd2, WM_MOUSEWHEEL, MAKEWPARAM(0, -WHEEL_DELTA), 0);
}
// Take a step backwards through the show
DllExport void PrevStep(int id)
{
DEBUG("PrevStep:%d\n", id);
SetForegroundWindow(pdfviewobj[id].hWnd);
SetFocus(pdfviewobj[id].hWnd);
PostMessage(pdfviewobj[id].hWnd2, WM_MOUSEWHEEL, MAKEWPARAM(0, WHEEL_DELTA), 0);
}
// Go directly to a slide
DllExport void GotoSlide(int id, int slideno)
{
//TODO: USE SETSCROLLINFO
}
// This hook is started with the AcroRd32.EXE process and waits for the WM_CREATEWND message.
// Release the hook as soon as we're complete to free up resources
LRESULT CALLBACK CbtProc(int nCode, WPARAM wParam, LPARAM lParam)
{
HHOOK hook = globalhook;
DEBUG("HOOK: %X\n", hook);
if (nCode < 0) {
return CallNextHookEx(hook, nCode, wParam, lParam);
}
else if(nCode==HCBT_CREATEWND)
{
DEBUG("CREATE WINDOW \n");
char csClassName[16];
char csCaptionName[16];
HWND hCurrWnd = (HWND)wParam;
DWORD retProcId = NULL;
GetClassName(hCurrWnd, csClassName, sizeof(csClassName));
GetWindowText(hCurrWnd, csCaptionName, sizeof(csCaptionName));
if((strcmp(csClassName, "AcrobatSDIWindow")==0)
||(strcmp(csClassName, "AVL_AVView")==0))
{
DEBUG("%s found \n", csClassName);
int id=-1;
DWORD windowthread = GetWindowThreadProcessId(hCurrWnd,NULL);
for(int i=0; i<MAX_PDFOBJS; i++)
{
if(pdfviewobj[i].dwThreadId==windowthread)
{
id=i;
break;
}
}
if(id>=0)
{
DEBUG("Matched threadid!\n");
if(strcmp(csClassName, "AVL_AVView")==0){
if (strcmp(csCaptionName, "AVPageView")==0){
pdfviewobj[id].hWnd2=hCurrWnd;
}
}
else
{
pdfviewobj[id].hWnd=hCurrWnd;
CBT_CREATEWND* cw = (CBT_CREATEWND*)lParam;
if(pdfviewobj[id].hParentWnd!=NULL)
cw->lpcs->hwndParent = pdfviewobj[id].hParentWnd;
}
if((pdfviewobj[id].hWnd!=NULL)&&(pdfviewobj[id].hWnd2!=NULL))
{
pdfviewobj[id].hook = SetWindowsHookEx(WH_CALLWNDPROC,CwpProc,hInstance,pdfviewobj[id].dwThreadId);
if (pdfviewobj[id].hook != NULL) {
DEBUG("Global UNHOOKED %X\n", globalhook);
UnhookWindowsHookEx(globalhook);
globalhook=NULL;
pdfviewobj[id].state = PDF_OPENED;
}
Sleep(10);
}
}
}
}
return CallNextHookEx(hook,nCode,wParam,lParam);
}
LRESULT CALLBACK CwpProc(int nCode, WPARAM wParam, LPARAM lParam){
CWPSTRUCT *cwp;
cwp = (CWPSTRUCT *)lParam;
HHOOK hook = NULL;
DWORD windowthread = GetWindowThreadProcessId(cwp->hwnd,NULL);
int id=-1;
for(int i=0; i<MAX_PDFOBJS; i++)
{
if(pdfviewobj[i].dwThreadId==windowthread)
{
id=i;
hook = pdfviewobj[id].hook;
break;
}
}
if((id>=0)&&(nCode==HC_ACTION))
{
DEBUG("CBT HC_ACTION\n");
if(cwp->message==SBM_SETSCROLLINFO)
{
DEBUG("CBT SBM_SETSCROLLINFO\n");
SCROLLINFO *scrInf;
scrInf = (SCROLLINFO *)cwp->lParam;
pdfviewobj[id].currentSlide = scrInf->nPos;
}
if((pdfviewobj[id].state != PDF_CLOSED)&&(cwp->message==WM_CLOSE||cwp->message==WM_QUIT)){
pdfviewobj[id].state = PDF_CLOSING;
}
}
return CallNextHookEx(hook,nCode,wParam,lParam);
}
heres the header if you need it
#define DllExport extern "C" __declspec( dllexport )
enum PDFVIEWSTATE { PDF_CLOSED, PDF_STARTED, PDF_OPENED, PDF_LOADED, PDF_CLOSING};
DllExport int OpenPDF(char *filename, HWND hParentWnd, int startSlide);
DllExport BOOL CheckInstalled();
DllExport void ClosePDF(int id);
DllExport int GetCurrentSlide(int id);
DllExport void NextStep(int id);
DllExport void PrevStep(int id);
DllExport void GotoSlide(int id, int slideno);
DllExport void SetDebug(BOOL onoff);
LRESULT CALLBACK CbtProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK CwpProc(int nCode, WPARAM wParam, LPARAM lParam);
BOOL GetPDFViewerPath(char *pdfviewerpath, int strsize);
void Unhook(int id);
//MAXUMUM NUMBER OF PDF-PROCESSES CURRENTLY SET TO ONE
#define MAX_PDFOBJS 1
struct PDFVIEWOBJ
{
HHOOK hook;
HWND hWnd;
HWND hWnd2;
HWND hParentWnd;
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
int currentSlide;
char filename[MAX_PATH];
PDFVIEWSTATE state;
};
Adobe Reader typically runs in protected mode. (See Edit/Preferences/Security (Enhanced). Un-check the "Enable Protected Mode at startup" checkbox.
Relaunch reader and see if you get your messages. You should. The issue is that the User Interface Privilege Isolation (UIPI) disallows many windows messages from crossing process boundaries between processes running at a different integrity level (low/medium/high). You are supposed to be able to change the windows message filter via ChangeWindowMessageFilterEx().
I am currently experiencing issues with Adobe Reader Xi where the ChangeWindowsMessageFilter and ChangeWindowMessageFilterEx do not seem to change the behavior of Adobe reader sending messages to the global hook receiver in the hooking process. I have copied noteapad.exe to notepad2.exe and lowered its integrity level to low via: icacls notepad2.exe /setintegritylevel low
(run from an elevated cmd prompt (i.e. run as admin)). When I do this my hooking works fine (using ChangeWindowMessageFilterEx()) but still does not get hooked messages from Adobe.
Also, Reader is 32-bit so make sure you are hooking it from a 32-bit hooking process otherwise you won't see messages either).

How can I be notified when a new window is created on Win32?

Is there a way using Win32, to register for notifications when a new window is created. I'm trying to keep a list of current open windows, but am now just polling the list of current windows using EnumWindows().
Anyone done something similar?
Thanks
I'm not sure if I'm doing this right, but I'm not able to get the SetWindowsHookEx method to fire.
anything come to mind?
here is my snip
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll")]
private static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
const int HSHELL_WINDOWCREATED = 1;
private static HookProc winDelegate = ShellHookProcDelegate;
internal static void RegisterWindowCreatedEvent()
{
SetWindowsHookEx(HookType.WH_SHELL, winDelegate, IntPtr.Zero, 0);
}
private static int ShellHookProcDelegate(int code, IntPtr wParam, IntPtr lParam)
{
if (code != HSHELL_WINDOWCREATED)
{
return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
//App specific code here
return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
Use SetWindowsHookEx to set up a WH_SHELL hook and look for the HSHELL_WINDOWCREATED event.
Here is some code based on UI automation events. It gives window opened, closed, and focused events.
C#
[STAThread]
public static void Main(string[] args)
{
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, (sender, e) =>
{
var element = (AutomationElement)sender;
var name = element.Current.Name;
Console.WriteLine("open: " + name + " hwnd:" + element.Current.NativeWindowHandle);
Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, element, TreeScope.Element, (s, e2) =>
{
Console.WriteLine("close: " + name + " hwnd:" + element.Current.NativeWindowHandle);
});
});
Automation.AddAutomationFocusChangedEventHandler((sender, e) =>
{
var element = (AutomationElement)sender;
var name = element.Current.Name;
Console.WriteLine("focused: " + name + " hwnd:" + element.Current.NativeWindowHandle);
});
Console.ReadLine();
Automation.RemoveAllEventHandlers();
}
C++ equivalent:
#include <windows.h>
#include <stdio.h>
#include <uiautomation.h>
// some useful macros
#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define __WFILE__ WIDEN(__FILE__)
#define MYTRACE wprintf
#define CHECKHR(expr) {hr=(expr);if(FAILED(hr)){ MYTRACE(L"HR FAILED line:%u file:%s\n", __LINE__, __WFILE__); goto cleanup; } }
#define CHECKWIN32(expr) {if(!(expr)){hr = HRESULT_FROM_WIN32(GetLastError()); MYTRACE(L"WIN32 FAILED line:%u file:%s\n", __LINE__, __WFILE__); goto cleanup; } }
#define CHECKARG(expr) {if(!(expr)){ MYTRACE(L"ARG FAILED line:%u file:%s\n", __LINE__, __WFILE__); hr = E_INVALIDARG; goto cleanup; } }
#define CHECKMEM(expr) {if(!(expr)){ MYTRACE(L"MEM FAILED line:%u file:%s\n", __LINE__, __WFILE__); hr = E_OUTOFMEMORY; goto cleanup; } }
#define CORELEASE(expr) {if(expr){ expr->Release(); expr = NULL; } }
#define HR HRESULT hr=S_OK;
class EventHandler :
public IUIAutomationEventHandler,
public IUIAutomationFocusChangedEventHandler
{
private:
LONG _ref;
IUIAutomation* _automation;
HWND _hwnd;
IUIAutomationElement* _sender;
public:
EventHandler(IUIAutomation* automation, IUIAutomationElement* sender, HWND hwnd) :
_ref(1),
_automation(automation),
_sender(sender),
_hwnd(hwnd)
{
if (sender)
{
sender->AddRef();
}
}
~EventHandler()
{
CORELEASE(_sender);
}
// IUnknown
ULONG STDMETHODCALLTYPE AddRef() { ULONG ret = InterlockedIncrement(&_ref); return ret; }
ULONG STDMETHODCALLTYPE Release() { ULONG ret = InterlockedDecrement(&_ref); if (!ret) { delete this; return 0; } return ret; }
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppInterface)
{
if (riid == __uuidof(IUnknown))
{
*ppInterface = (IUIAutomationEventHandler*)this;
}
else if (riid == __uuidof(IUIAutomationEventHandler))
{
*ppInterface = (IUIAutomationEventHandler*)this;
}
else if (riid == __uuidof(IUIAutomationFocusChangedEventHandler))
{
*ppInterface = (IUIAutomationFocusChangedEventHandler*)this;
}
else
{
*ppInterface = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
// IUIAutomationFocusChangedEventHandler
HRESULT STDMETHODCALLTYPE HandleFocusChangedEvent(IUIAutomationElement* sender)
{
HWND hwnd = NULL;
sender->get_CurrentNativeWindowHandle((UIA_HWND*)&hwnd);
wprintf(L"Window focused hwnd:%p'\n", hwnd);
return S_OK;
}
// IUIAutomationEventHandler
HRESULT STDMETHODCALLTYPE HandleAutomationEvent(IUIAutomationElement* sender, EVENTID eventID)
{
HR;
HWND hwnd = NULL;
EventHandler* windowHandler;
switch (eventID)
{
case UIA_Window_WindowOpenedEventId:
sender->get_CurrentNativeWindowHandle((UIA_HWND*)&hwnd);
wprintf(L"Window opened hwnd:%p\n", hwnd);
// register for close on this window
// we build a new handler, this is the only way to remember the hwnd (the close event doesn't have anything)
windowHandler = new EventHandler(_automation, sender, hwnd); // implicit addref
CHECKMEM(windowHandler);
CHECKHR(_automation->AddAutomationEventHandler(UIA_Window_WindowClosedEventId, sender, TreeScope_Element, NULL, windowHandler));
break;
case UIA_Window_WindowClosedEventId:
wprintf(L"Window closed hwnd:%p\n", _hwnd);
CHECKHR(_automation->RemoveAutomationEventHandler(UIA_Window_WindowClosedEventId, _sender, this));
Release(); // we release our own reference, 'this' we be deleted sometime when all COM references are gone. don't do 'delete this'!
break;
}
cleanup:
return hr;
}
};
int main()
{
HR;
IUIAutomationElement* root = NULL;
EventHandler* handler = NULL;
IUIAutomation* automation = NULL;
CoInitializeEx(NULL, COINIT_MULTITHREADED);
CHECKHR(CoCreateInstance(__uuidof(CUIAutomation), NULL, CLSCTX_INPROC_SERVER, __uuidof(IUIAutomation), (void**)&automation));
CHECKHR(automation->GetRootElement(&root));
handler = new EventHandler(automation, NULL, NULL);
CHECKMEM(handler);
CHECKHR(automation->AddAutomationEventHandler(UIA_Window_WindowOpenedEventId, root, TreeScope_Subtree, NULL, handler));
CHECKHR(automation->AddFocusChangedEventHandler(NULL, handler));
wprintf(L"Press any key to stop listening for events.\n");
getchar();
cleanup:
if (automation != NULL)
{
automation->RemoveAllEventHandlers();
CORELEASE(automation);
}
CORELEASE(handler);
CORELEASE(root);
CoUninitialize();
return hr;
}
Sure - you can write a CBT hook and watch for HCBT_CREATEWND. See also SetWindowsHookEx().
Note that this will allow you to be notified of all window creation, before the windows being created are even fully initialized. If all you need are unowned, top-level windows, RichieHindle's suggestion may work better...
Detours will permit you to attach hooks to arbitrary Win32 functions. However, polling is probably a more reliable way to approach the problem: you don't have to worry about whether you've missed a particular window-creation method (how many are there in Win32? I bet more than one!), and, of course, you won't be rewriting the machine code for windows functions at runtime.
But, you know, your call.
You could try WinEventHook library for autohotkey.
Try modifying the notepad popup blocker example with the following:
HookProc( hWinEventHook, Event, hWnd, idObject, idChild, dwEventThread, dwmsEventTime )
{
if Event ; EVENT_SYSTEM_FOREGROUND = 0x3
{
WinGetTitle, title, ahk_id %hWnd%
If (title = "your_window_name"
msgbox, your window has been created
}
}

Resources