Itrying to get a out of a string sysListView32 from another process.
The list is a tree. And for the first column returns an empty string.
Tell me where is the problem?
Image of tree:
tree
Code:
vProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ |
PROCESS_VM_WRITE, False, vProcessId);
vPointer = VirtualAllocEx(vProcess, NULL, 4096, MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE);
vItem.mask = LVIF_TEXT;
vItem.iItem = I;
vItem.iSubItem = J;
vItem.cchTextMax = sizeof(vBuffer);
vItem.pszText = (wchar_t*)vPointer +sizeof(TLVItem);
WriteProcessMemory(vProcess, vPointer, &vItem, sizeof(TLVItem),
&vNumberOfBytesRead);
SendMessageW(mHandle, LVM_GETITEMW, I, (LPARAM)vPointer);
ReadProcessMemory(vProcess, (wchar_t*)vPointer +sizeof(TLVItem),
vBuffer, sizeof(vBuffer), &vNumberOfBytesRead);
RESULT = UnicodeString(vBuffer);
VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE); // free mem
CloseHandle(vProcess);
You didn't VirtualAlloc the memory block in the target process, so it isn't there.
Instead of using extreme hackery to try to get this, why not use one of the accessibility APIs to get this info cross-process. UIA, for example, goes down to XP and supports this:
http://msdn.microsoft.com/en-us/library/ms726294(VS.85).aspx
Martyn
Related
I have created pretty much standard ListView.
RECT rec;
GetClientRect(hwnd, &rec);
ListView = CreateWindow(WC_LISTVIEW, (LPCSTR)L"", (WS_CHILD | WS_VISIBLE | LVS_REPORT), 0, 0, rec.right, rec.bottom-23, hwnd, (HMENU)8553, GetModuleHandle(NULL), NULL);
SendMessage(ListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES); // Set style
Now I insert few columns like that:
LVCOLUMNW listColumnW = { 0 };
listColumnW.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
listColumnW.pszText = L"Column1";
listColumnW.cx = 150;
listColumnW.fmt = LVCFMT_LEFT;
SendMessage(ListView,LVM_INSERTCOLUMNW, 0, (LPARAM)&listColumnW);
after that I instert items and subitems
LVITEMW lisW = { 0 };
lisW.mask = LVIF_TEXT | LVIF_PARAM;
lisW.pszText = item_text[i];
lisW.iItem = i;
lisW.lParam = i;
SendMessage(ListView, LVM_INSERTITEMW, 0, (LPARAM)&lisW);
Now I'd like to implement some kind of search / filtering.
I see many people suggest to delete/reinsert items, however as there are quite a number of items in my listview that isn't the best solution.
While googling I learned that it should be possible to set groupid and set state of item (hide it).
However I'm not sure exactly how to do that, lets say I have 10 items in my listview and I want to hide some items, how exactly do I do that?
EDIT:
I have set LVM_ENABLEGROUPVIEW and created two groups, one is supposed to be shown and other one hidden.
LVGROUP group = { 0 };
group.cbSize = sizeof(LVGROUP);
group.mask = LVGF_GROUPID;
group.iGroupId = 10;//shown
ListView_InsertGroup(ListView, -1, &group);
group.iGroupId = 11;//hidden
group.mask = LVGF_GROUPID | LVGF_STATE;
group.stateMask = LVGS_HIDDEN;
group.state = LVGS_HIDDEN;
ListView_InsertGroup(ListView, -1, &group);
Now I have added some items to both groups, only problem is I can see them both (they are separated), while the other one is supposed to be hidden.
Use LVM_SETITEM to assign a list item to a specific group via the LVITEM::iGroupId member:
LVITEMW lisW = { 0 };
lisW.mask = ... | LVIF_GROUPID;
...
lisW.iItem = ...;
lisW.iGroupId = ...;
SendMessage(ListView, 0, (LPARAM)&lisW);
You can use LVM_INSERTGROUP to insert a hidden group, or use LVM_SETGROUPINFO to hide an existing group, by setting its LVGROUP::state member to LVGS_HIDDEN:
LVGROUP grp = { 0 };
grp.cbSize = sizeof(grp);
grp.mask = LVGF_STATE;
grp.iGroupId = ...;
grp.stateMask = LVGS_HIDDEN | LVGS_NOHEADER | LVGS_COLLAPSED;
grp.state = LVGS_HIDDEN | LVGS_NOHEADER | LVGS_COLLAPSED;
SendMessage(ListView, LVM_INSERTGROUP, -1, (LPARAM)&grp);
or:
SendMessage(ListView, LVM_SETGROUPINFO, grp.iGroupId, (LPARAM)&grp);
Make sure you have enabled groups via LVM_ENABLEGROUPVIEW beforehand:
SendMessage(ListView, LVM_ENABLEGROUPVIEW, TRUE, 0);
That being said, when you have lots of items to display/search/filter in a ListView, you are usually better off using the ListView in virtual mode instead. You can pre-filter your data as needed, then use LVM_SETITEMCOUNT to specify the number of items you want to display, and then handle LVN_GETDISPINFO to retrieve data for specific items when the ListView requests them from you. This way, you can do all of your searching/filtering within your datasource directly (in memory, in a database, etc), then simply invalidate the portions of the ListView that need to be refreshed onscreen using LVM_REDRAWITEMS when you have new data to display. When you have items to add/remove, you simply re-send LVM_SETITEMCOUNT. This is a much faster and flexible way to handle lots of list items.
Using virtual lists
Why is the last statement throwing Access Violation?
I wanted to write the status of a service into an XML file.
#define STR_SERVICE_STATUS_INPUT__XML_CONTENT _T("<SERVICE NAME = \"%s\" STARTUP_TYPE = \"0x%d\" />\r\n\r\n")
CString csWriteBufferTemp;
DWORD dwBufferSize;
DWORD dwBytesNeeded;
SC_HANDLE schHandle;
LPQUERY_SERVICE_CONFIG st_lpqscServiceInfo;
schHandle = OpenService(IN_schHandle, (CString)cArgentServices[i], SERVICE_QUERY_CONFIG);
bRC = QueryServiceConfig(schHandle, NULL, 0, &dwBytesNeeded);
dwBufferSize = dwBytesNeeded; //Size needed.
st_lpqscServiceInfo = (LPQUERY_SERVICE_CONFIG) LocalAlloc(LMEM_FIXED, dwBufferSize);
bRC = QueryServiceConfig(schHandle, st_lpqscServiceInfo, dwBufferSize, &dwBytesNeeded);
csWriteBufferTemp.Format(STR_SERVICE_STATUS_INPUT__XML_CONTENT__,st_lpqscServiceInfo->lpDisplayName,0);
You are almost certainly using the wrong string format parameter in _T("<SERVICE NAME = \"%S\" STARTUP_TYPE = \"0x%d\" />\r\n\r\n"). The lpServiceStartName member of SERVICE_QUERY_CONFIG and CString are both TCHAR-based so they should have matching character types regardless of whether your compiling in Unicode mode or not. In that case, you should be using %s instead of %S.
Thanks for all your replies and suggestions, I found the answer on another site. I don't know if this is the correct way but it works!
LPQUERY_SERVICE_CONFIG
st_lpqscServiceInfo;
SC_HANDLE schHandle;
st_lpqscServiceInfo = (LPQUERY_SERVICE_CONFIG) LocalAlloc(LPTR, 4096);
schHandle = OpenSCManager(IN_pszMachineName,SERVICES_ACTIVE_DATABASE, SERVICE_QUERY_CONFIG);
bRC = QueryServiceConfig(schHandle, st_lpqscServiceInfo, dwBufferSize, &dwBytesNeeded);
csWriteBufferTemp.Format(STR_SERVICE_STATUS_INPUT__XML_CONTENT__,st_lpqscServiceInfo->lpDisplayName,0);
I'm trying to use CreateProcess to open a game .exe but I'm having the unhandled exception error. I already figured out the problem and the solution, but I need to get the path for the second parameter of CreateProcess from a file dialog box (that part is done and works). The problem is:
For the second parameter of CreateProcess, I need to declare a variable with the value to it (the 2nd param), but if I "point" it to the variable of the path to the file selected in the file dialog box, it doesn't work anymore.
I'm sorry if this is a really dumb question, but I'm starting in C++.
The code that works is here:
wchar_t szGameDir[] = L"PATH_TO_EXE";
if ( CreateProcess (
NULL,
szGameDir, NULL, NULL, FALSE,
CREATE_UNICODE_ENVIRONMENT,
NULL, NULL,
&pstStartupInfo, &pstProcInfo ) )
But when I set szGameDir to the value of 'pszGameDir' (path to the EXE selected by the user), it gives the unhandled exception error...
wchar_t* szGameDir = pszGameDir;
if ( CreateProcess (
NULL,
szGameDir, NULL, NULL, FALSE,
CREATE_UNICODE_ENVIRONMENT,
NULL, NULL,
&pstStartupInfo, &pstProcInfo ) )
And this is where I initialize 'pszGameDir':
OPENFILENAME DialogBox;
ZeroMemory ( &DialogBox, sizeof(DialogBox) );
DialogBox.lStructSize = sizeof(OPENFILENAME);
DialogBox.hwndOwner = NULL;
DialogBox.lpstrFilter = L"Grand Theft Auto: Vice City (*.exe)\0*.exe\0";
DialogBox.lpstrFile = (LPTSTR)this->pszGameDir;
DialogBox.nMaxFile = MAX_PATH;
DialogBox.nMaxFileTitle = sizeof ( L"gta-vc.exe" );
DialogBox.lpstrTitle = L"Please, select 'gta-vc.exe'";
DialogBox.Flags = 0x02000000 | 0x00001000 | 0x00000400 | 0x10000000 | 0x00020000 | 0x00000800 | 0x0000008;
DialogBox.nFileExtension = (WORD)"exe";
DialogBox.lpstrDefExt = L"exe";
return GetOpenFileName ( &DialogBox ) != 0 ? 1 : NULL;
Can someone help me? (Yes, I did search already, but honestly I haven't found about this specific thing, I wasn't able to fix it either..)
CreateProcess needs the command line parameter to be writeable. Read the description of the argument at MSDN:
The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.
It has to be writeable, I am afraid.
VS2010 introduced CMFCShellTreeCtrl which allows for a folder-browser tree ctrl to be dropped into our MFC apps.
However, there seems to be a serious lack of filtering capabilities in this class. i.e. it will build the list of container-objects (IShellFolder). But there doesn't seem to be a way to specify that .zip containers should not be displayed in the folder tree.
It does supply a virtual that could be used crudely for this purpose:
HRESULT CMFCShellTreeCtrl::EnumObjects(HTREEITEM hParentItem, LPSHELLFOLDER pParentFolder, LPITEMIDLIST pidlParent)
{
ASSERT_VALID(this);
ASSERT_VALID(afxShellManager);
LPENUMIDLIST pEnum = NULL;
HRESULT hr = pParentFolder->EnumObjects(NULL, m_dwFlags, &pEnum);
if (FAILED(hr) || pEnum == NULL)
{
return hr;
}
LPITEMIDLIST pidlTemp;
DWORD dwFetched = 1;
// Enumerate the item's PIDLs:
while (SUCCEEDED(pEnum->Next(1, &pidlTemp, &dwFetched)) && dwFetched)
{
TVITEM tvItem;
ZeroMemory(&tvItem, sizeof(tvItem));
// Fill in the TV_ITEM structure for this item:
tvItem.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
// AddRef the parent folder so it's pointer stays valid:
pParentFolder->AddRef();
// Put the private information in the lParam:
LPAFX_SHELLITEMINFO pItem = (LPAFX_SHELLITEMINFO)GlobalAlloc(GPTR, sizeof(AFX_SHELLITEMINFO));
ENSURE(pItem != NULL);
pItem->pidlRel = pidlTemp;
pItem->pidlFQ = afxShellManager->ConcatenateItem(pidlParent, pidlTemp);
pItem->pParentFolder = pParentFolder;
tvItem.lParam = (LPARAM)pItem;
CString strItem = OnGetItemText(pItem);
tvItem.pszText = strItem.GetBuffer(strItem.GetLength());
tvItem.iImage = OnGetItemIcon(pItem, FALSE);
tvItem.iSelectedImage = OnGetItemIcon(pItem, TRUE);
// Determine if the item has children:
DWORD dwAttribs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_DISPLAYATTRMASK | SFGAO_CANRENAME | SFGAO_FILESYSANCESTOR;
pParentFolder->GetAttributesOf(1, (LPCITEMIDLIST*) &pidlTemp, &dwAttribs);
tvItem.cChildren = (dwAttribs & (SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR));
// Determine if the item is shared:
if (dwAttribs & SFGAO_SHARE)
{
tvItem.mask |= TVIF_STATE;
tvItem.stateMask |= TVIS_OVERLAYMASK;
tvItem.state |= INDEXTOOVERLAYMASK(1); //1 is the index for the shared overlay image
}
// Fill in the TV_INSERTSTRUCT structure for this item:
TVINSERTSTRUCT tvInsert;
tvInsert.item = tvItem;
tvInsert.hInsertAfter = TVI_LAST;
tvInsert.hParent = hParentItem;
InsertItem(&tvInsert);
dwFetched = 0;
}
pEnum->Release();
return S_OK;
}
What I am confused by is the lack of ability to distinguish what type of object this is that is being enumerated (or a better way to control the enumeration in the first place so as to filter out non-filesystem objects such as these).
It's possible to look at the text for the item being enumerated, and simply exclude it if it ends in ".zip". However, this seems wonky to me. After all, an arbitrary folder could be named XYZ.zip (which still being a folder, and not a zip archive). Similarly, there are likely other archive types than just .zip that may be yet supported in the future - or other container types which are nevertheless not really folders.
I don't want to eliminate things like "Network" and "Computer" from being valid nodes. Just ones that are problematic such as "Downloads\Foobar.zip"
I apologize for this question being rambling. Any help in improving my understanding and creative approaches to knowing what sorts of objects are part of Microsoft's shell namespace, and how that they can be gainfully used would be appreciated!
A zip file/folder would have SFGAO_STREAM/SFGAO_DROPTARGET along with SFGAO_FOLDER, so if you can read the shell item as stream, then it probably isn't a directory. Another way to tell is to use SHGetPathFromIDList+PathIsDirectory, however this only works for pidl that have file system paths.
There are other kind of browsable files too, like a saved search (and if you browse into the file it takes like forever to finish enumerating the items), so you probably want to think about how to deal with those files too.
I got a certain registry key (created by our software) which needs to be removed on each local user account at some point.
Thus, I try to load the users hive and then use SHDeleteKey (as the key is not empty) to get the job done.
However, SHDeleteKey always returns LSTATUS 2 (ERROR_FILE_NOT_FOUND).
The Registry key for each user is placed under
HKCU\Software\XYZ
First, I set the required privileges within my code, which seems to work (return val is TRUE):
(...)
HANDLE th;
LUID rsto;
LUID bckp;
TOKEN_PRIVILEGES tp;
TOKEN_PRIVILEGES tp2;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &th);
LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &rsto);
LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &bckp);
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tp.Privileges[0].Luid = rsto;
tp2.PrivilegeCount = 1;
tp2.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tp2.Privileges[0].Luid = bckp;
BOOL p = AdjustTokenPrivileges(th, 0, &tp, 1024, 0, 0);
BOOL p2 = AdjustTokenPrivileges(th, 0, &tp2, 1024, 0, 0);
(...)
Then I use RegloadKey to load the users hive.
std::string connection contains the path to the respective ntuser.dat file. username is the local user account name.
So the hive should be loaded under HKEY_USERS\username:
(...)
DWORD result = RegLoadKey(HKEY_USERS, username.c_str(), connection.c_str());
return result == ERROR_SUCCESS;
(...)
Now, I try to delete:
(...)
k = username + "\\Software\\XYZ";
result = SHDeleteKey(HKEY_USERS, k.c_str());
And now result has value of 2.
But the key exists.
What am I doing wrong?
Thank you in advance...
UPDATED INFO:
I realized the problem needs to be somewhere on RegLoadKey.
When I load the hive via command line (REG.exe load "HKU\username" ...), I can see the node "username" under HKEY_USERS within regedit.exe. All child nodes are loaded under that node.
When I pause my program after RegLoadKey, the node "username" is also shown under HKEY_USERS, but the node is visualized as empty, so no child nodes are available.
How can this happen? This problem is driving me nuts.
Looked at my code again today and I just saw that I loaded "ntuser.BAT" instead of "ntuser.DAT" (both files exist).
I'm really sorry that I wasted your time. :-/