Can this code be changed to support a preview in the print dialog? - visual-c++

I am not sure if this is an issue that can be resolved with coding or is by design. I am running the latest version of Windows 11 with all updates. And my application is a MFC dialog application built with Visual Studio 2022.
My application uses a CHtmlView control and the user can trigger a Print Preview. This event will trigger the following code:
void CChristianLifeMinistryHtmlView::DoPrintPreview()
{
ExecWB(OLECMDID_PRINTPREVIEW, OLECMDEXECOPT_PROMPTUSER, nullptr, nullptr);
HWND HWND_PP = nullptr ;
const auto t1 = ::GetTickCount64();
auto t2 = ::GetTickCount64(); // Extra line required for 'while' rearrangement.
while (HWND_PP == nullptr && t2 - t1 <= 6000) // if found or Timeout reached...
{
HWND HWND_FG = ::GetForegroundWindow(); // Get the ForeGroundWindow
if (HWND_FG != nullptr)
{
TCHAR szClassName[256]{} ;
::GetClassName(HWND_FG, &szClassName[0], 256);
if (lstrcmp(&szClassName[0], IE_PPREVIEWCLASS) == 0) // Check for Print Preview Dialog
HWND_PP = HWND_FG ;
}
theApp.PumpMessage();
t2 = ::GetTickCount64();
}
if (HWND_PP != nullptr)
{
RECT workArea{};
::SystemParametersInfo( SPI_GETWORKAREA, 0, &workArea, 0 );
::MoveWindow(HWND_PP, workArea.left, workArea.top,
workArea.right - workArea.left, workArea.bottom - workArea.top, TRUE);
}
}
This code works fine, and I get a good print preview, resized to the display. For example:
Now, if I hit Print from here it displays:
Opting to print to PDF looks the same:
Prior to the recent Windows 11 Updates these latter "print dialogs" looked different with no preview anyway. But since this updated version of the system "print dialog" can cope with a preview, is there no way to get our existing CHtmlView preview displayed there?
This is only a cosmetic thing because I have a standard preview for the user to see anyway (snap 1). But it would be the icing on the cake if that preview could someone be fed into this popup print window.
Is this something we can do by code or is it a "system / by design issue"?

Related

MFC MDI dynamically changing tab style from a property dialog

It has been 10 months since I worked on my app due to a death in the family, just started looking at it again and still not sure how to solve the problem.
The project inquires/help started here:
MFC MDI Collecting control states for the "apply" button routine
Since this is a specific focused question, I didn't want to muck up my other thread, so what I'd like to do is change the documents tab styles after the view is loaded. I know that this can be done because the master repository from Microsoft with all the code examples has a project called VCSamples-master\VCSamples-master\VC2010Samples\MFC\Visual C++ 2008 Feature Pack\TabControl which I have looked at. It dawns on me that even though I can follow its code, the calls are from within the MDI window itself where my issue is I'm trying to do this via a property page dialog using OnApply which changes things.
I was able to do part of this properly with the help of the thread above to the OutputPane successfully because I was able to get the Pane handle and execute. I was told that for the MDI tabs after creation that I need to parse the tabs, count them, and then execute. So my issue here is after I capture the tabs......how to change their styles.
Here is the code as it stands:
BOOL CSettingsUserTabs::OnApply()
{
BOOL bResult = CMFCPropertyPage::OnApply();
if (bResult)
{
// Update Output Pane Tab Styles (Works 100%)
AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.RecalcLayout();
//Get the open file tabs in the MDI
for (POSITION pos = AfxGetApp()->GetFirstDocTemplatePosition(); pos != NULL; )
{
CDocTemplate* pTempl = AfxGetApp()->GetNextDocTemplate(pos);
for (POSITION pos1 = pTempl->GetFirstDocPosition(); pos1 != NULL; )
{
CDocument* pDoc = pTempl->GetNextDoc(pos1);
for (POSITION pos2 = pDoc->GetFirstViewPosition(); pos2 != NULL; )
{
CView* pView = pDoc->GetNextView(pos2);
if (pView->IsKindOf(RUNTIME_CLASS(CTrainView)))
{
// THIS IS WHERE MY ISSUE IS, NOW THAT ALL THE TABS ARE
// CAPTURED, HOW DO I ADDRESS THEM LIKE WHAT IS SHOWN
// ABOVE:
//((CMainFrame*)AfxGetMainWnd())->xxxxxx.yyyyyy.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
}
}
}
}
}
return bResult;
}
If I can figure this last piece out, I'll be basically finished, I just can't seem to find a solution on how to do this via property sheet via OnApply.
Any suggestions or actual code examples I can see to solve my problem?
FYI: No, I haven't had any time to take additional OOP to solve this. I'm hoping someone can provide some guidance so I can move on after getting this sorted.
Thanks,
Chris
EDIT 1:
So I took a closer look at Constantine's suggestion and here is what I came up with:
BOOL CSettingsUserTabs::OnApply()
{
BOOL bResult = CMFCPropertyPage::OnApply();
if (bResult)
{
// Update Output Pane Tab Styles
AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.RecalcLayout();
CMFCTabCtrl& MDI_STYLES = ((CMainFrame*)AfxGetMainWnd())->GetMDITabs();
MDI_STYLES.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
MDI_STYLES.RecalcLayout();
CMDIFrameWndEx* pMainFrame = DYNAMIC_DOWNCAST(CMDIFrameWndEx, GetTopLevelFrame());
pMainFrame->SetFocus();
pMainFrame->RecalcLayout();
}
return bResult;
}
The m_styles_tabs is getting the index value of 0-8 when I select the radio button. The code compiles and runs and I see the index value change when I break on it, but the tabs for the MDI are still not updating. Does the edited code make sense based on the members shown here:
https://learn.microsoft.com/en-us/cpp/mfc/reference/cmfctabctrl-class?view=msvc-170#modifytabstyle
I think this the right direction, am I missing something?

