vista service程序获取不了visible desktop的DC句柄

时间:2022-06-01 14:38:28
写个service程序,在vista下获取不了当前可见的desktop;
枚举WindowStation及其desktop输出如下:
WindowStation0:WinSta0
Desktop:Default;Desktop:Disconnect;Desktop:Winlogon;
WindowStation1:Service-0x0-3e7$

WindowStation2:Service-0x0-3e4$

WindowStation3:Service-0x0-3e5$

WindowStation4:msswindowstation
Desktop:mssrestricteddesk;

可是我获取不了当前显示的desktop的DC句柄。我尝试把在DC上画图,可是都不成功。
不是service模式到是运行正常。
请大侠帮忙!!!

6 个解决方案

#1


枚举测试代码:
int n=0;
void LogEvent(char *pStr)
{
FILE* pf=fopen("c:\\users\\paul\\desktop\\service\\TestGetDesktop.txt","at");
if(pf)
{
fwrite(pStr,1,strlen(pStr),pf);
fclose(pf);
}
}
BOOL CALLBACK EnumDesktopProc(
  LPTSTR lpszDesktop,
  LPARAM lParam
)
{
char buf[1024]={0};
wsprintf(buf,"Desktop:%s;",lpszDesktop);

HDESK hdCurrent=OpenDesktop(lpszDesktop,0,FALSE,MAXIMUM_ALLOWED); 

USEROBJECTFLAGS uoFlags;
    unsigned long pSize;
    if( GetUserObjectInformation(hdCurrent, UOI_FLAGS, &uoFlags, sizeof(uoFlags), &pSize) == 0 )
        LogEvent("GetUserObjectInformation call failed.\n");
    else
    {

SetProcessWindowStation((HWINSTA)lParam);
SetThreadDesktop(hdCurrent);
{
HBRUSH hredBr=CreateSolidBrush ( RGB(255,0,0));
HDC hdekdc=GetWindowDC(GetDesktopWindow());
HDC hrootdc = CreateDC(("DISPLAY"),NULL,NULL,NULL);
RECT rect={0,0,500,500};
FillRect(hrootdc,&rect,hredBr);
FillRect(hdekdc,&rect,hredBr);
DeleteDC(hrootdc);
DeleteDC(hdekdc);
}
    }

CloseDesktop(hdCurrent);
LogEvent(buf);
return TRUE;
}



int i=0;
BOOL CALLBACK EnumWindowStationProc(
  LPTSTR lpszWindowStation,
  LPARAM lParam
)
{
char buf[1024]={0};
wsprintf(buf,"\nWindowStation%d:%s\n",i++,lpszWindowStation);
LogEvent(buf);

HWINSTA hwinsta=OpenWindowStation(lpszWindowStation,FALSE,MAXIMUM_ALLOWED);
if(hwinsta)
{
EnumDesktops(hwinsta,EnumDesktopProc,(long)hwinsta);
}
CloseWindowStation(hwinsta);

return TRUE;
}


BOOL EnumWindowStationAndDesktop()
{
EnumWindowStations(EnumWindowStationProc,NULL);
return TRUE;
}

主函数调用EnumWindowStationAndDesktop();

#2


Vista中服务程序与应用程序所在的session不同。

#3


先切换SESSION ID来创建一个服务进程副本( 例子代码),然后再做上面的操作。

#4


引用的代码:
HANDLE hThisProcess = GetCurrentProcess(); // 获取当前进程句柄

// 打开当前进程令牌
HANDLE hTokenThis = NULL;
OpenProcessToken(hThisProcess, TOKEN_ALL_ACCESS, &hTokenThis);

// 复制一个进程令牌,目的是为了修改session id属性,以便在其它session中创建进程
HANDLE hTokenDup = NULL;
DuplicateTokenEx(hTokenThis, MAXIMUM_ALLOWED,NULL, SecurityIdentification, TokenPrimary, &hTokenDup);
DWORD dwSessionId = WTSGetActiveConsoleSessionId(); // 获取活动session id
SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionId, sizeof(DWORD)); // 把session id设置到备份的令牌中

// 好了,现在要用新的令牌来创建一个服务进程。注意:是“服务”进程!如果需要以用户身份运行,必须在前面执行LogonUser来获取用户令牌
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = "WinSta0\\Default";

LPVOID pEnv = NULL;
DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE); // 创建环境块
// 创建新的服务进程,这个进程应该接收参数来调用SHChangeNotify,它将工作在新的session 1中
CreateProcessAsUser(hTokenDup, NULL, _T("C:\\myservice.exe -Notify"), NULL, NULL, FALSE, dwCreationFlag, pEnv, NULL, &si, &pi);

还不是很明白,这个是一个service程序麽?还是有两个?运行后的新进程是服务?
最主要的是怎么把这代码使用到我的service里面?
这方面看不懂,望多指点下!

#5


这段代码在已有的服务进程中执行,你需要监控用户是否已经登录到桌面了,并获取这个桌面的SESSION ID,原理大概是这样的:
打开自己的进程令牌
复制一个新令牌
对新令牌设置用户桌面的SESSION ID
用新令牌创建一个进程,程序名就是服务程序EXE的路径,帐户还是LocalSystem,唯一的区别就是这个进程运行在桌面所在的SESSION中,虽然不是真正的服务,但是跟服务权限相同。
在新的进程中控制桌面。

