• Blog
  • Local Log
  • Tag Cloud
  • Key Log
  • Guestbook
  • RSS Feed
  • Write a Post
  • Admin

혹시 블로그 스킨이 깨져 보이시나요? 최신버전의 Internet Explorer(Windows용), Opera, Firefox를 사용해보세요.

Microsoft Windows XP 빠른 사용자 전환: 업무용 응용 프로그램 구축을 위한 디자인 가이드

Win32 API/MFC
2009/04/02 20:33
 

소개

Microsoft® Windows® XP는 Windows 2000 기술 기반의 새 운영 체제입니다. 빠른 사용자 전환은 Windows NT® 프로필의 데이터 구분 기술을 사용하고 사용자 계정 간의 전환을 위한 빠르고 편리한 메커니즘을 제공하는 Windows XP의 새 기능입니다.

NT 프로필을 사용한 데이터 구분

Windows XP는 개인용 시스템입니다. 예를 들면, 각 컴퓨터 사용자가 별도의 Windows 계정을 갖기 때문에 가족 구성원인 아버지, 어머니, 그리고 아들이 계정을 각각 하나씩 가지게 됩니다. 이러한 개별 Windows 계정은 대개 모든 가족 구성원이 하나의 계정을 공유했던 Windows 95 및 Windows 98에서 새롭게 발전된 개념입니다.

이러한 계정은 설치 과정 중에 만드는 것이 좋지만 나중에 제어판을 사용하여 만들 수도 있습니다. 이러한 계정은 각 사용자의 데이터가 구분되는 진정한 의미의 NT 프로필입니다. 기본적으로 이러한 계정은 암호로 보호되지 않지만 사용자는 자신의 계정에 암호를 설정할 수 있습니다.

빠른 사용자 전환 기능

Windows XP는 빠른 사용자 전환 기능을 도입하였습니다. Windows XP에서 사용자는 로그오프할 필요가 없습니다. 대신 사용자 계정은 항상 로그온되고 사용자는 열려 있는 모든 계정 간에 빠르게 전환할 수 있습니다. 예를 들어, 아버지가 회사에서 돌아와 컴퓨터를 사용한다고 가정해 보겠습니다. 아버지가 Microsoft PowerPoint®를 열어 문서 작업을 수행하던 중 아들이 다가와 컴퓨터를 써야 한다고 조릅니다. 아들은 시작 화면에서 자신의 이름을 클릭하고 게임을 시작합니다. 이 과정에서 아버지는 계속 로그온 상태이고, PowerPoint 프레젠테이션은 열려 있으며, 인터넷 연결도 계속 유지됩니다. 아버지는 원할 경우 아들을 로그오프하지 않고 자신의 열려 있는 계정으로 전환할 수 있습니다. 본질적으로 Windows XP를 사용하면 여러 사용자가 동시에 컴퓨터를 사용할 수 있는 것입니다.

Windows XP 시스템은 사용자가 로그온한 상태에서 그대로 두면 실행 중인 모든 응용 프로그램을 계속 유지하면서 시작 화면으로 돌아갑니다. 또한 로그온한 사용자 수, 읽지 않는 전자 메일의 존재 여부, 실행 중인 프로그램 수 등에 대한 정보를 제공하는 알림 메시지가 시작 화면에 나타납니다.

Windows XP Professional의 원격 데스크톱

Windows XP Professional을 사용하면 다른 시스템에서 데이터 및 응용 프로그램을 액세스할 수도 있습니다. 업무용 워크스테이션에서 Windows XP Professional을 사용하면 사용자는 원격 컴퓨터에서 자신의 테스크톱을 액세스할 수 있습니다.

예를 들어, 아버지는 사무실에서 자신의 워크스테이션을 잠금으로 설정하고 집으로 간 다음 사무실 워크스테이션에 연결할 수 있습니다. 가정용 컴퓨터는 사무실 워크스테이션에서 실행 중인 응용 프로그램을 원격으로 제어할 수 있습니다. 아버지는 다시 출근하여 데스크톱의 잠금을 해제하고 사무실에서 중단했던 작업을 집에서 계속 수행할 수 있습니다.

NT 프로필

빠른 사용자 전환 및 원격 데스크톱은 둘 다 터미널 서비스 기술을 사용하고 특별한 변경 없이 대부분의 이전 Microsoft Win32® 응용 프로그램에서 작동합니다. 지금 사용하고 있는 응용 프로그램이 Windows 2000 Logo Certified 제품이거나 Windows 2000의 Application Specification을 따르는 제품인 경우, 응용 프로그램은 Windows XP에서 아무 문제 없이 실행됩니다. 또한 Windows XP 시스템에서 응용 프로그램을 제대로 실행할 수 있게 도와주는 새 메시지를 사용할 수도 있습니다. 빠른 사용자 전환을 활용하는 새로운 방법과 함께 이 메시지는 이 문서에서 나중에 설명하도록 하겠습니다.

NT 프로필 인프라

Windows 2000은 사용자 데이터, 사용자 설정 및 컴퓨터 설정의 상태 구분을 지원하는 기본 인프라를 제공합니다. 이 인프라를 적절하게 사용하는 응용 프로그램은 최종 사용자에게 다음과 같은 이점을 제공합니다.

  • 응용 프로그램 및 운영 체제 파일을 백업하지 않고 개별 문서 및 설정을 쉽게 백업할 수 있습니다.

  • 여러 사용자가 단일 컴퓨터를 공유하고 각 사용자의 기본 및 일반 설정을 유지 관리할 수 있습니다.

  • 여러 컴퓨터에서 작업할 수 있고 특정 컴퓨터에서 다른 컴퓨터의 문서 및 설정을 유지 관리할 수 있습니다.

모든 사용자에 대해 시작 메뉴 항목 작성

시스템에 액세스할 수 있는 모든 사용자가 응용 프로그램을 실행할 수 있게 하려면 모든 사용자 프로필에 해당 응용 프로그램의 시작 메뉴를 만듭니다. 적절한 CSIDL과 함께 SHGetFolderPath를 사용하여 자신의 요구에 맞게 응용 프로그램 데이터를 저장할 수 있습니다. 컴퓨터, 특정 비-사용자, 비-로밍 데이터에 대해서는 CSIDL_COMMON_APPDATA를 사용하십시오.

내 문서 폴더를 사용자 작성 데이터의 기본 저장소로 설정

내 문서 폴더를 기본값으로 설정하려면 매개 변수 없이 Common File Open/Save를 호출합니다. 또는 CSIDL_PERSONAL을 SHGetFolderPath()에 전달하여 내 문서 폴더를 직접 대상으로 지정합니다. 내 문서 폴더는 임시 저장 또는 응용 프로그램 상태 데이터가 아니라 사용자 작성 데이터를 위한 것입니다. 이미징 응용 프로그램의 경우에는 내 문서 대신 내 문서의 하위 항목인 CSIDL_MYPICTURES를 사용하는 것이 좋습니다.

