Change Text of an Menu Item - c++

I'm trying to change the Text of a MenuItem created with the winAPI. I tried following:
HMENU menu = LoadMenu(_hInstance, MAKEINTRESOURCE(IDR_MENU1)); //getting the Menu
LPWSTR test = L"test";
MENUITEMINFO mii{ sizeof(MENUITEMINFO) };
if (!GetMenuItemInfo(menu, ID_USER_NAME, false, &mii))
{
return ; // not getting an error here
}
mii.fMask = MIIM_TYPE; // tried with MIIM_TYPE and MIIM_STRING
mii.fType = MFT_STRING;
mii.dwTypeData = test;
if (!SetMenuItemInfo(menu, ID_USER_NAME, false, &mii))
{
return; // no error here either
}
DrawMenuBar(_hWnd);
But it doesn't work it's is not giving an error either, so I guess I just forgot something ?

You will need to get the handle to the current menu displayed on the window and modify that, instead of loading a menu using LoadMenu().

Related

HtmlHelp MS API search string

Does any one in 2017 knows how to implement calling HtmlHelp function that will open .chm file with "Search" pane and query string in it's listbox and will be able to execute this query?
I try following:
HH_FTS_QUERY query;
query.cbStruct = sizeof(HH_FTS_QUERY);
query.fUniCodeStrings = TRUE;
query.pszSearchQuery = szSearchStr;
query.iProximity = HH_FTS_DEFAULT_PROXIMITY;
query.fStemmedSearch = TRUE;
query.fTitleOnly = TRUE;
query.fExecute = TRUE;
query.pszWindow = NULL;
HWND hHelpWnd = ::HtmlHelp(m_hWnd, szFile, HH_DISPLAY_SEARCH, (DWORD_PTR)&query);
but the query in query.pszSearchQuery won't be executed. I have opened .chm file with query.pszSearchQuery in "Search" pane's listbox on my screen, but I have to click "List Topics" myself to show results of search.
With the help of HTMLHelp API - VBA, VB6 and VB2003, i will try to reply.
I believe there no API function to list topics in vc++. You have send button click event to launched help window.
LRESULT OnSearch(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
TCHAR szBuf[128];
GetDlgItem(IDC_EDIT1).GetWindowText(szBuf, sizeof(szBuf));
if (!_tcslen(szBuf))
return 0;
//
// First, invoke HtmlHelp with HH_DISPLAY_SEARCH.
// It doesn't execute search, but it's need for showing 'search' tab.
//
HH_FTS_QUERY fq;
ZeroMemory(&fq, sizeof(fq));
fq.cbStruct = sizeof(fq);
fq.fUniCodeStrings = FALSE;
fq.pszSearchQuery = (LPCTSTR)szBuf;
fq.iProximity = HH_FTS_DEFAULT_PROXIMITY;
fq.fStemmedSearch = FALSE;
fq.fTitleOnly = FALSE;
fq.fExecute = FALSE;
fq.pszWindow = NULL;
HWND hwndHelp = ::HtmlHelp(NULL, _T("realplay.chm"), HH_DISPLAY_SEARCH, (DWORD)&fq);
//
// Find query combobox and set query text.
//
HWND hPaneWnd, hPaneLast, hTabWnd, hDlgWnd, hCtlWnd;
hPaneWnd = FindWindowEx(hwndHelp, NULL, _T("HH Child"), NULL);
for (;;) // last HH Child
{
hPaneLast = FindWindowEx(hwndHelp, hPaneWnd, _T("HH Child"), NULL); // last HH Child
if (!hPaneLast)
break;
hPaneWnd = hPaneLast;
}
ATLTRACE("hPaneWnd == %x", hPaneWnd);
hCtlWnd = FindWindowEx(hPaneWnd, NULL, _T("Button"), NULL); // skip Tab Control
//
// There are two types of interfaces:
//
// 1.
// Window hierarchy:
// + Main window
// + HH Child
// + Browser ...
// + HH Child <- second "HH Child" window
// - Edit <- we have to fill this edit
// - Buttons <- and press this buttons
// ...
if (hCtlWnd)
{
hCtlWnd = FindWindowEx(hPaneWnd, NULL, _T("Edit"), NULL); // skip Tab Control
// Set window text
ATLASSERT(hCtlWnd != NULL);
::SendMessage(hCtlWnd, WM_SETTEXT, 0, (LPARAM)szBuf); // fill it by our query
::SendMessage(hwndHelp, WM_COMMAND, 0xbc7, 0); // 0x3ee -- 'List Topics' button, it runs search
if (::SendMessage(GetDlgItem(IDC_CHECK1), BM_GETCHECK, 0, 0) == BST_CHECKED)
::SendMessage(hwndHelp, WM_COMMAND, 0xbbe, 0); // 0x3f1 -- 'Display' button, it shows first item
}
//2.
// Window hierarchy:
// + Main window
// + HH Child
// + Browser ...
// + HH Child <- second "HH Child" window
// + Tab Control
// + Dialog
// - Combobox <- we have to fill this combo
// - Buttons <- and press this buttons
// ...
else
{
hTabWnd = FindWindowEx(hPaneWnd, NULL, _T("SysTabControl32"), NULL); // skip Tab Control
hDlgWnd = FindWindowEx(hTabWnd, NULL, NULL, NULL); // skip dialog
TCHAR szClass[64];
GetClassName(hDlgWnd, szClass, sizeof(szClass));
ATLTRACE("hDlgWnd(1) == %x", hDlgWnd);
if (!_tcsstr(szClass, "#")) // Is it dialog?
hDlgWnd = FindWindowEx(hTabWnd, hDlgWnd, NULL, NULL); // skip dialog
hCtlWnd = FindWindowEx(hDlgWnd, NULL, _T("ComboBox"), NULL); // well, it's combobox
// Set window text
ATLASSERT(hCtlWnd != NULL);
::SendMessage(hCtlWnd, WM_SETTEXT, 0, (LPARAM)szBuf); // fill it by our query
//
// Run search and show first finded page
//
::SendMessage(hwndHelp, WM_COMMAND, 0x3ee, 0); // 0x3ee -- 'List Topics' button, it runs search
if (::SendMessage(GetDlgItem(IDC_CHECK1), BM_GETCHECK, 0, 0) == BST_CHECKED)
::SendMessage(hwndHelp, WM_COMMAND, 0x3f1, 0); // 0x3f1 -- 'Display' button, it shows first item
}
return 0;
}
EDIT:
You can get control ID of List Topic by spy++

