Retrieving the LPARAM with an item in treectrl using CTreeCtrl - c++

I have to add a list of sub keys recursively to an item in a Tree control.
I am using InsertItem as shown below. I am attaching the path of this element so that i can retrieve when clicked on the tree control.I am able to add the value but not able to retrieve it.
void CMyDlg::FillTreeWithRegistryKeysEx(CString sPath, HTREEITEM hItem)
{
CString sRegKey = sPath.Left(sPath.Find(_T("\\")));
sPath = sPath.Mid((sPath.Find(_T("\\")) + 1));
sPath = CleanRegistryKey(sPath);
int nKeyCount;
CString sSubKey = _T("");
HKEY handle = GetHkey(sRegKey);
HTREEITEM hReItem = NULL;
HKEY phkey;
std::vector<CString> sSubFolders;
if (RegOpenKeyEx(handle, sPath, 0, KEY_ALL_ACCESS, &phkey) == ERROR_SUCCESS)
sSubFolders = EnumRegistryKey(phkey);
nKeyCount = sSubFolders.size();
for (int nIndex = 0; nIndex < nKeyCount; nIndex++)
{
sSubKey = sPath + _T("\\") +sSubFolders.at(nIndex);
hReItem = m_cTreeCtrl.InsertItem(TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_STATE, sSubFolders.at(nIndex),
icoClosedFolder, icoOpenFolder, 0, 0, (LPARAM)(LPCTSTR)sSubKey, hItem, TVI_LAST);
FillTreeWithRegistryKeys(handle, sSubKey, hReItem);
}
RegCloseKey(phkey);
}
While retrieving the string is always blank.
void CMyDlg::OnTvnSelchangedTree(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
LPARAM lp = pNMTreeView->itemNew.lParam;
CString sKey = (LPCTSTR)lp;
}
what is going wrong?

The application-defined LPARAM passed to InsertItem is a string object that goes out of scope once the function completes not to mention it is replaced with each loop iteration with a new string. I don't think that is going to work.
The TreeView control however allocates its own copy of the text, so you are safe to fill in the pszText member with local buffers as it appears you are doing.

As pointed out in previous answer, you are using LPARAM to point to a string which is destroyed when the function exits. The solution is store the strings in permanent storage. There are many ways to do this. One option is to use CStringArray member data:
class CMyDlg : public CDialog
{
CStringArray m_strings;
...
};
Store the registry keys in m_strings.
Add the tree item with InsertItem, this returns HTREEITEM.
Use tree.SetItemData to match the tree item with an index in m_strings
Use tree.GetItemData to retrieve that index
Example:
BOOL CMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
...
m_strings.RemoveAll();
addKey(L"SOFTWARE\\Microsoft\\Internet Explorer", tree.GetRootItem());
return TRUE;
}
void CMyDlg::addKey(CString path, HTREEITEM parent)
{
CRegKey rg;
if (ERROR_SUCCESS != rg.Open(HKEY_CURRENT_USER, path))
return;
for (int i = 0;; i++)
{
wchar_t buf[300];
DWORD bufsize = 300;
if (rg.EnumKey(i, buf, &bufsize) != ERROR_SUCCESS) break;
HTREEITEM hitem = tree.InsertItem(buf, parent);
CString subkey = path + L"\\" + buf;
m_strings.Add(subkey);
tree.SetItemData(hitem, m_strings.GetCount() - 1);
addKey(subkey, hitem);
}
}
void CMyDlg::OnTvnSelchangedTree(NMHDR *pNMHDR, LRESULT *pResult)
{
HTREEITEM hitem = tree.GetSelectedItem();
if (!hitem) return;
int i = tree.GetItemData(hitem);
if (i >= 0 && i < m_strings.GetCount())
SetWindowText(m_strings[i]);
}

Related

How to find the names of all files from the clipboard

