2013년 1월 31일 목요일

object namespace


이벤트, 뮤텍스, 세마포, 공유 메모리등은 모두 커널오브젝트입니다.
윈도우 API 중에 무언가 생성할 때 이름을 파라미터로 받아들이는 녀석이 있다면 모두 커널오브젝트를 생성하는 API 라고 생각하면 됩니다.
다들 잘 아시는 것처럼 OS 는 이 커널오브젝트들을 관리하기 계층구조로 관리합니다. WinObj 같은 툴을 이용하면 쉽게 확인이 가능하죠.




이렇게 계층 구조로 커널오브젝트를 관리하기 때문에 오브젝트를 처리할 때는 네임스페이스처리를 잘 해주어야하는데요. Windows xp 까지는 콘솔세션은 세션 0, 원격데스크탑 세션은 세션 1, 세션 2 이렇게 세션이 생성되었습니다. ( 대부분의 사용자가 사용하는 ) 콘솔세션은 서비스와 어플리케이션이 동일한 세션에서 실행되었기때문에 별 문제가 없었습니다.

보통 이름있는 이벤트를 생성해서 서비스/응용프로그램간에 공유하는 경우 아래와 같은 코드가 문제없이 실행되었을 것입니다.
HANDLE event_handle = CreateEventW(
                          NULL, 
                          FALSE, 
                          FALSE, 
                          L"my_nmaed_event"
                          );
하지만 vista 이후에는 이런 코드가 정상작동하지 않을 것입니다.
vista 이후부터는 서비스 세션(세션 0)과 응용프로그램 세션(세션 1)이 분리되었기 때문이지요.



위의 코드는 \Sessions\응용프로그램이 실행된 세션번호\BaseNamedObjects 네임스페이스에 객체를 생성합니다.
아마 \Sessions\1\BaseNamedObjects\my_named_event 가 될 것입니다.
이 이벤트를 서비스에서 오픈해서 공유하려면 아래와 같은 코드를 사용하면 되겠죠.
HANDLE event_handle = OpenEventW(
                            EVENT_ALL_ACCESS,
                            FALSE, 
                            L"\\Sessions\\1\\BaseNamedObjects\\my_nmaed_event"
                            );
그런데 어플리케이션이 세션 2 에서 실행중이라면 어쩌죠? (원격데스크탑 등으로 접속해서 실행한 경우)
반대로 서비스에서 객체를 생성하면 항상 \Sessions\0\BaseNamedObjects\ 네임스페이스를 사용하니 되지 않겠느냐 라고 생각할 수도 있겠지만 그다지 좋은 해결책은 아니죠.
그래서 만들어진것이 Global 네임스페이스입니다.

Global 네임스페이스를 사용하면 세션에 구애 받지 않고, \BaseNamedObjects 네임스페이스를 사용하게 됩니다.
즉 아래 코드는 모두 \BaseNamedObject\my_named_event 객체를 생성하고, 오픈하게 됩니다.
따라서 서로 다른 세션에 있어도 오브젝트에 대한 접근이 가능하게 됩니다.
HANDLE event_handle = CreateEventW(
                          NULL, 
                          FALSE, 
                          FALSE, 
                          L"Global\\my_nmaed_event"
                          );
이렇게 오브젝트를 생성하고,
HANDLE event_handle = OpenEventW(
                          EVENT_ALL_ACCESS,
                          FALSE, 
                          L"Global\\my_nmaed_event"
                          );
이렇게 오픈해서 사용하면 됩니다.
만일 디바이스 드라이버의 경우 \BaseNamedObjects\my_named_object 에 접근하면 됩니다.
UNICODE_STRING us;
HANDLE   r0_handle;
PKEVENT   r0_object;
RtlInitUnicodeString(&us, L"\\BaseNamedObjects\\my_named_object");
r0_object = IoCreateNotificationEvent(&us, &r0_handle);


결론!
공유가 필요한 커널오브젝트를 사용하는 경우 오브젝트 네임스페이스 문제를 고려하자.
일반적으로 Global 네임스페이스를 이용하는게 편리하다.