Support modal and modeless dialogs in ActiveX controls - c++

I want to create an ActiveX control supporting modal and modeless dialogs, which can be used in multiple IE9~IE11 tab pages at the same time. It means a pop-up of dialog box by the ActiveX control within current IE tab page won't prevent us from switching to other IE tab pages and these dialogs only appear in current IE tab page. First I created an ATL Dynamic-link library (DLL) application named as AtlActiveX using ATL/C++ by Visual Studio 2010. Then add an ActiveX control named as AtlActiveXCtl, specifying Allow merging of proxy/stub code and IObjectWithSite (IE object support) options. And then add a dialog resource named as IDD_DIALOG1. If not mentioned here, other options are left as default. Since windows/dialogs need to be supported, the member variable m_bWindowOnly has to be assigned in CAtlActiveXCtl constructor. After that add a message handler for WM_CREATE message, displaying dialog boxes in OnCreate function. The full code of AtlActiveXCtl.h is attached below.
I referred to another question Display Modal Dialog Box with ActiveX on StackOverflow. Three pop-up dialogs are tested in OnCreate() function.
#pragma once
#include "resource.h"
#include <atlctl.h>
#include "AtlActiveX_i.h"
using namespace ATL;
// Modeless dialog
class CModelessDialog : public CDialogImpl<CModelessDialog>
{
public:
enum { IDD = IDD_DIALOG1 };
BEGIN_MSG_MAP(CModelessDialog)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
MESSAGE_HANDLER(WM_CLOSE, OnClose)
END_MSG_MAP()
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL &)
{
return 0;
}
LRESULT OnClose(UINT, WPARAM, LPARAM, BOOL &)
{
DestroyWindow();
return 0;
}
};
// CAtlActiveXCtl
class ATL_NO_VTABLE CAtlActiveXCtl :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<IAtlActiveXCtl, &IID_IAtlActiveXCtl, &LIBID_AtlActiveXLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
public IOleControlImpl<CAtlActiveXCtl>,
public IOleObjectImpl<CAtlActiveXCtl>,
public IOleInPlaceActiveObjectImpl<CAtlActiveXCtl>,
public IViewObjectExImpl<CAtlActiveXCtl>,
public IOleInPlaceObjectWindowlessImpl<CAtlActiveXCtl>,
public ISupportErrorInfo,
public IObjectWithSiteImpl<CAtlActiveXCtl>,
public IQuickActivateImpl<CAtlActiveXCtl>,
public IDataObjectImpl<CAtlActiveXCtl>,
public IProvideClassInfo2Impl<&CLSID_AtlActiveXCtl, NULL, &LIBID_AtlActiveXLib>,
public CComCoClass<CAtlActiveXCtl, &CLSID_AtlActiveXCtl>,
public CComControl<CAtlActiveXCtl>
{
public:
CAtlActiveXCtl()
{
m_bWindowOnly = 1;
}
DECLARE_OLEMISC_STATUS(OLEMISC_RECOMPOSEONRESIZE |
OLEMISC_CANTLINKINSIDE |
OLEMISC_INSIDEOUT |
OLEMISC_ACTIVATEWHENVISIBLE |
OLEMISC_SETCLIENTSITEFIRST
)
DECLARE_REGISTRY_RESOURCEID(IDR_ATLACTIVEXCTL)
BEGIN_COM_MAP(CAtlActiveXCtl)
COM_INTERFACE_ENTRY(IAtlActiveXCtl)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IViewObjectEx)
COM_INTERFACE_ENTRY(IViewObject2)
COM_INTERFACE_ENTRY(IViewObject)
COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY(IOleInPlaceObject)
COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY(IOleInPlaceActiveObject)
COM_INTERFACE_ENTRY(IOleControl)
COM_INTERFACE_ENTRY(IOleObject)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY(IQuickActivate)
COM_INTERFACE_ENTRY(IDataObject)
COM_INTERFACE_ENTRY(IProvideClassInfo)
COM_INTERFACE_ENTRY(IProvideClassInfo2)
COM_INTERFACE_ENTRY(IObjectWithSite)
END_COM_MAP()
BEGIN_PROP_MAP(CAtlActiveXCtl)
PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
// Example entries
// PROP_ENTRY_TYPE("Property Name", dispid, clsid, vtType)
// PROP_PAGE(CLSID_StockColorPage)
END_PROP_MAP()
BEGIN_MSG_MAP(CAtlActiveXCtl)
CHAIN_MSG_MAP(CComControl<CAtlActiveXCtl>)
DEFAULT_REFLECTION_HANDLER()
MESSAGE_HANDLER(WM_CREATE, OnCreate)
END_MSG_MAP()
// Handler prototypes:
// LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
// LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
// LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);
// ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid)
{
static const IID* const arr[] =
{
&IID_IAtlActiveXCtl,
};
for (int i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
{
if (InlineIsEqualGUID(*arr[i], riid))
return S_OK;
}
return S_FALSE;
}
// IViewObjectEx
DECLARE_VIEW_STATUS(VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE)
// IAtlActiveXCtl
public:
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
IOleInPlaceFrame *pOleInPlaceFrame = NULL;
IOleInPlaceUIWindow *pOleInPlaceUIwindow = NULL;
IOleInPlaceSite *pOleInPlaceSite = NULL;
OLEINPLACEFRAMEINFO oleInPlaceFrameInfo;
oleInPlaceFrameInfo.cb = sizeof(oleInPlaceFrameInfo);
HRESULT hr = m_spClientSite->QueryInterface(IID_IOleInPlaceSite, (LPVOID *)&pOleInPlaceSite);
if (hr != S_OK)
return S_OK;
RECT rc1, rc2;
hr = pOleInPlaceSite->GetWindowContext(&pOleInPlaceFrame, &pOleInPlaceUIwindow, &rc1, &rc2, &oleInPlaceFrameInfo);
{
HWND hWndTop = NULL;
pOleInPlaceSite->GetWindow(&hWndTop);
pOleInPlaceFrame->EnableModeless(TRUE);
::EnableWindow(hWndTop, FALSE);
//[1] Message box, cannot switch tab page
MessageBox(L"Hello, world!", L"MSG", MB_OK);
//[2] Modal dialog, cannot switch tab page
CSimpleDialog<IDD_DIALOG1> dlg;
dlg.DoModal(hWndTop);
//[3] Modeless dialog, can switch tab page, but always stays on top of IE browser
CModelessDialog *pDlg = new CModelessDialog;
pDlg->Create(hWndTop);
pDlg->ShowWindow(SW_SHOWNORMAL);
pOleInPlaceFrame->EnableModeless(FALSE);
::EnableWindow(hWndTop, TRUE);
}
return 0;
}
HRESULT OnDraw(ATL_DRAWINFO& di)
{
RECT& rc = *(RECT*)di.prcBounds;
// Set Clip region to the rectangle specified by di.prcBounds
HRGN hRgnOld = NULL;
if (GetClipRgn(di.hdcDraw, hRgnOld) != 1)
hRgnOld = NULL;
bool bSelectOldRgn = false;
HRGN hRgnNew = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
if (hRgnNew != NULL)
bSelectOldRgn = (SelectClipRgn(di.hdcDraw, hRgnNew) != ERROR);
Rectangle(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom);
SetTextAlign(di.hdcDraw, TA_CENTER|TA_BASELINE);
LPCTSTR pszText = _T("AtlActiveXCtl");
TextOut(di.hdcDraw,
(rc.left + rc.right) / 2,
(rc.top + rc.bottom) / 2,
pszText,
lstrlen(pszText));
if (bSelectOldRgn)
SelectClipRgn(di.hdcDraw, hRgnOld);
return S_OK;
}
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
};
OBJECT_ENTRY_AUTO(__uuidof(AtlActiveXCtl), CAtlActiveXCtl)
Modal and modeless dialogs need to be supported in ActiveX controls within IE11 tab pages just like in plain Win32 GUI applications. Can anyone kindly help me to solve this problem?

