Is it necessary to use InitCommonControlsEx() and InitCommonControls()? - winapi

I'm completely new to win32. I have been working on it the last 48 hours.
I'm trying to build a "grid", and I got examples of a List-View control and a Header control on msdn.microsoft.com .
The first one calls the InitCommonControls() function (besides I read this function is obsolete).
HWND DoCreateHeader(HWND hwndParent, HINSTANCE hInst)
{
HWND hwndHeader;
RECT rcParent;
HDLAYOUT hdl;
WINDOWPOS wp;
// Ensure that the common control DLL is loaded, and then create
// the header control.
InitCommonControls();
// ...
// hwndHeader = CreateWindowEx(0, WC_HEADER, ...
}
The second one calls the InitCommonControlsEx() function.
HWND CreateListView (HWND hwndParent, HINSTANCE hInst)
{
RECT rcl;
INITCOMMONCONTROLSEX icex;
// Ensure that the common control DLL is loaded.
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&icex);
// ...
// HWND hWndListView = CreateWindow(WC_LISTVIEW ...
}
Seems these functions need comctl32.lib library, but download it is a mess.
Furthermore I have noticed that if I remove these functions, everything keeps working well. Then, are they necessary?
Thanks!

Yes it is necessary. They are required to get the window classes for those custom controls registered. Odds are, some other component in your code is loading them. I'm not sure, but I think if you have support for comctl v6 (XP and up visual styles) in your manifest, you get commctl32.dll automatically.
More info on what InitCommonControlsEx does is here.
Not sure what you mean by downloading comctl32.lib, it is present on every Windows platform since NT 4 and Windows 95 so you don't need to redistribute it.

Related

Are there any Win32 functions I can use to get the count / itemdata from a CComboBoxEx control?

My parent dialog has a CComboBoxEx control (which is mapped to a derived class called CDatesComboBoxEx).
In one part of the application this dialog displays a popup modal dialog. And, inside the modal dialog, it needs access to information from the dates combo.
What I decided to do (which works fine) is pass the address of my combo in the constructor of the popup dialog. So I can now do things like:
m_pComboDates->GetCount()
m_pComboDates->GetItemDataPtr(i)
I was wondering if there was any way to use native Win32 code here instead?
We can get access to the parents handle (GetParent()->GetSafeHWnd()).
We know the ID of the control on the parent dialog (IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING).
So is it possible to somehow directly get the count and item data?
I know that there are these macros:
ComboBox_GetCount
ComboBox_GetItemData
But:
Can these be macros be used with CComboBoxEx control? And ...
How do we get the HWND on the combo given the context I previously described?
Actually, I think I missunderstood the purpose of those "macros". I can get the combo handle like this:
HWND hDatesCombo = ::GetDlgItem(
GetParent()->GetSafeHwnd(), IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING);
But, ComboBox_GetCount does not return a value. Nor the others. So I am somewhat confused.
Based on the answer, this bit is now fine:
HWND hDatesCombo = ::GetDlgItem(GetParent()->GetSafeHwnd(), IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING);
int iNumDates = static_cast<int>(::SendMessage(hDatesCombo, CB_GETCOUNT, 0, 0));
And inside my for loop I am doing this:
LRESULT itemData = ::SendMessage(hDatesCombo, CB_GETITEMDATA, static_cast<WPARAM>(i), 0);
auto* pEntry = static_cast<CChristianLifeMinistryEntry*>((LPVOID)itemData);
That is the only way I can find to cast it. If I try static_cast<LPVOID> it won't work either.
I was wondering if there was any way to use native Win32 code here instead?
Yes, there is. The SendMessage function (and its returned value) is what you need …
Once you have the HWND of your combo-box, you can send it the CB_GETCOUNT message to ask it how many items it contains:
HWND hDatesCombo = ::GetDlgItem(GetParent()->GetSafeHwnd(), IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING);
LRESULT nItems = ::SendMessage(hDatesCombo, CB_GETCOUNT, 0, 0);
And, to get the item data associated with a particular entry, send the CB_GETITEMDATA message, with the (zero-based) index of the item in question as the wParam argument:
//...
LRESULT *ItemData = new LRESULT[static_cast<size_t>(nItems)];
for (int i = 0; i < nItems; ++i) {
ItemData[i] = ::SendMessage(hDatesCombo, CB_GETITEMDATA, static_cast<WPARAM>(i), 0);
}
//...
delete[] ItemData; // When you're done with the data list
Of course, if your item data are pointers (such as if you have an owner-drawn combo with1 the CBS_HASSTRINGS style), you would need to modify the second code snippet accordingly, adding relevant the reinterpret_cast operations where necessary. (Note that both the LRESULT and WPARAM types are defined as being suitable for storing pointers.)
1 The linked M/S documentation page is a bit fuzzy on whether this applies to owner-drawn combos with or without the CBS_HASSTRINGS style.