Windows 디렉터리와 같이 사용자 프로필에 사용되지 않는 중앙 위치에서 매핑된 파일을 만들지 마십시오. 임시 파일, 메모리 매핑된 파일 및 문서는 모두 사용자 프로필 디렉터리의 해당 하위 디렉터리에 저장되어야 합니다. API SHGetFolderPathAndSubDir은 사용자를 위해 하위 디렉터리를 만들기 때문에 네임스페이스의 AppData 부분에서 더 쉽게 작업할 수 있게 해 줍니다.

SHGetFolderPath를 사용하여 파일의 해당 저장소 위치를 찾을 수 있습니다. CSIDL_COMMON_APPDATA를 응용 프로그램에 전달하면 응용 프로그램별 데이터의 일반 리포지토리로 사용되는 파일 시스템 디렉터리가 반환됩니다. CSIDL_COMMON_PROGRAMS를 전달하면 모든 사용자 시작 메뉴가 반환됩니다. 사용자는 다른 사용자의 공유 문서에 쓸 수 없으며, 읽기 기능만 가능합니다. 하지만 응용 프로그램으로 인해 CSIDL_COMMON_DOCUMENTS의 응용 프로그램별 하위 디렉터리에서 이 보안 설정이 변경될 수 있습니다. CSIDL_COMMON 파일 폴더에는 보안 문제가 수반됩니다. 제한된 사용자를 비롯하여 모든 사용자에 대해 전체 읽기/쓰기를 허용하려면 기본적으로 ACL 속성을 설정해야 합니다.

임시 파일과 같이 사용자가 바뀔 때 변경해야 하는 데이터에 대해서는 CSIDL_LOCAL_APPDATA를 사용해야 합니다.

정확한 응용 프로그램 데이터 분류 및 저장

사용자 기본 설정, 응용 프로그램 상태 및 임시 파일과 같은 응용 프로그램 데이터는 다음 분류에 따라 응용 프로그램 데이터 폴더나 레지스트리에 저장되어야 합니다.

  • 사용자별 로밍 데이터

  • 사용자별 비로밍 데이터

  • 사용자마다 다르지 않고 비로밍인 시스템별 데이터

적절한 "액세스 거부" 메시지 사용

다음과 같이 액세스를 적절하게 거부할 수 있습니다.

  • 우선 작업을 수행할 수 없도록 설정합니다.

    이 방법은 제어판의 시스템 설정에서 수행할 수 있습니다. 사용자는 시스템 전체 환경 변수를 설정할 수 없지만 고유한 환경 변수를 설정할 수 있습니다. 따라서 사용자가 이 애플릿을 실행하면 시스템 전체 환경 변수 옵션은 "회색으로" 표시됩니다. 관리자가 이 애플릿을 실행하면 시스템 전체 환경 변수를 수정할 수 있습니다.

  • 해당 오류 메시지를 표시합니다. 예를 들면 다음과 같습니다.

    • "이 작업을 수행하려면 관리자이어야 합니다."

    • "현재 권한으로는 이 개체 속성의 보기 기능만 사용할 수 있습니다."

    • "이 작업을 수행하려면 xyz 권한이 있어야 합니다."

    • "이 작업을 수행하려면 abc.xyz에 대한 쓰기 권한이 있어야 합니다."

레지스트리 사용

응용 프로그램은 읽기/쓰기 응용 프로그램 데이터 및 구성 파일 저장을 위해 레지스트리를 사용할 수도 있습니다.

HKCU 레지스트리는 작은 데이터(64KB 미만)의 저장 및 사용자별 정책 설정에 적당합니다.

기본적으로 일반 사용자가 전체 HKLM 트리에 대한 읽기 전용 권한을 가지므로 런타임 도중 HKLM에 대해 쓰기 작업을 하면 안됩니다. 또한 HKLM은 로밍을 지원하지 않습니다.

큰 파일 기반 데이터(64KB 이상)는 응용 프로그램 데이터 폴더에 두어야 합니다. 예를 들어, Internet Explorer의 임시 인터넷 캐시는 레지스트리가 아니라 사용자 프로필 내에 저장됩니다.

설치 시 응용 프로그램은 HKCU 및 HKLM에서 전체 용량이 128KB를 초과하여 저장하면 안됩니다. 단, HK 클래스 루트는 제외된다는 점에 주의하십시오.

자세한 내용은 http://msdn.microsoft.com/certification/ tous.gif 에서 Designed for Windows 2000 Logo를 참조하십시오.

Windows 세션 전환

세션 전환 이벤트 수신 대기

일반적으로 응용 프로그램은 세션 전환이 발생할 때 알림을 받을 필요가 없습니다. 그러나 세션 전환이 언제 발생하는지 알아야 할 경우 응용 프로그램은 세션 전환 알림을 등록할 수 있습니다. 직렬 포트나 컴퓨터의 다른 공유 리소스를 액세스하는 응용 프로그램에서는 이것을 확인해야 합니다. 알림을 등록하려면 Wtsapi32.h에서 찾을 수 있는 다음 함수를 사용합니다.

BOOL WINAPI
WTSRegisterSessionNotification(
    HWND hWnd,         // Window handle
    DWORD dwFlags         // Flags
    );

등록된 HWND는 WM_WTSSESSION_CHANGE 메시지를 받습니다.

dwFlags에서 다음을 지정할 수 있습니다.

  • NOTIFY_FOR_THIS_SESSION. 해당 창이 속한 세션에 영향을 미치는 세션 변경 이벤트에 대해서만 알림을 받습니다.

  • NOTIFY_FOR_ALL_SESSIONS. 모든 세션 변경 이벤트에 대해 알림을 받습니다.

세션에서 발생하는 동작은 다음 플래그 중 하나를 포함하는 wParam 코드에서 볼 수 있습니다.

WTS_CONSOLE_CONNECT
WTS_CONSOLE_DISCONNECT
WTS_REMOTE_CONNECT
WTS_REMOTE_DISCONNECT
WTS_SESSION_LOGON
WTS_SESSION_LOGOFF
WTS_SESSION_LOCK
WTS_SESSION_UNLOCK

lParam에는 영향을 받은 세션의 sessionId가 포함되어 있습니다.

이러한 알림이 더 이상 필요하지 않거나 종료되는 프로세스는 다음을 호출하여 알림의 등록을 취소해야 합니다.

BOOL WINAPI
WTSUnRegisterSesssionNotification(
    HWND hWnd      // window handle.
    );

