天天看點

轉載: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浏覽器。

繼續閱讀