Thread: Subitems in ListView and

  1. #1
    HelpingYouHelpUsHelpUsAll
    Join Date
    Dec 2007
    Location
    In your nightmares
    Posts
    223

    Subitems in ListView and

    For my Task Manager Program I am using a listview in report mode to display the list of running processes. The problem is how do you add subitems? The function i am using only adds itmes in column 0 and not any other column. Here is the function:

    Code:
    BOOL AddItem(char *item1,char *item2, char *item3, char *item4) {
    
        int i;
    	LVITEM LvI;
    	LvI.mask = LVIF_TEXT;
    	LvI.iItem = 0;
    	for (i=0;i<4;i++) {
    		LvI.iSubItem = i;
    		if (i == 0) LvI.pszText = item1;
    		if (i == 1) LvI.pszText = item2;
    		if (i == 2) LvI.pszText = item3;
    		if (i == 3) LvI.pszText = item4;
    		ListView_InsertItem(GetDlgItem(hwndMain, 3001), &LvI);
    	    }
    	return 1;
    }
    I have set up the columns allright and created the list view without using resources (ie. at runtime). Also I have a function that gets the names of all of processes and returns the number (to fill up the listview), which I want to refresh every second, howver the PdhEnumObjectItems function caches the list of processes and will only retreive the same list of processes each time. How can this be changed? I know to use the PdhEnumObjects Function and set the bRefresh param to TRUE. But what about the other parameters? Here is the function I already have, notice it calls the above AddItem().
    Code:
    int getProcesses() {
    	int numProcesses = 0;
    	PDH_STATUS pdhStatus = ERROR_SUCCESS;
    	LPTSTR szCounterListBuffer = NULL;
    	DWORD dwCounterListSize = 0;
    	LPTSTR szInstanceListBuffer = NULL;
    	DWORD dwInstanceListSize = 0;
    	LPTSTR szThisInstance = NULL;
    	// call the function to determine the required buffer size for the data
    	pdhStatus = PdhEnumObjectItems(NULL,NULL,"Process",szCounterListBuffer,
    		&dwCounterListSize,szInstanceListBuffer,&dwInstanceListSize,
    		PERF_DETAIL_WIZARD,0);
    	if (pdhStatus != ERROR_SUCCESS && pdhStatus != PDH_MORE_DATA) return 1;
    
    	szCounterListBuffer = (LPTSTR)malloc (dwCounterListSize );
    	szInstanceListBuffer = (LPTSTR)malloc(dwInstanceListSize);
    	if ((szCounterListBuffer == NULL) ||
    		    (szInstanceListBuffer == NULL)) {
    		//printf ("\nUnable to allocate buffers");
    		return 1;
    	}
    	pdhStatus = PdhEnumObjectItems(NULL, // reserved
    		NULL, // local machine
    		"Process", // object to enumerate
    		szCounterListBuffer, // pass in NULL buffers
    		&dwCounterListSize, // an 0 length to get
    		szInstanceListBuffer, // required size
    		&dwInstanceListSize, // of the buffers in chars
    		PERF_DETAIL_WIZARD, // counter detail level
    		0);
    	if (pdhStatus == ERROR_SUCCESS){
    		// walk the return instance list
    		for (szThisInstance = szInstanceListBuffer;
    *szThisInstance != 0;
    szThisInstance += strlen(szThisInstance) + 1) {
    			    //SendDlgItemMessage(hwndMain,IDLIST,LB_SETITEMDATA,1,(LPARAM)"hi");
    			if (szThisInstance != "_Total") {
    				AddItem(szThisInstance, szInstanceListBuffer, "item3", "item4");
    				numProcesses+=1;
    			}
    		}
    	}
    	if (szCounterListBuffer != NULL) free (szCounterListBuffer);
    	if (szInstanceListBuffer != NULL) free (szInstanceListBuffer);
    	return numProcesses;
    }

  2. #2
    HelpingYouHelpUsHelpUsAll
    Join Date
    Dec 2007
    Location
    In your nightmares
    Posts
    223
    Sorry for double posting, but i found out how to add sub items to a listview thanks to http://www.rohitab.com/discuss/index...howtopic=27236. AddItem is replaced with the following:
    Code:
    void AddIndex(int index, char *text) {
    
    	LVITEM LvI;
    	LvI.mask = LVIF_TEXT;
    	LvI.iItem = index;
    	LvI.iSubItem = 0;
    	LvI.pszText = text;
    	ListView_InsertItem(GetDlgItem(hwndMain, 3001), &LvI);
    }
    
    void AddRow(char *text, int index, int col)
    {
         ListView_SetItemText(GetDlgItem(hwndMain, 3001), index, col, text);
    }
    And so the end of the getProcesses function will be:
    Code:
    AddIndex(numProcesses, szThisInstance);
    AddRow("PID", numProcesses, 1);

  3. #3
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by P4R4N01D View Post
    Code:
    		// walk the return instance list
    		for (szThisInstance = szInstanceListBuffer;
    *szThisInstance != 0;
    szThisInstance += strlen(szThisInstance) + 1) {
    			    //SendDlgItemMessage(hwndMain,IDLIST,LB_SETITEMDATA,1,(LPARAM)"hi");
    			if (szThisInstance != "_Total") {
    				AddItem(szThisInstance, szInstanceListBuffer, "item3", "item4");
    				numProcesses+=1;
    			}
    		}
    That's... confusing. Suggest you actually make that for a single line or at least indent all that code inside for one more indent than the actual for.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  4. #4
    HelpingYouHelpUsHelpUsAll
    Join Date
    Dec 2007
    Location
    In your nightmares
    Posts
    223
    Yeah, sorry that wasn't laid out very well as it would make a pretty long line. I have fixed the layout of the function and this is what it looks like directly continuing on from your quote:

    Code:
    }
    	DWORD PIDs[numProcesses-1], cbNeeded, cPIDs;
    	EnumProcesses(PIDs,sizeof(PIDs),&cbNeeded);
    
    	for (i = 0; i < numProcesses-1; i++) {
                if(PIDs[i] != 0) {
    	        AddRow(itoa(PIDs[i],priorityBuf,10), i, 1);
    	        GetProcessInfo(PIDs[i], i);
    	    }
    	}
    The rest is unchanged from the original getProcesses() function. However, I think EnumProcesses() is getting the Process IDs in a different order to PdhEnumObjectItems() gets the names of all the processes. The result is the PIDs don't match the process names they are against. Also, this affects the call to GetProcessInfo() - which gets the Priority of the process and adds it to the list - so the priority class doesn't match the process either. I am sure of this by comparing the PIDs and Process Prioritys with task Manager and Process Explorer and they don't match. Also I set the program's priority to realtime and it doesn't even work for that. Does anyone know of a more full proof way of doing this?

  5. #5
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    I would create a structure to hold the details of a process.

    Make an array or linked list of the structures.

    This would allow you to write functions to match, display, add and remove processes.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  6. #6
    HelpingYouHelpUsHelpUsAll
    Join Date
    Dec 2007
    Location
    In your nightmares
    Posts
    223
    Thanks for the suggestion, I thought of getting the list of Process IDs, then using that to open a handle to each process and individually get its name and info: like priority class memory usage ect. I can get the list of PIDs using EnumProcesses and open each process. But on msdn i couldn't find a way of getting the name of the process given the handle (MUST be for WIN XP, not Vista). I don't require the full path to the process, just the short file name. E.g. services.exe. I think it is a better option to use an array instead of a struct.

  7. #7
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    But on msdn i couldn't find a way of getting the name of the process given the handle (MUST be for WIN XP, not Vista).
    Have you checked out CreateToolHelp32Snapshot as far as getting the name of the process?

  8. #8
    HelpingYouHelpUsHelpUsAll
    Join Date
    Dec 2007
    Location
    In your nightmares
    Posts
    223
    Thanks BobS0327, i'll have a look at system snapshots

  9. #9
    HelpingYouHelpUsHelpUsAll
    Join Date
    Dec 2007
    Location
    In your nightmares
    Posts
    223
    I have tried CreateToolhelp32Snapshot and pe32.szExeFile always equals "[System Process]". Any ideas why this happens? Here is how i am calling it, this is all that has changed:

    Code:
    DWORD PIDs[numProcesses], cbNeeded, cPIDs;
    EnumProcesses(PIDs,sizeof(PIDs),&cbNeeded);
    
    cPIDs = cbNeeded / sizeof(DWORD);
    for (i = 0; i < cPIDs; i++) {
      	       hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, PIDs[i]);
                   pe32.dwSize = sizeof(PROCESSENTRY32);
                   Process32First(hProcessSnap, &pe32);
    	       GetProcessInfo(PIDs[i], i, pe32);
    	       HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PIDs[i]);
    	       //AddIndex(0, pe32.szExeFile);
    }

  10. #10
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    EnumProcessses really isn't used with CreateToolHelp32Snapshot. Here is an example of how to use CreatetoolHelp32Snapshot.

    Here is a code example of how to use EnumProcesses to acquire the process name:

    Code:
    #include <windows.h>
    #include <stdio.h>
    #include <psapi.h>
    
    #pragma comment( lib, "psapi.lib" )  
    
    int main(void)
    {
    	HMODULE hModule;
    	char szProcessName[MAX_PATH] = {0};
    	DWORD dwProcesses[1024], cbNeeded, cProcesses;
    	unsigned int i;
    	if ( !EnumProcesses( dwProcesses, sizeof(dwProcesses), &cbNeeded ) )
    		return -1;
    	cProcesses = cbNeeded / sizeof(DWORD);
    	for ( i = 0; i < cProcesses; i++ )
    		if( dwProcesses[i] != 0 )
    		{
    			HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
    				PROCESS_VM_READ, FALSE, dwProcesses[i] );
    			if (NULL != hProcess )
    			{
    				strcpy(szProcessName, "Unknown");
    				if ( EnumProcessModules( hProcess, &hModule, sizeof(hModule), 
    					&cbNeeded) )
    				{
    					GetModuleBaseName( hProcess, hModule, szProcessName, 
    						sizeof(szProcessName)/sizeof(CHAR) );
    				}
    			}
    			printf("%s  pid: %u\n", szProcessName, dwProcesses[i] );
    			CloseHandle( hProcess );
    		}   
    	return 0;   
    }

  11. #11
    HelpingYouHelpUsHelpUsAll
    Join Date
    Dec 2007
    Location
    In your nightmares
    Posts
    223
    Thanks again BobS0327, I now have the list displaying the process names and doesn't cache the data so it can be refreshed. The problem now is that I can not add the PIDs into the second column. I have the following functions:
    Code:
    void AddRow(char *text, int index, int col)
    {
         ListView_SetItemText(GetDlgItem(hwndMain, 3001), index, col, text);
    }
    
    int getProcesses() {
            HMODULE hModule;
    	char szProcessName[MAX_PATH] = {0};
    	DWORD dwProcesses[1024], cbNeeded, cProcesses;
    	unsigned int i;
    	char PIDbuf[10];
    
    	if (!EnumProcesses(dwProcesses, sizeof(dwProcesses), &cbNeeded))
    		return -1;
    	cProcesses = cbNeeded / sizeof(DWORD);
    	for (i = 0; i < cProcesses; i++)
    		if(dwProcesses[i] != 0)
    		{
    			HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
    				PROCESS_VM_READ, FALSE, dwProcesses[i]);
    			if (NULL != hProcess)
    			{
    				if (EnumProcessModules(hProcess, &hModule, sizeof(hModule),
    					&cbNeeded))
    				{
    					GetModuleBaseName(hProcess, hModule, szProcessName,
    						sizeof(szProcessName)/sizeof(CHAR));
    				}
    			}
    			AddIndex(i, szProcessName);
    			itoa(dwProcesses[i],PIDbuf,10);
    	     	        AddRow(PIDbuf,i,1);
    			CloseHandle(hProcess);
    		}
    	return cProcesses;
    }
    The addIndex function works perfectly, so there is no need to worry about that, It accepts the index and text and puts that into column 0. The AddRow function did work before, so that may not be the problem. Note: GetDlgItem(hwndMain, 3001) is the correct handle to the ListView.
    long time no C; //seige
    You miss 100% of the people you don't C;
    Code:
    if (language != LANG_C && language != LANG_CPP)
        drown(language);

  12. #12
    Madly in anger with you
    Join Date
    Nov 2005
    Posts
    211
    you can't just add columns with ListView_SetItemText like that. the columns you are referring to are specified in the iSubItem member of the LVITEM structure. your AddRow function should look something similar to this:

    Code:
    void AddRow(char *text, int index, int col)
    {
        LVITEM lvItem;
    
        lvItem.mask = LVIF_TEXT;
        lvItem.cchTextMax = 10;
    
        lvItem.iItem = index;
        lvItem.iSubItem = col;
        lvItem.pszText = text;
    
        ListView_SetItem(GetDlgItem(hwndMain, 3001), &lvItem);
    }

  13. #13
    HelpingYouHelpUsHelpUsAll
    Join Date
    Dec 2007
    Location
    In your nightmares
    Posts
    223
    Thanks, that was a good suggestion as now i can combine the addIndex and AddRow functions into one. However it still does not solve the problem. Here are the two functions AddIndex and getProcesses:
    Code:
    void AddIndex(char *text, int index, int col) {
    
    	LVITEM LvI;
    	LvI.mask = LVIF_TEXT;
    	LvI.cchTextMax = 10;
    	LvI.iItem = index;
    	LvI.iSubItem = col;
    	LvI.pszText = text;
    	if (col > 0)
    	   SendMessage(GetDlgItem(hwndMain, 3001),LVM_SETITEM,0,(LPARAM)&LvI)
            else ListView_InsertItem(GetDlgItem(hwndMain, 3001), &LvI);
    }
    
    int getProcesses() {
    
    	HMODULE hModule;
    	char szProcessName[MAX_PATH] = {0};
    	DWORD dwProcesses[1024], cbNeeded, cProcesses;
    	unsigned int i;
    	char PIDbuf[10];
    
    	if (!EnumProcesses(dwProcesses, sizeof(dwProcesses), &cbNeeded))
    		return -1;
    	cProcesses = cbNeeded / sizeof(DWORD);
    	for (i = 0; i < cProcesses; i++)
    		if(dwProcesses[i] != 0)
    		{
    			HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
    				PROCESS_VM_READ, FALSE, dwProcesses[i]);
    			if (NULL != hProcess)
    			{
    				//strcpy(szProcessName, "Unknown");
    				if (EnumProcessModules(hProcess, &hModule, sizeof(hModule),
    					&cbNeeded))
    				{
    					GetModuleBaseName(hProcess, hModule, szProcessName,
    						sizeof(szProcessName)/sizeof(CHAR));
    				}
    			}
    			AddIndex(szProcessName, i, 0);
    			itoa(dwProcesses[i],PIDbuf,10);
    			AddIndex(PIDbuf, i, 1);
    			CloseHandle(hProcess);
    		}
    	return cProcesses;
    }
    using some error trapping combined with MessageBoxes, I hove found that SendMessage(GetDlgItem(hwndMain, 3001),LVM_SETITEM,0,(LPARAM)&LvI) fails (& returns FALSE) when I try to insert the PIDs into the second column with AddIndex(PIDbuf, i, 1);. I beleive it is the index (i) that is strangley large and out of bounds of the first column. Also I have to have if (col > 0) there or nothing works.
    long time no C; //seige
    You miss 100% of the people you don't C;
    Code:
    if (language != LANG_C && language != LANG_CPP)
        drown(language);

  14. #14
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    I beleive it is the index (i) that is strangley large and out of bounds of the first column. Also I have to have if (col > 0) there or nothing works.
    The i variable should be no greater than the number of processes identified by EnumProcesses. It's usually in the ballpark range of 50 to 80.

    Try clearing out the LvI structure:

    Code:
    void AddIndex(char *text, int index, int col) {
    
    	LVITEM LvI;
            memset(&lvI, 0, sizeof(LVITEM)); 	
            LvI.mask = LVIF_TEXT;
    	LvI.cchTextMax = 10;
    	LvI.iItem = index;
    	LvI.iSubItem = col;
    	LvI.pszText = text;
    	if (col > 0)
    	   SendMessage(GetDlgItem(hwndMain, 3001),LVM_SETITEM,0,(LPARAM)&LvI)
            else ListView_InsertItem(GetDlgItem(hwndMain, 3001), &LvI);
    }

  15. #15
    HelpingYouHelpUsHelpUsAll
    Join Date
    Dec 2007
    Location
    In your nightmares
    Posts
    223
    Sorry, that too didn't work. I have checked and the right values are being passed the AddIndex but the SendMessage fails. The index is in range with the values being within the bounds of the number of processes identifies by EnumProcesses (ie. they go from 0 to howver many processes are running).One of the most annoying things is that SendMessage() returns a value < 1 if it fails and doesn't say annything about what wnet wrong. Also this only happens with columns other than the first (0th).
    long time no C; //seige
    You miss 100% of the people you don't C;
    Code:
    if (language != LANG_C && language != LANG_CPP)
        drown(language);

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. ListView Refresh, Update
    By de4th in forum C++ Programming
    Replies: 1
    Last Post: 12-23-2006, 09:13 AM
  2. Replies: 6
    Last Post: 07-10-2006, 12:05 AM
  3. Moving items in a ListView
    By Cactus_Hugger in forum Windows Programming
    Replies: 1
    Last Post: 01-18-2006, 09:40 PM
  4. Troubles with ListView and Toolbar
    By cornholio in forum Windows Programming
    Replies: 8
    Last Post: 11-14-2005, 01:26 AM
  5. Listview??
    By SuperNewbie in forum C# Programming
    Replies: 4
    Last Post: 02-13-2003, 03:34 AM