ExtractIconEx: works but occasionally crashes - c++

I'm extracting icons from a file and displaying them in a dialog
const LPCWSTR path = L"c:\path\to\file";
const UINT nIconsCheck = ExtractIconEx(path, -1, nullptr, nullptr, 0);
if(nIconsCheck > 0)
{
HICON *iconHandles=new HICON;
const UINT nIcons = ExtractIconEx(path, 0, iconHandles, nullptr, nIconsCheck);
if(nIcons == nIconsCheck && nIcons != unsigned(-1))
{
IconSelect iconSelect(this); //dialog
for(UINT i=0; i<nIcons; i++)
{
qDebug() << i;
iconSelect.addIcon(QtWin::fromHICON(iconHandles[i])); //fromHICON returns QPixmap
DestroyIcon(iconHandles[i]);
}
iconSelect.exec();
}
}
The icons are being loaded correctly in the dialog, but sometimes it unpredictably causes the application to crash.
Any idea what is going on?
Documentation on ExtractIconEx
Edit: Thanks for the quick and helpful answers. Below is the complete working code I am using atm:
// In my case I have a QString `filePath`
// `QString::toWCharArray` retrieves a non-0-terminated string,
// so append a 0 to `path`
std::vector<WCHAR> path(unsigned(filePath.length())+1);
filePath.toWCharArray(path.data());
path.at(path.size()-1) = 0;
// Get number of icons in selected file
UINT nIcons = ExtractIconEx(path.data(), -1, nullptr, nullptr, 0);
if(nIcons == 0)
{
// Try to find associated file that contains icon(s)
// If found, `path` is replaced with the new path
WORD index=0;
DestroyIcon(ExtractAssociatedIcon(GetModuleHandle(nullptr), path.data(), &index));
// Get number of icons in associated file
nIcons = ExtractIconEx(path.data(), -1, nullptr, nullptr, 0);
}
if(nIcons > 0)
{
// Get array of HICONs
std::vector<HICON> iconHandles(nIcons);
nIcons = ExtractIconEx(path.data(), 0, iconHandles.data(), nullptr, nIcons);
for(UINT i=0; i<nIcons; i++) // Using iconHandles.size() is possibly safer,
// but AFAIK nIcons always carries the correct value
{
// Use iconHandles[i]
// In Qt you can use QtWin::fromHICON(iconHandles[i]) to generate a QPixmap
DestroyIcon(iconHandles[i]);
}
}

HICON *iconHandles=new HICON;
Here you are allocating only a single HICON object. If there are more than one icons in the given file, the next call to ExtractIconEx() creates a buffer overrun by writing past the allocated memory. You have entered the dark world of undefined behaviour.
To fix this problem, you could use a std::vector like this:
std::vector<HICON> iconHandles(nIconsCheck);
const UINT nIcons = ExtractIconEx(path, 0, iconHandles.data(), nullptr, iconHandles.size());
iconHandles.resize(nIcons); // Resize to the actual number of icons.
// Instead of: if(nIcons == nIconsCheck && nIcons != unsigned(-1))
if(!iconHandles.empty())
{
// Use icons
}
This has the advantage over manual allocation, that you don't need to delete the allocated memory. The vector destructor will do it automatically when the scope ends. Though you still have to call DestroyIcon() for each icon handle.

From the documentation you linked to:
Pointer to an array of icon handles that receives handles to the large icons extracted from the file. If this parameter is NULL, no large icons are extracted from the file.
You only gave it a pointer to one icon handle.
Allocate an array as large as the function expects; from the look of it, that means nIconsCheck elements. A vector is good for this, as zett42 says.

Related

Using a CListCtrl with Drag and Drop not working

Using Visual Studio 2017 I am trying to enable drag and drop on a CListCtrl, I have added the code for this such as:
ON_WM_DROPFILES()
DragAcceptFiles();
m_listctrl.DragAcceptFiles();
void ProgramNameHere::OnDropFiles(HDROP hDropInfo)
{
CString sFile;
DWORD nBuffer = 0;
// Get the number of files dropped
int nFilesDropped = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0);
for (int i = 0; i<nFilesDropped; i++)
{
// Get the buffer size of the file.
nBuffer = DragQueryFile(hDropInfo, i, NULL, 0);
// Get path and name of the file
DragQueryFile(hDropInfo, i, sFile.GetBuffer(nBuffer + 1), nBuffer + 1);
sFile.ReleaseBuffer();
MessageBox(PathFindFileName(sFile), PathFindFileName(sFile));
}
// Free the memory block containing the dropped-file information
DragFinish(hDropInfo);
CDialogEx::OnDropFiles(hDropInfo);
}
I am able to get it working when I drop a file onto the dialog itself anywhere but when I try and drop it onto the List Control it doesn't work.
Would anyone have any idea what I am doing wrong?

