서비스 <-> 응용 프로그램 간의 IPC

서비스 <- 응용 프로그램

서비스는 커널에서 동작 하기 때문에 공유 메모리 생성할때 전역 이름을 주어야 함 

Global\\이름


서비스->응용 프로그램

시스템영역의 프로세스와 일반 유저영역의 프로세스간의 파일 접근 권한이 다름

접근 권한으로 공유 메모리 생성해야 함

Kernel Object Namespaces

Kernel Object Namespaces
 
 
터미널 서비스 서버는 몇몇의 커널 객체(Event, Semaphore, Mutex, Waitable timer, File-mapping 객체 그리고  Job 객체)에
대해서  복합적인 이름을 사용할수 있도록 되어 있다. 전역적인 이름은 클라이언트/서버 형식의 응용프로그램들이
많이 사용한다. 그리고 각 클라이언트 세션(session)은 위에 열거한 객체들을 위해 분리된 독자적인 이름 공간을 가지고 있다.
이것은 모든 프로세스와 서비스가 위에 열거한 객체들을 위해 단일화된 이름공간을 공유한다는 원칙과 차이가 있다는것에
주의해야 한다.
 
이름공간을 클라이언트 세션마다 별도로 구별해서 사용하는것은 동일한 응용프로그램을 여러번 실행했을때,
여러번 실행된 각각의 프로그램이 서로에게 영향을 받지 않고 실행되도록 하기 위함이다.
클라이언트 세션에서 실행한 프로세스들은 기본적으로 세션내의 이름공간을 사용한다. 그러나 프로세스가 객체의
이름을 명시할때 "Global\"과 같은 접두어를 사용해서 이름을 명시한다면 전역적인 이름공간을 사용하게 된다.
 
예를 들어, 아래와 같이 CreateEvent함수를 사용하여 Tipsware라는 이름의 이벤트 객체를 생성하면 이 객체는
세션내에 이름공간을 사용할 것이다.
 
CreateEvent(NULL, FALSE, FALSE, "Tipsware");
 
하지만, 이름을 "Global\\Tipsware"라고 사용하면 시스템의 전역적인 이름공간을 사용하게 된다.
 
CreateEvent(NULL, FALSE, FALSE, "Global\\Tipsware");
 
터미널 서비스 환경에서 실행되는 서비스 응용프로그램들은 기본적으로 전역적인 이름공간을 사용한다. 그리고 
시작 세션(Session Zero, 일반적으로 콘솔 세션 또는 서비스 세션)하에서 실행되는 프로그램도 기본적으로 
전역적인 이름공간을 사용한다. 이것은 전역적인 이름공간을 사용해서 여러개의 클라이언트 세션에 실행된 각각의 
프로세스가 서로 통신하는것을 가능하게 한다.
 
예를들어, 터미널 서비스에 의해서 서버루틴이 실행되었거나 서비스루틴에 의해서 자동으로 서버루틴이 실행된 경우,
서버루틴에서 사용하는 객체들은 전역적인 이름공간을사용하기때문에 클라이언트 세션 또는 일반 사용자 세션에서
서버가 만든 객체를 사용하고자 한다면 객체의 이름을 명시할때, "Global\"이라는 접두어를 이름앞에 붙여서 사용해야 한다.
 
그리고 전역적인 이름공간을 사용하는 또다른 경우는, 각 세션마다 분리된 이름공간을 가지고 있기 때문에, 각 세션에서
커널객체를 이용하여 세션내에서 실행되는 프로그램에 대햐여 중복실행 체크가 가능하지만 다른 세션에 같은 프로그램이
실행되는것은 체크할수 없다. 따라서 현재 시스템에서 실행중인 모든세션에서 특정 프로그램이 중복 실행되지 않도록 하려면
전역적인 이름 공간을 사용해서 객체를 생성시키고 체크하면 된다.
 
클라이언트 프로세스들이 객체를 생성할때, 자신이 실행되는 세션의 이름공간에 생성하는것을 좀더 확실하게 보장받고
싶다면 "Local\"이라는 접두어를 사용하면 된다.
 
"Local", "Global" 그리고 "Session"이라는 용어는 시스템에 의해서 예약되어져 있기 때문에 커널 객체를 생성할때 이름으로
사용하면 안된다. 하지만 이용어들은 대소문자를 구별하기 때문에 LOCAL, loCAL과 같은 형태로 사용하는것은 문제가되지
않는다. 그리고 터미널 서비스를 제공하지 않는 Windows 2000 제품군에서는 위 용어들이 무시될 것이다.