WTSRegisterSessionNotification에 전달된 HWND 값은 개수가 계산된 참조이므로 WTSRegisterSessionNotification을 호출한 횟수만큼 WTSUnRegisterSessionNotification을 호출해야 합니다.

응용 프로그램은 WTS_CONSOLE_CONNECT, WTS_CONSOLE_DISCONNECT, WTS_REMOTE_CONNECT 및 WTS_REMOTE_DISCONNECT 메시지를 사용하여 자신의 상태를 추적하고 콘솔별 리소스를 해제 및 획득할 수 있습니다.

응용 프로그램 버전을 하나만 실행

대부분의 응용 프로그램에서는 응용 프로그램 인스턴스가 하나만 실행 중이어야 합니다. Windows XP에서는 여러 방법으로 이렇게 할 수 있습니다.

FindWindow 또는 FindWindowEx를 사용하여 응용 프로그램이 연 창을 검색합니다. 창이 이미 열려 있을 경우에는 이러한 명령을 사용하여 응용 프로그램이 열려 있는지 확인합니다.

응용 프로그램을 열 때 Mutex 또는 세마포를 만들고, 응용 프로그램 종료할 때 이를 닫습니다. 네임스페이스는 각 데스크톱에 따라 구분되므로 고유한 Mutex 또는 세마포 목록이 만들어집니다.

시스템 서비스 상호 작용

프로그램적 관점에서 볼 때 다음과 같은 몇 가지 경우를 고려해야 합니다.

  • 서버 프로세스가 클라이언트 프로세스로부터 직접 요청을 받습니다.

    메시지는 대개 LPC 또는 RPC를 통해 전송되며 어떤 경우든 클라이언트 토큰을 얻기 위한 API가 존재합니다. 이 경우에는 WTSQueryUserToken을 사용합니다. 클라이언트 토큰을 획득하면 서버는 advapi32!CreateProcessAsUser를 호출할 수 있으며, 결과적으로 클라이언트 사용자 토큰이 세션 태그를 갖고 있다는 가정 하에 정확한 윈도우 스테이션에서 프로세스가 수행됩니다.

  • 서버 프로세스는 일부 알림 양식을 받고 현재 사용자 컨텍스트에서 사용자 인터페이스(UI)를 표시해야 합니다.

    서버는 현재 사용자의 토큰을 가져야 합니다. 그런 다음 서비스는 서버 프로세스가 TS에 제공할 수 있는 주어진 세션 식별자에 대해 토큰을 서버 프로세스(TS에 대한 클라이언트 프로세스)로 복제해야 합니다. 이 경우, 서버 프로세스는 SYSTEM으로 실행 중이어야 합니다.

  • 서버 프로세스는 일부 알림 양식을 받고 UI를 표시해야 하지만, 현재 사용자 컨텍스트에 있을 필요는 없습니다.

    이 경우, 서버 프로세스는 기본 프로세스 토큰을 복제할 수 있고 세션 식별자를 현재 세션 식별자로 변경할 수 있습니다. 현재 세션 식별자는 WTSGetActiveConsoleSessionID를 사용하여 얻을 수 있습니다.

Windows XP Professional에서 원격 데스크톱 기능이 추가된 후에는 응용 프로그램에 필요 이상의 대역폭을 사용해서는 안됩니다. 데스크톱이 원격으로 연결된 경우 응용 프로그램은 광범위한 화면 드로잉 및 애니메이션 효과를 피해야 합니다. 사용자 데스크톱은 원격 및 콘솔 간에 동적으로 전환될 수 있습니다. 응용 프로그램은 WTS_REMOTE_CONNECT 및 WTS_REMOTE_DISCONNECT 메시지를 사용하여 원격 v/s 로컬 연결 상태와 동기화되어야 합니다. 또한 언제든지 GetSystemMetrics(SM_REMOTESESSION)를 호출하여 세션이 원격 또는 콘솔인지 여부를 확인할 수 있습니다.

세션 모니커 및 IUserNotification을 사용하여 COM 응용 프로그램을 활성 사용자 세션의 데스크톱에서 실행할 수 있습니다. 세션 모니커는 원격으로도 작동하는 IMoniker::BindToObject를 통해 사용할 수 있습니다. BindToObject의 경우, 아래에 설명된 것처럼 전체 구문을 사용해야 합니다.

HRESULT CoCreateOnConsoleWithBindToObject(REFCLSID rclsid, REFIID riid, void** ppv)
{
    *ppv = NULL;
    IBindCtx* pbc;
    HRESULT hr = CreateBindCtx(0, &pbc);
    if (SUCCEEDED(hr)) 
    {
        WCHAR sz[128], szClsid[64];
        StringFromGUID2(rclsid, szClsid, ARRAYSIZE(szClsid)); 
        LPWSTR psz = szClsid + 1;   // skip "{"
        while (*psz != L'}') 
            psz++;
        *psz = NULL;
        lstrcpyW(sz, L"Session:Console!clsid:");
        lstrcatW(sz, &szClsid[1]);
        // Parse the name and get a moniker:
        ULONG ulEaten;
        IMoniker* pmk;
        hr = MkParseDisplayName(pbc, sz, &ulEaten, &pmk);
        if (SUCCEEDED(hr))
        {
            IClassFactory *pcf;
            hr = pmk->BindToObject(pbc, NULL, IID_PPV_ARG(IClassFactory, &pcf));
            if (SUCCEEDED(hr))
            {
                hr = pcf->CreateInstance(NULL, riid, ppv);
                pcf->Release();
            }
            pmk->Release();
        }
        pbc->Release();
    }
    return hr;
} 

이 코드는 예제이며, 복사하여 붙여넣을 경우 컴파일되지 않는다는 것에 주의하십시오.

지문 판독기와 같은 생체 인증에 사용할 소프트웨어를 작성하면서 빠른 사용자 전환 기능을 사용하려면, fusqa@microsoft.com으로 전자 메일을 보내 특수 DLL의 사용 허가를 받으십시오.

네트워크 연결

빠른 사용자 전환을 사용하면 실행 중인 프로그램을 그대로 두고 다른 사용자의 로그인을 허용할 수 있습니다. 또한 특정 사용자에서 다른 사용자로 전환할 때 인터넷 연결을 계속 유지할 수 있습니다. 실행 중인 응용 프로그램에 따라 독립 소프트웨어 공급업체(ISV)는 연결을 종료하거나 그대로 둘 수 있습니다.

항상 활성화 상태인 연결

항상 활성화 상태를 유지하는 연결은 빠른 사용자 전환에 의해 영향을 받지 않습니다. 일반적으로 xDSL 또는 케이블 모뎀 연결이 여기에 해당하며, 무선 네트워킹 연결도 항상 활성화 상태인 연결로 볼 수 있습니다.

