What could prevent TBPF_INDETERMINATE progress bar from being displayed in taskbar? - c++

I'm trying to implement the new Windows 7 taskbar progress bar. I managed to get it to work with TBPF_NORMAL state using the following code:
CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&taskbarList));
HRESULT c = taskbarList->SetProgressState(hWnd, TBPF_NORMAL);
if (c != S_OK) MessageBox("ERROR");
taskbarList->SetProgressValue(hWnd, 5, 10);
However if I try the exact same code with TBPF_INDETERMINATE, it doesn't display anything and there's no error either:
CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&taskbarList));
HRESULT c = taskbarList->SetProgressState(hWnd, TBPF_INDETERMINATE);
if (c != S_OK) MessageBox("ERROR");
Does anybody know what could be causing this problem?

Okay, it looks like it was a problem with the configuration of my system. I post the answer here because it's not obvious why one progress bar animation would work but not another.
In System Properties / Performance Options, I had "Animations in the taskbar and Start Menu" disabled. This option apparently disables the "indeterminate" animation but not the regular one. By re-enabling the option, the indeterminate animation works.

This happened to me too. In the code, I have a Form A that shows a Form B (which Form B in the OnLoad event calls SetProgressState) and after that Form A calls the method Close to itself, and when it was in Indeterminate mode it didn't show anything!
Surpringsly, if I close first Form A and then show Form B, the problem is solved!
I hope this can help somebody having a headache with this.

Related

MFC WebBrowser Control: How many (normal) lines of code does it take to simulate Ctrl+N?

