Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9548794
  • 博文数量: 1227
  • 博客积分: 10026
  • 博客等级: 上将
  • 技术积分: 20273
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-16 12:40
文章分类

全部博文(1227)

文章存档

2010年(1)

2008年(1226)

我的朋友

分类: C/C++

2008-04-23 21:52:29

如何获取Windows系统登陆用户名

作者:



关键字:获取登陆用户名 GetUserName WTSQuerySessionInformation

  一般用 GetUserName(或 GetUserNameEx )函数可得到当前登陆登陆用户名(但不总会得到,下面会分析),此系统函数在Win95、WinNT 及以后所有操作系统中都可用。代码如下:

BOOL CSecurityTool::GetCurrProcessUser(CString& strName)

{	

	BOOL bRet(TRUE);

	strName = _T("");



	DWORD dwSize = MAX_PATH;

	TCHAR *pszName = new TCHAR[dwSize];

	if (!GetUserName(pszName, &dwSize))

	{

		delete[] pszName;

		pszName = new TCHAR[dwSize];

		bRet = GetUserName(pszName, &dwSize);

	}

	

	strName = pszName;

	delete[] pszName;		

	return bRet;

}      
  此函数目的准确来说是获取当前线程的用户名(MSDN语:retrieves the user name of the current thread)。如果是NT service(NT服务程序)将此进程启动,得到的结果是NT Service进程的用户名,即“SYSTEM”,而不是登陆用户名;同理,如果此进程是通过CreateProcessAsUser创建的,GetUserName获取的用户将是“AsUser”的用户名。另外,如果当前线程正impersonate其他用户环境(用函数ImpersonateLoggedOnUser可达到此目的),它获取的将是其他用户名。因此,此函数只能在特定环境中才可以获取登陆用户名。
  那如何不因进程本身运行环境的不同,而准确地获取登陆用户名呢?
  我们首先看看Windows XP操作系统,它提供了WTSQuerySessionInformation函数,这个函数可以获取会话(session)相关信息,其中一个用 途是获取会话的登陆用户。代码如下:
BOOL CSecurityTool::GetLogUserXP(CString& strName)

{

	BOOL bRet = FALSE;

	strName = _T("");



	//for xp or above

	TCHAR *szLogName = NULL;

	DWORD dwSize = 0;

	if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,

	                               WTS_CURRENT_SESSION,

	                               WTSUserName,

	                               &szLogName,

	                               &dwSize))

	{			

		strName = szLogName;

		WTSFreeMemory(szLogName);

		bRet = TRUE;

	}



	return bRet;

}      
  如果用户还没有登陆,获取的用户名将为空(譬如在NT service程序中)。虽然MSDN中指明WTSQuerySessionInformation可以在win2000 pro 中使用,但由于安装win2000 professional时,terminal service是没有安装的(除非用特殊方法如第三方工具可以安装terminal service) ,所以调用此函数会失败,需要寻找其他方法。
  再看Win2000:查阅了许多资料,未能发现在Win2000中直接获取登陆用户名的系统函数,看来只有曲线救国了。由于Explorer.exe进程的用户肯定是当前登 陆用户,所以获取到它的用户名就等于获取到登陆用户名。具体实现:首先枚举系统所有进程,找到Explorer.exe进程ID,然后通过ID获取此 进程的令牌(Token),再获取令牌的用户信息,即为登陆用户名。代码如下:
//获取win2000登陆用户      

BOOL CSecurityTool::GetLogUser2K(CString& strName)

{

	BOOL bRet = FALSE;

	HANDLE hSnapshot = NULL;

	strName = _T("");



    __try

	{

		// Get a snapshot of the processes in the system

        hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

        if (hSnapshot == NULL)

		{            

			__leave;

		}



        PROCESSENTRY32 pe32;

        pe32.dwSize = sizeof(pe32);



        // Find the "System" process

        BOOL fProcess = Process32First(hSnapshot, &pe32);

        while (fProcess)

		{

			if (lstrcmpi(pe32.szExeFile, TEXT("explorer.exe")) == 0)

			{	

				TCHAR szUserName[MAX_PATH];

				if (GetProcessUser(pe32.th32ProcessID, szUserName, MAX_PATH))

				{

					bRet = TRUE;

					strName = szUserName;

				}

				

				break;

			}

			fProcess = Process32Next(hSnapshot, &pe32);

		}

        if (!fProcess)

		{			

            __leave;    // Didn''t find "System" process

		}

	}

    __finally

	{

		// Cleanup the snapshot

       if (hSnapshot != NULL)

		   CloseHandle(hSnapshot);

    }



	return bRet;	

}



//获取进程的用户名

BOOL CSecurityTool::GetProcessUser(DWORD dwProcessID, TCHAR *szUserName, DWORD nNameLen)

{

	BOOL fResult  = FALSE;

    HANDLE hProc  = NULL;

	HANDLE hToken = NULL;

	TOKEN_USER *pTokenUser = NULL;

	

	__try

	{

        // Open the process with PROCESS_QUERY_INFORMATION access

        hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessID);

        if (hProc == NULL)

		{

			__leave;

		}

        fResult = OpenProcessToken(hProc, TOKEN_QUERY, &hToken);

        if(!fResult)  

		{

			__leave;

		}

		

		DWORD dwNeedLen = 0;		

		fResult = GetTokenInformation(hToken,TokenUser, NULL, 0, &dwNeedLen);

		if (dwNeedLen > 0)

		{

			pTokenUser = (TOKEN_USER*)new BYTE[dwNeedLen];

			fResult = GetTokenInformation(hToken,

			                              TokenUser,

			                              pTokenUser,

			                              dwNeedLen,

			                              &dwNeedLen);

			if (!fResult)

			{

				__leave;

			}

		}

		else

		{

			__leave;

		}



		SID_NAME_USE sn;

		TCHAR szDomainName[MAX_PATH];

		DWORD dwDmLen = MAX_PATH;

		fResult = LookupAccountSid(NULL,

		                           pTokenUser->User.Sid,

		                           szUserName,

		                           &nNameLen,

                                   szDomainName,

                                   &dwDmLen,

                                   &sn);

	}

	__finally

	{

		if (hProc)

			::CloseHandle(hProc);

		if (hToken)

			::CloseHandle(hToken);

		if (pTokenUser)

			delete[] (char*)pTokenUser;



		return fResult;

	}

}      
  熟悉win2000系统的同仁肯定会发现此方法存在缺陷:explorer.exe进程可能不存在(被用户kill掉或自己中断了),这时候这个方法就获取不到登陆用户名。但在没有更好方法前,只能将就。

总结

  因此,软件中如果需要获取登陆用户名,要根据具体情况选择不同的方法。如果确信自己的进程一定在登陆用户环境下启动,则GetUserName即可;否则,需要采用后面的两种方法,当然,在使用前需要判断一下操作系统的类型。附 :。

欢迎诸位与大家分享其他方法。
阅读(607) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~