사용자 초기화 연결

사용자 초기화 연결로는 가상 개인 네트워킹, PPPoX 및 원격 액세스 서버(RAS) 전화 접속 연결을 들 수 있습니다. 모든 사용자 초기화 연결은 시작 화면이 표시될 때 활성화 상태를 유지합니다. 대부분의 경우, 사용자 A가 네트워크 연결을 초기화한 다음 사용자 B가 활성 세션으로 시작되면 사용자 A의 연결은 끊어집니다.

항상 활성화 상태인 사용자 초기화 연결

일부 사용자 초기화 네트워크 연결은 특정 세션에서 다른 세션으로 빠른 사용자 전환이 이루어질 때 활성 상태를 유지할 수 있습니다. 다음과 같은 경우에 네트워크 연결이 유지됩니다.

  • 연결을 모든 사용자가 사용할 수 있는 경우

  • 연결 자격 증명이 저장된 경우

  • Home Edition에서 새 연결 마법사로 만든 모든 연결을 암시적으로 모든 사용자가 사용할 수 있는 경우

예외

사용자가 네트워크 연결을 초기화한 다음 로그오프하는 경우, 즉 사용자 전환이 없는 경우에는 네트워크 연결을 끊어집니다.

인터넷 연결 공유(ICS)를 사용하도록 설정된 연결은 사용자가 Windows 세션을 전환하거나 모든 사용자가 시스템에서 로그오프한 경우에도 연결 상태를 유지합니다. ICS 연결은 수동으로 끊거나 제한 시간 이후 자동으로 끊을 수 있습니다.

참고

현재 RAS API는 관리자 권한으로 사용자 컨텍스트에서 호출될 때 기본적으로 모든 사용자 연결을 만듭니다.

모든 사용자는 모든 사용자/전역 연결을 종료할 수 있습니다.

모든 세션에서 활성 연결은 아이콘으로 알림 영역에 표시됩니다.

모든 사용자 연결이 아니고 전역적으로 저장된 자격 증명이 있는 모든 연결은 사용자 전환 시 끊어집니다.

사용자 인터페이스 변경

작업 그룹에 추가된 Windows XP Home Edition과 Professional의 경우, 모든 사용자/전역(all-user/global-global) 연결 옵션이 기본으로 선택됩니다.

RAS API 변경

다음 플래그가 정의되었습니다.

#define RASCF_AllUsers 0x00000001 
#define RASCF_GlobalCreds 0x00000002

rasconnw 구조는 RASCONNW.dwSessionId 및 RASCONNW.dwFlags를 포함하도록 확장되었습니다.

  • RASCONNW.dwSessionId: 연결을 만든 사용자의 로그온 세션 ID입니다.

  • RASCF_AllUsers: 이 플래그는 연결에 사용되는 항목이 모든 사용자 연결인 경우에 설정됩니다.

  • RASCF_GlobalCreds: 이 플래그는 사용된 자격 증명이 RasSetCredentials(RASCM_DefaultCreds)로 설정된 자격 증명과 같은 기본 자격 증명인 경우에 설정됩니다.

또한 dnssuffix의 설정을 프로그램적으로 용이하게 하기 위해 szDnsSuffix 필드가 RASENTRY 구조에 추가되었습니다.

RASCONNW
{
    DWORD    dwSize;
    HRASCONN hrasconn;
    WCHAR    szEntryName[ RAS_MaxEntryName + 1 ];
#if (WINVER >= 0x400)
    WCHAR    szDeviceType[ RAS_MaxDeviceType + 1 ];
    WCHAR    szDeviceName[ RAS_MaxDeviceName + 1 ];
#endif
#if (WINVER >= 0x401)
    WCHAR    szPhonebook [ MAX_PATH ];
    DWORD    dwSubEntry;
#endif
#if (WINVER >= 0x500)
    GUID    guidEntry;
#endif
#if (WINVER >= 0x501)
   DWORD   dwSessionId;
   DWORD   dwFlags;
#endif 
};

이 코드는 예제이며, 복사하여 붙여넣을 경우 컴파일되지 않는다는 것에 주의하십시오.

전자 메일 알림

빠른 사용자 전환을 사용할 경우, 로그온한 사용자의 읽지 않은 전자 메일 수를 시작 로그온 화면에 표시할 수 있습니다. 시작 화면의 사용자 이름 아래에 읽지 않은 메일 수를 나타내는 하이퍼링크가 나타납니다. 이 하이퍼링크를 클릭하면 메일을 받은 계정을 볼 수 있습니다. 예를 들어, Bob이라는 사용자의 읽지 않은 전자 메일 수가 세 개라고 가정해 보겠습니다. 이 경우, 하이퍼링크를 클릭하면 Bob@MSN.com(2) 및 Bob@hotmail.com(1)이 표시됩니다. 이러한 작업을 위한 API인 SHGetUnreadMailCount 및 SHSetUnreadMailCount는 shellapi.h에 있습니다. 읽지 않은 전자 메일이 설정되면 날짜 스탬프도 함께 표시되며, 읽지 않은 전자 메일 항목이 2주를 경과하면 시작 화면은 해당 항목을 무시합니다.

터미널 서비스

일반 터미널 서비스 연결이 끊기면 RAS 및 FUS 전화 접속 아이콘이 자동으로 없어집니다.

OOBE(Out Of Box Experience) 도중에 사용자 계정 작성

단일 Windows 사용자 계정에서 여러 개의 인터넷 서비스 공급자(ISP) 계정을 만들면 안됩니다. 모든 Windows XP 사용자는 자신의 NT 프로필에서 고유한 ISP 계정을 가져야 데이터 구분, 개인 정보 보호 및 보안이 쉬워집니다. ISP 클라이언트 소프트웨어 또는 프로필을 가진 다른 모든 응용 프로그램은 자격 증명을 해당 사용자의 계정에 저장해야 합니다. 계정과 연관된 ISP 자격 증명 집합이 하나만 있을 경우, 사용자가 소프트웨어를 실행하면 응용 프로그램은 사용자를 확인할 필요가 없습니다. 마찬가지로 Windows에 의해 이미 확인 및 인증되었으므로 사용자에게 다시 확인 작업을 수행하라고 요청할 필요가 없습니다.

Windows XP를 사용하면 ISV는 OOBE(Out Of Box Experience) 도중에 별도의 사용자 계정을 만들 수 있습니다. 그러나 사용자 계정 작성 및 삭제는 관리할 사용자에게 맡기는 것이 좋습니다. 여러 응용 프로그램이 각각 사용자 계정을 개별적으로 만들 경우, 과도한 사용자 계정으로 인해 혼돈이 생길 수 있기 때문입니다. 사용자 계정을 반드시 만들어야 할 경우에는 계정을 만들기 전에 사용자가 그 사실을 알고 있는지 확인합니다. 그런 다음 아래에 설명된 것처럼 계정을 만듭니다.

