I want to change the value of column # 1 when ever there is a change in the value of column#3 of CListCtrl.
I used LVN_ENDLABELEDIT to do the same and my code seems to work fine whenever user change value of column#3 and press enter.
But this is not working in case user paste values in this ( Ctlr + V). Any Suggestion?
Code
ON_NOTIFY(LVN_ENDLABELEDIT, IDC_PAGEITEMS_LIST, &CClassList::OnListItemEndEditLabel)
void CClassList::OnListItemEndEditLabel(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
if(pDispInfo->item.iSubItem == 5)
{
CString days = m_List.GetItemText(pDispInfo->item.iItem, pDispInfo->item.iSubItem);
COleDateTime date = COleDateTime::GetCurrentTime() + COleDateTimeSpan(_ttoi(days),0,0,0);
m_List.SetItemText(pDispInfo->item.iItem , 4, date.Format(VAR_DATEVALUEONLY));
}else if(pDispInfo->item.iSubItem == 4)
{
CString dateStr = m_List.GetItemText(pDispInfo->item.iItem, pDispInfo->item.iSubItem);
COleDateTime date; date.ParseDateTime(dateStr);
CString DaysToDeliver;
DaysToDeliver.Format( L"%d", ( date - COleDateTime::GetCurrentTime() ).GetDays() +1 ) ;
m_List.SetItemText(pDispInfo->item.iItem , 5, DaysToDeliver);
}
// TODO: Add your control notification handler code here
*pResult = 0;
}
Related
I've got an issue with getting the rows in the Recordset as it is really slow.
We've got an virtual ListCtrl where the data is retrieved and set in the "OnGetdispinfo" method.
This is pretty fast (~2 Seconds for 300k rows on localhost) however if the connection is slow the GUI becomes unrepsonsive and completly unusable until the job is finished.
So I've tried to do the Sql stuff in a different thread and updating the list once all data is fetched.
The issue with the unresponsive GUI is solved with that, but the time it takes to get all the data jumped from 2 seconds to several minutes.
Even if I dont do anything but loop through the rows (just calling MoveNext() in the loop until EOF is reached) it will still take over a minute to complete.
How do I resolve the issue with the freezing GUI without completly destroying the performance here?
I've included the relevant code below
m_pRecordset is a normal Recordset
Old:
void KundenListControlSQLCommand::OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult)
{
if (m_pRecordset->IsBOF())
{
*pResult = 0;
return;
}
LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
LV_ITEM* pItem = &(pDispInfo)->item;
if (pItem->mask & LVIF_TEXT)
{
CString strData;
m_pRecordset->SetAbsolutePosition(pItem->iItem + 1);
if (getStatusRow() != pItem->iSubItem)
{
m_pRecordset->GetFieldValue(short(pItem->iSubItem), strData);
}
::lstrcpy(pItem->pszText, strData);
}
if (pItem->mask & LVIF_IMAGE)
{
int const nIndex = this->GetParent()->SendMessage(OT_VLC_ONGETIMAGEINDEX, pItem->iItem, 0);
if (0 != nIndex)
{
pItem->iImage = nIndex - 1;
}
}
*pResult = 0;
}
void KundenListControlSQLCommand::loadAndDisplayData()
{
ASSERT(!m_strSQLCommand.IsEmpty());
CWaitCursor wc;
try
{
if (!m_pDatabase->IsOpen())
{
CString strSQL = m_pDatabase->getDatabaseInfo().getConnectString();
m_pDatabase->OpenEx(strSQL);
}
// RecordCount ermitteln
m_nRecordCount = m_pRecordset->selectCount(_T("*"), m_strSQLCommand);
if (m_pRecordset->IsOpen())
m_pRecordset->Close();
m_pRecordset->Open(Recordset::snapshot, m_strSQLCommand + m_strSortOrder,
Recordset::executeDirect | Recordset::noDirtyFieldCheck |
Recordset::readOnly | Recordset::useBookmarks);
SetItemCountEx(m_nRecordCount);
}
catch (CDBException* e)
{
e->ReportError();
e->Delete();
}
}
New:
void KundenListControlSQLCommand::loadAndDisplayData()
{
ASSERT(!m_strSQLCommand.IsEmpty());
CWaitCursor wc;
try
{
if (!m_pDatabase->IsOpen())
{
CString strSQL = m_pDatabase->getDatabaseInfo().getConnectString();
m_pDatabase->OpenEx(strSQL);
}
// RecordCount ermitteln
m_nRecordCount = m_pRecordset->selectCount(_T("*"), m_strSQLCommand);
if (m_pRecordset->IsOpen())
m_pRecordset->Close();
m_pRecordset->Open(Recordset::dynaset, m_strSQLCommand + m_strSortOrder,
Recordset::executeDirect | Recordset::noDirtyFieldCheck |
Recordset::readOnly | Recordset::useBookmarks);
m_vResult.clear();
m_vResult.reserve(m_nRecordCount);
int nFieldCount = m_pRecordset->GetODBCFieldCount();
CString strData;
while (!m_pRecordset->IsEOF())
{
for (auto i = 0; i < nFieldCount; i++)
{
m_pRecordset->GetFieldValue(short(i), strData);
m_vResult.push_back(std::move(strData));
}
if (m_bAbort)
{
m_bAbort = false;
return;
}
m_pRecordset->MoveNext();
}
GetParent()->SendMessage(OT_VLC_ON_LIST_DONE, NULL, NULL);
}
catch (CDBException* e)
{
e->ReportError();
e->Delete();
}
}
void KundenListControlSQLCommand::OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult)
{
if (m_pRecordset->IsBOF())
{
*pResult = 0;
return;
}
LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
LV_ITEM* pItem = &(pDispInfo)->item;
UINT nItem = (pItem->iItem * m_pRecordset->GetODBCFieldCount()) + pItem->iSubItem;
if (pItem->mask & LVIF_TEXT && m_vResult.size() >= nItem)
{
::lstrcpy(pItem->pszText, std::move(m_vResult.at(nItem)));
}
if (pItem->mask & LVIF_IMAGE)
{
int const nIndex = this->GetParent()->SendMessage(OT_VLC_ONGETIMAGEINDEX, pItem->iItem, 0);
if (0 != nIndex)
{
pItem->iImage = nIndex - 1;
}
}
*pResult = 0;
}``
As I can see in your code, you read the data and place them into the vector. In such a setting, I think you don't really need a dynaset recordset, which according to the documentation is "A recordset with bi-directional scrolling". It fetches data row-by-row, which may be what makes the process slow. Also, "changes made by other users to the data values are visible following a fetch operation", but I think this is not of critical importance in this case. It would be mostly useful for displaying more "live" data, that are updated often.
Instead, a snapshot, or even forwardOnly recordset would suffice and would be faster. You can also experiment with the CRecordset::useMultiRowFetch option. The documentation says it's faster. It requires some changes to your code (moving next etc). Take a look here: Recordset: Fetching Records in Bulk (ODBC).
An alternative, radically different implementation would be to use bookmarks instead. Loading would be a lot faster, but scrolling somewhat sluggish, as you will have to fetch data in the OnGetdispinfo() function.
Finally a tip, if you are using the MS-SQL server, check the native driver, if you haven't already, many on the i-net claim that it's considerably faster.
I don't know much about ODBC, but suspect that there are better way to get bulk data.
Regardless, you do a lot of unnecessary copying of your vectors. Two easy fixes:
Right after m_vResult.clear();, resize your m_vResult to the number of records.
Instead of m_vResult.push_back(vResult); do m_vResult.push_back(std::move(vResult));, as you don't need your vResult after that.
Another solution is to do a cache list, handling LVN_ODCACHEHINT notification (this example is for CListView but you can adapt it on your CListCtrl:
// header.h
class CYourListView : public CListView
{
// ...
afx_msg void OnLvnOdcachehint(NMHDR* pNMHDR, LRESULT *pResult);
};
and implementation:
// YourListView.cpp
// ...
ON_NOTIFY_REFLECT(LVN_ODCACHEHINT, &CYourListView::OnLvnOdcachehint)
END_MESSAGE_MAP()
void CYourListView::OnLvnOdcachehint(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMLVCACHEHINT pCacheHint = reinterpret_cast<LPNMLVCACHEHINT>(pNMHDR);
const DWORD dwTo = pCacheHint->iTo;
const DWORD dwFetched = m_vResult.size();
if (dwTo >= dwFetched) // new rows must be fetched
{
const DWORD dwColCount = m_pRecordset->GetColumnCount();
m_vResult.resize(dwTo + 1);
for (DWORD dwRow = dwFetched; dwRow <= dwTo; ++dwRow)
{
CDBRecord* pRecord = new CDBRecord;
pRecord->SetSize(dwColCount);
for (DWORD dwCol = 1; dwCol <= dwColCount; dwCol++)
{
CDBValue* pDBValue = new CDBValue(m_pRecordset, dwCol);
pRecord->SetAt(dwCol - 1, pDBValue);
}
m_vResult.emplace(m_vResult.begin() + dwRow, pRecord);
m_pRecordset->MoveNext();
}
}
*pResult = 0;
}
might be need to adjust some variables / values with your certain situation.
I am deriving from CEdit, to make a custom control. It would be nice, if like the MFC Feature Pack controls (Mask, Browsable) that I could change GetWindowText to actually report back not what is normally displayed on the control (for example, convert the data between hex and decimal, then return back that string).
Is it this possible in a derived CEdit?
Add message map entries for WM_GETTEXT and WM_GETTEXTLENGTH to your derived CEdit class:
BEGIN_MESSAGE_MAP( CMyEdit, CEdit )
ON_WM_GETTEXT()
ON_WM_GETTEXTLENGTH()
END_MESSAGE_MAP()
As we are overriding these messages we need a method of getting the original text of the edit control without going into endless recursion. For this we can directly call the default window procedure which is named DefWindowProc:
CStringW CMyEdit::GetTextInternal()
{
CStringW text;
LRESULT len = DefWindowProcW( WM_GETTEXTLENGTH, 0, 0 );
if( len > 0 )
{
// WPARAM = len + 1 because the length must include the null terminator.
len = DefWindowProcW( WM_GETTEXT, len + 1, reinterpret_cast<LPARAM>( text.GetBuffer( len ) ) );
text.ReleaseBuffer( len );
}
return text;
}
The following method gets the original window text and transforms it. Anything would be possible here, including the example of converting between hex and dec. For simplicity I just enclose the text in dashes.
CStringW CMyEdit::GetTransformedText()
{
CStringW text = GetTextInternal();
return L"--" + text + L"--";
}
Now comes the actual handler for WM_GETTEXT which copies the transformed text to the output buffer.
int CMyEdit::OnGetText( int cchDest, LPWSTR pDest )
{
// Sanity checks
if( cchDest <= 0 || ! pDest )
return 0;
CStringW text = GetTransformedText();
// Using StringCchCopyExW() to make sure that we don't write outside of the bounds of the pDest buffer.
// cchDest defines the maximum number of characters to be copied, including the terminating null character.
LPWSTR pDestEnd = nullptr;
HRESULT hr = StringCchCopyExW( pDest, cchDest, text.GetString(), &pDestEnd, nullptr, 0 );
// If our text is greater in length than cchDest - 1, the function will truncate the text and
// return STRSAFE_E_INSUFFICIENT_BUFFER.
if( SUCCEEDED( hr ) || hr == STRSAFE_E_INSUFFICIENT_BUFFER )
{
// The return value is the number of characters copied, not including the terminating null character.
return pDestEnd - pDest;
}
return 0;
}
The handler for WM_GETTEXTLENGTH is self-explanatory:
UINT CMyEdit::OnGetTextLength()
{
return GetTransformedText().GetLength();
}
Thanks to everyone for pointing me in the right direction. I tried OnGetText, but the problem seemed to be I couldn't get the underlying string or it would crash when calling GetWindowText (or just called OnGetText again...and couldn't find the underlying string).
After seeing what they did on masked control, I did a simpler answer like this. Are there any drawbacks? It seemed to not cause any issues or side effects...
Derive directly from GetWindowText
void CConvertibleEdit::GetWindowText(CString& strString) const
{
CEdit::GetWindowText(strString);
ConvertibleDataType targetDataType;
if (currentDataType == inputType)
{
}
else
{
strString = ConvertEditType(strString, currentDataType, inputType);
}
}
I'm currently using a derived class of CListCtrl to build my application upon. I'm trying to add an item to the list, and immediately assign it to a group:
// int row, int grp_id, CString header; - all initialized previously
int ind = m_list.InsertItem(row, header);
VERIFY(m_list.SetRowGroupId(row, grp_id));
Here's an implementation of SetRowGroupID():
BOOL CGridListCtrlGroups::SetRowGroupId(int nRow, int nGroupId)
{
//OBS! Rows not assigned to a group will not show in group-view
LVITEM lvItem = { 0 };
lvItem.mask = LVIF_GROUPID;
lvItem.iItem = nRow;
lvItem.iSubItem = 0;
lvItem.iGroupId = nGroupId;
return SetItem(&lvItem);
}
Nothing too fancy here.
However, the code is asserting false for VERIFY().
While I was searching through MSDN documents to find out why, it seems that the flag I want to use as a mask isn't available for this purpose (refer to Remarks).
Am I not able to change the group ID this way? For the record, I've also tried to use MoveItemToGroup(), which gave me the same result (that is, the item doesn't show up while group view is enabled).
I cannot say why updating a member's group ID does not work directly.
However, setting .iGroupId = I_GROUPIDCALLBACK triggers the message LVN_GETDISPINFO and in its handler you can then assign the group ID.
Outline:
BEGIN_MESSAGE_MAP(CGridListCtrlGroups, CListCtrl)
ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetDispInfo)
END_MESSAGE_MAP()
void CGridListCtrlGroups::CGridListCtrlGroups(NMHDR* pNMHDR, LRESULT* pResult)
{
NMLVDISPINFO* dispInfo = LPNMLVDISPINFOW(pNMHDR);
LVITEM* item = &dispInfo->item;
if (item->mask & LVIF_GROUPID) {
// assign group id
item->iGroupId = ...;
}
*pResult = 0;
}
BOOL CGridListCtrlGroups::RequeryRowGroupId(int nRow)
{
LVITEM lvi = {
.mask = LVIF_GROUPID,
.iItem = nRow,
.iGroupId = I_GROUPIDCALLBACK
};
return SetItem(&lvi);
}
I'm creating a search features in an existing c++ CodeGear project.
When you double-click a word, the background of all occurrences of the word is paint in green like in notepad++.
Before color is applied, I'm saving the original TRichEDit text in a TMemoryStream to be able to get the original text back. I reset the color back to normal on click event in the TRichEdit.
I would like to know if there is a way to save each occurrence of the search word in a TMemoryStream or maybe by using a Message like EM_STREAMOUT?
Right now everything works fine, but when the TRichEdit text is too big, it takes forever to reload the big memo of all the text. I think it would be better to only remember the color of the word that changed rather then reload all the text.
I'm really a beginner in programming, any help is appreciate. Tell me if it's not clear enough.
Here is my code that is working and putting background color to occurrences of word:
`
void SearchInText::searchWordInText(TRichEdit* reTextToSearch, AnsiString strWordToFind)
{
lstOccurrences->Clear(); //reset lst
strTextToParse = AnsiReplaceText(strTextToParse, "\r\n", "\n");
int nPrevTagPos = 0;
int nTagPos = strTextToParse.AnsiPos(strWordToFind);
while (nTagPos != 0)
{
int nPosMin = nPrevTagPos + nTagPos - 1;
//List of all the occurrence in the TRichEdit with their position in the text
//It's not a list of int, but it POINT to adresses of INT so it's the same result =)
lstOccurrences->Add((TObject*) nPosMin);
//Change color of background when there is an occurrence
changeBgColor(reTextToSearch, strWordToFind, nPosMin +1, 155, 255, 155); //lime
bColorWasApplied = true;
nPrevTagPos = nPosMin + strWordToFind.Length();
strTextToParse = strTextToParse.SubString(nTagPos + strWordToFind.Length(), strTextToParse.Length());
nTagPos = strTextToParse.AnsiPos(strWordToFind);
}
}
`
Try something like this:
#include <vector>
struct WordOccurrence
{
CHARRANGE Range;
CHARFORMAT2 OriginalFormat;
};
std::vector<WordOccurrence> WordOccurrences;
void TMyForm::HighlightWords(const String &WordToFind)
{
// disable the RichEdit's notification messages
int OriginalEventMask = RichEdit1->Perform(EM_SETEVENTMASK, 0, 0);
// disable the RichEdit's painting
RichEdit1->Perform(WM_SETREDRAW, FALSE, 0);
// save the RichEdit's current selection
CHARRANGE OriginalSelection;
RichEdit1->Perform(EM_EXGETSEL, 0, (LPARAM)&OriginalSelection);
// assign values to use while searching
int WordLen = WordToFind.Length();
int TextLen = RichEdit1->GetTextLen();
TSearchTypes SearchTypes = TSearchTypes() << stWholeWord << stMatchCase;
// find the first occurrence of the word
int StartPos = RichEdit1->FindText(WordToFind, 0, TextLen, SearchTypes);
while (StartPos != -1)
{
WordOccurrence Occurrence;
Occurrence.Range.cpMin = StartPos;
Occurrence.Range.cpMax = StartPos + WordLen;
// select the word
RichEdit1->Perform(EM_EXSETSEL, 0, (LPARAM)&Occurrence.Range);
// get the word's current formatting
Occurrence.OriginalFormat.cbSize = sizeof(CHARFORMAT2);
RichEdit1->Perform(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&Occurrence.OriginalFormat);
// save it for later
WordOccurrences.push_back(Occurrence);
// set the word's new formatting
CHARFORMAT2 NewFormat = Occurrence.OriginalFormat;
NewFormat.dwMask |= (CFM_COLOR | CFM_BACKCOLOR);
NewFormat.crTextColor = ...;
newFormat.crBackColor = ...;
RichEdit1->Perform(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&NewFormat);
// find the next occurrence of the word
StartPos = RichEdit1->FindText(WordToFind, Occurrence.Range.cpMax, TextLen - Occurence.Range.cpMax, SearchTypes);
}
// restore the RichEdit's original selection
RichEdit1->Perform(EM_EXSETSEL, 0, (LPARAM)&OriginalSelection);
// re-enable the RichEdit's painting
RichEdit1->Perform(WM_SETREDRAW, TRUE, 0);
RichEdit1->Invalidate();
// re-enable the RichEdit's notification messages
RichEdit1->Perform(EM_SETEVENTMASK, 0, OriginalEventMask);
}
void TMyForm::RestoreHighlightedWords()
{
// are there any occurrences to restore?
if (WordOccurances.empty())
return;
// disable the RichEdit's notification messages
int OriginalEventMask = RichEdit1->Perform(EM_SETEVENTMASK, 0, 0);
// disable the RichEdit's painting
RichEdit1->Perform(WM_SETREDRAW, FALSE, 0);
// save the RichEdit's current selection
CHARRANGE OriginalSelection;
RichEdit1->Perform(EM_EXGETSEL, 0, (LPARAM)&OriginalSelection);
// restore the formatting of each occurrence
for (std::vector<WordOccurrence>::iterator iter = WordOccurrences.begin();
iter != WordOccurrences.end();
++iter)
{
WordOccurrence &occurrence = *iter;
// select the word
RichEdit1->Perform(EM_EXSETSEL, 0, (LPARAM)&occurrence.Range);
// restore the word's original formatting
RichEdit1->Perform(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&occurrence.OriginalFormat);
}
// clear the list
WordOccurances.clear();
// restore the RichEdit's original selection
RichEdit1->Perform(EM_EXSETSEL, 0, (LPARAM)&OriginalSelection);
// re-enable the RichEdit's painting
RichEdit1->Perform(WM_SETREDRAW, TRUE, 0);
RichEdit1->Invalidate();
// re-enable the RichEdit's notification messages
RichEdit1->Perform(EM_SETEVENTMASK, 0, OriginalEventMask);
}
Ok, so I finally get it!
I add a struct in my .h
Inside it, I store:
-the position in the TRichEdit of the word found (nStart)
-the length of the word (nLength)
-the actual text and his RTF
struct sSelectedWord : public TObject
{
public:
__fastcall ~sSelectedWord(); //destructor
int nStart;
int nLength;
TMemoryStream* memoRTF;
};
Here is the code that save the RTF of my TRichEdit in my struct I just created in the .h.
void SearchInText::searchWordInText(TRichEdit* reTextToSearch, AnsiString strWordToFind)
{
lstOccurrences->Clear(); //reset lst
lstOccWithRTFMemo->Clear();
nCountOccurrence = 0;
strTextToParse = AnsiReplaceText(strTextToParse, "\r\n", "\n");
int nPrevTagPos = 0;
int nTagPos = strTextToParse.AnsiPos(strWordToFind);
while (nTagPos != 0)
{
int nPosMin = nPrevTagPos + nTagPos - 1;
//List of all the occurrence in the TRichEdit with their position in the text
//It's not a list of int, but it POINT to adresses of INT so it's the same result =)
lstOccurrences->Add((TObject*) nPosMin);
nCountOccurrence++;
//selected the word in the TRichEdit to save it with is RTF
reTextToSearch->SelStart = nPosMin;
reTextToSearch->SelLength = strWordToFind.Length();
TMemoryStream* memo = new TMemoryStream;
//important part!
rtfSaveStream(reTextToSearch,memo);
sSelectedWord* currentWord = new sSelectedWord;
currentWord->nStart = nPosMin;
currentWord->nLength = strWordToFind.Length();
currentWord->memoRTF = memo;
//Here we go, we add our new object in a list to be able to loop through it when we will want to reset the color
lstOccWithRTFMemo->Add(currentWord);
lstOccWithRTFMemo->Count;
//Change color of background when there is an occurrence
changeBgColor(reTextToSearch, strWordToFind, nPosMin +1, 155, 255, 155); //lime
bColorWasApplied = true;
nPrevTagPos = nPosMin + strWordToFind.Length();
strTextToParse = strTextToParse.SubString(nTagPos + strWordToFind.Length(), strTextToParse.Length());
nTagPos = strTextToParse.AnsiPos(strWordToFind);
}
}
The important part was done with the EM_STREAMOUT message.
void SearchInText::rtfSaveStream(TRichEdit* re, TMemoryStream* memo)
{
// Create an instance of an EDITSTREAM that will contain:
// - The detail of our callback (StreamInCallback)
// - The TMemoryStream that contains the text to paste
EDITSTREAM es = {0};
ZeroMemory(&es, sizeof(es));
es.dwCookie = (DWORD_PTR) memo;
es.dwError = 0;
es.pfnCallback = &StreamSaveInCallback ; //pointer to function callBack
//To save the selected word of the TRichEdit, use STREAMOUT
re->Perform(EM_STREAMOUT, SF_RTF | SFF_SELECTION, (LPARAM)&es);
}
DWORD CALLBACK SearchInText::StreamSaveInCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{
TMemoryStream *memo = (TMemoryStream*)dwCookie;
memo->Write(pbBuff, cb);
*pcb = cb;
return 0;
}
In the pbuff, you can see the word that you selected in your TRichEdit with his RTF!
Hope it will help others with the same issue! Thanks for those who suggested code =)
I have been working with winapi just a little bit, making a project with owner draw on menus. When I called GetMenuItemInfo, it sets the text of the menu item, but not the fType UINT variable flags.
Currently I have declared:
MenuItem->fMask = MIIM_TYPE
And MSDN says:
MIIM_TYPE Retrieves or sets the fType and dwTypeData members.
I don't know If I got confused with the MIIM_TYPE flag.
Here is my code:
void SetOwnerDrawMenu(HMENU * menu)
{
MENUIF * menu_item_information;
HMENU sub_menu_ocational;
UINT uId_menuitem;
int nMenuCountItems = GetMenuItemCount(*menu);
MENUITEMINFO * MenuItem = (MENUITEMINFO*)malloc(sizeof(MENUITEMINFO));
for(int i=0;i<nMenuCountItems;i++)
{
menu_item_information = (MENUIF*)malloc(sizeof(MENUIF));
menu_item_information->isSeparator=false;
menu_item_information->max_width=0;
sub_menu_ocational = 0;
uId_menuitem = GetMenuItemID(*menu,i);
memset(&MenuItem,0,sizeof(MenuItem));
MenuItem = (MENUITEMINFO*)malloc(sizeof(MENUITEMINFO));
MenuItem->cbSize = sizeof(MenuItem);
MenuItem->fMask = MIIM_TYPE;
MenuItem->cch = MAX_ODM_CCH;
MenuItem->dwTypeData = menu_item_information->szItemText;
GetMenuItemInfo(*menu,uId_menuitem,FALSE,MenuItem);
UINT final_flags = MF_BYPOSITION | MF_OWNERDRAW;
if( ( MFT_SEPARATOR & MenuItem->fType ) == MFT_SEPARATOR )
{
final_flags |= MF_SEPARATOR;
menu_item_information->isSeparator = true;
}
else
{
// Not important stuff
}
sub_menu_ocational = GetSubMenu(*menu,i);
if(sub_menu_ocational!=NULL)
{
ModifyMenu(*menu,i,final_flags,0,(LPCTSTR)menu_item_information);
// We got a submenu, repeat this operation
SetOwnerDrawMenu(&sub_menu_ocational);
}
else
{
ModifyMenu(*menu,i,final_flags,0,(LPCTSTR)menu_item_information);
}
}
}
I am inserting the menus with the InsertMenu function:
InsertMenu(tid_cmenu,0,MF_BYPOSITION | MF_SEPARATOR,0,NULL);
InsertMenu(tid_cmenu,0, MF_BYPOSITION | MF_STRING, TID_EXIT, "Exit");
Exactly, why the GetMenuItemInfo is not retriving the fType?
If you were checking the return code from GetMenuItemInfo you would see that it is failing. Your error is in this line:
MenuItem->cbSize = sizeof(MenuItem);
The MENUITEMINFO::cbSize member is supposed to be set to the size of a MENUITEMINFO structure, but you are setting it to the size of a MENUITEMINFO* pointer (i.e. 4 or 8 bytes, depending on the platform).
Change your code to:
MenuItem->cbSize = sizeof(MENUITEMINFO);
Also, your code is allocating MenuItem outside the loop, as well as once per-iteration inside the loop, so you are leaking memory.
Ok. The problem is not syntax or memory size errors.
It is more like 'logic' error and a silly mistake.
The ModifyMenu was changing all the menu items and
setting the fType of each one to NULL or setting the MF_SEPARATOR to all of the items.
That happened because the fourth argument of the ModifyMenu should be the ID of the menu item, I was declaring it as 0.
I changed that argument to the real ID of the menu Item using the GetMenuItemID return value inside the uId_menuitem variable and passing it to the fourth argument of ModifyMenu. That fixed the problem.
Thanks!