How to allign text on ribbon button?

as adding a ribbon button, I am also supplying the name for it. It is performing its desired action, but placement of string is absurd. It should not be over the image, but under it. Any suggestions please. Adding code snippet and screenshot.
RibbonButtonProp* mRibbonProperties;
bool m_bsetlargeimage = FALSE;
if (ButtonProp.Lookup(m_nMenuItemID, mRibbonProperties) != 0)
{
if (ButtonProp[m_nMenuItemID]->m_bIfSmallButton == FALSE)
{
m_PanelImage.SetImageSize(CSize(32, 32));
m_PanelImage.Load(ButtonProp[m_nMenuItemID]->m_nImageResourceId);
m_bsetlargeimage = TRUE;
}
else
{
m_PanelImage.SetImageSize(CSize(16, 16));
m_PanelImage.Load(ButtonProp[m_nMenuItemID]->m_nImageResourceId);
m_bsetlargeimage = FALSE;
}
pRibbonButton = new CMFCRibbonButton(m_nMenuItemID, m_strMenuItemName, m_PanelImage.ExtractIcon(ButtonProp[m_nMenuItemID]->m_nImageIndex));
pRibbonButton->SetAlwaysLargeImage(m_bsetlargeimage);
Print should be just under the image
There is no way to change this. This is the way ribbons are shown and drawn.
A button text in "large-mode" is always drawn below the icon. I see nothing in the code to change this behavior AND I would never change this, because it is the default behavior people want/expect to see in a standard ribbon.
Earlier
AddCategory(m_strMenuName, NULL, NULL, NULL, NULL, i, NULL);
Now
AddCategory(m_strMenuName, NULL, NULL, CSize(16,16), CSize(32, 32), i, NULL);
Explanation: It was placing the text assuming that all the image size is small only as it was not defined already while calling create.

How to get multiline tooltip for a list control on a dialog box?

As text on my list box is very huge I am trying to get multiline a tooltip on the list control.
BOOL CTestDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
mylist.EnableToolTips(TRUE);
mylist.SetExtendedStyle(LVS_EX_INFOTIP | mylist.GetExtendedStyle());
mylist.InsertColumn(0, L"suri", LVCFMT_LEFT, 10000);
CString str1 = L"nonNegativeInteger GetVehicleOwnerHolderByRegNumAndDateResponse.GetVehicleOwnerHolderByRegNumAndDateResult[optional].GetVehicleOwnerHolderByRegNumAndDateResultType.VHOwnerHolderResponse.VHOwnerHolderResponseType.Body.VehicleCountries.VehicleCountriesType.VehicleCountry[1, unbound].VehicleCountryType.VehCountryReplies.VehCountryRepliesType.VehCountryReply[1, unbound].Messages[optional].Message[1, unbound].MessageType.MessageCode";
for (int i = 0; i < 20 ; i++) {
CString str2;
str2.Format(L"%d",i);
str2 = str2 + str1;
mylist.InsertItem(LVIF_TEXT | LVIF_PARAM, i, str2, 0, 0, 0, NULL);
}
return TRUE; // return TRUE unless you set the focus to a control
}
I am getting following output which is truncated text i.e complete text is missing.
How to get text on tooltip multiline?
EDIT: I used following also.
still same result.
CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
if (pToolTip)
pToolTip->SetMaxTipWidth(SHRT_MAX);
You can get multi-line tooltips using newlines-charecters with SetMaxTipWidth() set to a large value. And if calling SetMaxTipWidth() with a small value, then it will automatically break into multiple lines when meeting a space-character.
You need to subclass your tooltip/infotip in order to use it:
BEGIN_MESSAGE_MAP(CListCtrl_InfoTip, CListCtrl)
ON_NOTIFY_REFLECT_EX(LVN_GETINFOTIP, OnGetInfoTip)
ON_NOTIFY_EX(TTN_NEEDTEXTA, 0, OnToolNeedText)
ON_NOTIFY_EX(TTN_NEEDTEXTW, 0, OnToolNeedText)
END_MESSAGE_MAP()
void CListCtrl_InfoTip::PreSubclassWindow()
{
CListCtrl::PreSubclassWindow();
SetExtendedStyle(LVS_EX_INFOTIP | GetExtendedStyle());
}
BOOL CListCtrl_InfoTip::OnGetInfoTip(NMHDR* pNMHDR, LRESULT* pResult)
{
// Will only request tooltip for the label-column
NMLVGETINFOTIP* pInfoTip = (NMLVGETINFOTIP*)pNMHDR;
CString tooltip = GetToolTipText(pInfoTip->iItem, pInfoTip->iSubItem);
if (!tooltip.IsEmpty())
{
_tcsncpy(pInfoTip->pszText, static_cast<LPCTSTR>(tooltip), pInfoTip->cchTextMax);
}
return FALSE; // Let parent-dialog get chance
}
BOOL CListCtrl_InfoTip::OnToolNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult)
{
...
// Break tooltip into multiple lines if it contains newlines (\r\n)
CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
if (pToolTip)
pToolTip->SetMaxTipWidth(SHRT_MAX);
...
}
There are two aspects to consider:
1. The size of the window
To activate multilines mode. This instruction is sufficient :
pToolTip->SetMaxTipWidth(SHRT_MAX);
2. Number of characters to display
For this second point it is necessary to be wary because the size of the field pszText is limited to 80 characters:
typedef struct tagNMTTDISPINFOA {
NMHDR hdr;
LPSTR lpszText;
char szText[80];
...
}
Therefore, even if you change SetMaxTipWidth you will not see any difference.
I suggest you to use the lpszText field which has no limit. Below is the code fragment that interests you:
pTTTW->lpszText = T2W (strTipText.GetBuffer (strTipText.GetLength ()));
Where strTipText is the CString that contains the message to show in the pop-up