I just created an MFC application to find the file names from the clip board
AddClipboardFormatListener(AfxGetApp()->m_pMainWnd->m_hWnd);
LRESULT Cfile_trackerDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_CLIPBOARDUPDATE:
{
AfxBeginThread(FileArrival, NULL);
break;
}
case WM_CHANGECBCHAIN:
{
AfxBeginThread(FileArrival, NULL);
break;
}
}
return CDialog::WindowProc(message, wParam, lParam);
}
UINT FileArrival(LPVOID param)
{
TCHAR lpszFileName[MAX_PATH];
char *szTime;
time_t thistime;
OpenClipboard(0);
HGLOBAL hGlobal = (HGLOBAL)GetClipboardData(CF_HDROP);
if (hGlobal)
{
HDROP hDrop = (HDROP)GlobalLock(hGlobal);
if (hDrop)
{
time(&thistime);
szTime = ctime(&thistime);
DragQueryFile(hDrop, 0, lpszFileName, MAX_PATH);
WriteLog((char*)lpszFileName,1);
GlobalUnlock(hGlobal);
}
CloseClipboard();
}
return 0;
}
This code is working fine when we copy 1 file , but when we copy multiple files it only shows the first file. Is there is any method to find out all the file names that copied to the clipboard.
You use this to find the number of files that were dropped:
UINT fileCount = DragQueryFile(hDrop, 0xFFFFFFFF, nullptr, 0);
With this information, you can allocate an array of strings and store each filename into a string in the array:
TCHAR** filenames;
// other code . . .
filenames = malloc(fileCount * sizeof(TCHAR*));
// other code . . .
for (UINT i = 0; i < fileCount; ++i) {
UINT filenameLength = DragQueryFile(hDrop, i, nullptr, 0);
filenames[i] = malloc(filenameLength);
DragQueryFile(hDrop, i, filenames[i], filenameLength);
}
I figured this all out from reading the documentation.
EDIT: To use the C++ standard library here (prevents having to manually free allocated memory), you can use the following code:
std::vector<std::basic_string<TCHAR> > filenames(fileCount);
// other code . . .
for (UINT i = 0; i < fileCount; ++i) {
UINT filenameLength = DragQueryFile(hDrop, i, nullptr, 0);
filenames[i].reserve(filenameLength);
DragQueryFile(hDrop, i, &(filenames[i][0]), filenameLength);
// Uncomment the below line and comment the above line if you can use C++17 features
// DragQueryFile(hDrop, i, filenames[i].data(), filenameLength);
}

I want to display item lastly checked in tree control mfc