Related

How to create a c++ class that can send and receive window message in MFC

I just want to create a C++ class which can send and process window messages in MFC.
I had tried many ways that I obtained from the internet, roughly like this:
create class myClass::public CWnd using the class Wizard from visual studio 2019 community version.
Use the following code in constructor
CString wcn = ::AfxRegisterWndClass(NULL);
BOOL created = this->CreateEx(0, wcn, _T("YourExcellentWindowClass"), 0, { 0, 0, 0, 0 }, this->GetParent(), /*HWND_MESSAGE,*/ 0, 0);
Setup Message map
BEGIN_MESSAGE_MAP(myClass, CWnd)
ON_MESSAGE(WM_USER + 1, DoNOOP)
END_MESSAGE_MAP()
write message process function:
afx_msg LRESULT myClass::DoNOOP(WPARAM wParam, LPARAM lParam)
{
AfxMessageBox(_T("Get Reaaady for a Ruuummmmmmmbllllle!"));
return LRESULT(true);
}
Sent in message from parent window:
myClass* hmw = new myClass();
::SendMessage(hmw->m_hWnd, WM_USER + 1, 0, 0);
It passed Build process. However, when I run it in debugging mode, SendMessge triggered a Breakpoint in here:
_AFXWIN_INLINE CWnd* CWnd::GetParent() const
{ ASSERT(::IsWindow(m_hWnd)); return CWnd::FromHandle(::GetParent(m_hWnd)); }
which is in afxwin2.inl
I really don't know what to do now.
Please help. I will really appreciate it.
The complete class declaration:
#pragma once
class CMyMessageOnlyWindowClass : public CWnd
{
DECLARE_DYNAMIC(CMyMessageOnlyWindowClass)
public:
CMyMessageOnlyWindowClass();
virtual ~CMyMessageOnlyWindowClass();
protected:
DECLARE_MESSAGE_MAP()
afx_msg LRESULT DoNOOP(WPARAM wParam, LPARAM lParam);
};
Implementation:
#include "pch.h"
#include "MFCmsg.h"
#include "CMyMessageOnlyWindowClass.h"
// CMyMessageOnlyWindowClass
IMPLEMENT_DYNAMIC(CMyMessageOnlyWindowClass, CWnd)
CMyMessageOnlyWindowClass::CMyMessageOnlyWindowClass()
{
CString wcn = ::AfxRegisterWndClass(NULL);
BOOL created = this->CreateEx(0, wcn, _T("YourExcellentWindowClass"), 0, { 0, 0, 0, 0 }, this->GetParent(), /*HWND_MESSAGE,*/ 0, 0);
}
CMyMessageOnlyWindowClass::~CMyMessageOnlyWindowClass()
{
}
BEGIN_MESSAGE_MAP(CMyMessageOnlyWindowClass, CWnd)
ON_MESSAGE(WM_USER + 1, DoNOOP)
END_MESSAGE_MAP()
// CMyMessageOnlyWindowClass message handlers
afx_msg LRESULT CMyMessageOnlyWindowClass::DoNOOP(WPARAM wParam, LPARAM lParam)
{
AfxMessageBox(_T("Get Reaaady for a Ruuummmmmmmbllllle!"));
return LRESULT(true);
}
The main Window is a dialog with only OK and Cancel button.
When click on OK,
void CMFCmsgDlg::OnBnClickedOk()
{
CMyMessageOnlyWindowClass* hmw = new CMyMessageOnlyWindowClass();
hmw->SendMessage(WM_USER + 1);
CDialogEx::OnOK();
}
and this will trigger a breakpoint. Please help.