Using Qt control panel function in OpenCV error NULL pointer

I tried to google my question in several website but it still no answer.
My problem is look like this http://opencv-users.1802565.n2.nabble.com/Runtime-error-for-createTrackbar-in-control-panel-td7550203.html
I tried to create the control panel in OpenCV window using Qt integration as show in an example of OpenCV Document: http://docs.opencv.org/modules/highgui/doc/qt_new_functions.html
By this function, it should be separate between image window (with 'imshow()') and control panel (with in other window, called control panel).
However, it is not work when run to the code 'createTrackbar(num1, NULL, &val1 , 255, NULL);' the error message 'Null pointer' is shown. However, if I change the parameter to the window name it is work!.
My code is like this:
#include <...opencv.hpp>
#include <...highgui.hpp>
char* num1 = "testTrack";
int val1 = 100;
const string mainwin = "show";
int main()
{
while (true)
{
frame = capture();
createTrackbar(num1, NULL, &val1 , 255, NULL);
process_frame = image_processing(frame);
imshow(mainwin, process_frame);
// [Exit the system]
if (condition)
break;
}
}
Do you have any idea?
I don't know if this answer is useful after all this time, but you need to use an empty string instead of a null pointer.
Try with:
createTrackbar(num1, "", &val1 , 255, NULL);

MFC VC++ Custom Checkbox Image

