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

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
}
}

Related

images not working properly if I open it from my shell namespace extension folder

I have my custom shell namespace extension. Just want to have a virtual folder mapped to some folder on disk C:/ with the same functionality.
using namespace ATL;
class ATL_NO_VTABLE CMyShellFolder :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CMyShellFolder, &CLSID_CMyShellFolder>,
public IPersistFolder2,
public IShellFolder2,
public IExplorerPaneVisibility
{
CComHeapPtr<ITEMIDLIST_ABSOLUTE> m_pidl;
CComPtr<IShellFolder2> m_folder;
CComPtr<IThumbnailHandlerFactory> m_thFactory;
CComPtr<IUnknown> m_site;
public:
CMyShellFolder()
{
}
static HRESULT WINAPI UpdateRegistry(BOOL reg) throw();
BEGIN_COM_MAP(CMyShellFolder)
COM_INTERFACE_ENTRY(IShellFolder)
COM_INTERFACE_ENTRY(IShellFolder2)
COM_INTERFACE_ENTRY2(IPersist, IPersistFolder)
COM_INTERFACE_ENTRY(IPersistFolder)
COM_INTERFACE_ENTRY(IPersistFolder2)
COM_INTERFACE_ENTRY(IExplorerPaneVisibility)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
---------------------------------
HRESULT CMyShellFolder::BindToObject(PCUIDLIST_RELATIVE pidl, IBindCtx* pbc, REFIID riid, LPVOID* ppv)
{
if (riid == __uuidof(IShellFolder3))
return E_NOINTERFACE;
HR;
CComObject<CMyShellFolder>* folder = nullptr;
CHECKARG(pidl);
hr = E_NOINTERFACE;
if (riid == IID_IShellFolder ||
riid == IID_IShellFolder2)
{
// check it's a folder
SFGAOF atts = SFGAO_FOLDER;
auto hr2 = GetAttributesOf(1, (PCUITEMID_CHILD_ARRAY)&pidl, &atts);
if (FAILED(hr2) || !(atts & SFGAO_FOLDER))
goto cleanup; // nope, get out
CHECKHR(CreateInstanceAddRef(&folder));
CHECKHR(folder->_Initialize(this, pidl));
CHECKHR(folder->QueryInterface(riid, ppv));
}
RELEASE(folder);
HRONFAIL(L"CMyShellFolder::BindToObject"); }
HRESULT CMyShellFolder::CreateViewObject(HWND hwnd, REFIID riid, LPVOID* ppv)
{
HR;
SFV_CREATE sfvc = { 0 };
DEFCONTEXTMENU dcm = { 0 };
CHECKITEM;
CHECKARG(ppv);
hr = E_NOINTERFACE;
if (riid == IID_IShellView)
{
sfvc.cbSize = sizeof(SFV_CREATE);
CHECKHR(QueryInterface(IID_PPV_ARGS(&sfvc.pshf)));
QueryInterface(IID_PPV_ARGS(&sfvc.psfvcb));
CHECKHR(SHCreateShellFolderView(&sfvc, (IShellView**)ppv));
goto cleanup;
}
if (riid == IID_IContextMenu)
{
dcm.hwnd = hwnd;
//dcm.pidlFolder = (PCIDLIST_ABSOLUTE)m_pidl.m_pData;
QueryInterface(IID_PPV_ARGS(&dcm.pcmcb));
CHECKHR(QueryInterface(IID_PPV_ARGS(&dcm.psf)));
CHECKHR(SHCreateDefaultContextMenu(&dcm, riid, ppv));
goto cleanup;
}
if (riid == IID_ITransferSource || // for delete & file operations
riid == IID_IDropTarget) // for copy paste & dd
{
CHECKHR(m_folder->CreateViewObject(hwnd, riid, ppv));
goto cleanup;
}
CHECKHR(m_folder->CreateViewObject(hwnd, riid, ppv));
cleanup:
RELEASE(dcm.pcmcb);
RELEASE(dcm.psf);
RELEASE(sfvc.psfvcb);
RELEASE(sfvc.pshf);
HRONFAIL(L"CMyShellFolder::CreateViewObject"); }
But opening images from my custom folders don't work.
image image2
Actually it displays an image for about a second and then error message appears.
Nevertheless it could be opened normally via Paint
Your nse need handle IID_Istream to open file with UWP program
// Pseudo code
STDMETHODIMP NSEShellFolder::BindToObject (LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, void** ppvOut)
{
HRESULT hr = E NOTIMPL;
//.....//
if (riid == IID IStream) {
DWORD grfMode = 0;
if (pbcReserved) { //IBindCtx
BIND OPTS opts;
ZeroMemory (&opts, sizeof (BIND OPTS));
opts.cbStruct = sizeof (opts);
pbc->GetBindOptions (&opts);
grfMode = opts.grfMode;
}
//If your nse Has FileSystemPath, Win32 API ¹SHCreateStreamOnFile Ex' is useful.
//or you can implement own IStream interface.
LPWSTR pPath = CNSEData::GetPath (m_pidl);
BOOL bCreate = grfMode & STGM CREATE? TRUE : FALSE;
hr = SHCreateStreamOnFileEx (pPath,grfMode, FILE_ATTRIBUTE_NORMAL,bCreate,NULL, (IStream**)ppvOut);
}
//.....//
return hr;
}
Before
After

The keyboad hook dll can't work well

The code of keyhook.cpp in dll:
// KeyHook.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "stdio.h"
#include "Windows.h"
#pragma comment(linker, "/SECTION:.SHARED,RWS")
#pragma data_seg(".SHARED")
#define DEF_PROCESS_NAME "notepad.exe"
HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;
#pragma data_seg()
BOOL APIENTRY DllMain(HINSTANCE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
printf("main function in dll\n");
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInstance = hModule;
printf("max=%d\n", MAX_PATH);
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//OutputDebugString("what the hell\n");
char szPath[MAX_PATH] = { 0, };
char *p = NULL;
//printf("the callback function");
int shift = nCode;
if (shift = 0)
{
if (!(lParam & 0x80000000))
{
GetModuleFileNameA(NULL, szPath, MAX_PATH);
p = strrchr(szPath, '\\');
//printf("szPath=%s\n", szPath);
OutputDebugString("what the hell\n");
if (!_stricmp(p + 1, DEF_PROCESS_NAME))
return 1;
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
#ifdef __cplusplus
extern "C" {
#endif // DEBUG
__declspec(dllexport) void HookStart()
{
printf("hookstart function\n");
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
if (g_hHook == NULL)
printf("failed to install the keyboard hook");
else
{
printf("Succeed in installing the keyboard hook");
}
}
__declspec(dllexport) void HookStop()
{
if (g_hHook)
{
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
printf("hookstop function\n");
}
#ifdef __cplusplus
}
#endif // DEBUG
The calling application code:
// hkeybi.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include<stdio.h>
#include<conio.h>
#include<Windows.h>
#define DEF_DLL_NAME "KeyHook.dll"
#define DEF_HOOKSTART "HookStart"
#define DEF_HOOKSTOP "HookStop"
typedef void(*PFN_HOOKSTART)();
typedef void(*PFN_HOOKSTOP)();
int main()
{
int ch = -1;
HMODULE hDll = NULL;
PFN_HOOKSTART HookStart = NULL;
PFN_HOOKSTOP HookStop = NULL;
hDll = LoadLibraryA(DEF_DLL_NAME);
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
if ((HookStart != NULL) && (HookStop != NULL))
printf("hello start\n");
HookStart();
printf("press q to exit!\n");
while (_getch() != 'q');
HookStop();
FreeLibrary(hDll);
return 0;
}
When I run the app,after I input several words,it will go down. I spent long time in solving the problem.
There are several problems in your KeyboardProc function. The first one being that your shift variable is assigned instead of tested:
if (shift = 0)
The variable is assigned 0, the condition is therefore always false. Effectively, the only code executed after the test is the return CallNextHookEx(...). If the condition would be true, you may run into problems because the GetModuleFileNameA result is not tested. In case of an error, the following strrchr will likely fail and the p pointer will be NULL. This will result in a crash at the _stricmp. And why do you specifically use the ANSI version of GetModuleFileName, are you sure you're not using Unicode? Finally, returning the hook proc without calling CallNextHookEx is a bad idea. Here's what the documentation says:
If code is greater than or equal to zero, and the hook procedure did
not process the message, it is highly recommended that you call
CallNextHookEx and return the value it returns

Creating MFC dialog to let the user choose file path

As title says, I am trying to create MFC dialog-based app to let the user select the file destination folder.
I am doing this by using CMFCShellTreeCtrl. However, somehow there is only one root item which is Desktop. I would like to see all items like My Computer, C drive etc.
Could you please tell me what's wrong and how do I fix it. Thanks.
Edit: Sorry I forgot to post my code. (Very simple dialog for browsing)
// BrowseDlg.cpp : implementation file
//
#include "stdafx.h"
#include "MFC Grab to FTP.h"
#include "BrowseDlg.h"
#include "afxdialogex.h"
// CBrowseDlg dialog
IMPLEMENT_DYNAMIC(CBrowseDlg, CDialog)
CBrowseDlg::CBrowseDlg(CWnd* pParent /*=NULL*/)
: CDialog(CBrowseDlg::IDD, pParent)
{
}
CBrowseDlg::~CBrowseDlg()
{
}
void CBrowseDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_PATH, m_PathTree);
}
BEGIN_MESSAGE_MAP(CBrowseDlg, CDialog)
ON_NOTIFY(TVN_SELCHANGED, IDC_PATH, &CBrowseDlg::OnTvnSelchangedMfcshelltree1)
ON_BN_CLICKED(IDOK, &CBrowseDlg::OnBnClickedOk)
END_MESSAGE_MAP()
// CBrowseDlg message handlers
BOOL CBrowseDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
m_PathTree.Expand(m_PathTree.GetRootItem(),TVE_EXPAND);
return TRUE; // return TRUE unless you set the focus to a control
}
void CBrowseDlg::OnTvnSelchangedMfcshelltree1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
}
void CBrowseDlg::OnBnClickedOk()
{
m_PathTree.GetItemPath(path,m_PathTree.GetSelectedItem());
// TODO: Add your control notification handler code here
CDialog::OnOK();
}
//
#pragma once
#include "afxshelltreectrl.h"
// CBrowseDlg dialog
// header file
class CBrowseDlg : public CDialog
{
DECLARE_DYNAMIC(CBrowseDlg)
public:
CBrowseDlg(CWnd* pParent = NULL); // standard constructor
virtual ~CBrowseDlg();
// Dialog Data
enum { IDD = IDD_BROWSER };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnTvnSelchangedMfcshelltree1(NMHDR *pNMHDR, LRESULT *pResult);
CString path;
CMFCShellTreeCtrl m_PathTree;
afx_msg void OnBnClickedOk();
virtual BOOL OnInitDialog();
};
The problem is that CShellManager in not initialized. You have to do it manually. To initialize it you need to call CWinAppEx::InitShellManager() in OnInitInstance() of your CWinApp-derived class.
There is an alternative to using CMFCShellTreeCtrl, if you just want to pick a folder: IFileDialog.
This is my code for a select folder dialog (rather complicated, because I still want to support Windows XP using XFolderDialog):
HINSTANCE hEasyCTXP_DLL = NULL;
// Select Folder Dialog -- uses either CXFolderDialog or IFileDialog
// to open a folder picker dialog depending on OS version
// returns "" if Cancel was pressed or something else went wrong
// Note: This is just multi-byte code and not yet unicode compatible!
BOOL SelectFolder(LPSTR sFolder, LPCTSTR sTitle = "Choose Folder")
{
OSVERSIONINFOEX osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(osvi);
if (GetVersionEx((OSVERSIONINFO *)&osvi) && osvi.dwMajorVersion >= 6) // Vista or higher?
{
if (!(hEasyCTXP_DLL = LoadLibrary("XPHelper.dll")))
{
AfxMessageBox("Error opening Select Folder dialog. XPHelper.dll may not be installed properly.");
return FALSE;
}
else
{
BOOL (__cdecl *pSelectFolder)(LPSTR, LPCTSTR);
pSelectFolder = (BOOL (__cdecl *)(LPSTR, LPCTSTR))GetProcAddress(hEasyCTXP_DLL, "SelectFolder");
if (pSelectFolder)
{
return (*pSelectFolder)(sFolder, sTitle);
}
else
{
AfxMessageBox("Error opening Select Folder dialog. (SelectFolder() function entry point not found.)");
return FALSE;
}
}
}
else // XP
{
CXFolderDialog dlg(sFolder);
dlg.SetTitle(sTitle);
if (dlg.DoModal() == IDOK)
{
CString csPath = dlg.GetPath();
strcpy(sFolder, (LPCTSTR)csPath);
return TRUE;
}
else
return FALSE;
}
}
Here comes the function I provide in XPHelper.dll. This dll is delay loaded only, if Vista or later OS is used, so the main program won't fail because of missing dependencies in Windows XP:
// sFolder should be at least MAX_FILE in size!
extern "C" BOOL SelectFolder(LPSTR sFolder, LPCTSTR sTitle)
{
BOOL bRet = TRUE;
IFileDialog *pfd;
LPWSTR pwstrPath;
HRESULT hr;
if (SUCCEEDED(hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd))))
{
DWORD dwOptions;
hr = pfd->GetOptions(&dwOptions);
if (SUCCEEDED(hr))
hr = pfd->SetOptions(dwOptions | FOS_PICKFOLDERS); // put IFileDialog in folder mode
if (SUCCEEDED(hr))
{
WCHAR wcTitle[MAX_PATH+1];
if (MultiByteToWideChar(CP_ACP, 0, sTitle, (int)min(strlen(sTitle), MAX_PATH), wcTitle, sizeof(MAX_PATH + 1)))
{
wcTitle[min(strlen(sTitle), MAX_PATH)] = '\0';
pfd->SetTitle(wcTitle); // Set dialog title
}
char *cp = sFolder;
WCHAR wcDefaultPath[MAX_PATH+1];
IShellItem *psi;
if (MultiByteToWideChar(CP_ACP, 0, cp, (int)strlen(cp), wcDefaultPath, MAX_PATH + 1))
{
wcDefaultPath[strlen(cp)] = '\0';
if (SUCCEEDED(::SHCreateItemFromParsingName(wcDefaultPath, NULL, IID_PPV_ARGS(&psi))))
{
hr = pfd->SetFileName(wcDefaultPath);
hr = pfd->SetFolder(psi);
psi->Release();
}
}
}
if (SUCCEEDED(hr))
hr = pfd->Show(AfxGetMainWnd()->GetSafeHwnd());
if (SUCCEEDED(hr))
{
IShellItem *psi;
if (SUCCEEDED(pfd->GetResult(&psi)))
{
if(SUCCEEDED(psi->GetDisplayName(SIGDN_FILESYSPATH, &pwstrPath)))
{
int nSize = (int)wcslen(pwstrPath);
WideCharToMultiByte(CP_ACP, 0, pwstrPath, nSize, sFolder, MAX_PATH+1, NULL, NULL);
sFolder[nSize] = '\0';
::CoTaskMemFree(pwstrPath);
}
else
{
AfxMessageBox("IShellItem::GetDisplayName() failed.");
bRet = FALSE;
}
psi->Release();
}
else
{
AfxMessageBox("IFileDialog failed.");
bRet = FALSE;
}
}
pfd->Release();
}
if(!SUCCEEDED(hr))
bRet = FALSE;
return bRet;
}

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

Resources