Update: Answer: Two normal lines of code required. Thanks Noseratio!
I banged my head on the keyboard for more hours than I would have cared to trying to simulate IEs Ctrl+N behavior in my hosted Browser control app. Unfortunately, due to complications which I've abstracted out of my code examples below, I can't just let IE do Ctlr+N itself. So I have to do it manually.
Keep in mind that I am running a hosted browser. So typically, opening links in new windows will actuall open it within a new "tab" within my application (it's not really a tab, but another window... but appearance-wise it's a tab). However, Ctrl+N is different -- here, it is expected a fully-fledged IE window will launch when pressed.
I think my problem is that of framing the questions -- admittedly I am new to WebBrowser control and I find it to be a lot of yucky. Regardless, I've scoured the Internet for the past day and couldn't come up with an elegant solution.
Basically, the ideal solution would be to call a "NewWindow" function within WebBrowser control or its affiliate libraries; however, all I was able to find where the *On*NewWindow methods, which were event handlers, not event signallers. Which I understand that most of the time, the user will be creating the events... but what about programmatic simulation?
I tried looking into an SENDMESSAGE approach where I could use the IDs that the OnNewWindow events use... that ended up in nothing than crashes. Perhaps I could go back to get it work, but I'd like confirmation is that approach is even worth my time.
The next approach, which should have been the most elegeant, but sadly didn't pan out, was like the following:
Navigate2(GetLocationURL().GetBuffer(), BrowserNavConstants::navOpenInNewWindow);
It would have worked marvelously if it weren't for the fact that the new window would open in the background, blinking in the taskbar. needing clicking to bring it to the front.
I tried to get around the limitation in a myriad of ways, including getting the dispatcher of the current context, then calling OnNewWindow2 with that IDispatch object. Then I would invoke QueryInterface on the dispatch object for an IWebBrowser control. The webBrowser control (presumably under the control of the new window) could then navigate to the page of the original context. However... this too was a pretty messy solution and in the end would cause crashes.
Finally, I resorted to manually invoking JavaScript to get the desired behavior. Really?? Was there really no more elegant a solution to my problem than the below mess of code?
if ((pMsg->wParam == 'N') && (GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_SHIFT) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000))
{
LPDISPATCH pDisp = CHtmlView::GetHtmlDocument();
IHTMLDocument2 *pDoc;
if (SUCCEEDED(pDisp->QueryInterface(IID_IHTMLDocument2, (void **)&pDoc)))
{
IHTMLWindow2* pWnd;
pDoc->get_parentWindow(&pWnd);
BSTR bStrLang = ::SysAllocString(L"JavaScript");
CString sCode(L"window.open(\"");
sCode.Append(GetLocationURL().GetBuffer());
sCode.Append(L"\");");
BSTR bStrCode = sCode.AllocSysString();
COleVariant retVal;
pWnd->execScript(bStrCode, bStrLang, retVal);
::SysFreeString(bStrLang);
::SysFreeString(bStrCode);
pDoc->Release();
}
pDisp->Release();
I find it hard to believe that I must resort to such hackery as this to get something as simple as opening a new window when the user presses Ctrl+N.
Please stackoverflow, please point out the clearly obvious thing I overlooked.
Ctrl-N in IE starts a new window on the same session. In your case, window.open or webBrowser.Navigate2 will create a window on a new session, because it will be run by iexplore.exe process which is separate from your app. The session is shared per-process, this is how the underlying UrlMon library works. So you'll loose all cookies and authentication cache for the new window. On the other hand, when you create a new window which hosts WebBrowser control within your own app process, you'll keep the session.
If such behavior is OK for your needs, try first your initial Navigate2 approach, precededing it with AllowSetForegroundWindow(ASFW_ANY) call. If the new window still doesn't receive the focus correctly, you can try creating an instance of InternetExplorer.Application out-of-proc COM object, and use the same IWebBrowser2 interface to automate it. Below is a simple C# app which works OK for me, the new window is correctly brought to the foreground, no focus issues. It should not be a problem to do the same with MFC.
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace IeApp
{
public partial class MainForm : Form
{
// get the underlying WebBrowser ActiveX object;
// this code depends on SHDocVw.dll COM interop assembly,
// generate SHDocVw.dll: "tlbimp.exe ieframe.dll",
// and add as a reference to the project
public MainForm()
{
InitializeComponent();
}
private void NewWindow_Click(object sender, EventArgs e)
{
AllowSetForegroundWindow(ASFW_ANY);
// could do: var ie = new SHDocVw.InternetExplorer()
var ie = (SHDocVw.InternetExplorer)Activator.CreateInstance(Type.GetTypeFromProgID("InternetExplorer.Application"));
ie.Visible = true;
ie.Navigate("http://www.example.com");
}
const int ASFW_ANY = -1;
[DllImport("user32.dll")]
static extern bool AllowSetForegroundWindow(int dwProcessId);
}
}

IWebBrowser2: how to force links to open in new window?

The MSDN documentation on WebBrowser Customization explains how to prevent new windows from being opened and how to cancel navigation. In my case, my application is hosting an IWebBrowser2 but I don't want the user to navigate to new pages within my app. Instead, I'd like to open all links in a new IE window. The desired behavior is: user clicks a link, and a new window opens with that URL.
A similar question was asked and answered here and rather than pollute that answered post, it was suggested I open a new discussion.
The members on the related post suggested I should be able to do this by trapping DISPID_BEFORENAVIGATE2, setting the cancel flag, and writing code to open a new window, but I've found out that the browser control gets lots of BeforeNavigate2 events that seem to be initiated by scripts on the main page. For example, amazon.com fires BeforeNavigate2 events like crazy, and they are not a result of link invocation.
Replies appreciated!
What I ended up doing was using IHTMLDocument directly rather than IWebBrowser. IWebBrowser is a superset of IHTMLDocument, and the navigation model implemented by IWebBrowser isn't customizable to the degree I wanted.
I actually got MS Developer Support involved and this approach was their recommendation. They say this is what Outlook uses for HTML-based email, which is the user experience I wanted to emulate. They also confirmed that there's no reliable way to filter the OnBeforeNavigate events that result from user action from those that result from script activity.
Hope this helps anybody facing the same issues. It wasn't too hard to port the code to use IHTMLDocument. If you end up doing this, you may also find yourself looking for a way to figure out when the document is done loading. To do that, hook HTMLDocumentEvents instead of DWebBrowserEvents, and look for the DISPID_HTMLDOCUMENTEVENTS_ONREADYSTATECHANGE event. It doesn't tell you what the ready state is; you need to call IHTMLDocument::get_readyState and parse the resulting string. Goofy, but there you go.
You can bind to onclick event before document is complete while creating browser in OnCreate() using IHTMLDocument2::put_onclick():
#include <comutil.h>
ClickEvents<RootFrame> clickEvents;
_variant_t clickDispatch;
clickDispatch.vt = VT_DISPATCH;
clickDispatch.pdispVal = &clickEvents;
CComQIPtr<IDispatch> dispatch;
hr = webBrowser2->get_Document(&dispatch);
ASSERT_EXIT(SUCCEEDED(hr), "webBrowser->get_Document(&dispatch)");
CComQIPtr<IHTMLDocument2> htmlDocument2;
hr = dispatch->QueryInterface(IID_IHTMLDocument2, (void**) &htmlDocument2);
ASSERT_EXIT(SUCCEEDED(hr), "dispatch->QueryInterface(&htmlDocument2)");
htmlDocument2->put_onclick(clickDispatch);
ClickEvents class implements IDispatch, you only need to implement Invoke method, in rest return E_NOTIMPL:
HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
HRESULT hr;
CComQIPtr<IWebBrowser2> webBrowser2;
hr = rootFrame->GetDlgControl(rootFrame->rootview.GetDlgCtrlID(), IID_IWebBrowser2, (void**) &webBrowser2);
ASSERT_EXIT(SUCCEEDED(hr), "rootframe->GetDlgControl(IID_IWebBrowser2) failed");
CComQIPtr<IDispatch> dispatch;
hr = webBrowser2->get_Document(&dispatch);
ASSERT_EXIT(SUCCEEDED(hr), "webBrowser2->get_Document(&dispatch)");
CComQIPtr<IHTMLDocument2> htmlDocument2;
hr = dispatch->QueryInterface(IID_IHTMLDocument2, (void**) &htmlDocument2);
ASSERT_EXIT(SUCCEEDED(hr), "dispatch->QueryInterface(&htmlDocument2)");
CComQIPtr<IHTMLWindow2> htmlWindow2;
hr = htmlDocument2->get_parentWindow((IHTMLWindow2**) &htmlWindow2);
ASSERT_EXIT(SUCCEEDED(hr), "htmlDocument2->get_parentWindow(&htmlWindow2)");
CComQIPtr<IHTMLEventObj> htmlEvent;
hr = htmlWindow2->get_event(&htmlEvent);
ASSERT_EXIT(SUCCEEDED(hr), "htmlWindow2->get_event(&htmlEvent)");
CComQIPtr<IHTMLElement> htmlElement;
hr = htmlEvent->get_srcElement(&htmlElement);
ASSERT_EXIT(SUCCEEDED(hr), "htmlEvent->get_srcElement(&htmlElement)");
CComBSTR hrefAttr(L"href");
VARIANT attrValue;
VariantInit(&attrValue);
hr = htmlElement->getAttribute(hrefAttr, 0 | 2, &attrValue); // 0 = case insensitive, 2 = return BSTR
ASSERT_EXIT(SUCCEEDED(hr), "htmlElement->getAttribute()");
wchar_t href[2084]; // maximum url length in IE, http://support.microsoft.com/kb/208427
wcsncpy_s(href, _countof(href), attrValue.bstrVal, _TRUNCATE);
if (!rootFrame->IsURLAllowed(href)) {
VARIANT variant;
variant.vt = VT_BOOL;
variant.boolVal = VARIANT_FALSE;
htmlEvent->put_returnValue(variant);
ShellExecute(0, L"open", href, 0, 0, SW_SHOWNORMAL);
}
return S_OK;
}
As you can see after querying some interfaces I finally have the element that got clicked, then I call IsURLAllowed() defined in my root frame to check whether to allow opening url in current webbrowser window or whether to open it using default browser on user's computer.
This handles all links even if they were appended to document using javascript.
The same should be done with "onsubmit" events for forms.
I also think I have a solution for "window.location" redirects in javascript, I haven't tested it yet, but I will soon test it and I will update this answer then. You could use a combination of "onunload" and "onbeforeunload" events along with DWebBrowserEvents2::BeforeNavigate2(), after onunload/onbeforeunload are called you will know that user is leaving current page so now in BeforeNavigate2() you can cancel it. You can attach unload events using IHTMLWindow2::put_onunload() and IHTMLWindow2::put_onbeforeunload().
See sources of a complete solution for the "onclick" below.
AttachClickEvents in BrowserFrame:
http://code.google.com/p/phpdesktop/source/browse/phpdesktop-msie/msie/browser_frame.h?r=709d00b991b5#125
Invoke in ClickEvents(IDispatch):
http://code.google.com/p/phpdesktop/source/browse/phpdesktop-msie/msie/click_events.h?r=a5b0b350c933#132
I'm hypothesising here but yet another approach could be to maintain a count of navigation events, incrementing the counter on DISPID_BEFORENAVIGATE2 and decrementing it on occurrences of DISPID_NAVIGATECOMPLETE2 and DISPID_NAVIGATEERROR. With that in place, you could speculate that whenever you get DISPID_BEFORENAVIGATE2 and your counter is at zero, it is actual user navigation / link invocation.
I have no idea whether this approach would work, or whether those are the right events you'd need to make it work, but it could be worth investigating.
You could try a different approach instead and physically add the attribute target="_blank"
to all <a> tags in the rendered document.
This approach would involve waiting for DISPID_DOCUMENTCOMPLETE and then using IHTMLDocument3::getElementsByTagName() to fetch all of the anchor elements. You would then use IHTMLElement::setAttribute() to set target="_blank" on each of them.
It seems to me, that it you want "to open all links in a new IE window", it means that you want that the opening of new windows must be done in another process. The easiest way to do so: using CreateObject("InternetExplorer.Application") way (see another question which solve a problem, which is opposite to your question: InternetExplorer.Application object and cookie container). With this way you will receive the best isolation from your application and the user who clicks on the link receive all possibilities which exist in IE. You should of cause continue usage of BeforeNavigate2 events to find out the moment when "a new IE window" should be opened.