서비스 - 프로그램 IPC


서비스->응용프로그램, 응용프로그램->서비스 로의 IPC가 가능하네요.

기본적으로 응용프로그램->서비스로는 User Define 이벤트를 제공하니 문제가 안될 것 같구요.

 

우선 서비스를 아래와 같이 SERVICE_INTERACTIVE_PROCESS 프로세스를 추가하고,

 

 // 서비스를 등록한다.
http://i1.daumcdn.net/icon/editor/spacer2.gif?v=2 hSrv=CreateService(m_hScm, m_szSrvName, m_szDspName, SERVICE_PAUSE_CONTINUE | SERVICE_CHANGE_CONFIG,
  SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS, SERVICE_AUTO_START/*SERVICE_DEMAND_START*/,SERVICE_ERROR_IGNORE,SrvPath,
  NULL,NULL,NULL,NULL,NULL);

 

서비스에서의 데이터를 줄 때 저 같은 경우는 Shared Memory RegisterWindowMessage 를 사용했습니다.

데이터는 구조체를 썼구요.

 

서비스에서  Shared Memoery에 데이터를 썼구요.

어플리케이션으로 데이터가 있음을 알려주기 위해 RegisterWindowMessage를 사용했습니다

 

SERVICE_INFO pSer;
     strcpy(pSer.szMessage, tmp);
     CreateServiceShareMemory(&pSer);

 

HWND hwnd = ::FindWindow(NULL, "WaBis Service Manager");
    if(hwnd){
      ::PostMessage(hwnd, WM_SERVICE_MESSAGE, 0, 0);
      //::PostMessage(hwnd, WM_SERVICE_MESSAGE, (WPARAM)&pSer, 0);
     }

 

어플리케이션에서는

ON_REGISTERED_MESSAGE(WM_SERVICE_MESSAGE, OnWaBISEvtMessage) 정의하고,

 

LRESULT CWaBisServiceManagerDlg::OnWaBISEvtMessage(WPARAM wParam, LPARAM lParam)
{
 
 SERVICE_INFO pSer;// *pInfo = (SERVICE_INFO *)wParam;

 

 int isSuccess = ReadServiceSharedMemory(&pSer);
 if(isSuccess){
  //
서비스의 데이터가 있다면
  if(strlen(pSer.szMessage) !=0){
   m_ListBox.InsertString(0, pSer.szMessage);
   if(m_ListBox.GetCount() > 500){
    m_ListBox.ResetContent();
   }
  }
 } 

 

 return 0;
}

 


7.드라이버에서 라이브러리 사용하기

1.      Example

A.     Examlpe.h

B.      Examlpe.lib

C.      Examlpe.dll

2.      lib포함

A.     Source 파일에 라이브러리 포함

                         i.         TARGETLIBS=$Examlpe.lib

B.      라이브러리는 build를 하는 곳에 저장

C.      라이브러리 위치가 잘못 되었을 경우에는 build시 에러 메시지 발생

                         i.         Error U1073 : don’t know how to make ‘.lib’

                        ii.        

3.       

 

 

 

 

 

6. 드라이버와 에이전트와의 통신

1.      윈도우에서는 커널 모드와 유저 모드간에 데이터를 주고 받기 위해 크게 두 가지 방식 제공

A.     Buffered I/O

                i.         I/O manager에서 해당 데이터의 사이즈를 먼저 체크 한 후 해당 데이터가 들어갈 만한 충분한 사이즈의 페이지 되지 않은 버퍼메모리를 성성

               ii.         드라이버에 데이터를 쓰는 작업의 경우 유저 모드 데이터를 이 버퍼에 복사

              iii.         드라이버에서 이 버퍼 메모리의 값을 참조하거나 혹은 데이터 읽기 작업일 경우 버퍼에 드라이버가 가지고 잇는 데이터를 저장

              iv.         해당 작업이 완료되면 I/O manager는 버퍼에 있는 데이터를 다시 유저 모드 메모리에 복사하고 버퍼메모리를 해제

