Windows 서비스 작성(Windows Service,system 권한)프로그램 디 스 플레이 인터페이스 와 사용자 상호작용(xp,win 7 통용)

12039 단어 Windows 서비스
1.VC 2008 에서'Windows 서비스'(Windows Service)프로그램 을 작성 합 니 다.
원본 자원 다운로드:/201604/yuanma/TestService_jb51.rar
vc 2008 다음 에 새로 만 든 ATL 프로젝트-"서비스"형식의 ATL 프로젝트 TestService 를 선택 하면 다음 과 같은 코드 를 생 성 합 니 다.

class CTestServiceModule : public CAtlServiceModuleT< CTestServiceModule, IDS_SERVICENAME >
{
public :
DECLARE_LIBID(LIBID_TestServiceLib )
DECLARE_REGISTRY_APPID_RESOURCEID (IDR_TESTSERVICE, "{1FF78006-B225-4CC0-A7DE-E0C9D31C9937}" )
HRESULT InitializeSecurity () throw()
{
// TODO :   CoInitializeSecurity          
//     
//   - PKT        、
// RPC_C_IMP_LEVEL_IDENTIFY      
//       NULL      。
return S_OK ;
}
//            
HRESULT Run (int nShowCmd = SW_HIDE ) throw()
{
HRESULT hr = S_OK;
hr = __super ::PreMessageLoop( nShowCmd);
if (hr == S_OK)
{
if (m_bService )
{
//    #define _ATL_NO_COM_SUPPORT           
//         ,                 
//          ,       
LogEvent(_T ("widebright       ,   "));
SetServiceStatus(SERVICE_RUNNING );
}
//      ,       ,        Handler   ,   OnShutdown    。
__super::RunMessageLoop ();
}
if (SUCCEEDED (hr))
{
hr = __super ::PostMessageLoop();
}
//          Uninstall        
//__super::Uninstall();
return hr ;
}
//  ,      
void OnShutdown () throw()
{
LogEvent(_T ("TestService       ,         "));
}
};
CTestServiceModule _AtlModule;

//
extern "C" int WINAPI _tWinMain (HINSTANCE , HINSTANCE ,
LPTSTR , int nShowCmd)
{
return _AtlModule .WinMain( nShowCmd);
}
2.저 는 필요 에 따라 해당 하 는 함 수 를 다시 써 서 원 하 는 기능 을 실현 하면 됩 니 다.
예 를 들 어 당신 이 만 들 고 싶 은'서비스'는 시스템 에 따라 시 작 됩 니 다.CAtlServiceModuleT 의 Install 함 수 를 다시 쓸 수 있 습 니 다.그 안에 있 는 CreateService 함수 의 인 자 를 수정 할 수 있 습 니 다.예 를 들 어 사용자 와 의 상호작용 을 추가 하면 SERVICE 를 사용 할 수 있 습 니 다.INTERACTIVE_PROCESS,구체 적 으로 MSDN 에서 Create Service 라 는 API 에 대한 설명 을 찾 을 수 있 습 니 다.
서비스 정지 와 시작 동작 을 처리 하려 면 CAtlServiceModuleT 의 소스 코드 재 작성 OnStop()등 함 수 를 참고 하 세 요.제 위 에 Run 함 수 를 다시 쓸 정도 로 간단 합 니 다.'이벤트'를 출력 하 는 것 은 사실 구체 적 인 작업 은 여기에 놓 고 완성 할 수 있 습 니 다.
컴 파일,프로그램 생 성 후 테스트 할 수 있 습 니 다.
"TestService-/Service"를 실행 하면 서 비 스 를 시스템 에 등록 할 수 있 습 니 다.명령 행 인 자 는 사실 CAtlServiceModuleT:ParseCommandLine 이라는 함수 에서 처리 되 었 습 니 다.가서 보 셔 도 됩 니 다.필요 하 다 면 다시 쓰 셔 도 됩 니 다.게다가 UnInstall 을 호출 하여 서비스 코드 를 삭제 하 는 것 도 좋 을 것 같 습 니 다.
등록 후'sc start'나'net start'등의 명령 으로 서 비 스 를 조작 하 는 것 을 볼 수 있 습 니 다."서비스"컨트롤 러 에서 제어 할 수 있 습 니 다.그림 참조

3.이때 Run 함수 에서 Notepad.exe 를 시작 합 니 다.이 때 인터페이스 가 표시 되 지 않 고 xp 에서 다음 방법 으로 notepad 와 사용자 의 상호작용 을 실현 할 수 있 습 니 다.