Window의 NT 프로필을 만들기 전에 ISV는 응용 프로그램에 필요한 프로필을 만드는 설치 응용 프로그램을 실행할 수 있습니다. 예를 들어, 이 설치 응용 프로그램은 Bob@MSN.com을 만들 수 있습니다. 이 설치 응용 프로그램은 응용 프로그램 프로필 하나를 Windows 계정 하나와 연관시킬 수 있습니다. Windows XP OOBE 화면에서는 계정 이름을 입력하는 메시지가 나타납니다. 각 사용자의 편집 상자 옆에는 각 계정과 연관된 ISP 계정을 나타내는 편집 불가능한 정적 텍스트가 있습니다. 다음 예제에서 첫 번째 사용자는 편집 불가능한 정적 텍스트로 Bob@MSN.com을 가집니다. OOBE는 첫 번째 사용자의 프로필 이름을 Profilename=<string>에 기록하므로 설치 응용 프로그램은 레지스트리의 특정 키에서 응용 프로그램 프로필 하나를 Windows 사용자 계정 하나와 연관시킬 수 있습니다. 또한 ISP 계정 하나가 Windows 계정 하나와 연관되도록 OOBE 키 값을 업그레이드 전에 설정할 수 있습니다. 이렇게 하면 Windows Millennium와 같은 NT 프로필 기반이 아닌 운영 체제에서 업그레이드할 수 있습니다.

OOBE에서 ISP를 등록하는 도중에 ISP 응용 프로그램은 기본값이 ISP 이름(예: MSN)인 ISPAccount=<string>이라는 새 키를 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\OOBE에 기록합니다. 새 키에서 ISP 응용 프로그램은 기본값이 사용자의 ISP 계정(예: Bob@MSN.com)인 ISPUserName=<string>을 기록합니다. 또한 ISP가 ISPUserFriendlyName="<friendly name>" 값을 추가할 경우 OOBE는 작성할 첫 번째 프로필의 기본값으로 이 이름을 제안합니다.

OOBE에서 Windows 계정을 만드는 동안 OOBE는 ISPUserName에서 읽어 계정 작성 편집 상자 옆에 있는 사용자 인터페이스에 편집 불가능한 텍스트 값을 씁니다. 예를 들어, 첫 번째 사용자는 편집 상자 옆에 편집 불가능한 정적 텍스트로 Bob@MSN.com을 가집니다. 그런 다음 사용자는 Windows 계정 이름에 사용할 이름을 선택할 수 있습니다.

OOBE는 첫 번째 계정 작성 편집 상자에서 읽은 Windows 계정 이름을 Profilename=<string>에 씁니다. 예를 들어, 첫 번째 사용자의 계정 이름은 Bob Smith가 될 수 있습니다. 이 사용자가 로그온하면 ISP 응용 프로그램은 실행되고 위 키를 검색합니다. 현재 Windows 사용자가 "Profilename"과 일치하면 ISP 응용 프로그램은 ISPUserName과 Profilename을 연관시킵니다.

사용자 프로필 작성, 열거 및 로드를 위한 API는 MSDN 온라인의 Platform SDK에 있는 "Whistler Beta 1 Edition"에 모두 설명되어 있습니다.

다음 예제 코드는 계정을 사용자 계정으로 만듭니다. 사용자가 관리자가 되기를 원할 경우에는 "사용자(Users)" 대신 "관리자(Administrators)"를 지정하여 NetLocalGroupAddMembers를 다시 사용합니다. 아래의 도메인 필드는 시스템 이름을 포함할 수 있지만, 여기서는 ""로 두어도 충분합니다.

STDMETHODIMP CreateUserAccount(BSTR bstrLoginName)
{
    HRESULT hr;
    hr = E_FAIL;
    if ( bstrLoginName && *bstrLoginName )
    {
        NET_API_STATUS nasRet;
        USER_INFO_1 usri1 = {0};
        usri1.usri1_name     = bstrLoginName;
        usri1.usri1_password = TEXT("");
        usri1.usri1_flags    = UF_NORMAL_ACCOUNT |
       UF_SCRIPT | UF_PASSWD_NOTREQD | UF_DONT_EXPIRE_PASSWD;
        usri1.usri1_priv     = USER_PRIV_USER;
        nasRet = NetUserAdd(NULL,           // local computer
                            1,              // structure level
                            (LPBYTE)&usri1, // user infomarmation
                            NULL);          // don't care
        if ( nasRet == NERR_Success )
        {
            TCHAR szDomainAndName[256];
            LOCALGROUP_MEMBERS_INFO_3 lgrmi3;
            PLOCALGROUP_INFO_0 plgi0;
            wnsprintf(szDomainAndName, 
                      ARRAYSIZE(szDomainAndName), 
                      TEXT("%s\\%s"),
                      _szDomain,      /// this would be the machine name
                      bstrLoginName);
            lgrmi3.lgrmi3_domainandname = szDomainAndName;
            // by default newly created accounts will be user accounts
           nasRet = NetLocalGroupAddMembers(
                       NULL,
                       TEXT("Users"),
                       3,
                       (LPBYTE)&lgrmi3,
                       1);
        }
        hr = HRESULT_FROM_WIN32(nasRet);
    }
    else
    {
        hr = E_INVALIDARG;
    }
    return hr;
} 

이 코드는 예제이며, 복사하여 붙여넣을 경우 컴파일되지 않는다는 것에 주의하십시오.

이 코드는 계정을 만들지만 사용자가 처음 로그온할 때까지 프로필은 만들어지지 않습니다. LogonUser를 호출하여 프로필 작성을 강제로 할 수 있지만 또는 프로필 작성을 직접 할 수 있지만 성능이 현저하게 저하됩니다.

사용자 열거

dwPreferredSize = (sizeof(NET_DISPLAY_USER) + (3 * UNLEN) * s_iMaximumUserCount);
    pNDU = NULL;
    lError = NetQueryDisplayInformation(NULL,                  // NULL means LocalMachine
                                        1,                     // query User information
                                        0,                     // starting with the first user
                                        s_iMaximumUserCount,   // return a max of 100 users
                                        dwPreferredSize,       // preferred buffer size
                                        &dwEntriesRead,
                                        reinterpret_cast<void**>(&pNDU)); 

필수 호출 코드

(NET_API_STATUS)NetApiBufferFree(pNDU); 