Print a PDF file with MFC

In my app (MFC, C++) I have a button that creates a PDF file in a path.
Now I want to create another button that will print the pdf starting from the path and choosing some option like orientation and number of copies... but I'm not able to do that...
I saw that CPrintDialog shows the default dialog of the printer but I'm not able to attach the PDF file using the path.
I saw also the
ShellExecute(NULL, L"print", L"C:\\Documents\\1.pdf", NULL, NULL, SW_SHOWNORMAL);
that works but in this way I cannot choose any parameter...
How I can use the CPrintDialog to print an existing PDF that is in a path?
You have to use ShellExecuteEx and verb printto to get more control over printing:
SHELLEXECUTEINFO ShellInfo;
ZeroMemory(&ShellInfo, sizeof(SHELLEXECUTEINFO));
ShellInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShellInfo.lpVerb = L"printto";
ShellInfo.lpFile = L"C:\\Documents\\1.pdf";
ShellInfo.lpParameters = szPrinter;
ShellInfo.nShow = SW_SHOWNORMAL;
ShellInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_NOCLOSEPROCESS;
if(::ShellExecuteEx(&ShellInfo))
{
if((int)ShellInfo.hInstApp > 32)
{
if(ShellInfo.hProcess != NULL)
{
DWORD dwExitCode = STILL_ACTIVE;
while(dwExitCode == STILL_ACTIVE)
{
if(!::GetExitCodeProcess(ShellInfo.hProcess, &dwExitCode))
{
dwExitCode = 0;
}
}
::CloseHandle(ShellInfo.hProcess);
}
}
}
To get the printer name:
CPrintDialog dlg(TRUE);
if (dlg.DoModal() == IDOK)
{
CString sPrinterName = dlg.GetDeviceName();
}
I have solved with a workaround. Instead of use the ShellExecute, I draw all the thing that I want to print using a CDC object attached to the hDC of a CPrintDialog class. Rember to manage the size of the draw depending on printer DPI like here.
A snippet only to have an idea:
CPrintDialog printDialog(FALSE);
printDialog.GetDefaults();
printDialog.m_pd.Flags &= ~PD_RETURNDEFAULT;
DEVMODE* pDevMode = printDialog.GetDevMode();
pDevMode->dmFields = DM_ORIENTATION | DM_PAPERSIZE | DM_PRINTQUALITY ;
pDevMode->dmOrientation = DMORIENT_LANDSCAPE;
pDevMode->dmPaperSize = DMPAPER_A4;
::GlobalUnlock(printDialog.m_pd.hDevMode);
if (printDialog.DoModal() == IDOK)
{
CDC* pDC = new CDC;
pDC->Attach(printDialog.m_pd.hDC);
pDCPDF->StartDoc(_T(""));
pDCPDF->StartPage();
// ...
//draw what you want
// ...
pDCPDF->EndPage();
pDCPDF->EndDoc(); //this starts the printer
pDCPDF->DeleteDC();
}
Hope to reach soon the reputation of 15 to vote the answer of other questions.
Thanks to Andrew Komiagin answers.

