c++ - EnumDesktopWindows 错误 : (183) Cannot create a file when that file already exists

标签 c++ windows winapi c++11 service

任何人都知道为什么在调用 EnumDesktopWindows 时返回 183

这个进程是一个运行在System LocalService中的服务

我试图将窗口放在顶部,因为进程开始时最小化。

感谢帮助

我的代码:

BOOL CALLBACK EnumWindowsProc( HWND hwnd, LPARAM lParam ) 
{
    DWORD dwPID;
    GetWindowThreadProcessId( hwnd, &dwPID );

    if( dwPID == lParam ) {
        SetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0,  SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE );
        SwitchToThisWindow(hwnd, true);
        SetFocus( hwnd );
        return FALSE;
    }

    return TRUE;
}

BOOL CALLBACK EnumDesktopProc(LPTSTR lpszDesktop, LPARAM lParam) {
    HDESK hDesk = OpenDesktop(lpszDesktop, NULL, FALSE, GENERIC_ALL);

    if(hDesk != NULL) {

        if(!EnumDesktopWindows(hDesk,&EnumWindowsProc, lParam)) {
            //This call returns (183) Cannot create a file when that file already exists
        }

        CloseDesktop(hDesk);
    }

    return TRUE;
}


BOOL CALLBACK EnumWindowStationProc(LPTSTR lpszWindowStation, LPARAM lParam)
{

    HWINSTA hWinStat = OpenWindowStation(lpszWindowStation,FALSE,WINSTA_ENUMDESKTOPS|WINSTA_ENUMERATE); 

    if(hWinStat) {
        SetProcessWindowStation(hWinStat);
        EnumDesktops(hWinStat,&EnumDesktopProc,lParam);
        CloseWindowStation(hWinStat);
    }

    return TRUE;
}


bool Utils::execIntoDifferentSession(const std::wstring &aPath, const std::wstring &aParams, const std::wstring &aMode) 
{
    PROCESS_INFORMATION pi;
    STARTUPINFO si;
    BOOL bResult = FALSE;
    DWORD dwSessionId,winlogonPid;
    HANDLE hUserToken,hUserTokenDup,hPToken,hProcess;
    DWORD dwCreationFlags;

    // Log the client on to the local computer.

    dwSessionId = WTSGetActiveConsoleSessionId();

    //////////////////////////////////////////
    // Find the winlogon process
    ////////////////////////////////////////

    PROCESSENTRY32 procEntry;

    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnap == INVALID_HANDLE_VALUE)
        return false;

    procEntry.dwSize = sizeof(PROCESSENTRY32);

    if (!Process32First(hSnap, &procEntry))
        return false;

    do {
        if (_wcsicmp(procEntry.szExeFile, L"winlogon.exe") == 0) {          
            // We found a winlogon process...make sure it's running in the console session
            DWORD winlogonSessId = 0;
            if (ProcessIdToSessionId(procEntry.th32ProcessID, &winlogonSessId) && winlogonSessId == dwSessionId) {
                winlogonPid = procEntry.th32ProcessID;
                break;
            }
        }

    } while (Process32Next(hSnap, &procEntry));

    WTSQueryUserToken(dwSessionId,&hUserToken);
    dwCreationFlags = NORMAL_PRIORITY_CLASS|CREATE_NEW_CONSOLE;

    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb= sizeof(STARTUPINFO);
    si.lpDesktop = L"winsta0\\default";
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_SHOWNORMAL|SW_RESTORE;

    ZeroMemory(&pi, sizeof(pi));
    TOKEN_PRIVILEGES tp;
    LUID luid;
    hProcess = OpenProcess(MAXIMUM_ALLOWED,FALSE,winlogonPid);

    if(!::OpenProcessToken(hProcess,TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY
                                    |TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY|TOKEN_ADJUST_SESSIONID
                                    |TOKEN_READ|TOKEN_WRITE,&hPToken))
    {
        return false;
    }

    if (!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))
        return false;

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    DuplicateTokenEx(hPToken,MAXIMUM_ALLOWED,NULL,SecurityIdentification,TokenPrimary,&hUserTokenDup);

    //Adjust Token privilege
    SetTokenInformation(hUserTokenDup,TokenSessionId,(void*)dwSessionId,sizeof(DWORD));

    if (!AdjustTokenPrivileges(hUserTokenDup,FALSE,&tp,sizeof(TOKEN_PRIVILEGES),(PTOKEN_PRIVILEGES)NULL,NULL))
        return false;

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
        return false;

    LPVOID pEnv = NULL;

    if(CreateEnvironmentBlock(&pEnv,hUserTokenDup,TRUE))
        dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
    else
        pEnv = NULL;

    // Launch the process in the client's logon session.

    std::wstring params = aParams;
    std::wstring path = aPath;

    if(aMode == L"select") {
        TCHAR infoBuffer[MAX_PATH];
        GetSystemWindowsDirectory(infoBuffer, MAX_PATH);
        std::wstring windowsDir(infoBuffer);

        path = windowsDir+L"\\explorer.exe";
        params = L" /n, /select,"+replaceString(aPath, L"\\\\", L"\\");
    }

    bResult = CreateProcessAsUser(
        hUserTokenDup,     // client's access token
        path.c_str(),      // file to execute
        params.length() > 0 ? stringToLPWSTR(wideToUtf8(params)) : NULL,              // command line
        NULL,              // pointer to process SECURITY_ATTRIBUTES
        NULL,              // pointer to thread SECURITY_ATTRIBUTES
        FALSE,             // handles are not inheritable
        dwCreationFlags,   // creation flags
        pEnv,              // pointer to new environment block 
        NULL,              // name of current directory 
        &si,               // pointer to STARTUPINFO structure
        &pi                // receives information about new process
    );

    EnumWindowStations(&EnumWindowStationProc, (LPARAM)(pi.dwProcessId));

    // End impersonation of client.

    //GetLastError Shud be 0

    int rv = GetLastError();

    //Perform All the Close Handles task

    CloseHandle(hProcess);
    CloseHandle(hUserToken);
    CloseHandle(hUserTokenDup);
    CloseHandle(hPToken);

    return !rv;
}