B.     Direct I/O

                i.         데이터를 주고 받는 요청이 발생하면 I/O manager에서 해당 메모리가 스와핑 되지 않도록 일종에 잠금 장치를 함

               ii.         잠겨진(Locked) 메모리를 드라이버에서 쉽게 참조할 수 있도록 관련 정보가 저장된 MDL을 만듬

              iii.         드라이버에서는 DML을 가지고 참조 가능한 메모리 포인터를 리턴해주는 MmGetSystemAddresForMdl()이라고 하는 API를 사용하여 데이터를 주고 받을 포인터를 만들고 이 포인터를 이용해서 데이터를 처리

              iv.         I/O manager는 쓰기/읽기 동작이 완료되면 MDL을 제거하고 해당 메모리의 잠금 장치를 해제

               v.          


2.      두개의 파일 필요

A.     유저 어플인 exe

B.     디바이스 드라이버 sys

3.      유저 어플

A.     DeviceIoControl 함수

                i.         유저어플과 디바이스와 대화를 할 수 있는 함수

               ii.         양방향성을 가짐

B.     단방향성을 가지는 함수

                i.         ReadFile, WireFile

4.      디바이스 드라이버 코드

A.     DriverEntry

                i.         IoCreateDevice : 디바이스 생성

               ii.         IoCreateSymbolicLink : 이름 지정

 

5. 드라이버의 설치 및 확인 방법

1.      레지스트리의 값을 변경하여 설치 하는 방법

A




.     서비스의 파일의 위치 변경

                i.         C:\Windows\System32\drivers

B.     레지스트리 편집기를 이용하여 설치

                i.         [실행] – regedit

               ii.         HKEY_LOCAL_MACHINE\SYSTEM\Current ControlSet\Servcices

1.      sys파일 이름과 같은 키 생성

              iii.         키안에 값이 1 DWORD

1.      Start

2.      ErrorControl

3.      Type

C.     재부팅

 

2.      확인

A.     [컴퓨터] - 마우스 오른쪽 버튼 클릭 – [관리]

B.     [왼쪽 장치 관리자] – 마우스 오른쪽 버튼 – [보기] -  [숨김 장치 표시]

                i.        

C.     중앙에 [비 플러그 앤 플레이 드라이버] 메뉴 선택

                i.         설치 확인

               ii.         

서비스 프로그램에서 ShellExecute 사용법

아래 방법으로 서비스에서 응용 프로그램를 실행해 보았는데, 

프로그램의 UI는 나타나지 않고 

작업 관리자에 프로세스 실행은 확인 할 수 있었다. 제대로 동작하는지는 알 수 없ㅋ엉ㅋ ㅜㅜ


서비스에서는 콘솔, 윈도우 UI를 제공하지 않아서 

이것들을 가지고 있는 프로그램은 나타나지 않는다


STARTUPINFO si;

PROCESS_INFORMATION pi;


si.cb = sizeof(si);

si.lpDesktop = "WinSta0\\default";


CreateProcess( NULL, "c:\\1.txt", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);



==========================================================================

C++빌더나 Delphi로 Service 프로그램 만드는것은 매우 쉽습니다. 

그런데 이 서비스프로그램에서 일반 윈도우 어플리케이션을 실행하거나
어플리케이션과 통신을 해야 할때는 좀 까다로운 부분이 있습니다. 

그 이유는
Sercive가 실행되는 desktop이 일반 어플리케이션이 실행되는 desktop이 다르기 때문입니다.
http://cafe.naver.com/bcbmaster/765

그래서 Service프로그램에서 윈도우 어플리케이션을 실행하면 정상적으로 실행이 잘안됩니다.

또한 Service에서 
FindWindow, EnumWindows 등으로 
desktop에 실행된 윈도우 핸들도  찾아지지 않으며 
당연히 SendMessage로 메세지를 보낼수도 없습니다..(불가능한것은 아님)
 

 [Service에서 default 데트스탑에 어플리케이션 실행하기]

 그럼 Service프로그램에서 logon한 user화면에 어플리케이션을 실행하려면 어떻게 해야 할까요?
간단합니다. 

CreateProcess로 프로그램을 실행하면서
9번째 파라메터로 넘기는 STARTUPINFO 구제체에서 lpDesktop에 해당 desktop을 설정해주면 됩니다.
 

[델파이 예제]
  

  
procedure TService1.ServiceCreate(Sender: TObject);  
var  
  si: STARTUPINFO;  
  pi : PROCESS_INFORMATION;  
  bResult : Boolean;  