TreeView add Shell Icons

I already created treeview where I can add some items. Basicly I want to tree-view all directories and files with icons associated to them.
So I have these functions:
Adding items to treeview
HTREEITEM AddItemToTree(HWND hwndTree, char *text, int nLevel)
{
TVINSERTSTRUCT tvins;
static HTREEITEM hPrev = (HTREEITEM)TVI_FIRST;
static HTREEITEM hRootItem = NULL;
static HTREEITEM hPrevLev2Item = NULL;
AddIconToTree(hwndTree, text); //////////// THIS IS THE FUNCTION BELOW...
tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIS_STATEIMAGEMASK;
tvi.iImage = 0;
tvi.iSelectedImage = 0;
tvi.pszText = GetFileNameFromPath(text);
tvins.hInsertAfter = hPrev;
tvins.item = tvi;
if(nLevel == 1)
{
tvins.hParent = TVI_ROOT;
}
else if(nLevel == 2)
{
tvins.hParent = hRootItem;
}
else
{
TVITEM tviSetup;
tviSetup.hItem = hPrev;
tviSetup.mask = TVIF_PARAM;
TVITEM * tviLocal = &tviSetup;
TreeView_GetItem(hwndTree, tviLocal);
if(nLevel > tviLocal->lParam)
{
tvins.hParent = hPrev;
}
else
{
HTREEITEM hPrevLocal = TreeView_GetParent(hwndTree, hPrev);
tviLocal->hItem = hPrevLocal;
TreeView_GetItem(hwndTree, tviLocal);
for(int i = nLevel; i <= tviLocal->lParam;)
{
HTREEITEM hPrevLocalTemp = TreeView_GetParent(hwndTree, hPrevLocal);
hPrevLocal = hPrevLocalTemp;
tviLocal->hItem = hPrevLocal;
TreeView_GetItem(hwndTree, tviLocal);
}
tviLocal->mask = TVIF_TEXT;
TreeView_GetItem(hwndTree, tviLocal);
tvins.hParent = hPrevLocal;
}
}
hPrev = (HTREEITEM)SendMessage(hwndTree, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);
if(hPrev == 0)
{
return false;
}
if(nLevel == 1)
{
hRootItem = hPrev;
}
return hPrev;
}
ADDING ICONS TO TREEVIEW:
int AddIconToTree(HWND hwndTree, char *strPath)
{
SHFILEINFO sfi;
memset(&sfi, 0, sizeof(sfi));
SHGetFileInfo(strPath, FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_USEFILEATTRIBUTES | SHGFI_SMALLICON);
int index = sfi.iIcon;
ICONINFO iconinfo;
GetIconInfo(sfi.hIcon, &iconinfo);
HBITMAP hBitmap = iconinfo.hbmColor;
DestroyIcon(sfi.hIcon);
himg = ImageList_Create(16, 16, ILC_COLOR32, 1, 1);
int iImageList = ImageList_Add(himg, hBitmap, NULL);
DeleteObject(hBitmap);
//TreeView_SetImageList(hwndTree, himg, TVSIL_NORMAL);
SendMessage(hwndTree, TVM_SETIMAGELIST, (WPARAM)TVSIL_NORMAL, (LPARAM)(HIMAGELIST)himg);
MessageBox(hwnd, strPath, "Path:", MB_OK); /* Because of this msgbox I found out what is
really happening, because without it everything I have seen when I run the program was
treeview with icon of the last file, which was folder...So blank icon.*/
return index;
}
My main problem is, that when I'm setting some icon, it is set not only for the one item as I'm expecting, but on all items in treeview. So basicly every item's icon is overwritten by new item's icon. By the way I know that if I want to get icon of folder, I need to use FILE_ATTRIBUTE_DIRECTORY...
So that's it.
Any help would be greatly appriciated!
Thank You in advance :-)
In your AddIconToTree function you're creating a brand new image list every time, which will only ever have one icon in it. You need to maintain the same image list and add additional icons to it rather than re-creating it for every item.
Alternatively, you can get a handle to the shell imagelist with the SHGetImageList function and then assign it to the tree directly. If you don't need to add any of your own images to the tree's image list this is a much easier way of displaying system icons for files and folders as the shell imagelist handles all that for you.
// To initialise the tree's image list - do this one time only
HIMAGELIST himg;
if (SUCCEEDED(SHGetImageList(SHIL_SMALL, IID_IImageList, reinterpret_cast<void**>(&himg))))
SendMessage(hwndTree, TVM_SETIMAGELIST, (WPARAM)TVSIL_NORMAL, (LPARAM)himg);
Then your AddIconToTree function simply becomes:
int AddIconToTree(HWND hwndTree, char *strPath)
{
SHFILEINFO sfi;
memset(&sfi, 0, sizeof(sfi));
// SHGFI_SYSICONINDEX will return the icon's index within the shell image list
SHGetFileInfo(strPath, FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi),
SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES);
return sfi.iIcon;
}
And when you actually add items to the list, make sure you assign the index to the item:
tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIS_STATEIMAGEMASK;
tvi.iImage = tvi.iSelectedImage = AddIconToTree(hwndTree, text);