Edit cell in list control by clicking in MFC -- RESOLVED

Problem Dsecription: Application to show an editable table in new window. e.g.: In the following table, I want to be able to edit the register values. I tried to write the application using MFC on Visual Studio 2015 in C++
===========================================================================
I'm working on a MFC application using Visual Studio 2015 in C++
I've created a Dialog Editor In which I want to show a list of registers (type list control) with two columns, one that specifies the registers numbers and a second column to show their values. I've started with the simplest case, and created such list with only one register successfully with the following code:
.cpp file:
#include "stdafx.h"
#include "MFCApplication4.h"
#include "MFCApplication4Dlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
CMFCApplication4Dlg::CMFCApplication4Dlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_MFCAPPLICATION4_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMFCApplication4Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST1, listCtrl);
}
BEGIN_MESSAGE_MAP(CMFCApplication4Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &CMFCApplication4Dlg::OnLvnItemchangedList1)
END_MESSAGE_MAP()
BOOL CMFCApplication4Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
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);
}
}
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
listCtrl.InsertColumn(0, _T("Register Number"));
listCtrl.InsertColumn(1, _T("Register Value"));
listCtrl.SetColumnWidth(0, 200);
listCtrl.SetColumnWidth(1, 400);
int nItem = listCtrl.InsertItem(0, L"0x00");
listCtrl.SetItemText(nItem, 1, L"01000001");
return TRUE; // return TRUE unless you set the focus to a control
}
void CMFCApplication4Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
void CMFCApplication4Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
HCURSOR CMFCApplication4Dlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CMFCApplication4Dlg::OnLvnItemchangedList1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
}
.h file:
#pragma once
#include "afxcmn.h"
#include "afxwin.h"
class CMFCApplication4Dlg : public CDialogEx
{
public:
CMFCApplication4Dlg(CWnd* pParent = NULL); // standard constructor
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_MFCAPPLICATION4_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
protected:
HICON m_hIcon;
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnLvnItemchangedList1(NMHDR *pNMHDR, LRESULT *pResult);
CListCtrl listCtrl;
};
I would like to be able to edit the value of the register by clicking on it once or twice and typing the new value. I tried adding the following function to the header beneath the last line of CListCtrl listCtrl:
afx_msg void RightButtonClick(WPARAM wParam, LPARAM lParam, CPoint point);
the implementation of the function in .cpp file:
afx_msg void CMFCApplication4Dlg::RightButtonClick(WPARAM wParam, LPARAM lParam, CPoint point)
{
//listCtrl.SetFocus();
//CEdit* itemToEdit = listCtrl.EditLabel(1);
// The string replacing the text in the edit control.
LPCTSTR lpszmyString = _T("custom label!");
// If possible, replace the text in the label edit control.
CEdit* pEdit = listCtrl.GetEditControl();
if (pEdit != NULL)
{
pEdit->SetWindowText(lpszmyString);
}
}
and I added the following message to the message map in the .cpp file (last line in the block BEGIN_MESSAGE_MAP):
ON_WM_RBUTTONDBLCLK(LVN_ENDLABELEDIT, IDC_LIST1, &CMFCApplication4Dlg::RightButtonClick)
but unfortunately that's what I'm getting no results.
I've tried reading some more, and I've spent few hours trying to fix it, but haven't succeeded. I've tried to follow the answers to similar posts which are listed below:
Make single items editable in a list control (C++, MFC)
and
How to edit cell in listcontrol mfc?
but I wasn't able to implement the suggestions that were written there, I didn't understand how to connect all the parts of the answers. Additionally, I tried using this guide:
https://www.tutorialspoint.com/mfc/mfc_messages_events.htm
Unfortunately, nothing helped. I have a feeling that I'm missing something fundamental, perhaps I'm not handling the messages as I should. I'd appreciate it if anyone could explain to me what I'm missing and how to fix it.
Please let me know if my question isn't clear enough or if there's any other problem with it, so I'll edit it and get better.
Thank you very much for your time and attention.
I used this guide which solved my problem