다른 사용자 레지스트리에 대해 읽기/쓰기 작업을 하려면 RegLoadKey 및 RegUnloadKey를 사용하여 하이브를 로드합니다. 작업이 끝나면 이 키를 언로드해야 한다는 것에 주의하십시오.

크리에이티브 커먼즈 라이센스
Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
TAG Fast User Switching, Terminal Services, 빠른 사용자 전환, 터미널 서비스
No received trackback. / No comment.

Trackback Address :: http://sinwoong.co.kr/trackback/293

You can also say.

Windows XP에서 빠른 사용자 전환을 지원하는 응용 프로그램 작성

Win32 API/MFC
2009/04/02 20:00
 
Windows XP의 빠른 사용자 전환 기능으로 여러 사용자가 동일한 컴퓨터를 공유할 수 있습니다. 각 사용자마다 고유 프로필과 바탕 화면이 존재하며 로그 오프 하지 않고도 사용자 간 전환이 가능합니다. 그러므로 사용자 세션 전환이 발생할 때 데이터가 충돌하거나 손실되지 않으면서 빠른 사용자 전환을 지원하는 응용 프로그램을 작성해야 합니다.

빠른 사용자 전환을 지원하려면 응용 프로그램이 사용자 및 응용 프로그램 데이터를 유효한 위치에 저장해야 합니다.

또한 여러 사용자가 동시에 실행하면 불안전한 방식으로 전체 리소스가 사용되므로 프로그램 오류를 발생시키는 기능을 응용 프로그램에 적용해야 합니다. 이렇게 하려면 이 상황을 감지하고 적절하게 반응하는 코드를 응용 프로그램에 추가하십시오.

두 번째 응용 프로그램 인스턴스가 선택적(보조) 기능에 영향을 미치는 경우 사용자 응용 프로그램이 시작될 때 다음이 수행되어야 합니다.
  • 사용자가 응용 프로그램을 이미 실행 중인지 감지합니다.
  • 문제가 있는 기능을 차단합니다.
  • 현재 사용자에게 특정 기능을 사용할 수 없는 이유를 알립니다.
두 번째 응용 프로그램 인스턴스가 주 기능에 영향을 미치는 경우 응용 프로그램은 다음을 수행해야 합니다.
  • 사용자가 응용 프로그램을 이미 실행 중인지 감지합니다.
  • 오류 상황을 현재 사용자에게 보고하고 정상적으로 종료합니다.
마지막으로 응용 프로그램이 활성 사용자 세션에서 실행되는 시기와 세션 전환이 발생하는 시기를 감지해야 할 경우 세션 알림 메시지를 발생시키도록 등록할 수 있습니다. 예를 들어, 직렬 포트에 연결되는 장치를 모니터링하는 응용 프로그램은 활성 사용자 세션에서 실행하지 않을 때는 포트를 해제하고 세션이 다시 활성화되면 포트를 다시 요구할 수 있습니다. 또한 응용 프로그램은 비활성 사용자 세션에서 실행되는 동안 백그라운드 작업을 일시 중단하여 시스템 리소스를 절약할 수 있습니다.

다음은 빠른 사용자 전환을 지원하는 응용 프로그램 활성화 과정에 대한 전반적인 설명입니다.


 

요구 사항

다음은 권장 하드웨어, 소프트웨어, 네트워크 인프라 및 서비스 팩 목록입니다.

  • Windows XP Home Edition 또는 Windows XP Professional Edition
  • Microsoft Visual Studio .NET 또는 Visual Studio 6.0
  • Microsoft Platform SDK June 2001 버전 이상의 헤더 파일과 라이브러리
필요한 사전 지식:
  • Win32 응용 프로그램 개발
  • Windows XP 빠른 사용자 전환에 대한 지식
 

Win32 응용 프로그램 만들기

Visual Studio를 시작하고 새로운 Win32 응용 프로그램을 만들어 이름을 FastUserSwitching으로 지정합니다.
  • Visual C++ 6.0 사용자: 사용 가능한 프로젝트 형식 목록에서 Win32 Application을 선택한 다음 A typical "Hello World" application을 선택합니다.
  • Visual Studio .NET 사용자: Visual C++ 프로젝트에서 Win32 프로젝트를 선택하고 응용 프로그램 마법사에 나타나는 기본 응용 프로그램 설정을 그대로 적용합니다.
 

세션 전환 알림을 발생시키는 코드 추가

응용 프로그램이 활성 사용자 세션에서 실행되는 시기와 세션 전환이 발생하는 시기를 알아야 할 경우 WTSRegisterSessionNotification 함수를 호출하여 WM_WTSSESSION_CHANGE 메시지를 발생시키도록 등록할 수 있습니다.
  1. stdafx.h를 열고 windows.h의 포함 앞에 다음 #define 문을 추가합니다.
    #define _WIN32_WINNT 0x0501
    이것은 세션 알림 형식과 매크로를 제공하기 위해 winuser.h에서 요구하는 것입니다.
  2. 다음 헤더 파일(WTSRegisterSessionNotification 함수 프로토타입 포함)을 FastUserSwitching.cpp의 맨 위에 입력합니다.
    #include <wtsapi32.h>
  3. 프로젝트의 라이브러리 목록에 Wtsapi32.lib를 추가합니다.
  4. FastUserSwitching.cpp에서 InitInstance 함수를 찾습니다. 함수 하단의 최종 반환 문 바로 앞에 다음과 같이 WTSRegisterSessionNotification에 호출을 추가합니다.
    WTSRegisterSessionNotification(hWnd, NOTIFY_FOR_THIS_SESSION);
  5. WndProc 창 프로시저를 찾고 WM_WTSSESSION_CHANGE 메시지를 처리하는 case 문을 추가합니다. 이 메시지의 wParam에는 세션 변경 알림을 보낸 이유를 나타내는 상태 코드가 포함되어 있습니다. 다음 코드를 추가하여 사용 가능한 상태 코드의 하위 집합을 감지하고 어떤 상태 코드를 받았는지 나타내는 메시지 상자를 표시하도록 합니다.
    case WM_WTSSESSION_CHANGE:
       switch( wParam )
       {
          case WTS_CONSOLE_CONNECT:
             MessageBox(hWnd, TEXT("WTS_CONSOLE_CONNECT"), 
                        TEXT("WM_WTSSESSION_CHANGE"), MB_OK );
             break;
          case WTS_CONSOLE_DISCONNECT:
             MessageBox(hWnd, TEXT("WTS_CONSOLE_DISCONNECT"), 
                        TEXT("WM_WTSSESSION_CHANGE"), MB_OK );
             break;
          case WTS_SESSION_LOCK:
             MessageBox(hWnd, TEXT("WTS_SESSION_LOCK"), 
                        TEXT("WM_WTSSESSION_CHANGE"), MB_OK );
             break;
          case WTS_SESSION_UNLOCK:
             MessageBox(hWnd, TEXT("WTS_SESSION_UNLOCK"), 
                        TEXT("WM_WTSSESSION_CHANGE"), MB_OK );
             break;
       default:
          break;
       }
       break;
    
  6. WTSRegisterSessionNotification에 대한 모든 호출은 WTSUnRegisterSessionNotification에 대한 모든 호출과 일치해야 합니다. WndProc 내의 WM_DESTROY 메시지 처리를 다음과 같이 수정합니다.
    case WM_DESTROY:
       WTSUnRegisterSessionNotification(hWnd);
       PostQuitMessage(0);
       break;
 