"Debug Assertion" Runtime Error on VS2008?

I'm writing a C++ MFC program on VS2008 and I'm getting this "Debug Assertion Error" when I first run the program sometimes. When I try to debug it, it takes me to this winhand.cpp file which is not part of the program I wrote so I'm not sure how to debug this.
It takes the error to this place in winhand.cpp
CObject* pTemp = LookupTemporary(h);
if (pTemp != NULL)
{
// temporary objects must have correct handle values
HANDLE* ph = (HANDLE*)((BYTE*)pTemp + m_nOffset); // after CObject
ASSERT(ph[0] == h || ph[0] == NULL);
if (m_nHandles == 2)
ASSERT(ph[1] == h);
}
So why does this error happen? Why does it only happen sometimes (50% of the time)? How would I debug this?
I'll provide some code if is needed.
THANKS!
The code that is asserting is part of MFC's CHandleMap class. MFC deals with windows as CWnd objects, but Windows deals with them as HWND handles. the handle map allows MFC to 'convert' an HWND into a pointer to the MFC object representing that object.
What the assertion seems to be doing is checking that when a lookup of the handle finds an MFC object, that the MFC object also thinks it's wrapping the same handle.
If they're different, then you get the assertion.
So it would appear that something is corrupting the handle map or the MFC object for that handle or you're doing something incorrect that gets these 2 data structures out of sync.
Some things you might do to try to debug the problem is to determine:
what MFC object is being found in the lookup (that's what's being pointed to by pObject)
what the MFC object thinks it's wrapping (that's the handle ph[0] and/or ph[1] - I'm not sure why there can be 2 of them)
what the handle is for (that's h)
Do the handles look like handle values or do they look like garbage? Does pObject point to something that looks like an MFC object, or garbage? Do any of these these things seem related?
The answers to these questions may point to what you need to do next (maybe set a debug write breakpoint on the item that looks like it's trashed).
I got this same assertion few days ago, and after some google search,
I found the solution for my case here:
http://forums.codeguru.com/showthread.php?216770-What-would-cause-this-assertion
In my case, change to misused
CDC* dc = GetDC();
CSize spaceSize = dc->GetTextExtent(" ");
dc->DeleteDC();
to
CDC* dc = GetDC();
CSize spaceSize = dc->GetTextExtent(" ");
ReleaseDC(dc);
would fix it.
Look out for code along those lines (from memory from Stroustrup's book):
c1 = (t2+t3).c_str();
(in spirit, could be other commands and expressions of course).Temporary objects are destroyed after their enclosing full expression has been evaluated, or at least the standard allows them to be. That means that what you would like to allocate to c1 may, or may not, still be in memory where it can be assigned to c1. The compiler may alert you to this issue, and the issue may or may not arise depending on what exactly you assign and other circumstances (I am not compiler writer), which would also explain why you get this error message only sometimes.
So in your shoes, I'd scan my code for similar expressions and clean them up.
When the debugger breaks, head up the call stack to the first bit of your code (if there is any - hopefully there is!). Ideally it's as simple as something in your code calling a library function incorrectly, and the library is catching the error with an assert and alerting you to that. (I don't think anyone will be able to tell what's wrong from the library code, we need to see your code.)
Otherwise, you're in for some tricky debugging: you're doing something wrong with the library that is asserting (looks like MFC) so go back and review all your MFC code and make sure everything is correct and according to the documentation.
This looks suspiciously like an error I had this morning. Is this happening in OnIdle()?
I know this is a very old post, but hoping that someone may get a little help from my answer.
I also faced a similar issue recently because of my simple mistake, then I came across this post and got a hint from "pac"'s post.
What I found is that if I use DeleteDC() to release DC returned from GetWindowDC() or GetDC() I will get the above assertion in MFC frame once CPaintDC object instance goes out of scope.
CDC * pDC = GetWindowDC();
...
ReleaseDC(pDC);
You have to use DeleteDC() only in conjunction with CreateDC() API.
CDC * pDC = new CDC();
pDC->CreateDC();
....
pDC->DeleteDC();
We had this problem when some of our project dlls were linking MFC as static library and some as shared library (check "Use of MFC" in Project settings)

How to autodetect urls in RichEdit 2.0?

When we have a RichEdit control and send it an EM_AUTOURLDETECT message with WPARAM set to TRUE, it nicely hightlights the detected URLs and sends the EN_LINK notifications.
But it does this only for text that is entered into the control. I haven't found the way to do it for text that's loaded into the control with SetWindowText or EM_STREAMIN.
Please help! Thanks
Upd:
I've created a test application from scratch and it works fine there. I think the problem might be that I have superclassed the control, that is, created a new window class and just use the window procedure of the original class. I'm gonna try subclassing the control instead..
I just knocked up a basic WTL dialog based app containing a riched20 control and the following works fine:
CRichEditCtrl richedit = GetDlgItem(IDC_RICHEDIT);
richedit.SetAutoURLDetect(TRUE);
richedit.SetWindowText(_T("http://www.stackoverflow.com"));
I have some old MFC code that does something similar, albeit with ES_STREAM, and it works OK too.
FWIW the WTL CRichEditCtrl wrapper is pretty thin. SetAutoURLDetect simply calls SendMessage passing it EM_AUTOURLDETECT.
I am compiling with _RICHEDIT_VER set to 0x0200 FWIW.
Without knowing the format of the text you are trying to add to the control with SetWindowText and EM_STREAMIN I'm going to take a guess and say this might have something to do with the control's text mode. After setting the contents of the control try sending it a EM_GETTEXTMODE message and see if the TM_PLAINTEXT bit is set. If this is the case then try sending a EM_SETTEXTMODE message followed by EM_AUTOURLDETECT. Your code should look something like this:
UINT textmode = (UINT)::SendMessage(handle_to_control, EM_GETTEXTMODE, 0, 0);
if(textmode & TM_PLAINTEXT) {
textmode &= ~TM_PLAINTEXT; // Clear the TM_PLAINTEXT bit
textmode |= TM_RICHTEXT; // Set the TM_RICHTEXT bit
if(::SendMessage(handle_to_control, EM_SETTEXTMODE, textmode, 0) != 0) {
// Failed to set the text mode
}
}
::SendMessage(handle_to_control, EM_AUTOURLDETECT, TRUE, 0);
You might just have to rewrite the text to the control to get it to re-parse.

How do I display a tooltip for a CMFCRibbonButton in the status bar?

I have a CMFCRibbonStatusBar in my mainframe to which I add a CMFCRibbonButtonsGroup which again has a CMFCRibbonButton. This button has the same ID as a menu entry.
Creating the button is done as follows:
CMFCRibbonButtonsGroup* pBGroup = new CMFCRibbonButtonsGroup();
CMFCToolBarImages images;
images.SetImageSize(CSize(32, 16)); // Non-square bitmaps
if(images.Load(IDB_STATUSBAR_IMAGES))
{
pBGroup->SetImages(&images, NULL, NULL);
}
m_pStatusButton = new CMFCRibbonButton(ID_STATUS_SHOWSTATUS,
_T(""),
IMAGEINDEX_DEFAULTSTATUS);
pBGroup->AddButton(m_pStatusButton);
m_wndStatusBar.AddExtendedElement(pBGroup, _T(""));
I want to use this button as a status indicator.
I want to display a tool tip in the following two cases:
when the status changes and
when the user moves the mouse over the button.
I have no idea how to start in the first place. I have looked at the ToolTipDemo and DlgToolTips sample projects but couldn't figure out how to do it since all they do is display tooltips for the toolbar items or dialog buttons (CWnd-derived instead of CMFCRibbonButton).
If you are familiar with the ToolTipDemo sample project: Since there seem to be several ways of doing things, I would prefer the tooltip to look like the "Extended Visual Manager-based" tool tip as shown in this screenshot.
Thanks!
I don't think it's possible to show the tooltip without the mouse cursor being over the control. That's all done automatically.
However if you want to have a nice looking tooltip like in your screenshot, you need to call SetToolTipText and SetDescription, like this:
CMFCRibbonButton* pBtn = new CMFCRibbonButton(12345, _T(""), 1);
pBtn->SetToolTipText("This is the bold Title");
pBtn->SetDescription("This is the not-so-bold Description");
pGroup->AddButton(pBtn);
I am using CMFCRibbonButton controls within a CMFCRibbonButtonGroup, which is added to the CMFCRibbonStatusBar. Take note of the 4th parameter in the CMFCRibbonButton() constructor, bAlwaysShowDescription, as this seems to affect the behavior depending upon whether SetDescription() has been called.
Specifically, if SetDescription() has not been called, it doesn't matter whether bAlwaysShowDescription is TRUE or FALSE - the tool tip is displayed (as I would expect). If SetDescription() is set and bAlwaysShowDescription is FALSE, when hovering over the button the tool tip is displayed with the description below it.
What seems counterintuitive given the name of this bAlwaysShowDescription parameter, is that when this is TRUE and SetDescription() is set, NEITHER the tool tip nor the description appear. I wonder if this is related to this post:
https://connect.microsoft.com/VisualStudio/feedback/details/399646/cmfcribbonbutton-wont-show-tooltip-if-balwaysshowdescription-1
Hope this helps and you can achieve what you need with the different combinations of bAlwaysShowDescription parameter and whether SetDescription() is set.