VC++ How do I get the name of the main program a window belongs to?

EXAMPLE TESTING ENVIRONMENT:
I placed two folders on my Windows Desktop, each with its own content of files and subfolders. One of the folders is named ALPHA, the other is named BRAVO. I double clicked on each of these folders, so I have these two Windows Explorer windows open on my screen.
I also started Microsoft Word and created a new empty document (named Document1). Then pressed Ctrl+N to create another empty document (named Document2). So I have two Word windows open too.
I launched Firefox and navigated to bluepaint.com, then opened another browser window and navigated to yellowsand.org, so I also have two Firefox windows on my screen.
CODE:
Then in my Windows app (VS2019 Community) I capture the mouse button UP events and do something like this ...
TCHAR msg_tchar[256];
HWND wh_parent_window; // parent_window or owner_window
const unsigned int max_wintitle_length = 256;
TCHAR window_title_string[max_wintitle_length];
HWND wh_current_window = GetForegroundWindow();
GetWindowText(wh_current_window, window_title_string, (max_wintitle_length-1));
StringCchPrintf(msg_tchar, 255, _T("--NewWindow: \"%s\"\r\n"), window_title_string);
printToLogfile(msg_tchar);
if ((wh_parent_window = GetWindow(wh_current_window, GW_OWNER)) != NULL) {
GetWindowText(wh_parent_window, window_title_string, (max_wintitle_length-1));
StringCchPrintf(msg_tchar, 255, _T("ownerWindow: \"%s\"\r\n"), window_title_string);
printToLogfile(msg_tchar);
} //// if getOwnerWindow
if ((wh_parent_window = GetAncestor(wh_current_window, GA_PARENT)) != NULL) {
GetWindowText(wh_parent_window, window_title_string, (max_wintitle_length-1));
StringCchPrintf(msg_tchar, 255, _T("parentWindow: \"%s\"\r\n"), window_title_string);
printToLogfile(msg_tchar);
} //// if getParendWindow
It compiles and runs fine, and does what I told it to do. Clicking through the windows on my screen I get the below output.
--NewWindow: "Yellow Sand Beach informational portal - Mozilla Firefox"
parentWindow: ""
--NewWindow: "Categories of blue paints - Mozilla Firefox"
parentWindow: ""
--NewWindow: "Document2 - Word"
parentWindow: ""
--NewWindow: "Document1 - Word"
parentWindow: ""
--NewWindow: "ALPHA"
parentWindow: ""
--NewWindow: "BRAVO"
parentWindow: ""
Apparently, no OwnerWindow lines. Which I do not care much about.
BUT what I do care about is WHAT APPLICATION these windows belong to. And it seems that I am on the wrong track with this, as I was hoping to get the main application name of each window. So for ALPHA and BRAVO I would like to get something like "Windows Explorer" or "explorer.exe", for the documents I would like to get something like "Microsoft Office 2019" or "Microsoft Word" or "word.exe", and for the webpage windows I would like to get something like "firefox.exe" or "Firefox" or "Mozilla Firefox". Yes, I can see that Firefox and Word can be extracted from their window titles, but that does not work for ALPHA and BRAVO and for windows of other applications.
So insted of the window title, how do I get to the parent application name or the owning executable name?
I am more than happy to read the documentation, I just do not know what function name to look for. A nudge into the right direction would be appreciated.
Thanks to Igor's kick in the right direction, I wrote the below function and it serves the purpose I was looking for.
bool queryEXEnameOfWindow(HWND p_window_handle, std::wstring& p_exe_path) {
TCHAR tmp_path_str[MAX_PATH] = {0};
DWORD tmp_processID = 0;
DWORD tmp_length = MAX_PATH-1;
GetWindowThreadProcessId(p_window_handle, &tmp_processID);
HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, FALSE, tmp_processID);
BOOL tmp_result = QueryFullProcessImageName(processHandle, 0, tmp_path_str, &tmp_length);
CloseHandle(processHandle);
if (tmp_result) {
p_exe_path = tmp_path_str;
return true;
} //// if
return false;
} //// funcDef
Results returned in the wstring are like:
C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE
C:\Program Files\Firefox\firefox.exe
C:\Windows\explorer.exe
C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE
C:\Program Files\LibreOffice\program\soffice.bin