XP及以前的版本中,服务和第一个用户桌面共享session 0,所以代码没问题,但是在VISTA中,服务运行在session 0中,用户桌面从session 1开始的,跨session是无法访问窗口的,必须用一个相同session的程序才能访问桌面。

#6


哦,谢谢!

#1


枚举测试代码:
int n=0;
void LogEvent(char *pStr)
{
FILE* pf=fopen("c:\\users\\paul\\desktop\\service\\TestGetDesktop.txt","at");
if(pf)
{
fwrite(pStr,1,strlen(pStr),pf);
fclose(pf);
}
}
BOOL CALLBACK EnumDesktopProc(
  LPTSTR lpszDesktop,
  LPARAM lParam
)
{
char buf[1024]={0};
wsprintf(buf,"Desktop:%s;",lpszDesktop);

HDESK hdCurrent=OpenDesktop(lpszDesktop,0,FALSE,MAXIMUM_ALLOWED); 

USEROBJECTFLAGS uoFlags;
    unsigned long pSize;
    if( GetUserObjectInformation(hdCurrent, UOI_FLAGS, &uoFlags, sizeof(uoFlags), &pSize) == 0 )
        LogEvent("GetUserObjectInformation call failed.\n");
    else
    {

SetProcessWindowStation((HWINSTA)lParam);
SetThreadDesktop(hdCurrent);
{
HBRUSH hredBr=CreateSolidBrush ( RGB(255,0,0));
HDC hdekdc=GetWindowDC(GetDesktopWindow());
HDC hrootdc = CreateDC(("DISPLAY"),NULL,NULL,NULL);
RECT rect={0,0,500,500};
FillRect(hrootdc,&rect,hredBr);
FillRect(hdekdc,&rect,hredBr);
DeleteDC(hrootdc);
DeleteDC(hdekdc);
}
    }

CloseDesktop(hdCurrent);
LogEvent(buf);
return TRUE;
}



int i=0;
BOOL CALLBACK EnumWindowStationProc(
  LPTSTR lpszWindowStation,
  LPARAM lParam
)
{
char buf[1024]={0};
wsprintf(buf,"\nWindowStation%d:%s\n",i++,lpszWindowStation);
LogEvent(buf);

HWINSTA hwinsta=OpenWindowStation(lpszWindowStation,FALSE,MAXIMUM_ALLOWED);
if(hwinsta)
{
EnumDesktops(hwinsta,EnumDesktopProc,(long)hwinsta);
}
CloseWindowStation(hwinsta);

return TRUE;
}


BOOL EnumWindowStationAndDesktop()
{
EnumWindowStations(EnumWindowStationProc,NULL);
return TRUE;
}

主函数调用EnumWindowStationAndDesktop();

#2


Vista中服务程序与应用程序所在的session不同。

#3


先切换SESSION ID来创建一个服务进程副本( 例子代码),然后再做上面的操作。

#4


引用的代码:
HANDLE hThisProcess = GetCurrentProcess(); // 获取当前进程句柄

// 打开当前进程令牌
HANDLE hTokenThis = NULL;
OpenProcessToken(hThisProcess, TOKEN_ALL_ACCESS, &hTokenThis);

// 复制一个进程令牌,目的是为了修改session id属性,以便在其它session中创建进程
HANDLE hTokenDup = NULL;
DuplicateTokenEx(hTokenThis, MAXIMUM_ALLOWED,NULL, SecurityIdentification, TokenPrimary, &hTokenDup);
DWORD dwSessionId = WTSGetActiveConsoleSessionId(); // 获取活动session id
SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionId, sizeof(DWORD)); // 把session id设置到备份的令牌中

// 好了,现在要用新的令牌来创建一个服务进程。注意:是“服务”进程!如果需要以用户身份运行,必须在前面执行LogonUser来获取用户令牌
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = "WinSta0\\Default";

LPVOID pEnv = NULL;
DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE); // 创建环境块
// 创建新的服务进程,这个进程应该接收参数来调用SHChangeNotify,它将工作在新的session 1中
CreateProcessAsUser(hTokenDup, NULL, _T("C:\\myservice.exe -Notify"), NULL, NULL, FALSE, dwCreationFlag, pEnv, NULL, &si, &pi);

还不是很明白,这个是一个service程序麽?还是有两个?运行后的新进程是服务?
最主要的是怎么把这代码使用到我的service里面?
这方面看不懂,望多指点下!

#5


这段代码在已有的服务进程中执行,你需要监控用户是否已经登录到桌面了,并获取这个桌面的SESSION ID,原理大概是这样的:
打开自己的进程令牌
复制一个新令牌
对新令牌设置用户桌面的SESSION ID
用新令牌创建一个进程,程序名就是服务程序EXE的路径,帐户还是LocalSystem,唯一的区别就是这个进程运行在桌面所在的SESSION中,虽然不是真正的服务,但是跟服务权限相同。
在新的进程中控制桌面。

XP及以前的版本中,服务和第一个用户桌面共享session 0,所以代码没问题,但是在VISTA中,服务运行在session 0中,用户桌面从session 1开始的,跨session是无法访问窗口的,必须用一个相同session的程序才能访问桌面。

#6


哦,谢谢!