Vista提供了较XP更严格的安全管理机制, 使一些原本在XP上运行良好的应用程序, 在Vista上表现得水土不服。其中, 文件拖放即是其中一例。你可以试一下,拖动一个文本文件到一个双击打开的NotePad中,一切正常。 但当你Run As Administrator方式加载NotePad时,你会发现,文本文件怎么也无法被拖到NotePad中了。
我们尝试拿一个更复杂强大的文本编辑器,比如VisualStudio2005来试,结果,我们得到另外的答案,VS2005竟然显示了一个禁止拖放的图标来拒绝你的文件拖放请求!
从安全的角度来看,个中一定隐藏着一些无奈的考虑。我就不猜测其中的原因了。但要做到与VS2005一样,也还是要费一些周折。首先,我们需要一个函数来确定当前的进程是否运行在Administrator等级。为此, 我google了良久,终寻到一法,可解此题。
下面的这段代码,可以分辨出当前的进程是否Run As Administrator了。其原理十分简单,如果当前是Administrator,那么申请一个SID, 必为当前组标识中的一员。
#define MAX_NAME 256
BOOL SearchTokenGroupsForSID (VOID)
{
DWORD i, dwSize = 0, dwResult = 0;
HANDLE hToken;
PTOKEN_GROUPS pGroupInfo;
SID_NAME_USE SidType;
TCHAR lpName[MAX_NAME];
TCHAR lpDomain[MAX_NAME];
BYTE sidBuffer[100];
PSID pSID = (PSID)&sidBuffer;
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
BOOL retVal = FALSE;
// Open a handle to the access token for the calling process.
if (!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken )) {
WinDBGPrintf( _T("OpenProcessToken Error %u/n"), GetLastError() );
return FALSE;
}
// Call GetTokenInformation to get the buffer size.
if(!GetTokenInformation(hToken, TokenGroups, NULL, dwSize, &dwSize)) {
dwResult = GetLastError();
if( dwResult != ERROR_INSUFFICIENT_BUFFER ) {
WinDBGPrintf( _T("GetTokenInformation Error %u/n"), dwResult );
return FALSE;
}
}
// Allocate the buffer.
pGroupInfo = (PTOKEN_GROUPS) GlobalAlloc( GPTR, dwSize );
// Call GetTokenInformation again to get the group information.
if(! GetTokenInformation(hToken, TokenGroups, pGroupInfo,
dwSize, &dwSize ) ) {
WinDBGPrintf( _T("GetTokenInformation Error %u/n"), GetLastError() );
return FALSE;
}
// Create a SID for the BUILTIN/Administrators group.
if(! AllocateAndInitializeSid( &SIDAuth, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pSID) ) {
WinDBGPrintf( _T("AllocateAndInitializeSid Error %u/n"), GetLastError() );
return FALSE;
}
// Loop through the group SIDs looking for the administrator SID.
for(i=0; i<pGroupInfo->GroupCount; i++) {
if ( EqualSid(pSID, pGroupInfo->Groups[i].Sid) ) {
// Lookup the account name and print it.
dwSize = MAX_NAME;
if( !LookupAccountSid( NULL, pGroupInfo->Groups[i].Sid,
lpName, &dwSize, lpDomain,
&dwSize, &SidType ) ) {
dwResult = GetLastError();
if( dwResult == ERROR_NONE_MAPPED )
_tcscpy(lpName, _T("NONE_MAPPED"));
else {
WinDBGPrintf(_T("LookupAccountSid Error %u/n"), GetLastError());
return FALSE;
}
}
WinDBGPrintf( _T("Current user is a member of the %s//%s group/n"),
lpDomain, lpName );
// Find out whether the SID is enabled in the token.
if (pGroupInfo->Groups[i].Attributes & SE_GROUP_ENABLED)
{
//只有此时,才能断定当前进程以Administrator运行
WinDBGPrintf(_T("The group SID is enabled./n"));
retVal = TRUE;
}
else if (pGroupInfo->Groups[i].Attributes &
SE_GROUP_USE_FOR_DENY_ONLY)
//这个时候,只能说明User是Administrator,但并未以Adminstrator方式运行.
WinDBGPrintf(_T("The group SID is a deny-only SID./n"));
else
WinDBGPrintf(_T("The group SID is not enabled./n"));
}
}
if (pSID)
FreeSid(pSID);
if ( pGroupInfo )
GlobalFree( pGroupInfo );
return retVal;
}
我只是对这篇代码(
Searching for a SID in an Access Token in C++)作了轻微的调整,最好还是到代码的出处去了解详情吧。
有了这个工具,下一步就简单了:
OSVERSIONINFO osvi;
::memset(&osvi,0,sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
WinDBGPrintf(_T("MajorVersion: %d, MinorVersion: %d/n"), osvi.dwMajorVersion, osvi.dwMinorVersion);
if(osvi.dwMajorVersion > 5)
{
if(SearchTokenGroupsForSID())
{
WinDBGPrintf(_T("I'm Adminstrator, so disable AcceptDragFile/n"));
DragAcceptFiles(0);
}
else
{
WinDBGPrintf(_T("I'm Standard user, so enable AcceptDragFile/n"));
DragAcceptFiles(1);
}
}
else
{
WinDBGPrintf(_T("Current windows version is not VISTA. /n"));
DragAcceptFiles(1);
}
我们以前又没有这个限制,所以只对主版本号> 5的windows,执行这个判断,其它的情况嘛,照旧。