begin  

  ZeroMemory( @si, sizeof(si) );  
  si.cb := sizeof(si);  
  si.lpDesktop:=PChar('WinSta0\default');                    // 이부분이 핵심 

  bResult := CreateProcess( nil, // No module name (use command line). 
  'C:\Zzang\[기술예제]\DelphiToHTML\DelphiToHTML.exe', // Command line. 
  nil, // Process handle not inheritable. 
  nil, // Thread handle not inheritable. 
  FALSE, // Set handle inheritance to FALSE. 
  0, // No creation flags. 
  nil, // Use parent's environment block. 
  nil, // Use parent's starting directory. 
  si, // Pointer to STARTUPINFO structure. 
  pi ); // Pointer to PROCESS_INFORMATION structure. ) 

end; 
     

 

만약 로그인 화면에 어플리케이션을 실행하려면
STARTUPINFO 구조체의 lpDesktop에 "WinSta0\Winlogon" 으로 설정해주면됩니다.
 

 [서비스 프로그램에서 FindWindow  , SendMessage를 사용하려면]

 일반 윈도우 프로그램과 SendMessage등 상호 작용이 가능한 Service프로그램을
Interactive Service라고 합니다..
다시말해 Service프로그램을 Interactive Service로 설정해놓으면 일반 윈도우 어플리케이션과 상호작용이 가능합니다.
 

그럼 Service프로그램을 어떻게 Interactive Service로 설정할까요?

서비스 - 속성 - 로그온 에서 "서비스와 테스트톱 상호 작용 허용"을 체크해 놓으면 된다. 

 참조 http://www.simpleisbest.net/archive/2005/10/18/260.aspx

 그런데 일반 사용자가 Service 속성을 열어서 직접 수정할수는 없으니
Service프로그램을 Windows에 install할때 설정해서 install 해야 한다.

보통 서버스를 install 하려면   command 모드에서  서비스실행파일네임.exe  /install 하면 설치되고
 서비스실행파일네임.exe  /uninstall 하면 uninstall 된다.

그런데 Interactive Service로 install하려면
CreateService로   서비스 프로그램을 인스톨해야 합니다.

참조 http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=7998&ref=7998

 

그런데 위 그림을 보면 "로컬 시스템 계정"이라는 것 밑에 " Interactive Service"관련 항목이 있다.
즉 " Interactive Service"는 로컬 시스템 계정에서만 동작한다는 것이다.

 administrator가 아니면 user의 desktop의 윈도우와 상호작용이 불가능하다는 얘기...
앗참
 Interactive Service에서  윈도우 어플리케이션을 실행하면 기본적으로
"WinSta0\default" 데스크톱에서 실행됩니다.
STARTUPINFO 의 lpDesktop을 설정하지 않아도 된다는....


참고로..
XP나 Vista 같은경우 로그아웃 하지 않고 여러명의 User로 로그인한 상태가 되는경우도 있습니다.
이경우 원하는 데스트탑에 프로그램이 뜨지 않고 엉뚱한(다른계정의 desktop)에 프로그램이 뜰수도 있습니다.
이 문제는 좀더 머리아픕니다.


그럼..



출처 http://www.delmadang.com/community/bbs_view.asp?bbsNo=17&bbsCat=41&indx=409951&page=393

4. Admin 권한으로 실행되는 프로그램에 대한 정리

- Admin 권한으로 실행되는 프로그램에 대한 정리

1.      계정에 관리자 권한을 부여 한다는 것이(Admin 권한으로 프로그램을 실행한다는 것이) 사용자 계정 컨트롤 경고 창 조차 발생하지 않도록 하는 것 인줄 잘못 알았다.... ㅡㅡㅋ

  

1.      계정별 설치 확인

A.     관리자 계정 사용자 계정 컨트롤의 값이 항상 알림일 때

                i.         OpenSCManager() 함수 사용시 액세스 거부 되어 설치 불가능

B.     사용자 계정

                i.         관리자 계정과 같이 SCM 접근 불가로 설치 불가능

 



2.      계정에 관리자 권한 부여

A.     프로그램에 관리자 권한 부여 이유

                i.         일반 프로그램의 경우 사용자가 프로그램을 실행하였을 때 UAC의 값에 따라 서비스가 실패되는 경우가 발생

               ii.         UAC 확인 창을 명시적으로 띄워 관리자 권한으로 프로그램을 실행 할 경우에 사용자가 권한을 허용할 경우 서비스 설치 가능해짐

B.     Manifest파일 이용 방법과, 직접 코딩하여 관리자 권한을 부여하는 방법이 있음

C.     관리자 계정을 부여하지 않았을 경우 사용자가 수동으로 관리자 권한을 부여 하는 법

                i.         UAC 기능을 중지

1.      제어판 사용자 계정 및 가족 보호 사용자 계정

