¿Cómo obtengo GetModuleFileName () si solo tengo un identificador de ventana (hWnd)?

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

  •  07-07-2019
  •  | 
  •  

Pregunta

Estoy tratando de obtener el nombre del ejecutable de una ventana que está fuera de mi aplicación C # 2.0. Mi aplicación actualmente recibe un identificador de ventana (hWnd) usando la llamada GetForegroundWindow () de " user32.dll " ;.

De la excavación que he podido hacer, creo que quiero usar la función GetModuleFileNameEx () (de PSAPI) para obtener el nombre, pero GetModuleFileNameEx () requiere un controlador para un Proceso, no una Ventana.

¿Es posible obtener un identificador de proceso desde un identificador de ventana? (¿Necesito primero obtener el identificador de hilo de la ventana?)

EDITÓ la primera oración para aclarar lo que estoy tratando de hacer.

¡ACTUALIZACIÓN! Aquí está el código C # que encontré que funcionó para mí. La única advertencia es ocasionalmente que devuelve un archivo / ruta donde la letra de la unidad es un "? & Quot; en lugar de la letra de unidad real (como "C"). - Todavía no he descubierto por qué.

[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());
}
¿Fue útil?

Solución

Puede llamar a GetWindowThreadProcessId y que le devolverá el proceso asociado con la ventana.

A partir de eso, puede llamar a OpenProcess para abrir el proceso y obtener el control del proceso.

Otros consejos

He estado luchando con el mismo problema durante una hora ahora, también he reemplazado la primera letra por una ? usando GetModuleFileNameEx. Finalmente se le ocurrió esta solución utilizando la clase 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;
}

Si está ejecutando en una plataforma Windows de 64 bits, es posible que deba usar QueryFullProcessImageName en su lugar. Esto devuelve una ruta de estilo de usuario, en comparación con GetProcessImageFileName, que devuelve una ruta de estilo de sistema que debería convertirse mediante NtQuerySymbolicLinkObject o ZwQuerySymbolicLinkObject.

Una función de ejemplo de mamut: se recomienda dividir en bits reutilizables.

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

¿Qué es exactamente lo que estás tratando de hacer? Puede obtener el ID del proceso que creó una ventana con GetWindowThreadProcessId () , seguido de OpenProcess () para obtener el identificador de proceso. Pero esto parece muy torpe, y siento que hay una manera más elegante de hacer lo que quieres hacer.

intente esto para obtener el nombre del archivo ejecutable:

C #:

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

mfg

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top