I'm working on a custom header control CMyHeaderCtrl which is derived from the MFC class CHeaderCtrl and overrides the DrawItem method to do some custom drawing when the application is themed. At first I try to determine the theme font for header items, but it fails and GetThemeFont returns the result 'element not found' (0x80070490).
The application which uses this control is linked against Common Controls 6.
Here is some sample code:
void MyHeaderCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
if(IsThemeActive() && IsAppThemed() && ComCtlVersionOK())
{
if(HTHEME hTheme = OpenThemeData(m_hWnd, L"HEADER"))
{
LOGFONTW lfw;
HRESULT hr = GetThemeFont(hTheme, lpDrawItemStruct->hDC, HP_HEADERITEM, HIS_NORMAL, TMT_CAPTIONFONT, &lfw);
ASSERT(hr == S_OK);
// ...
CloseThemeData(hTheme);
}
}
}
I also already tried other properties than TMT_CAPTIONFONT like TMT_SMALLCAPTIONFONT, TMT_BODYFONTand so on. What could be wrong here?
I've never had any luck getting GetThemeFont() to return anything other than E_PROP_ID_UNSUPPORTED (0x80070490), either. Although it's not explicitly stated in MSDN, the idea seems to be that GetThemeFont() would only return something if the theme defined a font different from the default for the particular part and state specified by the other argument. At least, that's what one MSDN blog suggests: http://blogs.msdn.com/b/cjacks/archive/2006/06/02/614575.aspx
Given that, it seems that the correct approach is to try GetThemeFont(), and if that fails, try GetThemeSysFont(), something like this:
HTHEME theme = OpenThemeData(wnd,L"HEADER");
if (theme != 0)
{
LOGFONTW lf;
HRESULT hr = GetThemeFont(theme,dc,
HP_HEADERITEM,HIS_NORMAL,TMT_CAPTIONFONT,&lf);
if (FAILED(hr))
hr = GetThemeSysFont(theme,TMT_CAPTIONFONT,&lf);
ASSERT(SUCCEEDED(hr));
// Do something with the font ...
CloseThemeData(theme);
}
Related
I'm scratching my head in receiving the parent window handle in a namespace extension project i'm working on.
The use case is as follows:
User browses to a virtual folder through windows explorer
User performs search (search box above)
I need to retrieve the search box's text before the search starts.
I've managed to do that on a test console app with ISearchBoxInfo interface (https://msdn.microsoft.com/en-us/library/windows/desktop/dd562062%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396)
There are 2 ways i can receive a pointer to this interface:
Using IObjectWithSite::SetSite call - which is not relevant as the search is conducted in a different thread, and i cannot share the COM object between those threads
Identifying the window handle and retrieve the ISearchBox through IWebBrowser2 interface.
Both methods don't work as when i perform the search, the EnumObjects is called via a different thread, and i cannot find a way to identify who is te parent explorer window.
When doing search, the hwnd that comes is always null as follows:
This is the EnumObjects code:
// Allows a client to determine the contents of a folder by
// creating an item identifier enumeration object and returning
// its IEnumIDList interface. The methods supported by that
// interface can then be used to enumerate the folder's contents.
HRESULT CFolderViewImplFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
{
HRESULT hr;
_fd = hwnd;
if (hwnd != NULL) // NULL when performing a search
{
const int n = GetWindowTextLength(hwnd);
wstring text(n + 1, L'#');
if (n > 0)
{
GetWindowText(hwnd, &text[0], text.length());
}
}
if (m_nLevel >= g_nMaxLevel)
{
*ppenumIDList = NULL;
hr = S_FALSE; // S_FALSE is allowed with NULL out param to indicate no contents.
}
else
{
CFolderViewImplEnumIDList *penum = new (std::nothrow) CFolderViewImplEnumIDList(grfFlags, m_nLevel + 1, this);
hr = penum ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = penum->Initialize();
if (SUCCEEDED(hr))
{
hr = penum->QueryInterface(IID_PPV_ARGS(ppenumIDList));
}
penum->Release();
}
}
return hr;
}
In addition to my tests, as i have also implementation of IShellFolderViewCB.MessageSFVCB, which runs on the correct thread where i can retrieve the IShellBrowser and thus the handle - when i conduct the search i encounter the following messages:
103, 103, 67, UnmergeMenu, WindowClosing, 106, ViewRelease, ViewRelease
Afterward, no more messages are posted (no matter if i re-search) - the first breakpoint is always at EnumObjects method which i have no context about the parent window.
Any light shedding would be nice.
== EDIT ==
I've came up with some workaround - this is not perfect for all cases but works for most of them - other options will still be nice.
Whenever EnumObjects is called with hwnd = NULL, i'm doing the following: (it's in C# - but it can be easily in C++ also)
static public string PrepareSearch(string currentFolderName, IntPtr hwnd)
{
SHDocVw.ShellWindows shellWindows = new ShellWindows();
SHDocVw.IWebBrowser2 foundBrowser = null;
bool wasFound = false;
string foundTxt = null;
foreach (SHDocVw.IWebBrowser2 eie in shellWindows)
{
// as the search is conducted in another thread, while the main window is "free" and in a search mode, it should be first busy.
string locName = eie.LocationName;
string exeName = eie.FullName;
if (!string.IsNullOrEmpty(exeName) && exeName.IndexOf("explorer.exe", StringComparison.OrdinalIgnoreCase) >= 0 &&
!string.IsNullOrEmpty(locName) &&
eie.Busy && eie.ReadyState == tagREADYSTATE.READYSTATE_LOADING)
{
// in here we're ok, we would also want to get the window title to make sure we're searching correctly.
string title = NSEFolder.WindowText((IntPtr)eie.HWND);
if (!string.IsNullOrEmpty(title) &&
title.IndexOf(currentFolderName) >= 0)
{
// one or more windows were found, ignore the quick search.
if (wasFound)
{
return null;
}
wasFound = true;
foundTxt = locName;
}
}
}
if (wasFound && !string.IsNullOrEmpty(foundTxt))
{
return foundTxt;
}
return null;
}
Basically i'm going over all explorer windows, trying to find one that is indeed "explorer.exe" + not empty search string (LocationName) + busy... + title contains name of the current folder name.
it will fail when 2 windows are busy and have the same folder name in the title - but this might be good enough... Not sure here.
So, after a talk with microsoft - it's not possible to do so.
the workaround i've added is a valid one (thoguh not perfect).
I followed all the instructions that is referred here :
http://help.adobe.com/en_US/robohelp/robohtml/WS5b3ccc516d4fbf351e63e3d11aff59c571-7f43.html
My CMainFrame::HtmlHelp overiden handler looks like this :
void CMainFrame::HtmlHelp(DWORD_PTR dwData, UINT nCmd)
{
// TODO: Add your specialized code here and/or call the base class
CWaitCursor wait;
// Get the path to the Help system
CWinApp* pApp = AfxGetApp();
ASSERT_VALID(pApp);
// Set the path to server-based help
CString csOnlineHelpPath = _T("C:\\Help\\Final\\index.htm");
PrepareForHelp();
// must use top level parent (for the case where m_hWnd is in DLL)
CWnd* pWnd = GetTopLevelParent();
// finally, run the RoboHelp Help engine
if (!RH_ShowHelp(pWnd->m_hWnd, csOnlineHelpPath, nCmd, dwData))
AfxMessageBox(AFX_IDP_FAILED_TO_LAUNCH_HELP);
}
The problem is that the help is never opened. I tried to debug the RoboHelp_CSH.cpp file and I found out that at the line #3267 with the code
MultiByteToWideChar(CP_ACP, MB_USEGLYPHCHARS, szTempFile, (int)uLen, bstr, uLen+1);
the bstr buffer have an extra char at the end that makes the following code
hr=s_pstBrowser->Navigate(bstr, &vFlags, &vTargetFrameName, &vPostData, &vHeaders);
HWND hWnd;
hr=s_pstBrowser->get_HWND((long*)&hWnd);
if (SUCCEEDED(hr))
{
::SetForegroundWindow(hWnd);
}
::SysFreeString(bstr);
}
to fail. The original szTempFile has the data below
C:\Users\sdancer\AppData\Local\Temp\robohelp_csh.htm
and the bstr the the following (the DC2 is the symbol I show inside notepad++, unside VS2008 I see an up and down arrow).
C:\Users\sdancer\AppData\Local\Temp\robohelp_csh.htmDC2
What am I doing wrong here ?
i try to make a basic browser using IWebBrowser2 on dialog based mfc
i insert an active x control into dialog, such as "microsoft web browser"
and i try "AtlAxGetControl" for getting a IWebBrowser2 pointer
but, i always get a e_fail, "res" always fail
void CIWebBrowser2Dlg::OnBnClickedButtonGo() {
CComPtr<IUnknown> punkIE;
CComQIPtr<IWebBrowser2> pWB2;
HWND hWnd;
this->GetDlgItem(IDC_EXPLORER_MAIN, &hWnd);
HRESULT res = AtlAxGetControl(hWnd, &punkIE);
if (res == S_OK) {
pWB2 = punkIE;
if(pWB2) {
pWB2->GoHome();
}
}
}
i did "AtlAxWinInit();" on initdialog
thank you for reading
plz, let me know how to handle it
Or, why not use the class wizard to generate a wrapper class for you. Then, you won't have to worry about the implementation aspects of the control.
im trying to build a new window using the following class.
however im having some serious problems while trying to get the new generated window's name using the CWindow::m_hwnd variable. my guess is that i should pass a variable to the CWindowImpl::Create() function that holds my parent's HWND but i dont know how to get it.
STDMETHODIMP CVMNExporter::SetSite(IUnknown *pUnkSite) {
HRESULT hr;
if(FAILED(hr = ATL::IObjectWithSiteImpl<CVMNExporter>::SetSite(pUnkSite)))
return hr;
// We are rnning from a web browser
CONNECTION_CALLBACK = JSCRIPT_DISPID;
EXPORTING_CALLBACK = JSCRIPT_DISPID;
MERGING_CALLBACK = JSCRIPT_DISPID;
ABORTING_CALLBACK = JSCRIPT_DISPID;
AddRef();
if(NULL == Create(0)) {// Creates the hidden window
Release();
return HRESULT_FROM_WIN32(GetLastError());
}
ATLTRACE("TEST - SetSite(): this: %d, window: %d", this, m_hWnd);
m_dwApartmentMode = HWND_APARTMENT;
SendMessage(WM_EXPORTER_APARTMENT_SETUP, 0, 0);
return S_OK; }
CVMNExporter implements the following interfaces:
public ATL::IObjectSafetyImpl<CVMNExporter, INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA>,
public ATL::IObjectWithSiteImpl<CVMNExporter>,
public ATL::CWindowImpl<CVMNExporter, ATL::CWindow, ATL::CFrameWinTraits>,
thanks!
Well it took me some time, but managed to solve it by using GetActiveWindow()
I'm facing some weird (at least for me) behavior on using the Common Item Dialogs in my MFC Windows application running on Windows 7 or Vista.
According to the MSDN http://msdn.microsoft.com/en-us/library/windows/desktop/bb776913(v=vs.85).aspx I'm using the new interfaces to display file open and save dialogs:
bool OpenFileDialog(CString& strFile, CString strTitle, CStringArray& astrFilter, CStringArray& astrFilterExtension, ULONG nFlags, HWND hParentWnd)
{
USES_CONVERSION;
INT_PTR nResult = 0;
INT_PTR nFilterCount = astrFilter.GetCount();
IFileDialog* pfod = 0;
HRESULT hr = ::CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfod));
if(SUCCEEDED(hr))
{
// New dialog starting with Vista/Windows 7
COMDLG_FILTERSPEC* pOpenTypes = 0;
if((nFilterCount > 0) && (nFilterCount == astrFilterExtension.GetCount()))
{
pOpenTypes = new COMDLG_FILTERSPEC[nFilterCount];
for(int nIdx = 0; nIdx < nFilterCount; nIdx++)
{
pOpenTypes[nIdx].pszName = astrFilter[nIdx].GetBuffer();
pOpenTypes[nIdx].pszSpec = astrFilterExtension[nIdx].GetBuffer();
}
}
// Set the file types to display.
if(pOpenTypes)
{
hr = pfod->SetFileTypes(nFilterCount, pOpenTypes);
if(SUCCEEDED(hr))
hr = pfod->SetFileTypeIndex(0);
}
if(!strFile.IsEmpty())
pfod->SetFileName(strFile);
if(!strTitle.IsEmpty())
pfod->SetTitle(strTitle);
if(SUCCEEDED(hr))
{
// Ensure the dialog only returns file system paths.
DWORD dwFlags;
hr = pfod->GetOptions(&dwFlags);
if(SUCCEEDED(hr))
{
dwFlags |= FOS_FORCEFILESYSTEM;
if(nFlags & OFN_FILEMUSTEXIST)
dwFlags |= FOS_FILEMUSTEXIST;
if(nFlags & OFN_PATHMUSTEXIST)
dwFlags |= FOS_PATHMUSTEXIST;
hr = pfod->SetOptions(dwFlags);
if(SUCCEEDED(hr))
{
// Create an event handling object, and hook it up to the dialog.
IFileDialogEvents* pfde = NULL;
DWORD dwCookie;
// Actually only added for debugging purposes
/*hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
if(SUCCEEDED(hr))
{
// Hook up the event handler.
hr = pfod->Advise(pfde, &dwCookie);
if(!SUCCEEDED(hr))
{
pfde->Release();
pfde = 0;
}
}*/
// Now show the dialog. Usually called with hParent == 0
if(hParentWnd)
hr = pfod->Show(::GetWindow(hParentWnd, GW_OWNER));
else
hr = pfod->Show(0);
// do something with the path when the dialog was closed...
So the dialog appears and works fine if I want to select a file from a normal drive. I can navigate through the folders and select any file I want. On leaving the dialog I also get the correct file information.
But it doesn't work for one of the Libraries in the navigation pane on the left side. Whenever I try to select a Library like Documents, Videos or Pictures the dialog doesn't update the right pane which shows the folder/library content.
What I noticed is that on clicking a Library in the file open/save dialog the OnFolderChanging() event of the IFileDialogEvents interface is fired but the OnFolderChange() and OnSelectionChange() are not. Those events are fired if I click and navigate on a "normal" drive like C.
I also tried to call the dialogs early in my InitInstance method to avoid possible side-effects with my other code but this didn't help either.
Is there someone who had the same behavior and was able to resolve this?
Thanks a lot!
So I finally found the answer to this issue. Creating the new MFC project for the application was the actual hint to solve this. The reason was that the "Stack reserve size" was too big. The settings in the old VS6.0 project had the stack size increased to more than 100MB. Apparently the IFileDialog based dialogs do not work properly when the reserved stack size is simply too large (other thing might don't work also as expected). So I had to set it back to 15MB in my case.