2.      사용자 계정 컨트롤 설정 변경의 값을 항상 알리지 않음으로 설정

               ii.         실행 파일의 오른쪽 버튼 클릭 후 관리자 권한으로 이 프로그램 실행

              iii.         실행 파일의 속성의 호환성 탭에서 관리자 권한으로 이 프로그램 실행 체크

 



3.      Manifest파일 이용하는 방법과 직접 코딩으로 관리자 권한을 부여 하였을 때 장단점

A.     Manifest 파일 이용

                i.         장점 : 구현 용이, 코드 수정 없이 변경 가능

               ii.         단점 : UAC 실행 수준의 값 만이 가능

B.     직접 코딩 이용

                i.         장점 : 다양한 제어 가능

               ii.         단점 : 코딩 부담



 

4.      직접 코딩으로 계정에 권한 부여

A.     사용자 계정에 권한을 할당하는 함수 생성

                i.         권한에 SeServiceLogonRight 값 적용하여 권한 부여

1.      서비스로서 로그인

B.     프로세스에 권한을 할당하는 함수 생성

                i.         OpenProcessToken()

1.      프로세스의 Access Token을 가져옴

               ii.         LookupPrivilegeValue()

1.      Access TokenPrivilege 목록에 권한이 있는지 살펴 봄

              iii.         AdjustTokenPrivilege()

1.      권한 활성화



5.      Manifest 파일 이용하여 권한을 할당

A.     Manifest 파일을 생성 (ex) add.manifest)

B.     프로젝트 파일에 생성한 Manifest 파일 포함

C.     프로젝트 속성 매니페스트 도구 추가 매니페스트 파일

                i.         생성한 manifest 파일 이름으로 값 설정 (ex) $(ProjectDir)add.manifest

D.     프로젝트 속성 링커 매니페스트 파일 – UAC 실행 수준

                i.         requireAdministrator로 값 설정

E.      프로그램 실행 할 경우

F.      UAC 확인 창을 명시적으로 확인 가능

 

 

 

3. 드라이버 서비스 설치

-드라이버 서비스 설치

1.      드라이버 파일 생성

A.     sys 파일

B.      Makefile, sources, c 파일 이용

C.      test.c 파일 동작 과정

                         i.         드라이버 시작시

1.       DbgPrint(“I loaded!!\n”);

                        ii.         OnUnload()

1.      DbgPrint(“OnUnload called\n”);

2.      드라이버가 시작 될때마다 디버그 창에 출력

 

2.      드라이버 설치과정

A.     OpenSCManager() 권한 변경

                         i.         SC_MANAGER_ALL_ACCESS

B.      CreateService()

                         i.         서비스 유형 : SERVICE_KERNEL_DRIVER

                        ii.         서비스 실행 시점 : WERVICE_AUTO_START

 

3.      계정별로 설치 과정 확인

A.     관리자 계정

                         i.         사용자 계정 컨트롤의 값에 따라 항상 알림, 알리지 않음의 경우로 나뉨 

                        ii.         항상 알림

1.      SCM에 접근 불가하여 

2.      드라이버 설치 불가능

                       iii.         알리지 않음

1.      드라이버 설치 가능

 

B.      사용자 계정

                         i.         사용자 계정 컨트롤 변경이 불가능하여 설치 불가능

 

C.      관리자 계정에서 드라이버 설치할 경우 사용자 계정에도 설치가 되지만 표준 사용자 계정에서는 설치가 될 수 없음 (시작, 중지, 일시중지도 변경 불가능)

D.     문제점

                         i.         관리자 계정에서 항상 알림으로 사용자 계정 컨트롤의 값을 설정 하였을 때 SCM에 접근이 불가능 한 상황

2. 서비스 디버깅

-서비스 디버깅

: 서비스는 SCM에 의해서 실행되므로 디버깅이 되지 않는다. 즉 일반 응용 프로그램 처럼 바로 실행하지 않기 때문에 디버깅이 안됌

1.      서비스 실행 중임

2.      서비스 프로그램 소스코드에서 SleepBreak Point를 설정

A.     Break Point를 지나치지 않게 Sleep을 이용하여 디버깅

B.      Break Point 전에 Sleep 설정

3.      디버그 – [프로세스에 연결]

4.      모든 사용자의 프로세스 표시, 모든 세션의 프로세스 표시에 체크

5.      사용 가능한 프로세스 목록에서 서비스 프로세스 선택

6.      연결