2008-11-10 12 views
10

Próbuję uzyskać nazwę pliku wykonywalnego okna, które znajduje się poza moją aplikacją C# 2.0. Moja aplikacja aktualnie pobiera uchwyt okna (hWnd) za pomocą wywołania GetForegroundWindow() z "user32.dll".Jak mogę GetModuleFileName(), jeśli mam tylko uchwyt okna (hWnd)?

Z kopania, które udało mi się zrobić, myślę, że chcę użyć funkcji GetModuleFileNameEx() (z PSAPI), aby uzyskać nazwę, ale GetModuleFileNameEx() wymaga uchwytu do procesu, a nie do okna .

Czy można uzyskać uchwyt procesu z uchwytu okna? (Czy najpierw muszę pobrać uchwyt nici okna?)

EDYTUJ Pierwsze zdanie, aby było jaśniejsze, co próbuję zrobić.

AKTUALIZACJA! Oto kod C#, który znalazłem dla mnie. Jedynym zastrzeżeniem jest okazjonalnie zwraca plik/ścieżkę, gdzie literą dysku jest "?" zamiast rzeczywistej litery dysku (np. "C"). - Nie zrozumiałem jeszcze dlaczego.

[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()); 
} 

Odpowiedz

6

Możesz zadzwonić pod numer GetWindowThreadProcessId, a to spowoduje powrót do procesu powiązanego z oknem.

Z tego można wywołać OpenProcess, aby otworzyć proces i uzyskać uchwyt do procesu.

+0

Hej witamy na przepełnienie stosu - Byłem czytelnik długi czas Twój –

1

Co dokładnie próbujesz zrobić? Możesz uzyskać identyfikator procesu procesu, który utworzył okno z GetWindowThreadProcessId(), a następnie OpenProcess(), aby uzyskać uchwyt procesu. Ale wydaje się to bardzo kludgy i czuję, że istnieje bardziej elegancki sposób robienia tego, co chcesz robić.

+0

Może wydaje Ci się, że jest to kludgy, ale ktoś o reputacji 1 dał dokładnie taką samą odpowiedź zaledwie 1 minutę przed tobą i nie sądzę, że jego reputacja pozostanie na 1 przez bardzo długi czas. –

+0

Tak, dla mnie to też jest kłupie. Próbuję śledzić, które okno ma fokus. Oczywiście, klamki okien i nazwy pasków tytułu zmieniają się, ale nazwa aplikacji nie działa. – Pretzel

+0

OK, ale w każdym razie Larry Osterman podał tę samą odpowiedź, więc prawdopodobnie jest mniej odrażający niż wszystkie alternatywy. A jego reputacja już nie jest. –

7

Przez godzinę borykałem się z tym samym problemem, także pierwsza litera została zastąpiona przez ? za pomocą metody GetModuleFileNameEx. Ostatecznie wpadł na to rozwiązanie, używając klasy 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; 
} 
+2

Myślę, że to zdecydowanie najlepsze rozwiązanie oferowane tutaj. Brak dziwnych znaków i, co najważniejsze, nie poleganie na niespójnej funkcji GetModuleFileNameEx, która w systemie Windows 7 znajduje się w innej bibliotece DLL (nie psapi ale kernel32). Korzystanie z klasy .Net jest zdecydowanie preferowane w tym przypadku. Działa świetnie. – Axonn

+2

Rozumiem, że ten wątek jest nieaktualny, ale mała uwaga na temat tego sugerowanego rozwiązania. To nie zadziała, jeśli spróbujesz uzyskać nazwę pliku procesu 64-bitowego z procesu 32-bitowego (lub na odwrót). – Geoffrey

2

Jeśli korzystasz z 64-bitowej platformy Windows, może być konieczne użycie QueryFullProcessImageName zamiast tego. Zwraca ścieżkę stylu użytkownika, w porównaniu do GetProcessImageFileName, która zwraca ścieżkę stylu systemu, która musiałaby zostać przekonwertowana przy użyciu obiektu NtQuerySymbolicLinkObject lub ZwQuerySymbolicLinkObject.

Jedna gigantyczna przykładowa funkcja - polecam rozbicie na bity wielokrotnego użytku.

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(); 
} 
0

spróbować uzyskać nazwę pliku wykonywalnego:

C#:

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

mfg

+1

To jest trochę stare pytanie, ale w tamtym czasie próbowałem uzyskać nazwę EXE * innego * procesu opartego na identyfikatorze procesu. (Nie ten, który był uruchomiony.) Więc nie jest to przydatna odpowiedź w tym kontekście. – Pretzel