//for xp system
DWORD _stdcall LaunchAppIntoSession0( LPTSTR lpCommand )
{
////////////////////////////////////////////system show dlg////////////////////
HDESK hdeskCurrent ;
HDESK hdesk ;
HWINSTA hwinstaCurrent ;
HWINSTA hwinsta ;
hwinstaCurrent = GetProcessWindowStation ();
if (hwinstaCurrent == NULL)
{
return FALSE ;
}
hdeskCurrent = GetThreadDesktop (GetCurrentThreadId());
if (hdeskCurrent == NULL){
return FALSE ;
}
//  winsta0
//  winsta0
hwinsta = OpenWindowStation (L"Winsta0" , FALSE, WINSTA_ALL_ACCESS);
// WINSTA_ACCESSCLIPBOARD|
// WINSTA_ACCESSGLOBALATOMS |
// WINSTA_ENUMDESKTOPS |
// WINSTA_CREATEDESKTOP |
// WINSTA_CREATEDESKTOP |
// WINSTA_ENUMERATE |
// WINSTA_EXITWINDOWS |
// WINSTA_READATTRIBUTES |
// WINSTA_READSCREEN |
// WINSTA_WRITEATTRIBUTES);
if (hwinsta == NULL){
return FALSE ;
}
if (!SetProcessWindowStation (hwinsta))
{
return FALSE ;
}
//  desktop
hdesk = OpenDesktop (L"default" , 0, FALSE,
DESKTOP_CREATEMENU |
DESKTOP_CREATEWINDOW |
DESKTOP_ENUMERATE|
DESKTOP_HOOKCONTROL|
DESKTOP_JOURNALPLAYBACK |
DESKTOP_JOURNALRECORD |
DESKTOP_READOBJECTS |
DESKTOP_SWITCHDESKTOP |
DESKTOP_WRITEOBJECTS);
if (hdesk == NULL){
return FALSE ;
}
SetThreadDesktop(hdesk );
////////////////////////////////////////////end of system show dlg////////////////////

STARTUPINFO si = { sizeof( si) }; 
SECURITY_ATTRIBUTES saProcess , saThread; 
PROCESS_INFORMATION piProcessB , piProcessC; 

// Prepare to spawn Process B from Process A. 
// The handle identifying the new process 
// object should be inheritable. 
saProcess.nLength = sizeof( saProcess); 
saProcess.lpSecurityDescriptor = NULL; 
saProcess.bInheritHandle = TRUE; 


// The handle identifying the new thread 
// object should NOT be inheritable. 
saThread.nLength = sizeof( saThread);
saThread.lpSecurityDescriptor = NULL;
saThread.bInheritHandle = FALSE; 


CreateProcess(NULL , lpCommand, & saProcess, &saThread , 
FALSE, 0, NULL , NULL, & si, &piProcessB ); 

if (!SetProcessWindowStation (hwinstaCurrent))
return FALSE ;
if (!SetThreadDesktop (hdeskCurrent))
return FALSE ;
if (!CloseWindowStation (hwinsta))
return FALSE ;
if (!CloseDesktop (hdesk))
return FALSE ;
return TRUE ;
}
이런 방법의 관건 은 OpenWindowStation,SetProcess WindowStation,OpenDesktop 과 SetThreadDesktop 이라는 네 가지 함수 다.이러한 방법의 사고방식 은 현재 프로 세 스 가 처 한 Session 은 인터페이스 상호작용 능력 이 있어 야 대화 상 자 를 표시 할 수 있다 는 것 이다.첫 번 째 대화 식 사용 자 는 WinSta 0 이 있 는 Session 0 에 로그 인 하기 때문에 서비스 가 있 는 프로 세 스 를 WinSta 0 과 강제 적 으로 연결 하고 현재 데스크 톱 을 열 어 이 데스크 톱 에 작업 스 레 드 를 걸 면 대화 상 자 를 표시 할 수 있 습 니 다.
4.이런 방법 은 WinXP 와 Windows 2003 에서 잘 작 동 합 니 다.안 타 깝 게 도 Vista 와 Windows 2008 에서 OpenWindow Station 을 실행 하면 WinSta 0 워크스테이션 을 대신 열 려 고 할 때 프로그램 에 이상 이 생 깁 니 다.
우선 프로그램 이 어떤 조건 을 갖 추어 야 인터페이스 와 상호작용 을 할 수 있 는 지 알 아 보 자.윈도 는 세 가지 대상 을 제공 했다.사용자 인터페이스 대상(User Interface),GDI 대상 과 커 널 대상 이다.커 널 대상 은 안전성 이 있 지만 앞 에는 없다.앞의 두 가지 안전성 을 제공 하기 위해 워크스테이션 대상(Window station)과 데스크 톱 대상(Desktop)을 통 해 사용자 인터페이스 대상 을 관리 합 니 다.워크스테이션 대상 과 데스크 톱 대상 이 안전 하기 때 문 입 니 다.쉽게 말 하면 워크스테이션 은 프로 세 스 와 연 결 된 보안 기능 을 가 진 대상 으로 하나 이상 의 데스크 톱 대상 을 포함 합 니 다.워크스테이션 대상 이 생 성 되 었 을 때 호출 프로 세 스에 연결 되 어 현재 세 션 에 부 여 됩 니 다.대화 형 워크스테이션 WinSta 0 은 사용자 인터페이스 를 표시 하고 사용자 의 입력 을 받 아들 일 수 있 는 유일한 워크스테이션 입 니 다.키보드,마우스,디 스 플레이 장 치 를 포함 한 대화 형 사용자 에 게 부 여 된 로그 인 세 션 입 니 다.모든 다른 워크스테이션 은 비 상호작용 적 이다.이것 은 사용자 인 터 페 이 스 를 표시 할 수 없고 사용자 의 입력 을 받 아들 일 수 없다 는 것 을 의미한다.사용자 가 터미널 서 비 스 를 사용 하 는 컴퓨터 에 로그 인하 면 모든 사용자 가 세 션 을 시작 합 니 다.모든 Session 은 자신의 대화 형 워크스테이션 과 연결된다.데스크 톱 은 창 워크스테이션 대상 에 보안 기능 을 가 진 대상 입 니 다.데스크 톱 대상 은 창,메뉴,갈고리 등 사용자 인터페이스 대상 을 포함 하 는 논리 적 인 디 스 플레이 영역 이 있 습 니 다.
Vista 에 앞서 Winsta 0 과 결 성 된 데스크 톱 디 스 플레이 대화 상 자 를 열 수 있 는 이 유 는 서비스 든 첫 번 째 로 로그 인 한 대화 형 사용자 든 모두 Session 0 에 로그 인 하기 때 문 입 니 다.따라서 서비스 프로그램 은 윈 스타 0 과 데스크 톱 을 강제로 열 어 상호작용 을 할 수 있다.
그러나 Vista 와 윈도 2008 에서 Session 0 은 서비스 와 사용자 와 상호작용 하지 않 는 다른 응용 프로그램 에 만 사용 된다.첫 번 째 로그 인 으로 대화 식 작업 을 할 수 있 는 사용자 가 세 션 1 에 연결 되 었 습 니 다.두 번 째 로그 인 을 진행 한 사용 자 는 세 션 2 에 배정 되 어 유추 된다.Session 0 은 사용자 와 대화 할 프로 세 스 를 전혀 지원 하지 않 습 니 다.서비스 프로 세 스에 서 하위 프로 세 스 를 시작 해서 대화 상 자 를 표시 하면 하위 대화 상 자 를 표시 할 수 없습니다.OpenWindowStation 시스템 API 로 WinSta 0 을 여 는 방법 을 사용 하면 함수 호출 이 실패 합 니 다.한 마디 로 비 스타 와 윈도 2008 은 세 션 0 에서 인터페이스 상호작용 을 하 는 길 을 막 았 다.그게 이유 야.
그렇다면 서비스 에서 대화 상 자 를 꺼 낼 수 없 는 것 일 까?서비스 프로 세 스 자체 에 있어 서 는 운영 체제 가 이 길 을 막 았 다.그러나 우리 가 원 하 는 것 은'서비스 프로 세 스에 서 팝 업 대화 상자'가 아 닙 니 다.우리 가 원 하 는 것 은'서비스 가 어떤 상황 이 발생 했 을 때 데스크 톱 에서 팝 업 대화 상자'에 불과 합 니 다.세 션 0 에서 대화 상 자 를 꺼 낼 수 없 으 며,우리 가 보 는 데스크 톱 은 세 션 X 입 니 다.세 션 0 이 아 닌 자 연 스 러 운 생각 은 세 션 0 이 다른 세 션 에 게 알려 주 고 현재 데스크 톱 에 표시 되 어 있 는 세 션 이 대화 상 자 를 꺼 낼 수 있 습 니까?
다행히 이렇게 할 수 있 었 다.