Deactivating desktop background when a pop up is shown to user

I have a win32 application that runs full screen when started. The application has some button which invoke pop up dialogs.
Is there a way to make the entire desktop (except the pop up) go transparent black unless the pop up is dismissed by the user? what I am talking of is similar to windows 7 UAC pop ups and the background it causes.
Is it possible to do similar stuff for a full screened window app?
It is possible do this…sort of. Perhaps I should say, you can simulate this effect. It won't actually be like the UAC dialog, as the user will still be able to interact with other running applications. There is no such concept as "system modal" available to applications. That's by design, of course. But you can certainly show a "light box" that dims out the rest of the desktop and forces focus on your app's dialog box.
The way I would do it is to create a giant layered window that sits on top of all other windows and covers the entire screen, fill it with black, and set the opacity as desired. Then, before you show a modal dialog (either by calling the MessageBox function or using the DialogBox function to show one of your own custom dialogs), display your light box window. Finally, after the user dismisses the modal dialog, you will destroy the light box window.
Here's some sample code. Error checking is omitted for brevity. So is other good style, like wrapping this mess up in one or more classes.
INT_PTR ShowLightBoxedDialog(HINSTANCE hInstance,
LPCTSTR pDlgTemplate,
HWND hwndParent,
DLGPROC pDlgProc,
BYTE opacityLevel)
{
const TCHAR szLightBoxClass[] = TEXT("LightBoxWndClass");
// Register the light box window class.
static bool lightBoxRegistered = false;
if (!lightBoxRegistered)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(wcex);
wcex.style = CS_NOCLOSE | CS_SAVEBITS;
wcex.lpfnWndProc = LightBoxWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hIconSm = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szLightBoxClass;
RegisterClassEx(&wcex);
lightBoxRegistered = true;
}
// Create and display the light box window.
HWND hwndLightBox = CreateWindowEx(WS_EX_NOACTIVATE | WS_EX_LAYERED,
szLightBoxClass,
NULL,
WS_POPUPWINDOW,
0, 0, 0, 0,
hwndParent,
NULL,
hInstance,
NULL);
SetLayeredWindowAttributes(hwndLightBox, 0, opacityLevel, LWA_ALPHA);
SetWindowPos(hwndLightBox,
HWND_TOP,
GetSystemMetrics(SM_XVIRTUALSCREEN),
GetSystemMetrics(SM_YVIRTUALSCREEN),
GetSystemMetrics(SM_CXVIRTUALSCREEN),
GetSystemMetrics(SM_CYVIRTUALSCREEN),
SWP_SHOWWINDOW);
// Display the modal dialog box (as you normally would).
// NOTE: The dialog box won't appear centered on the screen.
// For that, you will need to write centering code in response
// to the WM_INITDIALOG message in the dialog procedure.
INT_PTR result = DialogBox(hInstance, pDlgTemplate, hwndLightBox, pDlgProc);
//
// For demonstration purposes, I used the following code:
// INT_PTR result = MessageBox(hwndLightBox,
// TEXT("OH NOEZ!\n\nYour system is kaput! Abandon þe all hope."),
// NULL,
// MB_ABORTRETRYIGNORE | MB_ICONERROR);
// Destroy the light box window.
DestroyWindow(hwndLightBox);
// Return the result of the modal dialog box.
return result;
}
You'll notice that basically what I've done is created a wrapper around the DialogBox function, which you use whenever you want a dialog box with a "light box" effect. It takes all of the same parameters (the first 4), and then there's an additional one tacked on the end that allows you to specify the opacity level used for the "light box" effect. Something in the range of 150–200 is probably good. Naturally, you could pick something and hard-code it, but I suffer from severe allergies to hard-coded values. Anyway, it's super easy to call this function from anywhere:
ShowLightBoxedDialog(hInstance, /* your application instance */
MAKEINTRESOURCE(IDD_SAMPLE), /* your dialog template */
hWnd, /* parent window for dialog */
SampleWndProc, /* ptr to dialog procedure */
175); /* light box opacity level */
Because the code takes advantage of how modal dialogs already work in Windows, the user won't be able to interact with any other pieces of your application until they dismiss the dialog box. And because the "light box" window is positioned on top of everything else, it eats all mouse clicks and prevents setting focus to any other application. But it is trivial to work around using something like Alt+Tab.
So this is not a security feature! It is merely a visual effect!
And because it's just a silly visual effect, it's likely to be a frustrating one for your users. I don't actually recommend using it. But now you know how to do it. Wield such power responsibly, etc.