How to get focus back when SHBrowseForFolder returns

I'm currently using SHBrowseForFolder() to open a browse folder window but how do I return focus to my main window when Cancel / OK is pressed. I read that I should re-enable my main window before the dialog closes but where is that exactly? Any thoughts?
void buttonPush(HWND hWnd) {
EnableWindow(hWnd, FALSE);
BROWSEINFO bi = { 0 };
TCHAR szDir[MAX_PATH] = { 0 };
LPITEMIDLIST pid = NULL;
LPMALLOC pMalloc = NULL;
if (SUCCEEDED(SHGetMalloc(&pMalloc)))
{
ZeroMemory(&bi,sizeof(bi));
bi.hwndOwner = NULL;
bi.pszDisplayName = NULL;
bi.pidlRoot = NULL;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_USENEWUI;
bi.lpfn = BrowseCallbackProc;
pidl = SHBrowseForFolder(&bi);
if (pidl)
{
// Folder selected in dialog
pMalloc->Free(pidl);
}
pMalloc->Release();
}
EnableWindow(hWnd, TRUE);
}
Instead of enabling and disabling your main window, just set bi.hwndOwner = hWnd; Then it will enable and disable automatically.
EnableWindow(hWnd, false);
This goes wrong because you are helping too much. When the dialog closes, there is no window left in your application that can still receive the focus. Your hWnd is still disabled, it doesn't get enabled until later. So the Windows window manager is forced to find another window to give the focus to. That will be the window of another app. Inevitably your window will disappear behind it.
Delete the EnableWindow() calls. That is enough, but you can tell the dialog about your window so it won't have to guess at it, useful if your window isn't the active window for some reason:
bi.hwndOwner = hWnd;

How to show my own context menu in Internet Explorer

I'm writing add-on for Internet Explorer 9 and I have to change default context menu to my own. I'm writing a BHO in C++ and I'm using ATL. I managed to handle event of showing context menu (HTMLDocumentEvents2::oncontextmenu), but I can't display my own one. Here is the code fired when you click right mouse button:
VARIANT_BOOL STDMETHODCALLTYPE CSpellCheckerBHO::OnContextMenu( IHTMLEventObj *pEvtObj)
{
HMENU contextMenu = CreatePopupMenu();
MENUITEMINFO item_info = { 0 };
item_info.cbSize = sizeof(MENUITEMINFO);
item_info.fMask = MIIM_TYPE | MIIM_ID;
item_info.fType = MFT_STRING;
item_info.wID = 0;
item_info.dwTypeData = L"TEST";
item_info.cch = 4;
BOOL result = InsertMenuItem(contextMenu, 0, FALSE, &item_info);
HWND browserHandle = 0;
HRESULT hr = _webBrowser->get_HWND((LONG_PTR*)&browserHandle);
result = TrackPopupMenuEx(contextMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, 0,0, browserHandle , NULL);
return VARIANT_FALSE;
}
_webBrowser is a pointer to IWebBrowser2 object, I got it from SetSite function.
The standard context menu is not displayed (due to returning VARIANT_FALSE), but TrackPopupMenuEx does nothing and returns 0.
Do you know what I am doing wrong? I need simple menu with some text items.
I fgured it out. Igor Tandetnik helped me on IE addon forum. HWND was from different proccess and TrackPopupMenuEx expects HWND belonging to the calling thread. Here's the code that works:
VARIANT_BOOL STDMETHODCALLTYPE CSpellCheckerBHO::OnContextMenu( IHTMLEventObj *pEvtObj)
{
HMENU contextMenu = CreatePopupMenu();
MENUITEMINFO item_info = { 0 };
item_info.cbSize = sizeof(MENUITEMINFO);
item_info.fMask = MIIM_ID | MIIM_STRING;
item_info.wID = 0;
item_info.dwTypeData = L"TEST";
item_info.cch = 4;
BOOL result = InsertMenuItem(contextMenu, 0, TRUE, &item_info);
CComPtr<IDispatch> dispDoc;
_webBrowser->get_Document(&dispDoc);
CComQIPtr<IOleWindow> oleWindow = dispDoc;
HWND browserHandle;
oleWindow->GetWindow(&browserHandle);
CComQIPtr<IHTMLEventObj2> htmlEventObj = pEvtObj;
long x, y;
htmlEventObj->get_screenX(&x);
htmlEventObj->get_screenY(&y);
result = TrackPopupMenuEx(contextMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, browserHandle , NULL);
return VARIANT_FALSE;
}