//for win7
DWORD _stdcall LaunchAppIntoDifferentSession( LPTSTR lpCommand )
{
DWORD dwRet = 0;
PROCESS_INFORMATION pi ;
STARTUPINFO si ;
DWORD dwSessionId ;
HANDLE hUserToken = NULL;
HANDLE hUserTokenDup = NULL;
HANDLE hPToken = NULL;
HANDLE hProcess = NULL;
DWORD dwCreationFlags ;
HMODULE hInstKernel32 = NULL;
typedef DWORD (WINAPI * WTSGetActiveConsoleSessionIdPROC)();
WTSGetActiveConsoleSessionIdPROC WTSGetActiveConsoleSessionId = NULL;
hInstKernel32 = LoadLibrary (L"Kernel32.dll" );
if (!hInstKernel32 ) 
{
return FALSE ;
}
OutputDebugString(L "LaunchAppIntoDifferentSession 1
" ); WTSGetActiveConsoleSessionId = (WTSGetActiveConsoleSessionIdPROC )GetProcAddress( hInstKernel32,"WTSGetActiveConsoleSessionId" ); // Log the client on to the local computer. dwSessionId = WTSGetActiveConsoleSessionId (); do { WTSQueryUserToken( dwSessionId ,&hUserToken ); dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE; ZeroMemory( &si , sizeof( STARTUPINFO ) ); si.cb = sizeof( STARTUPINFO ); si.lpDesktop = L"winsta0\\default" ; ZeroMemory( &pi , sizeof( pi) ); TOKEN_PRIVILEGES tp ; LUID luid ; if( !::OpenProcessToken ( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE , &hPToken ) ) { dwRet = GetLastError (); break; } else; if ( !LookupPrivilegeValue ( NULL, SE_DEBUG_NAME, &luid ) ) { dwRet = GetLastError (); break; } else; tp.PrivilegeCount =1; tp.Privileges [0].Luid = luid; tp.Privileges [0].Attributes = SE_PRIVILEGE_ENABLED; if( !DuplicateTokenEx ( hPToken, MAXIMUM_ALLOWED, NULL , SecurityIdentification , TokenPrimary, & hUserTokenDup ) ) { dwRet = GetLastError (); break; } else; //Adjust Token privilege if( !SetTokenInformation ( hUserTokenDup,TokenSessionId ,(void*)& dwSessionId,sizeof (DWORD) ) ) { dwRet = GetLastError (); break; } else; if( !AdjustTokenPrivileges ( hUserTokenDup, FALSE, &tp , sizeof(TOKEN_PRIVILEGES ), (PTOKEN_PRIVILEGES) NULL, NULL ) ) { dwRet = GetLastError (); break; } else; LPVOID pEnv =NULL; if( CreateEnvironmentBlock ( &pEnv, hUserTokenDup, TRUE ) ) { dwCreationFlags|=CREATE_UNICODE_ENVIRONMENT ; } else pEnv =NULL; // Launch the process in the client's logon session. if( CreateProcessAsUser ( hUserTokenDup, // client's access token NULL, // file to execute lpCommand, // command line NULL, // pointer to process SECURITY_ATTRIBUTES NULL, // pointer to thread SECURITY_ATTRIBUTES FALSE, // handles are not inheritable dwCreationFlags,// creation flags pEnv, // pointer to new environment block NULL, // name of current directory & si, // pointer to STARTUPINFO structure & pi // receives information about new process ) ) { } else { dwRet = GetLastError (); break; } } while( 0 ); //Perform All the Close Handles task if( NULL != hUserToken ) { CloseHandle( hUserToken ); } else; if( NULL != hUserTokenDup) { CloseHandle( hUserTokenDup ); } else; if( NULL != hPToken ) { CloseHandle( hPToken ); } else; return dwRet ; }
5.서 비 스 를 시작 한 후에 system 권한 의 Notepad.exe 를 표시 하고 사용자 와 상호작용 을 할 수 있 습 니 다.

물론 이 예 에서 프로 세 스 를 시작 하 는 곳 에 대화 상 자 를 만 드 는 것 도 대화 상 자 를 표시 할 수 있 습 니 다.

좋은 웹페이지 즐겨찾기