I tried this
void GetCheckedItems(const CTreeCtrl& tree, CArray<HTREEITEM> *checkedItems, HTREEITEM startItem = NULL)
{
if (startItem == NULL)
startItem = tree.GetRootItem();`
for (HTREEITEM item = startItem; item != NULL; item = tree.GetNextItem(item, TVGN_NEXT))
{
// figure out if this item is checked or not
UINT state = (tree.GetItemState(item, TVIS_STATEIMAGEMASK) >> 12) & 15;
if (state == 2)
checkedItems->Add(item);
// deal with children if present
HTREEITEM child = tree.GetNextItem(item, TVGN_CHILD);
if (child != NULL)
GetCheckedItems(tree, checkedItems, child);
}
}
now I have an array contains checked values but how do i know which i checked last time.
Just use a handler to TVN_ITEMCHANGED. It is called whenever an item state is changing.
You need to check the state flags that are changed. You can detect changes to the TVIS_STATEIMAGEMASK.
CString m;
HTREEITEM selItem;
selItem=m_treeRel.GetSelectedItem();
UINT uFlags = 0;
CPoint pt(0, 0);
GetCursorPos(&pt);
m_treeRel.ScreenToClient(&pt);
HTREEITEM hItem = m_treeRel.HitTest(pt, &uFlags);
if(NULL != hItem && (TVHT_ONITEM & uFlags))
{
/*if(selItem == hItem)
{
m=_T("Selected Item....");
}
else */
if(!m_treeRel.GetCheck(hItem))
{
m=m_treeRel.GetItemText(hItem);
//m_treeRel.SetCheck(hItem,true);
}
else
{
//m=_T("ERROR");
//m_treeRel.SetCheck(hItem,false);
}
}
ShowMessage(m);
*pResult = 0;
I Used This to find out which item was checked last time....!

get the handle of a (real) monitor by its index

Suppose I have 3 monitors. How do I get the handle of the second one only by its index? EnumDisplayMonitors() won't work because it enumerates the pseudo-devices as well and EnumDisplayDevices() doesn't give me the handle.
You need to use EnumDisplayMonitors() instead of EnumDisplayDevices() to access the HMONITOR handle of each monitor.
However, monitors are not identified by index. GetMonitorInfo() can tell you which monitor is "primary", but that is all. There is no way to know which monitor is "second", "third", etc. And you can't use monitor locations to determine that, either, as the "second" monitor could be positioned anywhere in relation to the "primary" monitor, and then the "third" monitor can be positioned anywhere in relation to either "first" or "second" monitor.
So you have to hope that EnumDisplayMonitors() enumerates in the order that the monitors are installed, then you can do something like this:
struct sEnumInfo
{
int iIndex;
HMONITOR hMonitor;
};
BOOL CALLBACK GetMonitorByIndex(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
sEnumInfo *info = (sEnumInfo*) dwData;
if (--info->iIndex < 0)
{
info->hMonitor = hMonitor;
return FALSE;
}
return TRUE;
}
sEnumInfo info;
info.iIndex = 1;
info.hMonitor = NULL;
EnumDisplayMonitors(NULL, NULL, GetMonitorByIndex, (LPARAM)&info);
if (info.hMonitor != NULL)
{
//...
}
You can enumerate devices with EnumDisplayMonitors() and check if it is pseudo monitor with EnumDisplayDevices()
While you iterating through display monitors using GetMonitorInfo() you can get MONITORINFOEX with a name of a monitor's device.
Then using EnumDisplayDevices() you can get DISPLAY_DEVICE which contains StateFlags with info if current monitor is a pseudo monitor (or as in case bellow attached to desktop)
BOOL DispayEnumeratorProc(_In_ HMONITOR hMonitor, _In_ HDC hdcMonitor, _In_ LPRECT lprcMonitor, _In_ LPARAM dwData)
{
TClass* self = (TClass*)dwData;
if (self == nullptr)
return FALSE;
MONITORINFOEX monitorInfo;
::ZeroMemory(&monitorInfo, sizeof(monitorInfo));
monitorInfo.cbSize = sizeof(monitorInfo);
BOOL res = ::GetMonitorInfo(hMonitor, &monitorInfo);
if (res == FALSE)
return TRUE;
DISPLAY_DEVICE displayDevice;
::ZeroMemory(&displayDevice, sizeof(displayDevice));
displayDevice.cb = sizeof(displayDevice);
res = ::EnumDisplayDevices(monitorInfo.szDevice, 0, &displayDevice, 0);
if (res == FALSE)
return TRUE;
if (displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
self->RegisterDisplay(monitorInfo);
return TRUE;
}
void TClass::EnumerateDisplayMonitors()
{
BOOL res = ::EnumDisplayMonitors(NULL, NULL, &DispayEnumeratorProc, (LPARAM)this);
if (res == FALSE)
Print("Failed");
}
Also you can sort your monitors by iterating through EnumDisplayDevices()
If you pass NULL as first parameter to EnumDisplayDevices() it will return adapter's info based on second parameter. In this case your devices will have determined order.
You can compare DeviceName from DISPLAY_DEVICE with szDevice from MONITORINFOEX that you stored before to sort your HMONITORs
void TClass::SortDisplayMonitors()
{
DISPLAY_DEVICE displayDevice;
::ZeroMemory(&displayDevice, sizeof(displayDevice));
displayDevice.cb = sizeof(displayDevice);
std::map<std::string, DWORD> devices;
for (DWORD iDevNum = 0; ::EnumDisplayDevices(NULL, iDevNum, &displayDevice, 0) != FALSE; ++iDevNum)
devices.insert({displayDevice.DeviceName, iDevNum});
auto compare = [&devices](MONITORINFOEX& l, MONITORINFOEX& r)
{
DWORD il = -1;
DWORD ir = -1;
auto foundL = devices.lower_bound(l.szDevice);
if (foundL != devices.end())
il = foundL->second;
auto foundR = devices.lower_bound(r.szDevice);
if (foundR != devices.end())
ir = foundR->second;
return (il < ir);
};
std::sort(m_monitors.begin(), m_monitors.end(), compare);
}
PS: You can write
DWORD il = std::numeric_limits< DWORD >::max();
insted of
DWORD il = -1;
but don't forget to define NOMINMAX before you include Windows.h
you can exclude primary monitor, here is sample code (styles may vary) :
if DEVMODE dmPosition x == 0 and y == 0, then it is primary monitor.
For display devices only, a POINTL structure that indicates the
positional coordinates of the display device in reference to the
desktop area. The primary display device is always located at
coordinates (0,0).
check x, y to define second or third.
LONG second_x=0;
LONG second_y=0;
DWORD deviceNum = 0;
DISPLAY_DEVICE displayDevice;
DEVMODE devMode;
memset(&displayDevice, 0, sizeof(displayDevice));
displayDevice.cb = sizeof(DISPLAY_DEVICE);
while(EnumDisplayDevices(NULL, deviceNum, &displayDevice, 0))
{
EnumDisplaySettings(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &devMode);
if (devMode.dmPosition.x == 0 && devMode.dmPosition.y == 0)
{
// primary monitor
}
else
{
// second or third monitor
second_x = devMode.dmPosition.x;
second_y = devMode.dmPosition.y;
}
++deviceNum;
}
m_pMainWnd->SetWindowPos(NULL,(int)second_x,(int)second_y,0,0,SWP_SHOWWINDOW | SWP_NOSIZE);

Trying to get text from tooltips not working

I've been trying for a couple of hours to interrogate tooltips to give up the text they contain to no avail. I've found How to get tooltip text for a given HWND? and tried that without success.
This shouldn't be that hard. I'm just not sure what I'm doing wrong. Here's a section of my code:
BOOL CALLBACK EnumWindowsProc(
_In_ HWND hwnd,
_In_ LPARAM lParam
)
{
TCHAR className[200];
GetClassName(hwnd, className, _countof(className));
ASSERT(IsWindow(hwnd));
if (_tcscmp(className, _T("tooltips_class32")) == 0)
{
TOOLINFO ti = { 0 };
ti.cbSize = sizeof(TOOLINFO);
TCHAR text[500] = { 0 };
ti.lpszText = text;
ti.hwnd = GetParent(hwnd);
IsWindow(ti.hwnd);
ti.uId = GetDlgCtrlID(hwnd);
int result = SendMessage(hwnd, TTM_GETTEXT, _countof(text), (LPARAM)&ti);
CString info;
info.Format(_T("%p: %s \"%s\"\r\n"), hwnd, className, ti.lpszText);
CString& output = *(CString*)lParam;
output += info;
}
return 1;
}
void CTooltipVerifyDlg::OnTimer(UINT_PTR nIDEvent)
{
m_output = "";
VERIFY(EnumWindows(EnumWindowsProc, (LPARAM)&m_output));
SYSTEMTIME systemTime;
GetLocalTime(&systemTime);
CString text;
text.Format(_T("%02u:%02u:%02u.%03u\r\n"), systemTime.wHour, systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds);
m_output = text + m_output;
this->UpdateData(FALSE);
CDialogEx::OnTimer(nIDEvent);
}
CTooltipVerifyDlg is a dialogue with a text box which I communicate to with m_output, a CString that is bound to the text box.
When the SendMessage call is done, something on my desktop (or even the desktop manager) crashes. Any ideas why it would be crashing and not giving me the text that I desire?

Random crash for MFC dialog member variable not in scope

I have a MFC application with property window. I property window I have implemented a custom CMFCPropertyGridProperty, So that whenever user click the button added in that property, another dialog pops up.
void CMFCPropertyGridCustomProperty::OnClickButton(CPoint point) {
CSampleDlg* configDlg = new CSampleDlg(NULL);
INT_PTR bResult = configDlg->DoModal();
if(bResult != 1) //If user cancels bResult is 2 ,if select ok bResult is 1
{
return;
}
CString selectedOutput = configDlg->GetSelectionOutput();
CString configValue = configDlg->GetSelectionElemValue();
but the problem is , When I am running in debug mode, sometimes it is working fine but some times it crashes on configDlg->GetSelectionOutput() line. In callStack, I found that the variable is out of scope at that time.
Here is part of my dialog code:
IMPLEMENT_DYNAMIC(CSomeClassDlg, CDialogEx)
CSomeClassDlg::CSomeClassDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CSomeClassDlg::IDD, pParent)
{
m_DevNameStr = "";
m_ElemValue = "";
}
CSomeClassDlg::~CSomeClassDlg()
{
delete m_DataXMLTree;
m_DataXMLTree = NULL;
delete m_TemplateXMLTree;
m_TemplateXMLTree = NULL;
}
void CSomeClassDlg::OnItemchangedElementList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
int nPos = -1;
nPos = m_ElementListCtrl.GetNextItem(-1,LVNI_SELECTED);
if(nPos != -1)
{
elemDataNode = (CTreeNode*)(m_ElementListCtrl.GetItemData(nSelectedItemIndex));
elemTepmplateNode = GetTemplateElement(elemDataNode);
ShowElemDescription(elemDataNode,elemTepmplateNode);
}
else
{
return;
}
*pResult = 0;
}
void CSomeClassDlg::ShowElemDescription(CTreeNode* dataElemNode, CTreeNode* templateElemNode)
{
if(dataElemNode==NULL || templateElemNode==NULL)
{
m_DescEditCtrl.SetWindowTextA("");
return;
}
CString PIXITStr = templateElemNode->GetAttributeValue("AssociatedPixit");
CString descriptionStr = templateElemNode->GetAttributeValue("Description");
CString valueStr = dataElemNode->GetAttributeValue("Value");
m_DescEditCtrl.SetWindowTextA(descriptionStr);
m_DevNameStr = PIXITStr;
m_ElemValue = valueStr;
}
CString CSomeClassDlg::GetSelectionOutput()
{
return m_DevNameStr;
}
CString CSomeClassDlg::GetSelectionElemValue()
{
return m_ElemValue;
}
But I don't understand if that is the problem then why it is not happening each time. And if it is really the problem what is the best wat to get multiple member variable after doing doModal() . Please help.
Finally I have made some changes which had solved this issue.
1.changes the constructor of custom CMFCPropertyGridProperty class , parameter specially of type COleVariant.
2.Changed the dialog class constructor to take a CTreeNode* and store it as member variable. Somehow that pointer was getting corrupted after DoModal.
3.Added some Assert() to check the validity and a Redraw() in onclickbutton() of custom CMFCPropertyGridProperty class.
I do not know the exact reason for all these changes. Found a reference code in Google code
Here is the OnClickButton of the custom CMFCPropertyGridProperty class:
void CMFCPropertyGridPropertyCustomClass::OnClickButton(CPoint point)
{
ASSERT_VALID(this);
ASSERT_VALID(m_pWndList);
ASSERT_VALID(m_pWndInPlace);
ASSERT(::IsWindow(m_pWndInPlace->GetSafeHwnd()));
m_bButtonIsDown = TRUE;
Redraw();
BOOL bUpdate = FALSE;
CString selOutput = "";
CSomeClassDlg* configDlg = new CSomeClassDlg(m_selNode);
INT_PTR bResult = configDlg->DoModal();
if(bResult == IDOK)
{
bUpdate = TRUE;
//Do Something
}
if (bUpdate)
{
if (m_pWndInPlace != NULL)
{
m_pWndInPlace->SetWindowText(selOutput);
}
m_varValue = (LPCTSTR) selOutput;
}
m_bButtonIsDown = FALSE;
Redraw();
if (m_pWndInPlace != NULL)
{
m_pWndInPlace->SetFocus();
}
else
{
m_pWndList->SetFocus();
}
}
Here is the constructor of the custom CMFCPropertyGridProperty class:
CMFCPropertyGridPropertyCustomClass::CMFCPropertyGridPropertyCustomClass(CTreeNode* pNode, const CString& strName, const CString& varValue, const CString& description) : CMFCPropertyGridProperty(strName, COleVariant((LPCTSTR)varValue), description)
{
//some initialization
}