Distinguish single click from double click C++

I have an application in which double clicking over an image view area changes the layout of the image view. Also on single click a dot will be placed on the image.
My problem is, both functionality is working when double clicked.
Of course I know that, when a double click occurs the control first goes to LButtonDown. I don't want the dot functionality to work when double click occurs. I have been working around this for more than a week. Please help.
The easiest way to solve this is to build a finite-state machine for handling mouse clicks.
Basically, this will be a singleton object, which takes input from the mouse click events you're currently using.
It's output will be SingleClickDetected, DoubleClickDetected, ....
Red arrows indicate events which you are reporting to the rest of your application.
Parentheses indicate what event you are reporting.
Of course, this state machine will have to be modified if you have to deal directly with MouseDown and MouseUp events, instead of MouseClick events.
It will be slightly larger, but the idea is basically the same.
EDIT:
From the comments, it looks like Windows doesn't cleanly report single- vs double-clicks, and you need to separate them.
A state-machine for this scenario:
This is probably overkill for what you're trying to do, especially since most, if not all GUI-based programs in the history of everything have never ever used a double-click drag.
It does show the basic idea, and shows how you can extend your state machine to handle different types of button clicks.
Furthermore, if you wanted to, you could handle double-right-clicks, a drag that involves both left and right buttons, or any other scenario you could think of and incorporate into your UI.
I wrote the following code and it works.
UINT TimerId;
int clicks;
VOID CALLBACK TimerProc(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
KillTimer(NULL, TimerId);
if (clicks < 2 && !double_click){
MessageBox(hWnd, L"Show Widget", L"Widget", MB_OK);
}
clicks = 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
UINT uID;
UINT uMouseMsg;
uID = (UINT)wParam;
uMouseMsg = (UINT)lParam;
if (uMouseMsg == WM_LBUTTONDBLCLK){
double_click = true;
MessageBox(hWnd, L"Double click", L"CAPT", MB_OK);
return 0;
}
if (uMouseMsg == WM_LBUTTONDOWN){
double_click = false;
clicks++;
//single click opens context menu
if (clicks == 1){
TimerId = SetTimer(NULL, 0, 500, &TimerProc);
}
return 0;
}
,...
}
Try storing the timestamp of the last LButtonDown; if the time difference between the last timestamp and the timestamp produced in the current event is too short, you can just cancel your operation (but still store the new LButtonDown timestamp)
The only thing you can do is wait for a short amount of time each time you receive click event, and test if in the meantime the equivalent of a double click event doesn't occur before performing the single click response. This may be source to new bugs and unresponsive UI. Maybe try to change the user interaction to toss the problem away.
EDIT: The fact that you are working around this for more than a week is a symptom of bad user interaction design. A "double click" still means two clicks occurs, which means the application naturally should perform the operation of a single click. Check the ui of the apps installed on your desktop to verify this. Have you considered using a different user medium to trigger the UI response? For instance, you can use the right button to put the dot on the image.
#E.T's answer is spot-on. To implement something like that, you really need a timer running along with your message loop.
In my application I wanted to be able to distinguish mouse down/up from double click because I want a double click to not undo a drag operation (imagine selection box with left button drag and double click to zoom to fit).
The following code does this using PreTranslateMessage. I did not add the timer out of laziness. So the UI behaves a bit "funny" if you don't immediately move the mouse after the left button is held down. In my case, this is a minor issue.
BOOL MyWindow::PreTranslateMessage(MSG *pMsg)
{
//From https://msdn.microsoft.com/en-us/library/windows/desktop/ms645606(v=vs.85).aspx
//Double-clicking the left mouse button actually generates a sequence of four messages:
//WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, and WM_LBUTTONUP
//So here's the problem. If an button up message arrives, we can't just
//take it, because it may be followed by a double click message. So we check
//for this.
//Ideally you need a timer - what happens if you don't get any messages
//after the button up or down? But here we get lazy and assume a message
//will come "soon enough".
static bool upMessageStored = false;
static bool dnMessageStored = false;
static MSG upMessage, dnMessage;
static bool processDnNow = false, processUpNow = false;
//This logic sequence absorbs all button up and down messages, storing them to be sent
//if something other than a double click message immediately follows.
if (!(pMsg->message == WM_LBUTTONDBLCLK ||
pMsg->message == WM_LBUTTONDOWN ||
pMsg->message == WM_LBUTTONUP) &&
(upMessageStored || dnMessageStored))
{
//If we receive any message other than double click and we've stored the messages,
//then post them.
Output::Message(L"Stored messages posted.\n");
if (upMessageStored)
{
processUpNow = true;
upMessageStored = false;
this->PostMessage(upMessage.message, upMessage.wParam, upMessage.lParam);
}
if (dnMessageStored)
{
processDnNow = true;
dnMessageStored = false;
this->PostMessage(dnMessage.message, dnMessage.wParam, dnMessage.lParam);
}
return TitlelessWindow::PreTranslateMessage(pMsg);
}
if (pMsg->message == WM_LBUTTONDOWN && !processDnNow)
{
Output::Message(L"WM_LBUTTONDOWN absorbed; message stored\n");
dnMessage = *pMsg;
dnMessageStored = true;
return TRUE; //swallow this message.
}
else if (pMsg->message == WM_LBUTTONUP && !processUpNow)
{
Output::Message(L"WM_LBUTTONUP absorbed; message stored\n");
upMessage = *pMsg;
upMessageStored = true;
return TRUE; //swallow this message.
}
else if (pMsg->message == WM_LBUTTONDBLCLK)
{
Output::Message(L"WM_LBUTTONDBLCLK; stored message discarded\n");
upMessageStored = false;
dnMessageStored = false;
processUpNow = false;
processDnNow = false;
}
//If we get here, we are processing messages normally. Be sure we clear the flags
//for up and down.
processDnNow = false;
processUpNow = false;
return ParentClass::PreTranslateMessage(pMsg);
}
I really liked the finite state machine answer BUT there is a flaw in it.
There is no such thing as "single click time" which you can exceed.
If you look at a the working of your mouse closely you will see that:-
single click not one event but = WM_LBUTTONDOWN, WM_LBUTTONUP which is independent of time in between, the appropriate action will take place anyway
Double-clicking the left mouse button actually generates a sequence of four messages: WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, and WM_LBUTTONUP
so you should use the 3rd flag to your advantage
BTW, I am also working on something like this. Thanks!