세션 전환 알림 확인

이 작업을 실행하려면 Windows XP 사용자 계정이 두 개 이상 있어야 합니다. 계정이 하나만 있는 경우 먼저 새 계정을 만드십시오.
  1. 프로젝트를 다시 빌드합니다.
  2. 응용 프로그램 실행합니다.
  3. 시작 메뉴에서 로그오프를 누른 다음 사용자 전환을 누릅니다.
  4. 현재 사용자 이름을 눌러 이전 사용자 세션으로 되돌립니다.
  5. WTS_SESSION_LOCK 및 WTS_SESSION_UNLOCK 알림을 받았는지 확인합니다.
  6. 확인을 눌러 두 메시지 상자를 닫습니다.
  7. 시작 메뉴에서 로그오프를 누른 다음 사용자 전환을 누릅니다.
  8. 새로운 사용자 세션으로 전환한 다음 원래 사용자 세션으로 다시 전환합니다.
  9. WTS_SESSION_LOCK, WTS_CONSOLE_DISCONNECT, WTS_SESSION_UNLOCK 및 WTS_CONSOLE_CONNECT 알림을 받았는지 확인합니다.
  10. 확인을 눌러 메시지 상자를 모두 닫습니다.
  11. 응용 프로그램을 닫습니다.
 

기존 응용 프로그램 인스턴스 감지

응용 프로그램은 다음 작업을 수행하기 위해 기존에 실행된 인스턴스를 감지해야 합니다.
  • 여러 인스턴스에서 공유할 수 없는 특정 기능을 해제합니다.
  • 극단적인 경우 후속 인스턴스가 실행되지 못하게 방지합니다.
가능하면 Windows XP 응용 프로그램은 같은 사용자 세션이든 다른 사용자 세션이든 여러 인스턴스가 동시에 실행되는 것을 지원해야 합니다. 여러 인스턴스를 허용하지 않는 응용 프로그램은 최상의 Windows XP 응용 프로그램으로 간주되지 않습니다.

기존 응용 프로그램 인스턴스를 감지하려면 잘 알려진 전역 뮤텍스나 세마포 개체를 사용하십시오. 전역 네임스페이스를 사용한다는 것을 확인할 수 있도록 개체 이름에 "Global\" 접두사를 붙입니다. 이렇게 하면 다른 사용자 세션에서 실행되는 응용 프로그램의 인스턴스를 감지할 수 있습니다.

FindWindow 또는 FindWindowEx를 사용하는 기존 방법은 다른 데스크톱의 사용자 세션에서 실행되는 응용 프로그램 인스턴스를 감지하지 못하기 때문에 빠른 사용자 전환 기능을 사용하는 Windows XP에서는 작동하지 않습니다.
  1. FastUserSwitching.cpp를 편집합니다.
  2. 파일 맨 위에 있는 기존의 전역 변수 아래에 뮤텍스 개체의 핸들을 저장하는 전역 변수를 선언하고 초기화합니다.
    HANDLE g_hMutexAppRunning = NULL;
  3. 생성할 새로운 함수에 대한 프로토타입을 다음과 같이 추가하여 기존 응용 프로그램 인스턴스가 존재하는지 여부를 감지하도록 합니다.
    BOOL AppInstanceExists();
  4. 소스 파일의 아래 부분에 다음 코드를 사용하여 AppInstanceExists 함수를 만듭니다. 이 코드는 전역 뮤텍스 개체를 만든 다음 오류 코드 ERROR_ALREADY_EXISTS를 검사하여 뮤텍스 개체가 만들어졌거나 열렸는지 확인합니다. 이 시나리오에서 오류 코드는 기존 응용 프로그램 인스턴스가 실행 중인지 나타냅니다. 기존 응용 프로그램 인스턴스가 실행 중인 경우 코드는 뮤텍스 개체를 닫고 "TRUE"를 반환합니다. 함수가 새로운 뮤텍스 개체를 성공적으로 만든 경우 "FALSE"를 반환하여 이것이 첫 번째 응용 프로그램 인스턴스임을 나타냅니다.
    BOOL AppInstanceExists()
    {
       BOOL bAppRunning = FALSE;
       // Create a global mutex. Use a unique name, for example 
    // incorporating your company and application name.
       g_hMutexAppRunning = CreateMutex( NULL, FALSE, 
                                         "Global\\My Company MpApp.EXE");
       // Check if the mutex object already exists, indicating an
       // existing application instance
       if (( g_hMutexAppRunning != NULL ) && 
             ( GetLastError() == ERROR_ALREADY_EXISTS))
       {
          // Close the mutex for this application instance. This assumes
          // the application will inform the user that it is 
          // about to terminate 
          CloseHandle( g_hMutexAppRunning );
          g_hMutexAppRunning = NULL;
       }
       // Return False if a new mutex was created, 
    // as this means it's the first app instance
       return ( g_hMutexAppRunning == NULL );
    }
  5. 실행 중인 응용 프로그램 인스턴스를 종료할 때 뮤텍스 개체가 닫혔는지 확인해야 합니다. WinMain 함수의 아래 부분 중 메시지 루프와 최종 return 문 사이에 다음 코드를 추가합니다.
    if (g_hMutexAppRunning != NULL )
    {
      CloseHandle(g_hMutexAppRunning);
      g_hMutexAppRunning = NULL;
    }
 

기존 응용 프로그램 인스턴스를 포그라운드로 설정

응용 프로그램에서 단일 인스턴스만 허용하는 경우 기존 인스턴스가 현재 사용자 세션에서 실행되고 있으면 후속 인스턴스가 기존 인스턴스를 포그라운드로 보내기 시작할 때 FindWindow와 SetForegroundWindow API를 사용해야 합니다. 기존 응용 프로그램 인스턴스가 다른 사용자 세션에서 실행되는 경우 NULL을 반환하므로 FindWindow의 반환 값을 테스트해야 합니다.

