Come posso ottenere GetModuleFileName () se ho solo un handle di finestra (hWnd)?

StackOverflow https://stackoverflow.com/questions/277085

  •  07-07-2019
  •  | 
  •  

Domanda

Sto cercando di ottenere il nome dell'eseguibile di una finestra esterna alla mia applicazione C # 2.0. La mia app attualmente ottiene un handle di finestra (hWnd) utilizzando la chiamata GetForegroundWindow () da " user32.dll " ;.

Dallo scavo che sono stato in grado di fare, penso di voler utilizzare la funzione GetModuleFileNameEx () (da PSAPI) per ottenere il nome, ma GetModuleFileNameEx () richiede un handle per un processo, non una finestra.

È possibile ottenere un handle di processo da un handle di finestra? (Devo prima ottenere l'handle di thread della finestra?)

MODIFICATO la prima frase per rendere più chiaro ciò che sto cercando di fare.

AGGIORNAMENTO! Ecco il codice C # che ho trovato ha funzionato per me. L'unica avvertenza è occasionalmente restituisce un file / percorso in cui la lettera di unità è un "quot"? " anziché la lettera di unità effettiva (come " C "). - Non ho ancora capito perché.

[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

[DllImport("kernel32.dll")]
static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);

[DllImport("psapi.dll")]
static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);

[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);

private string GetWindowModuleFileName(IntPtr hWnd)
{
    uint processId = 0;
    const int nChars = 1024;
    StringBuilder filename = new StringBuilder(nChars);
    GetWindowThreadProcessId(hWnd, out processId);
    IntPtr hProcess = OpenProcess(1040, 0, processId);
    GetModuleFileNameEx(hProcess,IntPtr.Zero,filename,nChars);
    CloseHandle(hProcess);
    return (filename.ToString());
}
È stato utile?

Soluzione

Puoi chiamare GetWindowThreadProcessId e questo ti restituirà il processo associato alla finestra.

Da questo, puoi chiamare OpenProcess per aprire il processo e gestisci il processo.

Altri suggerimenti

Sono stato alle prese con lo stesso problema per un'ora ora, anche la prima lettera è stata sostituita da un ? usando GetModuleFileNameEx. Alla fine è arrivata questa soluzione usando la classe System.Diagnostics.Process .

[DllImport("user32.dll")]
public static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

void GetProcessPathFromWindowHandle(IntPtr hwnd)
{
   uint pid = 0;
   Win32.GetWindowThreadProcessId(hwnd, out pid);
   Process p = Process.GetProcessById((int)pid);
   return p.MainModule.FileName;
}

Se si esegue su una piattaforma Windows a 64 bit, potrebbe essere necessario utilizzare QueryFullProcessImageName. Ciò restituisce un percorso stile utente, rispetto a GetProcessImageFileName che restituisce un percorso stile sistema che dovrebbe essere convertito utilizzando NtQuerySymbolicLinkObject o ZwQuerySymbolicLinkObject.

Una gigantesca funzione di esempio: consiglia di suddividere in bit riutilizzabili.

typedef DWORD (__stdcall *PfnQueryFullProcessImageName)(HANDLE hProcess, DWORD dwFlags, LPTSTR lpImageFileName, PDWORD nSize);
typedef DWORD (__stdcall *PfnGetModuleFileNameEx)(HANDLE hProcess, HMODULE hModule, LPTSTR lpImageFileName, DWORD nSize);

std::wstring GetExeName( HWND hWnd ){
// Convert from Window to Process ID
DWORD dwProcessID = 0;
::GetWindowThreadProcessId(hWnd, &dwProcessID);

// Get a handle to the process from the Process ID
HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessID);

// Get the process name
if (NULL != hProcess) {
    TCHAR szEXEName[MAX_PATH*2] = {L'\0'};
    DWORD nExeName = sizeof(szEXEName)/sizeof(TCHAR);

    //  the QueryFullProcessImageNameW does not exist on W2K
    HINSTANCE hKernal32dll = LoadLibrary(L"kernel32.dll");
    PfnQueryFullProcessImageName pfnQueryFullProcessImageName = NULL;
    if(hKernal32dll != NULL) {
        pfnQueryFullProcessImageName = (PfnQueryFullProcessImageName)GetProcAddress(hKernal32dll, "QueryFullProcessImageNameW");
        if (pfnQueryFullProcessImageName != NULL) 
            pfnQueryFullProcessImageName(hProcess, 0, szEXEName, &nExeName);
        ::FreeLibrary(hKernal32dll);
    } 

    // The following was not working from 32 querying of 64 bit processes
    // Use as backup for when function above is not available 
    if( pfnQueryFullProcessImageName == NULL ){ 
        HINSTANCE hPsapidll = LoadLibrary(L"Psapi.dll");
        PfnGetModuleFileNameEx pfnGetModuleFileNameEx = (PfnGetModuleFileNameEx)GetProcAddress(hPsapidll, "GetModuleFileNameExW");
        if( pfnGetModuleFileNameEx != NULL )    
            pfnGetModuleFileNameEx(hProcess, NULL, szEXEName, sizeof(szEXEName)/sizeof(TCHAR));
        ::FreeLibrary(hPsapidll);
    }

    ::CloseHandle(hProcess);

    return( szEXEName );
} 
return std::wstring();
}

Che cosa stai esattamente cercando di fare? È possibile ottenere l'ID del processo che ha creato una finestra con GetWindowThreadProcessId () , seguito da OpenProcess () per ottenere l'handle del processo. Ma questo sembra molto disgustoso, e sento che c'è un modo più elegante di fare quello che vuoi fare.

prova questo per ottenere il nome del file dell'eseguibile:

C #:

string file = System.Windows.Forms.Application.ExecutablePath;

Mfg

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top