• vista开发注意事项及解决方案


    1.判断UAC是否启动
    BOOL _stdcall IsRunUAC()//判断是否启动UAC
    {
    BOOL bRet = FALSE;
    LONG lErr;
    HKEY hKEY;
    DWORD dwEnableLUA;
    DWORD dwType = REG_DWORD;
    DWORD dwSize = sizeof( DWORD );

    if( IsVISTA() )
    {
    lErr = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
    _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\\"),
    0,
    KEY_READ,
    &hKEY );
    if( lErr == ERROR_SUCCESS )
    {
    lErr = RegQueryValueEx( hKEY,
    _T( "EnableLUA" ),
    NULL,
    &dwType,
    (BYTE*)&dwEnableLUA,
    &dwSize );
    if( lErr == ERROR_SUCCESS )
    {
    if( 0 == dwEnableLUA )
    {
    bRet = FALSE;
    }
    else
    {
    bRet = TRUE;
    }
    }
    else;

    RegCloseKey( hKEY );
    }
    else;
    }
    else;

    return bRet;
    }

    以上代码是判断一个注册表键值,不是正规方式。UAC启动要通过重起系统完成,这个标志位不表示当前UAC状态,所以此代码要在系统启动时执行才有效。

    2.解决UAC打开时,不同权限之间的应用程序间不能广播消息
    Vista UAC打开时,不同权限的应用程序广播消息是收不到的。

    UINT UIBroadcastCommand = ::RegisterWindowMessage( SNA_MESSAGE );
    ON_REGISTERED_MESSAGE( UIBroadcastCommand, OnFromMessage )

    将以下代码加入程序启始位置
    BOOL AllowMeesageForVista( UINT uMessageID, BOOL bAllow )//注册Vista全局消息
    {
    BOOL bResult = FALSE;
    HMODULE hUserMod = NULL;

    do
    {
    //vista and later
    hUserMod = LoadLibrary( "User32.dll" );
    if( NULL == hUserMod ) break;

    _ChangeWindowMessageFilter pChangeWindowMessageFilter = (_ChangeWindowMessageFilter)GetProcAddress( hUserMod, "ChangeWindowMessageFilter" );
    if( NULL == pChangeWindowMessageFilter )break;

    bResult = pChangeWindowMessageFilter( uMessageID, bAllow ? 1 : 2 );//MSGFLT_ADD: 1, MSGFLT_REMOVE: 2
    }
    while( 0 );

    if( NULL != hUserMod )
    {
    FreeLibrary( hUserMod );
    }
    else;

    return bResult;
    }

    这里使用Vista提供的标准函数ChangeWindowMessageFilter注册一个全局消息。但是由于系统服务与应用程序间的session不同,所以应用程序无法响应系统服务的广播消息。

    3. 系统服务与应用程序的事件通讯

    在Vista中高权限进程创建的事件使用低权限进程是无法open的(其它windows也一样)。在创建事件时使用以下代码
    DWORD _stdcall MyCreateEvent( HANDLE* phEvent, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName )
    {
    DWORD dwRet = 0;
    PSID pEveryoneSID = NULL, pAdminSID = NULL;
    SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
    EXPLICIT_ACCESS ea[1];
    PSECURITY_DESCRIPTOR pSD = NULL;
    PACL pACL = NULL;
    SECURITY_ATTRIBUTES sa;

    *phEvent = NULL;

    ////////////////////////
    do
    {
    // Create a well-known SID for the Everyone group.
    if( !AllocateAndInitializeSid( &SIDAuthWorld,
    1,
    SECURITY_WORLD_RID,
    0, 0, 0, 0, 0, 0, 0,
    &pEveryoneSID ) )
    {
    dwRet = GetLastError();
    break;
    }
    else;

    // Initialize an EXPLICIT_ACCESS structure for an ACE.
    ZeroMemory( &ea, sizeof(EXPLICIT_ACCESS) );
    ea[0].grfAccessPermissions = EVENT_ALL_ACCESS;
    ea[0].grfAccessMode = SET_ACCESS;
    ea[0].grfInheritance= NO_INHERITANCE;
    ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
    ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
    ea[0].Trustee.ptstrName = (LPTSTR) pEveryoneSID;

    // Create a new ACL that contains the new ACEs.
    dwRet = SetEntriesInAcl( 1, ea, NULL, &pACL );
    if( ERROR_SUCCESS != dwRet )
    {
    break;
    }
    else;

    // Initialize a security descriptor.
    pSD = (PSECURITY_DESCRIPTOR)LocalAlloc( LPTR,
    SECURITY_DESCRIPTOR_MIN_LENGTH );
    if( NULL == pSD )
    {
    dwRet = GetLastError();
    break;
    }
    else;

    if( !InitializeSecurityDescriptor( pSD, SECURITY_DESCRIPTOR_REVISION ) )
    {
    dwRet = GetLastError();
    break;
    }
    else;

    // Add the ACL to the security descriptor.
    if (!SetSecurityDescriptorDacl( pSD,
    TRUE, // bDaclPresent flag
    pACL,
    FALSE ) ) // not a default DACL
    {
    dwRet = GetLastError();
    break;
    }
    else;

    // Initialize a security attributes structure.
    sa.nLength = sizeof( SECURITY_ATTRIBUTES );
    sa.lpSecurityDescriptor = pSD;
    sa.bInheritHandle = FALSE;

    ////////////////
    *phEvent = CreateEvent( &sa, bManualReset, bInitialState, lpName );
    if( NULL == *phEvent )
    {
    dwRet = GetLastError();
    break;
    }
    else;
    }
    while( 0 );

    //////////
    if( pEveryoneSID )
    {
    FreeSid( pEveryoneSID );
    }
    else;

    if( pACL )
    {
    LocalFree( pACL );
    }
    else;

    if( pSD )
    {
    LocalFree(pSD);
    }
    else;

    return dwRet;
    }
    为CreateEvent注册一个Everyone的事件,这段代码也可以注册Everyone的文件和filemap的属性。


    4.全局名称
    在VISTA下所有跨进程事件,互斥量、名称都必须加“Global\”标识。否则无法和其他用户环境下通讯。


    5.服务启动进程
    VISTA下服务为session0,所以用服务直接启动进程,会出现非交互试对话框的问题。以下代码解决此问题。
    DWORD _stdcall LaunchAppIntoDifferentSession( LPTSTR lpCommand )
    {
    DWORD dwRet = 0;
    PROCESS_INFORMATION pi;
    STARTUPINFO si;
    DWORD dwSessionId;
    HANDLE hUserToken = NULL;
    HANDLE hUserTokenDup = NULL;
    HANDLE hPToken = NULL;
    HANDLE hProcess = NULL;
    DWORD dwCreationFlags;

    // Log the client on to the local computer.
    dwSessionId = WTSGetActiveConsoleSessionId();

    do
    {
    WTSQueryUserToken( dwSessionId,&hUserToken );
    dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
    ZeroMemory( &si, sizeof( STARTUPINFO ) );
    si.cb= sizeof( STARTUPINFO );
    si.lpDesktop = "winsta0\\default";
    ZeroMemory( &pi, sizeof(pi) );
    TOKEN_PRIVILEGES tp;
    LUID luid;

    if( !::OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY
    | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID
    | TOKEN_READ | TOKEN_WRITE, &hPToken ) )
    {
    dwRet = GetLastError();
    break;
    }
    else;

    if ( !LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &luid ) )
    {
    dwRet = GetLastError();
    break;
    }
    else;
    tp.PrivilegeCount =1;
    tp.Privileges[0].Luid =luid;
    tp.Privileges[0].Attributes =SE_PRIVILEGE_ENABLED;

    if( !DuplicateTokenEx( hPToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hUserTokenDup ) )
    {
    dwRet = GetLastError();
    break;
    }
    else;

    //Adjust Token privilege
    if( !SetTokenInformation( hUserTokenDup,TokenSessionId,(void*)&dwSessionId,sizeof(DWORD) ) )
    {
    dwRet = GetLastError();
    break;
    }
    else;

    if( !AdjustTokenPrivileges( hUserTokenDup, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, NULL ) )
    {
    dwRet = GetLastError();
    break;
    }
    else;

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

    // Launch the process in the client's logon session.
    if( CreateProcessAsUser( hUserTokenDup, // client's access token
    NULL, // file to execute
    lpCommand, // 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
    ) )
    {
    }
    else
    {
    dwRet = GetLastError();
    break;
    }
    }
    while( 0 );

    //Perform All the Close Handles task
    if( NULL != hUserToken )
    {
    CloseHandle( hUserToken );
    }
    else;

    if( NULL != hUserTokenDup)
    {
    CloseHandle( hUserTokenDup );
    }
    else;

    if( NULL != hPToken )
    {
    CloseHandle( hPToken );
    }
    else;

    return dwRet;
    }

    以上代码取得服务Token,通过SetTokenInformation将进程调到当前session,这样创建的进程具有system权限又不会出现非交互试对话框。
    以下程序是降权限运行进程,同样也能解决非交互试对话框的问题。

    BOOL PrivDown_Execute( LPTSTR lpFilePath )
    {
    BOOL bRet = FALSE;
    HANDLE hToken = NULL;

    EnablePrivilege( SE_DEBUG_NAME );
    hToken = GetCurrentUserToken();
    if( NULL != hToken )
    {
    bRet = StartInteractiveClientProcess( NULL, NULL, NULL, lpFilePath, hToken, NULL );
    }
    else;

    return bRet;
    }

    ///////////////////////////////////////////

    HANDLE GetCurrentUserToken()
    {
    HANDLE hProc = NULL;
    HANDLE hToken = NULL;
    BOOL bSuccess = FALSE;
    BOOL fResult;

    __try
    {
    // Enable the SE_DEBUG_NAME privilege in our process token
    if (!EnablePrivilege(SE_DEBUG_NAME))
    {
    printf("GetLSAToken EnablePrivilege Failed");
    __leave;
    }

    // Retrieve a handle to the "System" process
    hProc = OpenExplorerProcess();
    if(hProc == NULL)
    {
    printf("GetLSAToken OpenSystemProcess Failed");
    __leave;
    }

    // Open the process token with READ_CONTROL and WRITE_DAC access. We
    // will use this access to modify the security of the token so that we
    // retrieve it again with a more complete set of rights.
    fResult = OpenProcessToken(hProc, READ_CONTROL | WRITE_DAC,
    &hToken);
    if(FALSE == fResult)
    {
    printf("GetLSAToken OpenProcessToken Failed");
    __leave;
    }

    // Add an ace for the current user for the token. This ace will add
    // TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY rights.
    if (!ModifySecurity(hToken, TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY
    | TOKEN_QUERY | TOKEN_ADJUST_SESSIONID))
    {
    printf("GetLSAToken ModifySecurity Failed");
    __leave;
    }


    // Reopen the process token now that we have added the rights to
    // query the token, duplicate it, and assign it.
    fResult = OpenProcessToken(hProc, TOKEN_QUERY | TOKEN_DUPLICATE
    | TOKEN_ASSIGN_PRIMARY | READ_CONTROL | WRITE_DAC, &hToken);
    if (FALSE == fResult)
    {
    printf("GetLSAToken OpenProcessToken Failed");
    __leave;
    }
    bSuccess = TRUE;
    }
    __finally
    {
    // Close the System process handle
    if (hProc != NULL) CloseHandle(hProc);
    if(bSuccess)
    return hToken;
    else
    {
    CloseHandle(hToken);
    return NULL;
    }
    }
    }

    #define DESKTOP_ALL (DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | \
    DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK | \
    DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | \
    DESKTOP_SWITCHDESKTOP | STANDARD_RIGHTS_REQUIRED)

    #define WINSTA_ALL (WINSTA_ENUMDESKTOPS | WINSTA_READATTRIBUTES | \
    WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | \
    WINSTA_WRITEATTRIBUTES | WINSTA_ACCESSGLOBALATOMS | \
    WINSTA_EXITWINDOWS | WINSTA_ENUMERATE | \
    WINSTA_READSCREEN | \
    STANDARD_RIGHTS_REQUIRED)

    #define GENERIC_ACCESS (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL)

    BOOL AddAceToWindowStation(HWINSTA hwinsta, PSID psid);

    BOOL AddAceToDesktop(HDESK hdesk, PSID psid);

    BOOL GetLogonSID(HANDLE hToken, PSID *ppsid)
    {
    PWTS_PROCESS_INFO pProcessInfo = NULL;
    DWORD ProcessCount = 0;
    BOOL ret=FALSE;
    DWORD CurrentProcess;
    if (WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pProcessInfo, &ProcessCount))
    {
    // dump each process description
    for (CurrentProcess = 0; CurrentProcess < ProcessCount; CurrentProcess++)
    {

    if( strcmp(pProcessInfo[CurrentProcess].pProcessName, "System") == 0 )
    {
    //*ppsid = pProcessInfo[CurrentProcess].pUserSid;
    DWORD dwLength = GetLengthSid(pProcessInfo[CurrentProcess].pUserSid);
    *ppsid = (PSID) HeapAlloc(GetProcessHeap(),
    HEAP_ZERO_MEMORY, dwLength);
    if (*ppsid == NULL)
    break;
    if (!CopySid(dwLength, *ppsid, pProcessInfo[CurrentProcess].pUserSid))
    {
    HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
    break;
    }
    ret=TRUE;
    break;
    }
    }

    WTSFreeMemory(pProcessInfo);
    }

    return ret;
    }

    BOOL GetLogonSID_1 (HANDLE hToken, PSID *ppsid)
    {
    BOOL bSuccess = FALSE;
    DWORD dwIndex;
    DWORD dwLength = 0;
    PTOKEN_GROUPS ptg = NULL;

    // Verify the parameter passed in is not NULL.
    if (NULL == ppsid)
    goto Cleanup;

    // Get required buffer size and allocate the TOKEN_GROUPS buffer.

    if (!GetTokenInformation(
    hToken, // handle to the access token
    TokenGroups, // get information about the token's groups
    (LPVOID) ptg, // pointer to TOKEN_GROUPS buffer
    0, // size of buffer
    &dwLength // receives required buffer size
    ))
    {
    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    goto Cleanup;

    ptg = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(),
    HEAP_ZERO_MEMORY, dwLength);

    if (ptg == NULL)
    goto Cleanup;
    }


    // Get the token group information from the access token.

    if (!GetTokenInformation(
    hToken, // handle to the access token
    TokenGroups, // get information about the token's groups
    (LPVOID) ptg, // pointer to TOKEN_GROUPS buffer
    dwLength, // size of buffer
    &dwLength // receives required buffer size
    ))
    {
    goto Cleanup;
    }

    // Loop through the groups to find the logon SID.

    for (dwIndex = 0; dwIndex < ptg->GroupCount; dwIndex++)
    if ((ptg->Groups[dwIndex].Attributes & 0xE)
    == 0xE)
    {
    // Found the logon SID; make a copy of it.

    dwLength = GetLengthSid(ptg->Groups[dwIndex].Sid);
    *ppsid = (PSID) HeapAlloc(GetProcessHeap(),
    HEAP_ZERO_MEMORY, dwLength);
    if (*ppsid == NULL)
    goto Cleanup;
    if (!CopySid(dwLength, *ppsid, ptg->Groups[dwIndex].Sid))
    {
    HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
    goto Cleanup;
    }
    bSuccess = TRUE;
    break;
    }

    Cleanup:

    // Free the buffer for the token groups.

    if (ptg != NULL)
    HeapFree(GetProcessHeap(), 0, (LPVOID)ptg);

    return bSuccess;
    }


    VOID FreeLogonSID (PSID *ppsid)
    {
    HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
    }


    BOOL StartInteractiveClientProcess( LPTSTR lpszUsername, // client to log on
    LPTSTR lpszDomain, // domain of client's account
    LPTSTR lpszPassword, // client's password
    LPTSTR lpCommandLine, // command line to execute
    HANDLE Token,
    PROCESS_INFORMATION* ppi ) //返回PROCESS_INFORMATION
    {
    HANDLE hToken;
    HDESK hdesk = NULL;
    HWINSTA hwinsta = NULL, hwinstaSave = NULL;
    PROCESS_INFORMATION pi;
    PSID pSid = NULL;
    STARTUPINFO si;
    BOOL bResult = FALSE;

    // Log the client on to the local computer.

    if(Token!=NULL)
    {
    printf("%08x\n", Token);
    hToken = Token;
    }
    else if (!LogonUser(
    lpszUsername,
    lpszDomain,
    lpszPassword,
    LOGON32_LOGON_INTERACTIVE,
    LOGON32_PROVIDER_DEFAULT,
    &hToken) )
    {
    goto Cleanup;
    }

    // Save a handle to the caller's current window station.
    if ( (hwinstaSave = GetProcessWindowStation() ) == NULL)
    goto Cleanup;

    // Get a handle to the interactive window station.
    hwinsta = OpenWindowStation(
    "winsta0", // the interactive window station
    FALSE, // handle is not inheritable
    READ_CONTROL | WRITE_DAC); // rights to read/write the DACL

    if (hwinsta == NULL)
    goto Cleanup;

    // To get the correct default desktop, set the caller's
    // window station to the interactive window station.

    if (!SetProcessWindowStation(hwinsta))
    goto Cleanup;

    // Get a handle to the interactive desktop.
    hdesk = OpenDesktop(
    "default", // the interactive window station
    0, // no interaction with other desktop processes
    FALSE, // handle is not inheritable
    READ_CONTROL | // request the rights to read and write the DACL
    WRITE_DAC |
    DESKTOP_WRITEOBJECTS |
    DESKTOP_READOBJECTS);

    // Restore the caller's window station.
    if (!SetProcessWindowStation(hwinstaSave))
    goto Cleanup;

    if (hdesk == NULL)
    goto Cleanup;

    // Get the SID for the client's logon session.
    if (!GetLogonSID(hToken, &pSid))
    {
    if (!GetLogonSID_1(hToken, &pSid))
    {
    goto Cleanup;
    }
    }

    // Allow logon SID full access to interactive window station.
    if (! AddAceToWindowStation(hwinsta, pSid) )
    goto Cleanup;

    // Allow logon SID full access to interactive desktop.
    if (! AddAceToDesktop(hdesk, pSid) )
    goto Cleanup;

    // Impersonate client to ensure access to executable file.
    if (! ImpersonateLoggedOnUser(hToken) )
    goto Cleanup;

    // Initialize the STARTUPINFO structure.
    // Specify that the process runs in the interactive desktop.
    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb= sizeof(STARTUPINFO);
    si.lpDesktop = TEXT("winsta0\\default"); //You can use EnumWindowStations to enum desktop

    // Launch the process in the client's logon session.
    bResult = CreateProcessAsUser(
    hToken, // client's access token
    NULL, // file to execute
    lpCommandLine, // command line
    NULL, // pointer to process SECURITY_ATTRIBUTES
    NULL, // pointer to thread SECURITY_ATTRIBUTES
    FALSE, // handles are not inheritable
    NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, // creation flags
    NULL, // pointer to new environment block
    NULL, // name of current directory
    &si, // pointer to STARTUPINFO structure
    &pi // receives information about new process
    );

    ////
    if( NULL != ppi )
    {
    *ppi = pi;
    }
    else;

    // End impersonation of client.
    RevertToSelf();

    goto Cleanup;
    //return bResult; <------------------------------------------------------------------------

    if (bResult && pi.hProcess != INVALID_HANDLE_VALUE)
    {
    WaitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hProcess);
    }

    if (pi.hThread != INVALID_HANDLE_VALUE)
    CloseHandle(pi.hThread);

    Cleanup:

    if (hwinstaSave != NULL)
    SetProcessWindowStation (hwinstaSave);

    // Free the buffer for the logon SID.

    if (pSid)
    FreeLogonSID(&pSid);

    // Close the handles to the interactive window station and desktop.

    if (hwinsta)
    CloseWindowStation(hwinsta);

    if (hdesk)
    CloseDesktop(hdesk);

    // Close the handle to the client's access token.

    if (hToken != INVALID_HANDLE_VALUE)
    CloseHandle(hToken);

    return bResult;
    }

    6.system权限进程的一些问题。
    为了解决UAC启动给程序造成的权限问题,我们采用了将进程提升到system权限的方法。但是出现以下一系列问题。
    1)使用SHBrowseForFolder无法显示盘目录。
    解决方案:重新做了个目录选择的模块替代SHBrowseForFolder。

    2)使用CFileDialog 会指向{sys}\config\systemprofile\desktop空目录,且无法保存到用户桌面。
    解决方案:创建一个{sys}\config\systemprofile\desktop目录。但无法解决保存到用户桌面的问题。

    3)无法打开帮助文件。
    解决方案:开始使用降权限运行hh.exe但是无效。重新做了个exe,用HtmlHelp调用帮助,降权限运行就可以了。

    4)继承CHtmlView内至的IE浏览器无法打开。
    解决方案:目前只能从外部打开IE浏览器。

    5) vista 系统下shellexecute open ms office 会出错,具体如下http://social.msdn.microsoft.com/Forums/en-US/windowscompatibility/thread/716cb8fa-c2ca-45ba-ad2e-b71b32b2129b

     

  • 相关阅读:
    IM 融云 之 初始化及登录
    IM 融云 之 安装cocoapods 安装 SDK
    github desktop 下载
    iOS 架构模式
    IM 融云 之 通讯能力库API
    IM 融云 之 开发基础概念
    IM 之 融云
    php获得文件的属性
    js模拟复制
    linux修改yum源
  • 原文地址:https://www.cnblogs.com/rainbowzc/p/1767812.html
Copyright © 2020-2023  润新知