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,执行这个判断,其它的情况嘛,照旧。