c++ - Shell Execute 将窗口置于最前面

标签 c++ c windows

我正在使用此函数从我的 MSI 调用可执行文件。然而,可执行文件的窗口隐藏在我的 MSI 窗口后面。有什么办法可以把它带到前面吗?

我已经尝试在调用 ShellExecute 之前最小化所有窗口,但仍然没有将可执行窗口置于最前面。

extern "C" UINT __stdcall InstallDrivers(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
HANDLE hFile = INVALID_HANDLE_VALUE;
BYTE* pbData = NULL;
DWORD cbData = 0;
char pwzFilename[MAX_PATH], szDriverType[MAX_PATH], pwzSentinelFilename[MAX_PATH], szIsInstalled[MAX_PATH];
LPWSTR szValueBuf = NULL, szIsHaspInstalled = NULL, szIsSentinelInstalled = NULL;

hr = WcaInitialize(hInstall, "InstallDrivers");
ExitOnFailure(hr, "Failed to initialize");

WcaLog(LOGMSG_STANDARD, "Initialized.");
WcaLog(LOGMSG_STANDARD, "%s", szValueBuf);
CreateDirectory("C:\\Temp", NULL);

strcpy_s(pwzFilename, "C:\\Temp\\HASPUserSetup.exe");

hr = ExtractBinary(L"Hasp", &pbData, &cbData);
ExitOnFailure(hr, "failed to extract binary data");

if ((hFile = CreateFile(pwzFilename, GENERIC_WRITE,FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) 
{
    PMSIHANDLE hRecord = MsiCreateRecord(0);
    MsiRecordSetString(hRecord, 0, TEXT("Could not create new temporary file"));
    MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
    return ERROR_INSTALL_USEREXIT;  
} 

DWORD cbWritten = 0;
if ( !WriteFile(hFile, pbData, cbData, &cbWritten, NULL) )
{
    PMSIHANDLE hRecord = MsiCreateRecord(0);
    MsiRecordSetString(hRecord, 0, TEXT("Could not write to file"));
    MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
    return ERROR_INSTALL_USEREXIT;
}

CloseHandle(hFile);

strcpy_s(pwzSentinelFilename, "C:\\Temp\\sentinel_setup.exe");
hr = ExtractBinary(L"Sentinel", &pbData, &cbData);
ExitOnFailure(hr, "failed to extract binary data");

if ((hFile = CreateFile(pwzSentinelFilename, GENERIC_WRITE,FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) 
{
    PMSIHANDLE hRecord = MsiCreateRecord(0);
    MsiRecordSetString(hRecord, 0, TEXT("Could not create new temporary file"));
    MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
    return ERROR_INSTALL_USEREXIT;  
} 


if ( !WriteFile(hFile, pbData, cbData, &cbWritten, NULL) )
{
    PMSIHANDLE hRecord = MsiCreateRecord(0);
    MsiRecordSetString(hRecord, 0, TEXT("Could not write to file"));
    MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
    return ERROR_INSTALL_USEREXIT;
}

CloseHandle(hFile);

hr = WcaGetProperty(L"DRIVER", &szValueBuf);
ExitOnFailure(hr, "failed to get driver info");

wcstombs(szDriverType, szValueBuf, 260);
if (strcmp(szDriverType, "Hasp") == 0)
{   

    hr = WcaGetProperty(L"SENTINELINSTALLED", &szIsSentinelInstalled);
    ExitOnFailure(hr, "failed to get driver info");
    wcstombs(szIsInstalled, szValueBuf, 260);
    if (strcmp(szIsInstalled, "Sentinel Protection Installer 7.6.5") == 0)
    {
    ShellExecute(NULL, "open",  pwzSentinelFilename, NULL, NULL, SW_SHOWNORMAL);
    }
    ShellExecute(NULL, "open",  pwzFilename, NULL, NULL, SW_SHOWNORMAL);
}else
{
    hr = WcaGetProperty(L"HASPINSTALLED", &szIsHaspInstalled);
    ExitOnFailure(hr, "failed to get driver info");
    wcstombs(szIsInstalled, szIsHaspInstalled, 260);
    if (strcmp(szIsInstalled, "Sentinel Runtime") == 0)
    {
    ShellExecute(NULL, "open",  pwzFilename, NULL, NULL, SW_SHOWNORMAL);
    }
    ShellExecute(NULL, "open",  pwzSentinelFilename, NULL, NULL, SW_SHOWNORMAL);
}
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}

更新代码:

extern "C" UINT __stdcall InstallDrivers(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
HANDLE hFile = INVALID_HANDLE_VALUE;
BYTE* pbData = NULL;
DWORD cbData = 0;
char pwzFilename[MAX_PATH], szDriverType[MAX_PATH], pwzSentinelFilename[MAX_PATH], szIsInstalled[MAX_PATH];
LPWSTR szValueBuf = NULL, szIsHaspInstalled = NULL, szIsSentinelInstalled = NULL;
SHELLEXECUTEINFO ShExecInfo;

ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = NULL;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = NULL;
ShExecInfo.lpParameters = NULL;
ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_SHOWNORMAL;
ShExecInfo.hInstApp = NULL;

hr = WcaInitialize(hInstall, "InstallDrivers");
ExitOnFailure(hr, "Failed to initialize");

WcaLog(LOGMSG_STANDARD, "Initialized.");
WcaLog(LOGMSG_STANDARD, "%s", szValueBuf);
CreateDirectory("C:\\Temp", NULL);

strcpy_s(pwzFilename, "C:\\Temp\\HASPUserSetup.exe");

hr = ExtractBinary(L"Hasp", &pbData, &cbData);
ExitOnFailure(hr, "failed to extract binary data");

if ((hFile = CreateFile(pwzFilename, GENERIC_WRITE,FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) 
{
    PMSIHANDLE hRecord = MsiCreateRecord(0);
    MsiRecordSetString(hRecord, 0, TEXT("Could not create new temporary file"));
    MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
    return ERROR_INSTALL_USEREXIT;  
} 

DWORD cbWritten = 0;
if ( !WriteFile(hFile, pbData, cbData, &cbWritten, NULL) )
{
    PMSIHANDLE hRecord = MsiCreateRecord(0);
    MsiRecordSetString(hRecord, 0, TEXT("Could not write to file"));
    MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
    return ERROR_INSTALL_USEREXIT;
}

CloseHandle(hFile);

strcpy_s(pwzSentinelFilename, "C:\\Temp\\sentinel_setup.exe");
hr = ExtractBinary(L"Sentinel", &pbData, &cbData);
ExitOnFailure(hr, "failed to extract binary data");

if ((hFile = CreateFile(pwzSentinelFilename, GENERIC_WRITE,FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) 
{
    PMSIHANDLE hRecord = MsiCreateRecord(0);
    MsiRecordSetString(hRecord, 0, TEXT("Could not create new temporary file"));
    MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
    return ERROR_INSTALL_USEREXIT;  
} 


if ( !WriteFile(hFile, pbData, cbData, &cbWritten, NULL) )
{
    PMSIHANDLE hRecord = MsiCreateRecord(0);
    MsiRecordSetString(hRecord, 0, TEXT("Could not write to file"));
    MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
    return ERROR_INSTALL_USEREXIT;
}

CloseHandle(hFile);

hr = WcaGetProperty(L"DRIVER", &szValueBuf);
ExitOnFailure(hr, "failed to get driver info");

wcstombs(szDriverType, szValueBuf, 260);
if (strcmp(szDriverType, "Hasp") == 0)
{   

    hr = WcaGetProperty(L"SENTINELINSTALLED", &szIsSentinelInstalled);
    ExitOnFailure(hr, "failed to get driver info");
    wcstombs(szIsInstalled, szValueBuf, 260);
    if (strcmp(szIsInstalled, "Sentinel Protection Installer 7.6.5") == 0)
    {
        AllowSetForegroundWindow(ASFW_ANY);
        ShExecInfo.lpFile = pwzSentinelFilename;
        ShellExecuteEx(&ShExecInfo);
        /*ShellExecute(NULL, "open",  pwzSentinelFilename, NULL, NULL, SW_SHOWNORMAL);*/
    }
    AllowSetForegroundWindow(ASFW_ANY);
    ShExecInfo.lpFile = pwzFilename;
    ShellExecuteEx(&ShExecInfo);

    /*ShellExecute(NULL, "open",  pwzFilename, NULL, NULL, SW_SHOWNORMAL);*/
}else
{
    hr = WcaGetProperty(L"HASPINSTALLED", &szIsHaspInstalled);
    ExitOnFailure(hr, "failed to get driver info");
    wcstombs(szIsInstalled, szIsHaspInstalled, 260);
    if (strcmp(szIsInstalled, "Sentinel Runtime") == 0)
    {
        AllowSetForegroundWindow(ASFW_ANY);
        /*ShellExecute(NULL, "open",  pwzFilename, NULL, NULL, SW_SHOWNORMAL);*/
        ShExecInfo.lpFile = pwzFilename;
        ShellExecuteEx(&ShExecInfo);
    }
    AllowSetForegroundWindow(ASFW_ANY);
    ShExecInfo.lpFile = pwzSentinelFilename;
    ShellExecuteEx(&ShExecInfo);
    /*  ShellExecute(NULL, "open",  pwzSentinelFilename, NULL, NULL, SW_SHOWNORMAL);*/
}
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}

最佳答案

Windows 不允许进程抢占前台窗口,除非用户启动它们。这是为了避免诸如说服用户在错误的窗口中输入他们的银行详细信息之类的事情。但是,当前的前台进程可以将权限传递给另一个进程来执行此操作。参见 AllowSetForegroundWindow了解详情。为此,您必须为将成为前台的进程提供进程 ID,而 ShellExecute 不提供该进程。但是,如果您切换到使用 ShellExecuteEx您可以从 SHELLEXECUTEINFO 结构的 hProcess 成员获取它。

然后您可以调用 SetForegroundWindow在您的新流程中,它将被允许。否则它只会开始在任务栏上闪烁。

编辑

如果您的初始应用程序是前台应用程序,并且您从该应用程序启动了一个子进程,那么该子进程应该自动成为前台,如这些功能的文档中所述。

以下是我们如何选择启用和设置另一个应用程序成为前台窗口的示例。在这种情况下,它只是创建一个文本文件并使用 open 动词调用 ShellExecuteEx。我们必须等待子进程开始运行并准备好它的窗口,然后我们可以从进程 ID 找到窗口并授予它权限并将其窗口设置为前台。在这种情况下,启动的记事本(或任何你对 .txt 文件的“打开”动词)都将在前台显示。如果您单独运行一个记事本进程并在进程 ID 中替换我们通常放入子进程 ID 的进程,那么我们可以使另一个进程成为前台——一个不属于我们进程的进程树。这可以使用带有 cl -nologo -W3 -O2 -MD fg_test.cpp 的 Visual C++ 编译以生成 fg_test.exe。

#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <shellapi.h>

#pragma comment(lib, "shell32")
#pragma comment(lib, "user32")

static void PrintError(LPCTSTR szPrefix, DWORD dwError);
static BOOL CALLBACK OnGetWindowByProcess(HWND hwnd, LPARAM lParam);

typedef struct {
    DWORD pid;
    HWND hwnd;
} WINDOWPROCESSINFO;

int _tmain(int argc, TCHAR *argv[])
{
    SHELLEXECUTEINFO sxi = {0};
    sxi.cbSize = sizeof(sxi);
    sxi.nShow = SW_SHOWNORMAL;

    FILE *fp = NULL;
    _tfopen_s(&fp, _T("junk.txt"), _T("wt"));
    _fputts(_T("Example text file\n"), fp);
    fclose(fp);

    sxi.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NOASYNC | SEE_MASK_WAITFORINPUTIDLE;
    sxi.lpVerb = _T("open");
    sxi.lpFile = _T("junk.txt");
    if (!ShellExecuteEx(&sxi))
        PrintError(_T("ShellExecuteEx"), GetLastError());
    else
    {
        WINDOWPROCESSINFO info;
        info.pid = GetProcessId(sxi.hProcess); // SPECIFY PID
        info.hwnd = 0;
        AllowSetForegroundWindow(info.pid);
        EnumWindows(OnGetWindowByProcess, (LPARAM)&info);
        if (info.hwnd != 0)
        {
            SetForegroundWindow(info.hwnd);
            SetActiveWindow(info.hwnd);
        }
        CloseHandle(sxi.hProcess);

    }
    return 0;
}

static BOOL CALLBACK OnGetWindowByProcess(HWND hwnd, LPARAM lParam)
{
    WINDOWPROCESSINFO *infoPtr = (WINDOWPROCESSINFO *)lParam;
    DWORD check = 0;
    BOOL br = TRUE;
    GetWindowThreadProcessId(hwnd, &check);
    _tprintf(_T("%x %x %x\n"), hwnd, check, infoPtr->pid);
    if (check == infoPtr->pid)
    {
        _tprintf(_T("found window %x for process id %x\n"), hwnd, check);
        infoPtr->hwnd = hwnd;
        br = FALSE;
    }
    return br;
}

static void
PrintError(LPCTSTR szPrefix, DWORD dwError)
{
    LPTSTR lpsz = NULL;
    DWORD cch = 0;

    cch = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
                        | FORMAT_MESSAGE_FROM_SYSTEM
                        | FORMAT_MESSAGE_IGNORE_INSERTS,
                        NULL, dwError, LANG_NEUTRAL, (LPTSTR)&lpsz, 0, NULL);
    if (cch < 1) {
        cch = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
                            | FORMAT_MESSAGE_FROM_STRING
                            | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                            _T("Code 0x%1!08x!"),
                            0, LANG_NEUTRAL, (LPTSTR)&lpsz, 0,
                            (va_list*)&dwError);
    }
    _ftprintf(stderr, _T("%s: %s"), szPrefix, lpsz);
    LocalFree((HLOCAL)lpsz);
}

关于c++ - Shell Execute 将窗口置于最前面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17166178/

相关文章:

C - 为什么它每次都返回 NULL 值?

c++ - Windows C++ 进程与线程

c++ - 从 C/C++ 代码中删除注释

c++ - 使用 CryptoApi 向证书添加扩展名

android - 使用 C++(OpenGL ES 1.x) 加载纹理数据

c++ - 全局变量和静态全局变量

c - 在推送/弹出其他寄存器时从堆栈访问相对于 EBP 的函数参数?

c - scanf.Warning 中的固定长度数组的格式

windows - 什么是最普遍/最常见的南美西类牙文化

c++ - 生成基于硬件的 computerID