Pulizia Memoria per uso di Struct

di il
5 risposte

Pulizia Memoria per uso di Struct

Ciao a tutti,
questo è il primo post che pubblico su questo forum.
Premetto che sono un neofita per quanto riguarda C/C++, vi chiedo pertanto molta pazienza se quello che scrivo può risultare poco preciso.

Fin'ora navigando sul web sono riuscito a trovare le "soluzioni" ai miei problemi, questa volta ho difficoltà a trovarne una e, prima di prendere una decisione diversa, vorrei sottoporvelo nella speranza che di ricevere un suggerimento.

Sto creando un programma Win32 console da usare in un script.
Tra le cose che fa, scansiona i processi attivi.
Di questi processi prende solo queste informazioni: PID, hWND, ParentPID, ParentHWND, processname, windowstitle, pathexe e commandline.
Sò che un PID può avere più hWND in base al numero di finestre che apre, ma per me è sufficiente che me ne dia uno solo.
Uno degli obiettivi è che se uno tra: processname, windowstitle, pathexe o commandline contiene una stringa passata nell'argument, vado a farmi una "risalita" sui Parent (non ancora implementato), dove, per ogni Parent, vado a prendermi le stesse info.

Immaginandomi di dover ciclare tra i Parent, ho pensato di crearmi una STRUCT che raccogliesse le info necessarie di ciascun processo:

struct PROCESS_DATA
{
    HWND  hWndID;
    DWORD ProcessID;

    HWND  hWndParentID;
    DWORD ParentProcessID;

    char  ProcessName[MAX_PATH];
    char  WindowsTitle[MAX_PATH];
    char  PathExe[MAX_PATH];
    char  CommandLine[MAX_PATH];
};
Nella callback EnumWindows ho fatto questo:

BOOL CALLBACK ListWindowsProc(HWND hwnd, LPARAM lParam)
{
	int style = GetWindowLong(hwnd, GWL_STYLE);

    // Windows with child flag set
    if ( swDBG ) cout << "Check if is a popup window" << endl;
    if(style & WS_CHILD) {
        if ( swDBG ) cout << "It is. Excluded" << endl;
        return true;
    }

    int exstyle = GetWindowLong(hwnd, GWL_EXSTYLE) ;
    if((exstyle & WS_EX_TOOLWINDOW) && ((style & WS_POPUP) || (GetWindow(hwnd, GW_OWNER) != NULL))) {
        if ( swDBG ) cout << "It is. Excluded" << endl;
        return true;
    }

    PROCESS_DATA pdProcData = GetProcessData(hwnd);
    PrintProcessData(pdProcData);
    
    return true;
}
Mi sono creato, quindi, la funzione che vedete sotto, che restitusce la struttura popolata.

PROCESS_DATA GetProcessData(HWND hwnd) {
    PROCESS_DATA pdl;

    pdl.hWndID = hwnd;
    GetWindowThreadProcessId(hwnd, &pdl.ProcessID);

    pdl.hWndParentID = GetParent(hwnd);
    GetWindowThreadProcessId(pdl.hWndParentID, &pdl.ParentProcessID);
    GetProcessName(pdl.ProcessID, pdl.ProcessName);

    GetWindowText(hwnd, pdl.WindowsTitle, sizeof(pdl.WindowsTitle));
    GetProcessPathExe(pdl.ProcessID, pdl.PathExe);
    GetPidCommandLine(pdl.ProcessID, pdl.CommandLine);
    
    return pdl;
}
sembra tutto bellissimo, ma il programma va in errore perchè riempie la memoria (memory leak).
Praticamente la variabile di tipo STRUCT non si distrugge.

Come controprova, ho provato a usare direttamente le variabili senza la struttura e il programma arriva in fondo.
Ho provato a definire la variable: pdProcData a livello globale, ho pensato, la creo una volta sola e la popolo di volta in volta, non cambia nulla, va lo stesso in errore.