How do I get a 3-state checkbox to use a different bitmap for the Indeterminate state?
I want to change the image used by my 3-state checkboxes to use a different one; the controls are in Win98-style, and the indeterminate state of such checkboxes is difficult to distinguish from disabled checkboxes (this is presumably why they changed this for the WinXP-style controls, but I cannot use those because of other details in my project).
I'm using Visual C++ 2010, and I've defined an 8x8 bitmap in VS's Resource Editor. The bitmap's ID is IDB_INDET_CHECK.
I'm not entirely sure what the standard "technique" for something like this is; I've only really just started getting into manipulating Windows controls and MFC.
My first attempt was to create a class, CTriButton, that derives from CButton, override the DrawItem function, and try to draw it myself. I then used SubclassDlgItem to turn one of the checkboxes in my window into this class (I think?). This... sort of works? The checkbox no longer appears, and if I click on where it should be, an empty checkbox frame appears, but nothing else happens (and the debug message in my code is not being sent).
Here's the relevant code, though I'm not sure any of this is right. First, code from my window's OnInitDialog.
BOOL CAffixFilterDlg::OnInitDialog() // CAffixFilterDlg is my CDialog-derived window
{
CDialog::OnInitDialog(); // call basic version
// subclass a CButton-derived control with CTriButton
if ( CBipedHead.SubclassDlgItem(IDC_HEAD, this) ) // CBipedHead is a CTriButton member of CAffixFilterDlg, IDC_HEAD is a checkbox
SetWindowLong(CBipedHead.m_hWnd, GWL_STYLE, CBipedHead.GetStyle() | BS_OWNERDRAW); // set the ownerdraw style
else // subclassing didn't work
_ERROR("Subclassing failed."); // I do not see this error message, so SubclassDlgItem worked?
// initialization continues, but is not relevant...
UpdateWindow();
Invalidate();
return TRUE;
}
Next, the code for my custom button's DrawItem.
void CTriButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
_DMESSAGE("Drawing TriButton"); // never see this message
CDC dc;
dc.Attach(lpDrawItemStruct->hDC); //Get device context object
int nWidth = GetSystemMetrics(SM_CXMENUCHECK);
int nMargin = ( nWidth - 8 ) / 2;
CRect textRt = lpDrawItemStruct->rcItem;
textRt.right = textRt.right - nWidth - nMargin;
CString text;
GetWindowText(text);
UINT textDrawState = DST_TEXT;
if ( lpDrawItemStruct->itemState & ODS_DISABLED )
textDrawState |= DSS_DISABLED;
dc.DrawState(CPoint(textRt.left, textRt.top), textRt.Size(), text, textDrawState, TRUE, 0, (CBrush*)NULL);
CRect rt = lpDrawItemStruct->rcItem; // initial rect is for entire button
rt.left = rt.right - nWidth; // set left margin
LONG center = ( rt.bottom + rt.top ) / 2;
rt.top = center - nWidth/2;
rt.bottom = center + nWidth/2;
UINT checkDrawState = DFCS_BUTTONCHECK;
if ( lpDrawItemStruct->itemState & ODS_DISABLED )
checkDrawState |= DFCS_INACTIVE;
if ( lpDrawItemStruct->itemState & ODS_CHECKED )
checkDrawState |= DFCS_CHECKED;
else if ( GetCheck() == BST_INDETERMINATE ) {
_VMESSAGE("Indeterminate; custom draw.");
CBitmap indet_check = CBitmap();
indet_check.LoadBitmap(IDB_INDET_CHECK);
CPoint pt = CPoint(rt.left + nMargin, rt.top + nMargin);
CSize sz = CSize(8, 8);
dc.DrawState(pt, sz, &indet_check, DST_BITMAP|DSS_NORMAL);
}
dc.DrawFrameControl(rt, DFC_BUTTON, checkDrawState);
}
In OnInitDialog() you need to call InvalidateRect() after changing the window style otherwise it doesn't know it needs to be redrawn. It's also a good idea to call UpdateWindow() after changing window styles. Some information is usually cached by the common controls and won't acknowledge the change until UpdateWindow() has been called.
In DrawItem() you are responsible for rendering all states of the control. You should not call CButton::DrawItem() as it does nothing. Try something like the following:
void CTriButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CBitmap indet_check
_DMESSAGE("Drawing TriButton"); // I never see this message
int checkState = GetCheck();
if ( checkState == BST_CHECKED )
{
indet_check.LoadBitmap(IDB_INDET_CHECK);
}
else if ( checkState == BST_UNCHECKED )
{
indet_check.LoadBitmap(IDB_INDET_UNCHECKED);
}
else if ( checkState == BST_INDETERMINATE )
{
indet_check.LoadBitmap(IDB_INDET_INDETERMINATE);
}
// ... rest of your drawing code here ...
// don't forget to draw focus and push states too ;)
}
Addendum:
I can't believe I missed this first time around but your call to SubclassDlgItem is probably not having the desired effect. This call causes messages intended for the button to be processed by the controls parent window first. Because the default implementation of DrawItem in CWnd (the superclass of CDialog) does nothing the message never gets passed to the control.
Replace this with the following snippet and everything should be ok:
HWND hWndButton;
GetDlgItem(IDC_HEAD, &hWndButton);
CBipedHead.SubclassWindow(hWndButton);
Two side notes here:
It's usually not a good idea to use the same naming convention for both classes and class members. It makes for a confusing read.
I'm guessing you are always compiling and running in release mode. If you are - don't. This prevents assertions from being thrown and letting you know something is wrong.
Not the answer, but an answer: this custom CCheckBox I found more-or-less enables what I want. It doesn't, by default, allow 3 states, but I fixed that up with some of my own tweaks. I'm not 100% sure it works out of the box (I've had some issues, that don't seem to be due to my edits, but I can't be sure), but it was the solution I've used. I'm not going to call this the answer, though, in case someone can spy what was wrong with my code and wants to illuminate me.