How to use 'SHCreateDefaultContextMenu' to display the shell context menu for multiple files from different folders

I want to create a function which displays the shell context menu for a given file list (as string),
but the files are from different location on the shell namespace.
(It is the same the context menu which is shown when you right click selected files in the search results area in Windows Explorer)
I saw that the issue was discussed in couple of questions before, but none of them gave a solution:
1. Showing a Windows context menu for multiple items
2. Display file properties dialog for files in different directories
3. How to display system context menu for multiple files in different folders?
However, I noticed that the comments there are referring to this blog (2007), it is written by the author of a file manager for windows. (his file manager software defenetly shows that he did it)
He mentions overriding IShellFolder::GetUIObjectOf method and implementing a new IDataObject interface.
13 years have been passed since then, so I'm thinking using new functions intorduced in Windows Vista:
1. I'm trying to use SHCreateDefaultContextMenu instead CDefFolderMenu_Create2.
2. Maybe the function SHCreateDataObject can be used instead of implementing a new DataObject?
I'm not a COM expert, and I struggle to do those tasks,
Hope someone will fill the missing blocks.
I have put here my effort.
It is implemented as a dll to make it easier for other to use and share.
#include <Windows.h>
#include <shlobj.h>
#define MIN_SHELL_ID 1
#define MAX_SHELL_ID 30000
extern "C"
{
HRESULT ProcessCMCommand(LPCONTEXTMENU pCM, UINT idCmdOffset, HWND hWnd);
// Try to convert pICv1 into IContextMenu2 or IcontextMenu3
// In case of success, release pICv1.
HRESULT UpgradeContextMenu(
LPCONTEXTMENU pICv1, // In: The context menu version 1 to be converted.
void** ppCMout, // Out: The new context menu (or old one in case
// the convertion could not be done)
int* pcmType) // Out: The version number.
{
HRESULT hr;
// Try to get version 3 first.
hr = pICv1->QueryInterface(IID_IContextMenu3, ppCMout);
if (NOERROR == hr)
{
*pcmType = 3;
}
else
{
hr = pICv1->QueryInterface(IID_IContextMenu2, ppCMout);
if (NOERROR == hr)
*pcmType = 2;
}
if (*ppCMout)
{
pICv1->Release(); // free version 1
}
else { // only version 1 is supported
*pcmType = 1;
*ppCMout = pICv1;
hr = NOERROR;
}
return hr;
}
// Global ref to the context menu to be used while
// the window messages are being handled.
LPCONTEXTMENU2 g_pIContext_v2 = NULL;
// Handle window messages.
// The messages: WM_DRAWITEM, WM_MEASUREITEM, WM_INITMENUPOPUP, WM_MENUSELECT
// should be redirected to this function.
__declspec(dllexport) void OnWindowMessage(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
if (g_pIContext_v2 == NULL) // Version 1 is used.
return;
switch (msg)
{
case WM_DRAWITEM:
case WM_MEASUREITEM:
if (wp)
break;
case WM_INITMENUPOPUP:
g_pIContext_v2->HandleMenuMsg(msg, wp, lp);
break;
case WM_MENUSELECT:
// Get the help text and update the status bar , Not implemented yet.
break;
}
}
// Shows the shell context menu for the given file list at the given position.
// * error checking is not implemented here.
__declspec(dllexport) HRESULT ShowContextMenu(
HWND hWnd,
int xPos,
int yPos,
wchar_t **fullFileNames, // path + file name
size_t fullFileNamesSize
)
{
HRESULT hr;
// absolute pidl of the files.
LPITEMIDLIST *pidlChilds;
pidlChilds = new LPITEMIDLIST[fullFileNamesSize];
// Get the desktop folder.
LPSHELLFOLDER pDesktop;
SHGetDesktopFolder(&pDesktop);
size_t pidlChildsSize = 0;
for (size_t i = 0; i < fullFileNamesSize; i++)
{
PIDLIST_ABSOLUTE pidl_absolute;
UINT attributes;
hr = SHParseDisplayName(fullFileNames[i], NULL, &pidl_absolute, SFGAO_SYSTEM, (SFGAOF*)&attributes);
pidlChilds[pidlChildsSize] = pidl_absolute;
pidlChildsSize++;
}
// Probably I need to use the new class in place of the pDesktop arg.
DEFCONTEXTMENU dcm =
{
hWnd,
NULL, // contextMenuCB
NULL, // pidlFolder,
pDesktop,
pidlChildsSize,
(LPCITEMIDLIST*)pidlChilds,
NULL, // *punkAssociationInfo
NULL, // cKeys
NULL // *aKeys
};
IContextMenu *pCMv1 = NULL; // Version 1
IContextMenu *pCMx = NULL; // Versions 2 or 3
hr = SHCreateDefaultContextMenu(&dcm, IID_IContextMenu, (void**)&pCMv1);
int pcmType = 0;
hr = UpgradeContextMenu(pCMv1, (void**)&pCMx, &pcmType);
if (pcmType > 1) // pCMv1 was feed inside UpgradeContextMenu function.
{
g_pIContext_v2 = (LPCONTEXTMENU2)pCMx;
}
HMENU hMenu = CreatePopupMenu();
hr = pCMx->QueryContextMenu(hMenu, 0, MIN_SHELL_ID, MAX_SHELL_ID, CMF_EXPLORE | CMF_CANRENAME);
UINT cmdID = TrackPopupMenu(hMenu,
TPM_LEFTALIGN | TPM_RETURNCMD,
xPos, yPos, 0, hWnd, 0);
if (cmdID >= MIN_SHELL_ID && cmdID <= MAX_SHELL_ID)
ProcessCMCommand(pCMx,
cmdID - MIN_SHELL_ID, // offset passed
hWnd
);
// Free resources
for (size_t i = 0; i < pidlChildsSize; i++)
CoTaskMemFree(pidlChilds[i]);
pDesktop->Release();
pCMx->Release();
delete[] pidlChilds;
g_pIContext_v2 = NULL;
return hr;
}
HRESULT ProcessCMCommand(LPCONTEXTMENU pCM, UINT idCmdOffset, HWND hWnd)
{
CMINVOKECOMMANDINFOEX ici;
ZeroMemory(&ici, sizeof(ici));
ici.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
ici.hwnd = hWnd;
ici.fMask = CMIC_MASK_UNICODE;
ici.lpVerb = MAKEINTRESOURCEA(idCmdOffset);
ici.lpVerbW = MAKEINTRESOURCEW(idCmdOffset);
ici.nShow = SW_SHOWNORMAL;
return pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
}
/*
class CIShellFolderSpy : public IShellFolder
{
public:
// constructor
CIShellFolderSpy(LPSHELLFOLDER sf) {
sf->AddRef();
m_iSF = sf;
}
~CIShellFolderSpy() { m_iSF->Release(); }
// IUnknown methods --------
STDMETHOD_(ULONG, AddRef)() {
return m_iSF->AddRef();
}
STDMETHOD_(ULONG, Release)() {
return m_iSF->Release();
}
STDMETHOD_(HRESULT, QueryInterface(REFIID riid, void **ppvObject)) {
return m_iSF->QueryInterface(riid, ppvObject);
}
// IShellFolder methods ----
STDMETHOD_(HRESULT, BindToObject)(
PCUIDLIST_RELATIVE pidl,
IBindCtx *pbc,
REFIID riid,
void **ppv) {
return m_iSF->BindToObject(pidl, pbc, riid, ppv);
}
STDMETHOD_(HRESULT, BindToStorage)(
PCUIDLIST_RELATIVE pidl,
IBindCtx *pbc,
REFIID riid,
void **ppv) {
return m_iSF->BindToStorage(pidl, pbc, riid, ppv);
}
STDMETHOD_(HRESULT, CompareIDs)(
LPARAM lParam,
PCUIDLIST_RELATIVE pidl1,
PCUIDLIST_RELATIVE pidl2) {
return m_iSF->CompareIDs(lParam, pidl1, pidl2);
}
STDMETHOD_(HRESULT, CreateViewObject)(
HWND hwndOwner,
REFIID riid,
void **ppv) {
return m_iSF->CreateViewObject(hwndOwner, riid, ppv);
}
STDMETHOD_(HRESULT, EnumObjects)(
HWND hwnd,
SHCONTF grfFlags,
IEnumIDList **ppenumIDList) {
return m_iSF->EnumObjects(hwnd, grfFlags, ppenumIDList);
}
STDMETHOD_(HRESULT, GetAttributesOf)(
UINT cidl,
PCUITEMID_CHILD_ARRAY apidl,
SFGAOF *rgfInOut) {
return m_iSF->GetAttributesOf(cidl, apidl, rgfInOut);
}
STDMETHOD_(HRESULT, GetDisplayNameOf)(
PCUITEMID_CHILD pidl,
SHGDNF uFlags,
STRRET *pName) {
return m_iSF->GetDisplayNameOf(pidl, uFlags, pName);
}
STDMETHOD_(HRESULT, ParseDisplayName)(
HWND hwnd,
IBindCtx *pbc,
LPWSTR pszDisplayName,
ULONG *pchEaten,
PIDLIST_RELATIVE *ppidl,
ULONG *pdwAttributes
) {
return m_iSF->ParseDisplayName(hwnd,
pbc,
pszDisplayName,
pchEaten,
ppidl,
pdwAttributes);
}
STDMETHOD_(HRESULT, SetNameOf)(
HWND hwnd,
PCUITEMID_CHILD pidl,
LPCWSTR pszName,
SHGDNF uFlags,
PITEMID_CHILD *ppidlOut
) {
return m_iSF->SetNameOf(
hwnd,
pidl,
pszName,
uFlags,
ppidlOut);
}
virtual HRESULT STDMETHODCALLTYPE GetUIObjectOf(
HWND hwndOwner,
UINT cidl,
LPCITEMIDLIST *apidl,
REFIID riid,
UINT *rgfReserved,
void **ppv)
{
if (InlineIsEqualGUID(riid, IID_IDataObject))
{
// i ignore the pidl array supplied and return the good data object
return m_pMDObj->QueryInterface(riid, ppv);
}
else {
// i've seen IDropTarget requests, let base handle them
return m_iSF->GetUIObjectOf(hwndOwner, cidl, apidl, riid, rgfReserved, ppv);
}
}
protected:
LPSHELLFOLDER m_iSF;
CMultiDataObject* m_pMDObj;
}; // end of class
*/
}