Ho ragionato, sbagliando evidentemente, come faccio anche con altri linguaggi (tipo java, c#, vb), uscendo dalla funzione, si "distruggono" le variabili.

Spero di aver esposto chiaramente il mio problema. Qualcuno mi sa dire come risolvere ?

5 Risposte

  • Re: Pulizia Memoria per uso di Struct

    Per poter capire dove sta il problema, bisogna dare un'occhiata completa al codice e compilarlo.

    Ma non hai riportato larghe parti di codice (dove potrebbe stare l'errore), compreso il main ...

    Il codice che riporti deve essere compilabile senza errori.
  • Re: Pulizia Memoria per uso di Struct

    Su che LIBRI stai studiando?
  • Re: Pulizia Memoria per uso di Struct

    migliorabile ha scritto:


    Su che LIBRI stai studiando?
    Ciao migliorabile,
    sperando di non farti arrabbiare (e di conseguenza farmi bannare), non sto studiando su alcun libro .
    Non ho molto tempo libero per leggere. Ho diversi anni di programmazione alle spalle e faccio affidamento su questo andando, di volta in volta, a leggere (e quindi imparare) dal web.
    Inoltre penso che "sporcandosi" le mani e scontrandosi con i problemi di un mio progetto e non di un semplice esercizio proposto da un testo che, tralaltro, sono ad-hoc per quello che spiegano, si apprenda molto velocemente.
    Certo non si ha una conoscenza "strutturata" di un linguaggio (soprattutto me ne sto accorgendo con C/C++) e questo mio post mostra tutta la mia monumentale lacuna.
    Ma non essendo questo il mio "lavoro" principale... mi serve quanto basta e fin'ora, tra web e forum, me la sono sempre cavata...
  • Re: Pulizia Memoria per uso di Struct

    Diversi anni di programmazione?
    Quel programma mi sa tanto di copia - incolla, comunque niente panico.
    É successo a tutti di essere principianti (forse non per anni).
    posta il sorgente e vediamo che si può fare.
  • Re: Pulizia Memoria per uso di Struct

    Vi ringrazio in anticipo del confronto, indipendentemente se arriverò o meno ad una soluzione.
    Per la compilazione io sto usando: Dev-C++ 5.7.1 con MinGW GCC 4.8.1 32-bit.
    Uso questo perchè altri IDE C/C++ portable non ne ho trovati. Ho letto che non è il massimo, ma come ho scritto nel mio primo post, mi basta che faccia quello che deve...

    questo è il codice

    nella linkage ho aggiunto il comando: -lpsapi

    ListProcess.cpp
    
    /*
     */
    #include <windows.h>
    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    
    //For GetProcessName
    #include <Psapi.h>
    
    //Stuct
    struct PROCESS_DATA
    {
        HWND  hWndID;
        DWORD ProcessID;
    
        HWND  hWndParentID;
        DWORD ParentProcessID;
    
        char  ProcessName[MAX_PATH];
        char  WindowsTitle[MAX_PATH];
        char  PathExe[MAX_PATH];
        char  CommandLine[MAX_PATH];
    };
    
    /* test without stucture
    HWND  hWndID;
    DWORD ProcessID;
    
    HWND  hWndParentID;
    DWORD ParentProcessID;
    
    char  ProcessName[MAX_PATH];
    char  WindowsTitle[MAX_PATH];
    char  PathExe[MAX_PATH];
    char  CommandLine[MAX_PATH];
    */
    
    //Global Variables
    bool swDBG;
    //PROCESS_DATA pdProcData;  /* test as global variable */
    
    //Functions Definition
    bool ParseArguments(int argc, char **argv, bool *swDBG, bool *swLP);
    BOOL CALLBACK ListWindowsProc(HWND hwnd, LPARAM lParam);
    PROCESS_DATA GetProcessData(HWND hwnd);
    void PrintProcessData(PROCESS_DATA pd);
    void GetProcessName (DWORD dwPid, char* sProcName);
    void GetProcessPathExe (DWORD dwPid, char* sPathExe);
    //void GetProcessData2(HWND hwnd);    /* test without structure */
    //void PrintProcessData2();           /* test without structure */
    
    //command Line Section
    #include "CommandLine.h"
    
    int WINAPI WinMain(
        HINSTANCE hInst,
        HINSTANCE hPrevInst,
        LPSTR lpCmdLine,
        int nShowCmd)
    {
    	bool swLP    = false;
    	swDBG = false;
     	
        if (!ParseArguments(_argc, _argv, &swDBG, &swLP))
        {
            cerr << "Error parsing arguments." << endl;
            return 10;
    	}
    
        if ( swDBG ) {
        	cout << "[DBG] ==================================================" << endl;
        	cout << "[DBG]  Received Arguments" << endl;
        	cout << "[DBG]  ";
        	for(int i = 1; i < _argc; i++) { cout << _argv[i] << " "; }
        	cout << endl;
    		cout << "[DBG] --------------------------------------------------" << endl;
        	cout << "[DBG]  Detected Arguments" << endl;
        	cout << endl;
            cout << "[DBG]           Debug " << swDBG << endl;
            cout << "[DBG]    List Process " << swLP << endl;
        	cout << "[DBG] ==================================================" << endl;
        }
    
    	if ( swLP )
    	{
            bool success = EnumWindows(ListWindowsProc, 0);
    		return 0;
    	}
    
    	return 0;
    }
    
    bool ParseArguments(int argc, char **argv, bool *swDBG, bool *swLP)
    {
        *swDBG   = false;
        *swLP    = false;
    
       	string option;
    
        // Check command line parameter count
        // argc < 2 means no arguments
        if (argc < 2)
        {
            cerr << "Error, no arguments." << endl;
            return false;
        }
    
        for(int i = 1; i < argc; i++) {
    
           if ( *swDBG ) cout << "[DBG] " << "Parsing argument: " << argv[i] << endl;
    
           if ( !*swDBG ) {
              option = "/Debug";
              if ( stricmp(argv[i], option.c_str()) == 0 ) {
     		     *swDBG = true; 
    
     		     continue;
              }
           }
    
           if ( !*swLP ) {
              option = "/ListProcess";
              if ( stricmp(argv[i], option.c_str()) == 0 ) {
     		     *swLP = true; 
    
                 if ( *swDBG ) cout << "[DBG] " << "It's a List Process" << endl;
    
     		     continue;
              }
           }
    
           if ( !*swLP ) {
              option = "/LP";
              if ( stricmp(argv[i], option.c_str()) == 0 ) {
     		     *swLP = true; 
    
                 if ( *swDBG ) cout << "[DBG] " << "It's a List Process" << endl;
    
     		     continue;
              }
           }
    
           cerr << "Unknown Argument: " << argv[i] << endl;
           return false;
        }
        
        return true;
    }
    
    BOOL CALLBACK ListWindowsProc(HWND hwnd, LPARAM lParam)
    {
    	int style = GetWindowLong(hwnd, GWL_STYLE);
    
        // Windows with child flag set
        if ( swDBG ) cout << "Check if is a popup window" << endl;
        if ( style & WS_CHILD ) {
            if ( swDBG ) cout << "It is. Excluded" << endl;
            return true;
        }
    
        int exstyle = GetWindowLong(hwnd, GWL_EXSTYLE) ;
        if ( (exstyle & WS_EX_TOOLWINDOW) && ( (style & WS_POPUP) || (GetWindow(hwnd, GW_OWNER) != NULL) ) ) {
            if ( swDBG ) cout << "It is. Excluded" << endl;
            return true;
        }
    
        PROCESS_DATA pdProcData = GetProcessData(hwnd);
    //    pdProcData = GetProcessData(hwnd);
        PrintProcessData(pdProcData);
    //    GetProcessData2(hwnd);
    //    PrintProcessData2();
        
        return true;
    }
    
    PROCESS_DATA GetProcessData(HWND hwnd) {
    	PROCESS_DATA pdl;
    
        pdl.hWndID = hwnd;
        GetWindowThreadProcessId(hwnd, &pdl.ProcessID);
    
        pdl.hWndParentID = GetParent(hwnd);
        GetWindowThreadProcessId(pdl.hWndParentID, &pdl.ParentProcessID);
        GetProcessName(pdl.ProcessID, pdl.ProcessName);
    
        GetWindowText(hwnd, pdl.WindowsTitle, sizeof(pdl.WindowsTitle));
        GetProcessPathExe(pdl.ProcessID, pdl.PathExe);
        GetPidCommandLine(pdl.ProcessID, pdl.CommandLine);
        
        return pdl;
    }
    
    void PrintProcessData(PROCESS_DATA pd) {
    	cout << "=========================================================================" << endl;
        cout << "Process ID " << pd.ProcessID << endl;
    	cout << "   Handle Window ID " << pd.hWndID << endl;
    	cout << "   Parent ID " << pd.ParentProcessID << endl;
    	cout << "   Parent Handle Windows ID " << pd.hWndParentID << endl;
    	cout << endl;
    	cout << "   Process Name " << pd.ProcessName << endl;
    	cout << "       Path Exe " << pd.PathExe << endl;
    	cout << "   Command Line " << pd.CommandLine << endl;
    	cout << "   Window Title " << pd.WindowsTitle << endl;
    }
    
    
    void GetProcessName (DWORD dwPid, char* sProcName)
    {
        char szProcessName[MAX_PATH]={0};
    
        HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
                                       PROCESS_VM_READ, 0, dwPid );
        if ( hProcess == 0 ) return;
    
        HMODULE hMod;
        DWORD cbNeeded;
    
        if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), 
                                 &cbNeeded) )
        {
           GetModuleBaseName( hProcess, hMod, szProcessName, 
                              sizeof(szProcessName)/sizeof(TCHAR) );
        }
    	 
        CloseHandle(hProcess);
        
        strcpy(sProcName, szProcessName);
        return;
    }
    
    void GetProcessPathExe (DWORD dwPid, char* sPathExe)
    {
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, dwPid);
        if ( hProcess == 0 ) return;
    
        typedef DWORD (WINAPI *tGetModuleFileNameExA)( HANDLE, HMODULE, LPTSTR, DWORD );
        tGetModuleFileNameExA pGetModuleFileNameExA = 0;
    
        HINSTANCE  hIns = ::LoadLibrary("Psapi.dll");
        if (hIns) 
           pGetModuleFileNameExA = (tGetModuleFileNameExA)::GetProcAddress(hIns,"GetModuleFileNameExA"); 
    
        char szFileName[MAX_PATH] = {0};
        if( pGetModuleFileNameExA )
            pGetModuleFileNameExA(hProcess, 0, szFileName, sizeof(szFileName));
    	 
        CloseHandle(hProcess);
        
        strcpy(sPathExe, szFileName);
        return;
    }
    
    /* test without structure
    void GetProcessData2(HWND hwnd) {
        hWndID = hwnd;
        GetWindowThreadProcessId(hwnd, &ProcessID);
    
        hWndParentID = GetParent(hwnd);
        GetWindowThreadProcessId(hWndParentID, &ParentProcessID);
        GetProcessName(ProcessID, ProcessName);
    
        GetWindowText(hwnd, WindowsTitle, sizeof(WindowsTitle));
        GetProcessPathExe(ProcessID, PathExe);
        GetPidCommandLine(ProcessID, CommandLine);
        
        return;
    }
    
    void PrintProcessData2() {
    	cout << "=========================================================================" << endl;
        cout << "Process ID " << ProcessID << endl;
    	cout << "   Handle Window ID " << hWndID << endl;
    	cout << "   Parent ID " << ParentProcessID << endl;
    	cout << "   Parent Handle Windows ID " << hWndParentID << endl;
    	cout << endl;
    	cout << "   Process Name " << ProcessName << endl;
    	cout << "       Path Exe " << PathExe << endl;
    	cout << "   Command Line " << CommandLine << endl;
    	cout << "   Window Title " << WindowsTitle << endl;
    }
    */
    
    Questi gli altri oggetti che vi servono per la compilazione

    CommandLine.h
    
    int
    GetPidCommandLine(	int pid,
    					char* cmdParameter
    					);
    
    CommandLine.cpp
    
    // CommandLine.c
    //
    //
    //
    #include "stdafx.h"
    #include "CommandLine.h"
    
    #include <stdio.h>
    #include <windows.h>
    
    #define ProcessBasicInformation 0
    #define BUF_SIZE 512
    
    typedef struct
    {
        USHORT Length;
        USHORT MaximumLength;
        PWSTR  Buffer;
    } UNICODE_STRING, *PUNICODE_STRING;
    
    typedef struct
    {
        ULONG          AllocationSize;
        ULONG          ActualSize;
        ULONG          Flags;
        ULONG          Unknown1;
        UNICODE_STRING Unknown2;
        HANDLE         InputHandle;
        HANDLE         OutputHandle;
        HANDLE         ErrorHandle;
        UNICODE_STRING CurrentDirectory;
        HANDLE         CurrentDirectoryHandle;
        UNICODE_STRING SearchPaths;
        UNICODE_STRING ApplicationName;
        UNICODE_STRING CommandLine;
        PVOID          EnvironmentBlock;
        ULONG          Unknown[9];
        UNICODE_STRING Unknown3;
        UNICODE_STRING Unknown4;
        UNICODE_STRING Unknown5;
        UNICODE_STRING Unknown6;
    } PROCESS_PARAMETERS, *PPROCESS_PARAMETERS;
    
    typedef struct
    {
        ULONG               AllocationSize;
        ULONG               Unknown1;
        HINSTANCE           ProcessHinstance;
        PVOID               ListDlls;
        PPROCESS_PARAMETERS ProcessParameters;
        ULONG               Unknown2;
        HANDLE              Heap;
    } PEB, *PPEB;
    
    typedef struct
    {
        DWORD ExitStatus;
        PPEB  PebBaseAddress;
        DWORD AffinityMask;
        DWORD BasePriority;
        ULONG UniqueProcessId;
        ULONG InheritedFromUniqueProcessId;
    }   PROCESS_BASIC_INFORMATION;
    
    typedef LONG (WINAPI *PROCNTQSIP)(HANDLE,UINT,PVOID,ULONG,PULONG);
    
    PROCNTQSIP NtQueryInformationProcess;
    
    BOOL GetProcessCmdLine(DWORD dwId,LPWSTR wBuf,DWORD dwBufLen);
    
    int GetPidCommandLine(int pid, char* cmdParameter){
    	int    dwBufLen = BUF_SIZE*2;
    	WCHAR  wstr[BUF_SIZE]   = {'\0'};
    	char   mbch[BUF_SIZE*2] = {'\0'};
    	DWORD  nOut, dwMinSize;
    	HANDLE hOut;
    
        //printf("  Call GetPidCommandLine, pid: %i, %i\n", pid, dwBufLen);
    
        NtQueryInformationProcess = (PROCNTQSIP)GetProcAddress(
                                                GetModuleHandle("ntdll"),
                                                "NtQueryInformationProcess"
                                                );
        if (!NtQueryInformationProcess){ return -1;}
    
        if (! GetProcessCmdLine(pid, wstr, dwBufLen)){
    		cmdParameter = '\0';
    
    		#ifdef _DEBUG
    	    printf("  Error: cannot get %i's command line string\n", pid);
    		#endif
    		return 0;
    	}else
    		#ifdef _DEBUG
    	    wprintf(L"  %i's unicode command line string: %d byte %s\n", pid, wcslen(wstr), wstr);
    		#endif
    
    		//count the byte number for second call, dwMinSize is length
    		dwMinSize = WideCharToMultiByte(CP_OEMCP, 0, wstr, -1, NULL, 0, NULL, NULL);
    
    		//convert utf16 to multibyte and save to mbch
    		dwMinSize = WideCharToMultiByte(CP_OEMCP, 0, (PWSTR)wstr, -1, mbch, dwMinSize, NULL, NULL);
    
    #ifdef _DEBUG
    		//write the utf16 string to a file
    		hOut = CreateFile("_pidCmdLine.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    		if (hOut == INVALID_HANDLE_VALUE) {
    			printf ("Cannot open output file. Error: %x\n", GetLastError ());
    			return -1;
    		}
    
    		//make sure no over buffer
    		if(wcslen(wstr) < BUF_SIZE){
    			if(WriteFile (hOut, wstr, wcslen(wstr) * 2, &nOut, NULL)){
    				printf("\n  write %i byte to _pidCmdLine.txt that is in unicode\n", nOut);
    			}
    		}
    		CloseHandle (hOut);
    #endif
    
    		//copy to return buffer
    		strncpy(cmdParameter, mbch, dwMinSize);
    
    		#ifdef _DEBUG
    		printf("  convert unicode to MB string: %s \n", mbch);
    		#endif
    
    		return dwMinSize;
    }
    
    BOOL GetProcessCmdLine(DWORD dwId,LPWSTR wBuf,DWORD dwBufLen)
    {
        LONG                      status;
        HANDLE                    hProcess;
        PROCESS_BASIC_INFORMATION pbi;
        PEB                       Peb;
        PROCESS_PARAMETERS        ProcParam;
        DWORD                     dwDummy;
        DWORD                     dwSize;
        LPVOID                    lpAddress;
        BOOL                      bRet = FALSE;
    
        // Get process handle
        hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,FALSE,dwId);
        if (!hProcess)
           return FALSE;
    
        // Retrieve information
        status = NtQueryInformationProcess( hProcess,
                                            ProcessBasicInformation,
                                            (PVOID)&pbi,
                                            sizeof(PROCESS_BASIC_INFORMATION),
                                            NULL
                                          );
    
    
        if (status)
           goto cleanup;
    
        if (!ReadProcessMemory( hProcess,
                                pbi.PebBaseAddress,
                                &Peb,
                                sizeof(PEB),
                                &dwDummy
                              )
           )
           goto cleanup;
    
        if (!ReadProcessMemory( hProcess,
                                Peb.ProcessParameters,
                                &ProcParam,
                                sizeof(PROCESS_PARAMETERS),
                                &dwDummy
                              )
           )
           goto cleanup;
    
        lpAddress = ProcParam.CommandLine.Buffer;
        dwSize = ProcParam.CommandLine.Length;
    
        if (dwBufLen<dwSize)
           goto cleanup;
    
        if (!ReadProcessMemory( hProcess,
                                lpAddress,
                                wBuf,
                                dwSize,
                                &dwDummy
                              )
           )
           goto cleanup;
    
    
        bRet = TRUE;
    
    cleanup:
    
        CloseHandle (hProcess);
    
    
        return bRet;
    }
    
    ed infine

    stdafx.h
    
    // stdafx.h : include file for standard system include files,
    //  or project specific include files that are used frequently, but
    //      are changed infrequently
    //
    
    #if !defined(AFX_STDAFX_H__C627C2F4_FE03_4BAB_B7B4_6D3FD88DBAF2__INCLUDED_)
    #define AFX_STDAFX_H__C627C2F4_FE03_4BAB_B7B4_6D3FD88DBAF2__INCLUDED_
    
    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000
    
    
    // Insert your headers here
    #define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
    
    #include <windows.h>
    
    // TODO: reference additional headers your program requires here
    
    //{{AFX_INSERT_LOCATION}}
    // Microsoft Visual C++ will insert additional declarations immediately before the previous line.
    
    #endif // !defined(AFX_STDAFX_H__C627C2F4_FE03_4BAB_B7B4_6D3FD88DBAF2__INCLUDED_)
    
    vi confermo che questo codice si compila.
    Se doveste avere problemi, vi mando lo zip compreso l'eseguibile.
Devi accedere o registrarti per scrivere nel forum
5 risposte