Removing menu in MFC

In MFC how to remove a menu-item of POPUP type. RemoveMenu() either take ID or position. Since, there is no ID for POPUP menu, option left is by using position.
But I am not getting how and where I can call RemoveMenu().
File Edit Test
Test_submenu_1
Test_submenu_2
Test_submenu_3 > submenu_3_item_1
Test_submenu_4
Test_submenu_5
I want to remove Test_submenu_3? I am not getting how do find CMenu object for Test so that I can call RemoveMenu() by passing position "2" for submenu_3_item_1.
Any suggestion for doing this will be greatly appreciated.
Thanks!
You cannot use LoadMenu, since this function does just that.
After modifying loaded menu it is killed when menu object used to load it goes out of scope. You have to modify menu that is currently used.
Your menu is a popup part of the main menu, second in position. It contains 5 items and second one is another popup. To my understanding, you want to remove this item and popup of this item.
To make it work you will have to ask main window for the current menu:
CMenu* pMenu = GetMenu(); // get the main menu
CMenu* pPopupMenu = pMenu->GetSubMenu(2);//(Test menu with item....)
pPopupMenu->RemoveMenu(2, MF_BYPOSITION);
Of course, this code is from the main frame. If you want to use it elsewhere, you will have to access all using pointer to the main frame.
Try the below. You get the Test sub-menu first (index 2), then once you have that you tell it to remove its Test_submenu_3 submenu by position (also 2).
CMenu topMenu;
topMenu.LoadMenu(IDR_YOUR_MENU);
CMenu& testSubMenu = *topMenu.GetSubMenu(2);
testSubMenu.RemoveMenu(2,MF_BYPOSITION);
'Test' is the 3rd menu item (by position) on the top level menu. It's just been rendered horizontally rather than vertically. Assuming you have a handle to the top level menu use the same code you'd use to the get the sub menus as you would to get the 'Test' menu.
You can use this below code to remove the submenu, by comparing name
bool RemoveSubmenu(CMenu * pMenu) {
for (int pos = 0; pos < pMenu->GetMenuItemCount(); pos++) {
wchar_t *name = new wchar_t[mf.cch + 1];
MENUITEMINFO mf;
ZeroMemory(&mf, sizeof(mf));
mf.cbSize = sizeof(mf);
mf.fMask = MIIM_SUBMENU | MIIM_FTYPE | MIIM_STRING;
mf.fType = MIIM_STRING;
mf.dwTypeData = NULL;
if (!GetMenuItemInfo(pMenu->m_hMenu, pos, TRUE, &mf))
break;
if (mf.hSubMenu != NULL){
mf.fMask = MIIM_TYPE;
mf.fType = MFT_STRING;
++mf.cch;
mf.dwTypeData = (LPSTR)name;
if (!GetMenuItemInfo(pMenu->m_hMenu, pos, TRUE, &mf)){
bRet = false;
break;
}
//
// compare sub menu name (i.e mf.dwTypeData) here, do the required
// modifications
//
pMenu->RemoveMenu(pos, MF_BYPOSITION);
bRet = true;
break;
}
}
}