load a ttf font with the Windows API

With CreateFont one can specify font name and a bunch of other properties. However, what if I have a font.ttf file, and I want that particular font to be loaded by windows? How do I specify that specific file to be used?
I'm pretty sure you can't. All requests for fonts go through the font mapper, and it picks out the font file that comes the closest to meeting the specifications you've given. Though I'm not sure it even does in reality, it could at least theoretically use (for example) data from two entirely separate font files to create one logical font.
It's admittedly rather indirect, but you could utilize GDI interop with DWrite when running on Windows 7+.
#include <Windows.h>
#include <WindowsX.h>
#include <DWrite.h>
...
// Make the font file visible to GDI.
AddFontResourceEx(fontFileName, FR_PRIVATE, 0);
if (SUCCEEDED(GetLogFontFromFileName(fontFileName, &logFont)))
{
logFont.lfHeight = -long(desiredPpem);
HFONT hf = CreateFontIndirect(&logFont);
HFONT oldFont = SelectFont(hdc, hf);
...
// Do stuff...
...
SelectFont(hdc, oldFont);
}
RemoveFontResource(fontFileName);
....
HRESULT GetLogFontFromFileName(_In_z_ wchar const* fontFileName, _Out_ LOGFONT* logFont)
{
// DWrite objects
ComPtr<IDWriteFactory> dwriteFactory;
ComPtr<IDWriteFontFace> fontFace;
ComPtr<IDWriteFontFile> fontFile;
ComPtr<IDWriteGdiInterop> gdiInterop;
// Set up our DWrite factory and interop interface.
IFR(DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&dwriteFactory)
);
IFR(g_dwriteFactory->GetGdiInterop(&gdiInterop));
// Open the file and determine the font type.
IFR(g_dwriteFactory->CreateFontFileReference(fontFileName, nullptr, &fontFile));
BOOL isSupportedFontType = false;
DWRITE_FONT_FILE_TYPE fontFileType;
DWRITE_FONT_FACE_TYPE fontFaceType;
UINT32 numberOfFaces = 0;
IFR(fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces));
if (!isSupportedFontType)
return DWRITE_E_FILEFORMAT;
// Set up a font face from the array of font files (just one)
ComPtr<IDWriteFontFile> fontFileArray[] = {fontFile};
IFR(g_dwriteFactory->CreateFontFace(
fontFaceType,
ARRAYSIZE(fontFileArray), // file count
&fontFileArray[0], // or GetAddressOf if WRL ComPtr
0, // faceIndex
DWRITE_FONT_SIMULATIONS_NONE,
&fontFace
);
// Get the necessary logical font information.
IFR(gdiInterop->ConvertFontFaceToLOGFONT(fontFace, OUT logFont));
return S_OK;
}
Where IFR is just a failure macro that returns on a FAILED HRESULT, and ComPtr is a helper smart pointer class (substitute with your own, or ATL CComPtr, WinRT ComPtr, VS2013 _com_ptr_t...).
One possibility is to EnumFonts(), save the results. Then add your private font with AddFontResourceEx(), and EnumFonts() again, the difference is what you added. Note that TTF and bitmap fonts enumerate differently, but for this test, that shouldn't matter.
If you were using bitmap fonts, they could be easily parsed (.FNT and .FON). TTF you'd likely have to build (or borrow, as another commenter suggested FreeType) a parser to pull the "name" table out of the TTF file.
That seems like a lot of work for a font you're controlling or supplying with your app.
We use AddFontResourceEx() to add a private font, but since we control the font we're adding, we just hardcode the fontname passed to CreateFontIndirect() to match.
If you dont care about installing the font you can do so with AddFontResource then you can fetch the relationship between the physical .TTF and it logical/family name by looking at the mappings in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts.
I mentioned PrivateFontCollection in my comment because I thought you wanted to do this temporarily; you can load a TTF into a PFC with PrivateFontCollection::AddFontFile, fetch back the new FontFamily object from the collection & examime GetFamilyName. (I've done similar with the .net implementation of this but not the raw API)

Qt: QPainter + GDI in the same widget?

I'm trying to use the method described here to use a QPainter and GDI calls on the same widget.
Unfortunately this tutorial seem to have been written on an earlier version of Qt and now it does not work.
I set the WA_PaintOnScreen flag and reimplement paintEngine() to return NULL.
Then on the paintEvent() I create a QPainter, use it and then use some GDI calls to paint a bitmap. The GDI calls work fine but the QPainter does nothing. I get the following error on the console:
QPainter::begin: Paint device returned engine == 0, type: 1
Is this simply not supported anymore? how can I do it?
I've also tried creating an additional widget on top of the GDI-painting widget but that didn't go well as well since the top widget appears black and blocks the GDI widget.
I got this working in QT 4.7-beta 2 as follows
In the constructor call setAttribute(Qt::WA_PaintOnScreen,true);
Do NOT reimplement paintEngine() to return NULL;
Use the following code in the paintEvent();
QPainter painter(this);
HDC hdc = painter.paintEngine()->getDC(); // THIS IS THE CRITICAL STEP!
HWND hwnd = winID();
// From this point on it is all regular GDI
QString text("Test GDI Paint");
RECT rect;
GetClientRect(hwnd, &rect);
HBRUSH hbrRed = CreateSolidBrush(RGB(255,0,0));
FillRect(hdc, &rect, hbrRed);
HBRUSH hbrBlue = CreateSolidBrush(RGB(40,40,255));
HPEN bpenGreen = CreatePen(PS_SOLID, 4, RGB(0,255,0));
SelectObject(hdc,bpenGreen);
SelectObject(hdc,hbrBlue);
Ellipse(hdc,10,10,rect.right-20,rect.bottom-20);
SetTextAlign(hdc, TA_CENTER | TA_BASELINE);
TextOutW(hdc, width() / 2, height() / 2, text.utf16(), text.size());
ReleaseDC(hwnd, hdc);
This worked with Qt 4.0 and 4.1, but stopped working in either 4.2 or 4.3, when Trolltech reimplemented the Windows paint engine. In the second edition of the Qt 4 book, we added the sentence:
"For this to work, we must also reimplement QPaintDevice::paintEngine() to return a null pointer and set the Qt::WA_PaintOnScreen attribute in the widget's constructor."
I haven't tested it using later versions of Qt (I'm no longer at Trolltech/Nokia and have no Windows machine) but I hope it will still work.