InitInstance 함수를 찾아 다음과 같이 수정합니다.
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
   hInst = hInstance; 
   // Check if another application instance is already running
   if ( AppInstanceExists() == TRUE )
   {
      HWND hWndOtherInstance;
      hWndOtherInstance = FindWindow(szWindowClass, szTitle);
      if ( hWndOtherInstance != (HWND)NULL )
      {
         // Application is running in current user's session
         if (IsIconic(hWndOtherInstance)) 
            ShowWindow(hWndOtherInstance, SW_RESTORE);
         SetForegroundWindow(hWndOtherInstance);
      }
      else
      {
         MessageBox(NULL, TEXT(
         "An instance of this app is running in another user's session"),
         szTitle, MB_OK);
      }
      return FALSE;
   }
   hWnd =  CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL,
                          hInstance, NULL );
   if (!hWnd)
      return FALSE;

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
   WTSRegisterSessionNotification(hWnd, NOTIFY_FOR_THIS_SESSION);
   return TRUE;
}
 

응용 프로그램 감지 확인

  1. 프로젝트를 빌드합니다.
  2. 응용 프로그램 실행합니다.
  3. 응용 프로그램을 최소화합니다.
  4. 응용 프로그램의 다른 인스턴스를 시작하고 기존 응용 프로그램 인스턴스가 복원되었으며 포그라운드로 보내졌는지 확인합니다.
  5. 추가 응용 프로그램 인스턴스를 반복해서 시작해 보고 그 때마다 기존 응용 프로그램 인스턴스가 포그라운드로 보내지는지 확인합니다.
  6. 응용 프로그램의 한 인스턴스를 실행한 상태에서 새로운 사용자 세션으로 전환합니다.
  7. 응용 프로그램을 시작하여 응용 프로그램이 다른 사용자 세션에서 이미 실행 중이라는 메시지가 표시되는지 확인합니다.
  8. 확인을 눌러 메시지 상자를 닫습니다.
  9. 원래 사용자 세션으로 되돌리고 세션 전환 알림 메시지 상자를 닫고 응용 프로그램을 닫습니다.
 

문제 해결

  • 응용 프로그램이 Windows XP나 Windows 2000에서 실행 중인 경우 전역 네임스페이스를 사용하는 뮤텍스와 세마포 개체만 지정할 수 있습니다. 이전 버전의 Windows는 전역 네임스페이스를 지원하지 않으며 커널 개체 이름에 백슬래시 문자("\")를 사용하려고 시도할 경우 오류가 발생합니다.
  • 응용 프로그램이 Windows XP나 Windows 2000 및 다른 버전의 Windows에서 실행하도록 디자인된 경우 버전 확인 코드를 포함해야 하며 해당 운영 체제의 전역 뮤텍스 이름만 사용해야 합니다.
  • 서비스를 개발 중인 경우 서비스의 사용자 상호 작용이 올바른 사용자로 발생하는지 확인하십시오. Windows XP에서는 활성 세션 번호가 어느 것이든 될 수 있으므로 세션 0이 현재 바탕 화면 세션이라고 가정하지 마십시오. 활성 세션을 확인하려면 WTSGetActiveConsoleSessionID를 사용합니다.
 

본 문서의 정보는 다음의 제품에 적용됩니다.
  • Microsoft Windows XP Professional
  • Microsoft Visual C++ .NET 2002 Standard Edition
  • Microsoft Visual C++ 6.1
크리에이티브 커먼즈 라이센스
Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
TAG Fast User Switching, Terminal Services, 빠른 사용자 전환, 터미널 서비스
No received trackback. / No comment.

Trackback Address :: http://sinwoong.co.kr/trackback/292

You can also say.

Prev 1 2 3 4 5 6 7 ... 143 Next
달빛는 지금.. 파이팅파이팅
C와 Assembly를 제2외국어로...
남자RH+ O형사수자리경기도

카테고리

  • 전체 (285)
    • Project HSL (2)
    • Project Talk (8)
    • 생각 (16)
    • 일상 (18)
    • 정보/스크랩 (64)
    • 용어사전 (4)
    • IT외부행사 (15)
    • ASSEMBLY (7)
    • Win32 API/MFC (29)
    • Hacking/Virus/Vaccine (24)
    • 관심거리 (3)
    • 스킨/플러그인 (2)
    • 시간 죽이기 (2)
    • 천연 비누/화장품 (10)
    • Windows Server 2008 (57)
    • 디바이스 드라이버 (23)
    • 카드 (1)
    • 지식베이스캠프 (0)
    • 소프트캠프 업무 일지 (0)

태그목록

  • 오픈 소스
  • Crash Dump
  • 목욕법
  • 엔드 포인트 보호
  • XP Style
  • 플러그인
  • 예제
  • 자격증
  • 무단가입
  • DDK
  • 스파이웨어 기준안
  • 사용자 설치 동의
  • Virus
  • 유니코드
  • 책장
  • Diamond Mine
  • VS 2008
  • 블랙해커
  • IDE
  • GIAC
  • dll
  • 프리웨어
  • 버퍼 오버플로우
  • 운영체제 이론
  • iTX
  • Bottled Music
  • Vista 권한
  • Drop
  • 1TB
  • 스케줄링

최근에 올라온 글

  • ExitProcess(), Termina....
  • C/C++ 격리된 응용 프로....
  • EnumPrinters.
  • 알고 있어야 도움이 되....
  • Microsoft Windows XP....

최근에 달린 댓글

  • 좋은 자료 감사합니다.. Minjong Kim 08/09
  • 학습로드맵 잘 보고 갑.... Minjong Kim 08/09
  • 잘보고 갑니다 : ). hothead 07/16
  • 좋은 글 잘읽었습니다. ^^. Rhea君 05/06
  • 좋은 소프트웨어네여. montreal florist 01/12

최근에 받은 트랙백

  • Windows 시스템 실행파.... 김재호의 디지털보단 아... 02/14
  • 스레드만 사용하면 컴퓨.... 철없는강아지의 블로그 2008
  • 언제 다이어그램을 그려.... 너구리의 프로그래밍 세계 2007
  • 국내 웹환경에 기대를.... *~アスタリスク~ 2007
  • [플러그인] IS Flash Vi.... ISCUBIX.COM 2007

글 보관함

  • 2010/06 (1)
  • 2010/03 (2)
  • 2009/09 (1)
  • 2009/04 (2)
  • 2009/03 (5)

달력

«   2010/09   »
일 월 화 수 목 금 토
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    

링크

  • IT 용어사전.
  • Total : 288482
  • Today : 18
  • Yesterday : 245
Tattertools
Eolin
rss

달빛's blog is powered byTattertools1.5.3.1 : Adamantine
Skin by Nyangkum