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