关于在windows服务程序中调用与用户桌面相关的shell函数的问题 可以启动另一个程序,CreateProcessAsUser等来让这个进程跑到user session,然后执行相关动作 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 SHELL跟SESSION完全不相干,不是同一个层面的东西,对楼主的问题没有帮助。先理解一下SHELL的概念:1.操作系统本来可以不需要SHELL,比如服务器在正常运行的时候不存在SHELL,只有需要进入桌面环境的帐户才会启动SHELL(桌面本身就是SHELL提供的),桌面应用软件可以无障碍地执行任何SHELL函数。2.SHELL是绑定账号的,也就是说SHELL必须以某个桌面帐户身份运行。服务不同,服务没有桌面(或者说服务的桌面是CONSOLE),电脑登录进入桌面之前没有SHELL在执行,但是服务已经运行了,服务中不能使用任何SHELL函数。其次,服务是以system帐户身份运行的,system是内置帐户,不是桌面帐户,不能启用桌面。即使服务被设置成能跟桌面交互,那也只是系统开的一个后门,不代表能显示窗口就能使用SHELL函数。某些服务可以以桌面帐户身份执行,但同样不能使用SHELL,所以不要以为桌面帐户运行的程序就一定能用SHELL函数。说得有点绕,总结一下:只有桌面帐户在桌面环境下的应用才能使用SHELL函数;所有服务程序(包括桌面帐户身份运行的服务程序)都不能使用SHELL函数。所以:找其它API替代吧。 首先,服务中一些SHELL函数是绝对可以用的,因为我试过了很多次了,只是涉及到用户桌面相关的路径时无法获取到。然后,虽然我没有什么官方依据,但从网上的一些资料来看,服务是有桌面的,只是跟用户桌面不在同一个域里,尤其的win7中。服务是可以有窗口的,只是显示在了看不见的区域而已。还有,Shell部分虽然我不太精通,但我感觉桌面只是一个UI显示的东西而已,我把explorer这个进程结束掉就不能调用Shell函数了么? 楼主找到解决方法吗我也碰到了一个类似的问题带UI的服务调用SHBrowseForFolder无法显示窗口就直接返回 没解决自己写一个仿SHBrowseForFolder功能的UI 这个问题搞定了,分几步:得到当前登录用户的会话ID通过会话ID得到令牌模仿成当前登录用户就可以获取到当前用户的桌面的特殊目录,例如开始菜单,快速启动栏,桌面等代码如下:int getcuruserdir(TCHAR lpPath[], int i) // i表示获取不同值标志..1,开始菜单,2桌面,3开始启动栏{ BOOL bRes = FALSE; // char lpPath[MAX_PATH]; DWORD RetVal = 0; DWORD ErrCode = 0; DWORD ConsoleSessionId = 0; // 函数的句柄 HMODULE hInstKernel32 = NULL; HMODULE hInstWtsapi32 = NULL; // Token的句柄 HANDLE hTokenUser = NULL; HANDLE hTokenThisProcess = NULL; HANDLE hTokenThis = NULL; // WTSGetActiveConsoleSessionId 函数,得到当前登录用户的会话ID // 这里的代码用的是VC6,新版的SDK已经包括此函数,无需LoadLibrary了。 typedef DWORD(WINAPI * WTSGetActiveConsoleSessionIdPROC)(); WTSGetActiveConsoleSessionIdPROC WTSGetActiveConsoleSessionId = NULL; hInstKernel32 = LoadLibrary("Kernel32.dll"); if (!hInstKernel32) { return FALSE; } WTSGetActiveConsoleSessionId = (WTSGetActiveConsoleSessionIdPROC) GetProcAddress(hInstKernel32, "WTSGetActiveConsoleSessionId"); if (!WTSGetActiveConsoleSessionId) { return FALSE; } // WTSQueryUserToken 函数,通过会话ID得到令牌 typedef BOOL(WINAPI * WTSQueryUserTokenPROC)(ULONG SessionId, PHANDLE phToken); WTSQueryUserTokenPROC WTSQueryUserToken = NULL; hInstWtsapi32 = LoadLibrary("Wtsapi32.dll"); if (!hInstWtsapi32) { return FALSE; } WTSQueryUserToken = (WTSQueryUserTokenPROC)GetProcAddress(hInstWtsapi32, "WTSQueryUserToken"); if (!WTSQueryUserToken) { return FALSE; } // 得到当前激活用户的会话ID ConsoleSessionId = WTSGetActiveConsoleSessionId(); // 得到当前登录用户的令牌 bRes = WTSQueryUserToken(ConsoleSessionId, &hTokenUser); if (!bRes) { return FALSE; } // 模仿成当前登录用户 bRes = ImpersonateLoggedOnUser(hTokenUser); if (!bRes) { return FALSE; } // 取得当前用户的Startup文件夹路径 if (i == 1) // 开始菜单 bRes = SHGetSpecialFolderPath(NULL, lpPath, CSIDL_STARTMENU, TRUE); else if (i == 2) // 桌面 bRes = SHGetSpecialFolderPath(NULL, lpPath, CSIDL_DESKTOPDIRECTORY, TRUE); else if (i == 3) // 启动栏 bRes = SHGetSpecialFolderPath(NULL, lpPath, CSIDL_STARTUP, TRUE); // 终止模拟,返回 RevertToSelf();} 利用odbc连接数据源的问题 VC6.0断点设置失败 listbox的简单操作,怎么在上面查找指定项并删除之 还是static text的问题求知情者透露一下 使用CIN输入时,怎样才能使空格成为输入的一部分?在线等待!!!!1 出现这类错误该怎样解决? 请教高手!如何在CPropertyPage中设置相应的CPropertySheet的标签名称 请问在什么叫做本垒打? 怎么往数据库里添加记录》 *&是什么含义? 关于文件的存取问题 CFrameWnd类对象(this)->可调用的成员怎么有CMENU的成员m_hMenu呢???
1.操作系统本来可以不需要SHELL,比如服务器在正常运行的时候不存在SHELL,只有需要进入桌面环境的帐户才会启动SHELL(桌面本身就是SHELL提供的),桌面应用软件可以无障碍地执行任何SHELL函数。
2.SHELL是绑定账号的,也就是说SHELL必须以某个桌面帐户身份运行。服务不同,服务没有桌面(或者说服务的桌面是CONSOLE),电脑登录进入桌面之前没有SHELL在执行,但是服务已经运行了,服务中不能使用任何SHELL函数。其次,服务是以system帐户身份运行的,system是内置帐户,不是桌面帐户,不能启用桌面。
即使服务被设置成能跟桌面交互,那也只是系统开的一个后门,不代表能显示窗口就能使用SHELL函数。
某些服务可以以桌面帐户身份执行,但同样不能使用SHELL,所以不要以为桌面帐户运行的程序就一定能用SHELL函数。说得有点绕,总结一下:只有桌面帐户在桌面环境下的应用才能使用SHELL函数;所有服务程序(包括桌面帐户身份运行的服务程序)都不能使用SHELL函数。所以:找其它API替代吧。
首先,服务中一些SHELL函数是绝对可以用的,因为我试过了很多次了,只是涉及到用户桌面相关的路径时无法获取到。
然后,虽然我没有什么官方依据,但从网上的一些资料来看,服务是有桌面的,只是跟用户桌面不在同一个域里,尤其的win7中。服务是可以有窗口的,只是显示在了看不见的区域而已。
还有,Shell部分虽然我不太精通,但我感觉桌面只是一个UI显示的东西而已,我把explorer这个进程结束掉就不能调用Shell函数了么?
我也碰到了一个类似的问题
带UI的服务调用SHBrowseForFolder无法显示窗口就直接返回
自己写一个仿SHBrowseForFolder功能的UI
分几步:
得到当前登录用户的会话ID
通过会话ID得到令牌
模仿成当前登录用户
就可以获取到当前用户的桌面的特殊目录,例如开始菜单,快速启动栏,桌面等
代码如下:int getcuruserdir(TCHAR lpPath[], int i) // i表示获取不同值标志..1,开始菜单,2桌面,3开始启动栏
{
BOOL bRes = FALSE;
// char lpPath[MAX_PATH];
DWORD RetVal = 0;
DWORD ErrCode = 0;
DWORD ConsoleSessionId = 0;
// 函数的句柄
HMODULE hInstKernel32 = NULL;
HMODULE hInstWtsapi32 = NULL;
// Token的句柄
HANDLE hTokenUser = NULL;
HANDLE hTokenThisProcess = NULL;
HANDLE hTokenThis = NULL;
// WTSGetActiveConsoleSessionId 函数,得到当前登录用户的会话ID
// 这里的代码用的是VC6,新版的SDK已经包括此函数,无需LoadLibrary了。
typedef DWORD(WINAPI * WTSGetActiveConsoleSessionIdPROC)();
WTSGetActiveConsoleSessionIdPROC WTSGetActiveConsoleSessionId = NULL;
hInstKernel32 = LoadLibrary("Kernel32.dll");
if (!hInstKernel32) {
return FALSE;
}
WTSGetActiveConsoleSessionId = (WTSGetActiveConsoleSessionIdPROC)
GetProcAddress(hInstKernel32, "WTSGetActiveConsoleSessionId");
if (!WTSGetActiveConsoleSessionId) {
return FALSE;
}
// WTSQueryUserToken 函数,通过会话ID得到令牌
typedef BOOL(WINAPI * WTSQueryUserTokenPROC)(ULONG SessionId,
PHANDLE phToken);
WTSQueryUserTokenPROC WTSQueryUserToken = NULL;
hInstWtsapi32 = LoadLibrary("Wtsapi32.dll");
if (!hInstWtsapi32) {
return FALSE;
}
WTSQueryUserToken = (WTSQueryUserTokenPROC)GetProcAddress(hInstWtsapi32,
"WTSQueryUserToken");
if (!WTSQueryUserToken) {
return FALSE;
}
// 得到当前激活用户的会话ID
ConsoleSessionId = WTSGetActiveConsoleSessionId();
// 得到当前登录用户的令牌
bRes = WTSQueryUserToken(ConsoleSessionId, &hTokenUser);
if (!bRes) {
return FALSE;
}
// 模仿成当前登录用户
bRes = ImpersonateLoggedOnUser(hTokenUser);
if (!bRes) {
return FALSE;
} // 取得当前用户的Startup文件夹路径 if (i == 1) // 开始菜单
bRes = SHGetSpecialFolderPath(NULL, lpPath, CSIDL_STARTMENU, TRUE);
else if (i == 2) // 桌面
bRes = SHGetSpecialFolderPath(NULL, lpPath, CSIDL_DESKTOPDIRECTORY,
TRUE);
else if (i == 3) // 启动栏
bRes = SHGetSpecialFolderPath(NULL, lpPath, CSIDL_STARTUP, TRUE);
// 终止模拟,返回
RevertToSelf();
}