How to manipulate DLGTEMPLATE programmatically?

What?
I have a DLGTEMPLATE loaded from a resource DLL, how can I change the strings assigned to the controls at runtime programmatically?
I want to be able to do this before the dialog is created, such that I can tell that the strings on display came from the resource DLL, and not from calls to SetWindowText when the dialog is initialized.
Google has found examples of creating DLGTEMPLATE in code, or twiddling simple style bits but nothing on editing the strings in memory.
How?
I am doing this by hooking the Dialog/Property Sheet creation API's. Which gives me access to the DLGTEMPLATE before the actual dialog is created and before it has a HWND.
Why?
I want to be able to do runtime localization, and localization testing. I already have this implemented for loading string (including the MFC 7.0 wrapper), menus and accelerator tables, but I am struggling to handle dialog/property sheet creation.
Code examples would be the perfect answer, ideally a class to wrap around the DLGTEMPLATE, if I work out my own solution I will post it.
You can't edit the strings in memory. The DLGTEMPLATE structure is a direct file mapping of the relevent bytes of the resource dll. Thats read only.
You are going to need to process the entire DLGTEMPLATE structure and write out a new one with the altered length strings.
It will frankly be easier to just hook the WM_INITDIALOG and alter the strings by interacting with the controls than building a DLGTEMPLATE writer. Because there arn't a lot of those around. Unless you have an additional requirement to actually save altered dialog resources to disk as raw .res files (or attempt to modify the .dll inplace) Id really recommend you avoid this approach.
You say you are already doing this for accellerator tables and menu strings - if you can guarantee that the patched in strings are going to be shorter, then just make a binary copy of the DLGTEMPLATE struct, and write the non trivial scanning code necessary to find each string so you can patch the copy in place.
There is a file out there somewhere (which I think originated at Microsoft but I am not completely sure) called RESFMT.ZIP which explains this with some code examples. Raymond Chen also does some excellent explanations of this on his blog. Note that the format of DIALOGEX and DIALOG controls are different.
As noted in some other answers you would need to create the structure again from the start. This isn't all bad as you already have the basic information. Adding the controls is where is gets hard.
Basically, allocate a largish block of memory into a WORD *lpIn. Then add the structure up on top of that. adding the basic information for the DIALOG (see DLGTEMPLATE) and the controls is pretty obvious as the information is there in MSDN.
The two biggest problems you will encounter are: Making sure that the various part start on an alignment boundary, and interpreting the values of DIALOG controls, especially when to add a just a string or, a string or ordinal. Each control needs to start on an even boundary.
For the first (borrowed from somewhere I think RESFMT.ZIP):
WORD *AlignDwordPtr (WORD *lpIn)
{
ULONG ul;
ul = (ULONG) lpIn;
ul +=3;
ul >>=2;
ul
What I did was build a series of functions like this one following that allowed me to assemble DIALOGS in memory. (My need was so I could have some common code that didn't need an associated RC file for some very basic messages).
Here is an example...
WORD *AddStringOrOrdinalToWordMem( WORD *lpw, char *sz_Or_Ord )
{
LPWSTR lpwsz;
int BufferSize;
if (sz_Or_Ord == NULL)
{
*lpw++ = 0;
}
else
{
if (HIWORD(sz_Or_Ord) == 0) //MAKEINTRESOURCE macro
{
*lpw++ = 0xFFFF;
*lpw++ = LOWORD(sz_Or_Ord);
}
else
{
if (strlen(sz_Or_Ord))
{
lpwsz = ( LPWSTR ) lpw;
BufferSize = MultiByteToWideChar( CP_ACP, 0, sz_Or_Ord, -1, lpwsz, 0 );
MultiByteToWideChar( CP_ACP, 0, sz_Or_Ord, -1, lpwsz, BufferSize );
lpw = lpw + BufferSize;
}
else
{
*lpw++ = 0;
}
}
}
return( lpw );
}
The header file to the complete module included these functions:
WORD *AddControlToDialogTemplateEx(MTDialogTemplateType *dlgtmp,
char *Title,
WORD Id,
char *WinClass,
DWORD Style,
short x,
short y,
short cx,
short cy,
DWORD ExStyle,
int HelpID);
int DestroyDlgTemplateEx(MTDialogTemplateType *dlgtmp);
MTDialogTemplateType *CreateDlgTemplateEx( char *Name, // We use name just for reference, so it can be NULL
short x,
short y,
short cx,
short cy,
DWORD ExtendedStyle,
DWORD Style,
char *Menu,
char *WinClass,
char *Caption,
char *FontTypeFace,
int FontSize,
int FontWeigth,
int FontItalic,
int Charset,
int HelpID,
int NumberOfControls);
Which allowed me to assemble whole dialogs easily from code.
See the API function ::EnumChildWindows( HWND, WNDENUMPROC, LPARAM )
You can call this in a CFormView::Create or CDialog::OnInitDialog to give yourself a chance to replace the control captions. Don't worry, the old strings don't flicker up before you replace them.
In your dialog resource, set the control captions to a key in some kind of dictionary. If you're compiling /clr you can use a managed string table resource. In your callback, look up the translated string in your dictionary and set the control's caption to the translation. Another benefit of /clr and managed string table is that you can automatically look up the right language by Windows (or you) having already set System::Threading::Thread::CurrentThread->CurrentUICulture.
Something like this
CMyDialog::OnInitDialog()
{
::EnumChildWindows(
this->GetSafeHwnd(),
CMyDialog::UpdateControlText,
(LPARAM)this )
}
BOOL CALLBACK CMyDialog::UpdateControlText( HWND hWnd, LPARAM lParam )
{
CMyDialog* pDialog = (CMyDialog*)lParam;
CWnd* pChildWnd = CWnd::FromHandle( hWnd );
int ctrlId = pChildWnd->GetDlgCtrlID();
if (ctrlId)
{
CString curWindowText;
pChildWnd->GetWindowText( curWindowText );
if (!curWindowText.IsEmpty())
{
CString newWindowText = // some look up
pChildWnd->SetWindowText( newWindowText );
}
}
}
You'll have to locate the string you want to modify in the mem buffer that represents the template. The only way to do that is to traverse the whole template. Which is not easy.
Once you've done that, either insert bytes in the buffer if your new string is longer than the original one. Or shrink the buffer if the new string is shorter.
As Chris wrote, it would be much easier to modify the text in WM_INITDIALOG and try to re-phrase you requirement that says you may not call SetWindowText().
Thanks all, I actually had 24 hours rest on the problem, then went with a global windows hook filtering WM_INITDIALOG which was a much simpler method, worked out just fine, no API hooking required, 2 pages of code down to just a few lines.
Thanks for all the answers.

Resources