最佳答案

错误 183 是 ERROR_ALREADY_EXISTSEnumDesktopWindows() 不会设置该错误,因此它一定是早期 API 调用的遗留问题。如果您阅读文档,会这样说:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms682615.aspx

You must ensure that the callback function sets SetLastError if it fails.

http://msdn.microsoft.com/en-us/library/windows/desktop/ms682614.aspx

If the callback function fails, the return value is zero. The callback function can call SetLastError to set an error code for the caller to retrieve by calling GetLastError.

所以尝试更像这样的东西:

struct WndInfo
{
    DWORD dwProcessID;
    HWND hWnd;
};

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) 
{
    WndInfo *pInfo = (WndInfo*) lParam;

    DWORD dwPID;
    GetWindowThreadProcessId(hwnd, &dwPID);

    if (dwPID == pInfo->dwProcessID)
    {
        pInfo->hWnd = hwnd;
        SetLastError(0);
        return FALSE;
    }

    return TRUE;
}

BOOL CALLBACK EnumDesktopProc(LPTSTR lpszDesktop, LPARAM lParam)
{
    HDESK hDesk = OpenDesktop(lpszDesktop, NULL, FALSE, GENERIC_ALL);
    if (hDesk != NULL)
    {
        if (!EnumDesktopWindows(hDesk, &EnumWindowsProc, lParam))
        {
            if (GetLastError() != 0)
            {
                // handle error as needed...
            }
        }

        CloseDesktop(hDesk);

        WndInfo *pInfo = (WndInfo*) lParam;
        if (pInfo->hWnd != NULL)
        {
            SetLastError(0);
            return FALSE;
        }
    }

    return TRUE;
}

BOOL CALLBACK EnumWindowStationProc(LPTSTR lpszWindowStation, LPARAM lParam)
{
    HWINSTA hWinStat = OpenWindowStation(lpszWindowStation, FALSE, WINSTA_ENUMDESKTOPS|WINSTA_ENUMERATE); 
    if (hWinStat != NULL)
    {
        SetProcessWindowStation(hWinStat);

        if (!EnumDesktops(hWinStat, &EnumDesktopProc, lParam))
        {
            if (GetLastError() != 0)
            {
                // handle error as needed...
            }
        }

        CloseWindowStation(hWinStat);

        WndInfo *pInfo = (WndInfo*) lParam;
        if (pInfo->hWnd != NULL)
        {
            SetLastError(0);
            return FALSE;
        }
    }

    return TRUE;
}

HWND findWindowForProcess(DWORD PID)
{
    WndInfo info;
    info.dwProcessID = PID;
    info.hWnd = NULL;

    if (!EnumWindowStations(&EnumWindowStationProc, (LPARAM)&info))
    {
        if (GetLastError() != 0)
        {
            // handle error as needed...
        }
    }

    return info.hWnd;
}
bool Utils::execIntoDifferentSession(const std::wstring &aPath, const std::wstring &aParams, const std::wstring &aMode) 
{
    ...
    bResult = CreateProcessAsUser(...);
    if (bResult)
    {
        HWND hWnd = findWindowForProcess(pi.dwProcessId);
        if (hWnd != NULL)
        {
            SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE);
            SwitchToThisWindow(hWnd, TRUE);
            SetFocus(hWnd);
        }
    }
    ...   
}

话虽如此,由于您真正想要做的只是在特定用户 session 中执行一个新进程,因此您无需为所有这些枚举逻辑操心。您根本不需要找到 WinLogon 进程,您已经从 WTSQueryUserToken() 中获得了用户 token ,因此只需根据需要复制+调整该 token 即可。并且您在窗口枚举中没有做任何有用的事情,新进程在启动时默认情况下不会这样做,所以也摆脱那个逻辑。

然后最后修复您的错误处理(或缺少),以便您可以关闭任何打开的句柄而不泄漏它们。

关于c++ - EnumDesktopWindows 错误 : (183) Cannot create a file when that file already exists,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24418122/

相关文章:

C++ : How to write a const_iterator?

python - 在python中将unix纪元时间转换为windows纪元时间

c++ - 如何获取当前执行代码的 HMODULE?

c - 如何使用文件大小来读取 WinAPI 中的文件?

c++ - 使用 Doxygen 生成项目指标?

c++ - 带有指针 this 的模板函数 "is_same",意外行为?

c - c中的全局键盘 Hook

windows - 在 Windows 上执行互斥安装?

用于创建用户配置文件的 Windows API

c++ - 计算卷积的最快方法