EnumChildWindows never calls its callback

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

Add tooltip to a CStatic

I haven't been able to find a concise chunk of code that allows me to add/display tooltips to a CStatic (and CLed) control. Obviously, the standard code to do so does not apply for this type of control. Can someone post code snippets?
I hope this code will solve your problem .One important thing make NOTIFY property of CStatic =TRUE.
if( !m_ToolTip.Create(this))
{
TRACE0("Unable to create the ToolTip!");
}
else
{
CWnd* pWnd = GetDlgItem(IDC_STATIC_MASTER_PWD);
m_ToolTip.AddTool(pWnd,"Ok");
m_ToolTip.Activate(TRUE);
}
Let me know if any problem.
I don't know if this is still needed, but here's what I used to solve the problem:
just add SS_NOTIFY to dwStyle when creating the static label. (or simply set "Nofity" "True" in the properties). That worked fine for me.
When I add CStatic on Dialog based autocreated mfc application, tooltips don't show until I add RelayEvent in pretranslate dialog message
BOOL CTooltipStaticDlg::PreTranslateMessage(MSG* pMsg)
{
m_ToolTip.RelayEvent(pMsg);
return CDialog::PreTranslateMessage(pMsg);
}
I've had success with multiline tooltips using this simple class:
Create a class for ToolTips:
class ToolTip
{
public:
static HWND CreateToolTip(int toolID, HWND hDlg, UINT id);
};
Next, implement a tooltip creation function:
HWND ToolTip::CreateToolTip(int toolID, HWND hDlg, UINT id)
{
if (!toolID || !hDlg || !id)
{
return FALSE;
}
CString strTTText;
strTTText.LoadString( id );
// Get the window handle of the control to attach the TT to.
HWND hwndTool = ::GetDlgItem(hDlg, toolID);
// Create the tooltip window
HWND hwndTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
WS_POPUP |TTS_ALWAYSTIP,// | TTS_BALLOON,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
hDlg, NULL,
AfxGetInstanceHandle() , NULL);
if (!hwndTool || !hwndTip)
{
return (HWND)NULL;
}
// Associate the tooltip with the tool.
TOOLINFO toolInfo = { 0 };
toolInfo.cbSize = sizeof(toolInfo);
toolInfo.hwnd = hDlg;
toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
toolInfo.uId = (UINT_PTR)hwndTool;
toolInfo.lpszText = (char*)(LPCTSTR)strTTText;
::SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
::SendMessageA(hwndTip, TTM_SETMAXTIPWIDTH, 0, 40); // force multi-line
return hwndTip;
}
Call it somewhere in your InitDialog:
CMyDialog::InitDialog()
{
ToolTip::CreateToolTip( PickAUniqueNumber, m_hWnd, IDS_MY_RESOURCE_STRING );
}
I've had on my dialog label with assigned custom ID IDC_PATH. I needed to turn on Notify flag (SS_NOTIFY) of the label and I needed to overload CWnd method OnToolHitTest and handle tooltip hit test like this:
INT_PTR CPath::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{
INT_PTR r = CWnd::OnToolHitTest(point,pTI);
this->ClientToScreen(&point);
CRect rcLbl;
GetDlgItem(IDC_PATH)->GetWindowRect(&rcLbl);
if( rcLbl.PtInRect(point) )
{
pTI->uFlags |= TTF_IDISHWND;
pTI->uFlags &= ~TTF_NOTBUTTON;
pTI->uId = (UINT_PTR)GetDlgItem(IDC_PATH)->m_hWnd;
return IDC_PATH;
}
return r;
}
Then my dialog started to receive TTN_NEEDTEXT notification, which I handled and dynamicaly set text for tooltip.
BOOL CPath::OnTtnNeedText(UINT id, NMHDR *pNMHDR, LRESULT *pResult)
{
UNREFERENCED_PARAMETER(id);
TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
UINT_PTR nID = pNMHDR->idFrom;
BOOL bRet = FALSE;
if (pTTT->uFlags & TTF_IDISHWND)
{
// idFrom is actually the HWND of the tool
nID = ::GetDlgCtrlID((HWND)nID);
if(nID == IDC_PATH)
{
pTTT->lpszText = (LPSTR)(LPCTSTR)m_FullDestPath;
bRet = TRUE;
}
}
*pResult = 0;
return bRet;
}