the dialog desn't show correctly when it's created in sub-thread

I'm trying to add a pop-up message function in my project.And I make it run in a subthread since I need a realtime notification.But I find if the notification dialog is created in my subthread(started by AfxBeginThread),all the elements(buttons,urls....) of the dialog are not shown.The message box is just a blank dialog. If I extract the function and start it by a button then it works fine.Here's the function,it's quite simple.
UINT sendNotification(LPVOID pParam)
{
int x = GetSystemMetrics(SM_CXSCREEN);
int y = GetSystemMetrics(SM_CYSCREEN);
testPopDlg *testPop = new testPopDlg;
testPop->Create(IDD_TEST,0);
CRect lprect(0,0,0,0);
testPop->GetWindowRect(lprect);
int w = lprect.Width();
int h = lprect.Height();
testPop->web.Navigate("http://www.google.com",NULL,NULL,NULL,NULL);
testPop->ShowWindow(SW_SHOW);
for(int k=0;k<20;k++) //slide out
{
testPop->MoveWindow(x-w,y-h*k/20,w,y-h*k/20,1);
Sleep(20);
}
Sleep(5000); //will close after 5 sec
return 0;
}
Why this function doesn't work right in subthread?
To use a dialog on a different thread in MFC you should create it on a UI thread. To create a UI thread you basically have to create a class derived from CWinThread. See also this code project article.

Resources