vista 开发的一些注意事项!

时间:2022-12-18 22:45:00

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( π, 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
                                    π            // 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
      π                // 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浏览器。