호스트 컴퓨터에 서비스 설치

다음 코드 예제에서는 호스트 컴퓨터에 디렉터리 사용 서비스를 설치하는 기본 단계를 보여 줍니다. 다음 작업을 수행합니다.

  1. OpenSCManager 함수를 호출하여 로컬 컴퓨터의 SCM(서비스 제어 관리자)에 대한 핸들을 엽니다.
  2. CreateService 함수를 호출하여 SCM 데이터베이스에 서비스를 설치합니다. 이 호출은 서비스의 로그온 계정 및 암호뿐만 아니라 서비스의 실행 파일 및 서비스에 대한 기타 정보를 지정합니다. 지정한 로그온 계정이 잘못된 경우 CreateService가 실패합니다. 그러나 CreateService는 암호의 유효성을 검사 않습니다. 또한 계정에 로컬 컴퓨터에서 바로 서비스로 로그온이 있는지 확인하지 않습니다. 자세한 내용은 호스트 컴퓨터에서 서비스 권한으로 로그온 허용을 참조하세요.
  3. 디렉터리에 SCP(서비스 연결 지점 개체)를 만드는 서비스의 ScpCreate 서브루틴을 호출하여 서비스의 이 instance 위치를 게시합니다. 자세한 내용은 클라이언트가 서비스 연결 지점을 찾고 사용하는 방법을 참조하세요. 이 루틴은 또한 서비스의 바인딩 정보를 SCP에 저장하고, 서비스가 런타임에 액세스할 수 있도록 SCP에 ACE를 설정하고, 로컬 레지스트리에 SCP의 고유 이름을 캐시하고, 새 SCP의 고유 이름을 반환합니다.
  4. 서비스의 클래스 문자열과 SCP의 고유 이름을 사용하여 SPN(서비스 사용자 이름)을 구성하는 서비스의 SpnCompose 서브루틴을 호출합니다. 자세한 내용은 SCP를 사용하여 서비스에 대한 SPN 작성을 참조하세요. SPN은 서비스의 이 instance 고유하게 식별합니다.
  5. 서비스의 로그온 계정과 연결된 계정 개체에 SPN을 등록하는 서비스의 SpnRegister 서브루틴을 호출합니다. 자세한 내용은 서비스에 대한 SPN 등록을 참조하세요. SPN을 등록하면 클라이언트 애플리케이션이 서비스를 인증할 수 있습니다.

이 코드 예제는 로그온 계정이 로컬 또는 도메인 사용자 계정인지 또는 LocalSystem 계정인지에 관계없이 올바르게 작동합니다. 도메인 사용자 계정의 경우 szServiceAccountSAM 매개 변수에는 계정의 Domain**\**UserName 이름이 포함되고 szServiceAccountDN 매개 변수에는 디렉터리에 있는 사용자 계정 개체의 고유 이름이 포함됩니다. LocalSystem 계정의 경우 szServiceAccountSAMszPasswordNULL이고 szServiceAccountSN 은 디렉터리에 있는 로컬 컴퓨터의 계정 개체의 고유 이름입니다. szServiceAccountSAM이 로컬 사용자 계정(이름 형식은 ".\UserName")을 지정하는 경우 로컬 사용자 계정에 대해 상호 인증이 지원되지 않으므로 코드 예제는 SPN 등록을 건너뜁니다.

기본 보안 구성을 사용하면 도메인 관리자만 이 코드를 실행할 수 있습니다.

또한 이 코드 예제는 작성된 대로 서비스가 설치되는 컴퓨터에서 실행되어야 합니다. 따라서 일반적으로 스키마를 확장하거나 UI를 확장하거나 그룹 정책을 설정하는 서비스 설치 코드와 별도의 설치 실행 파일에 있습니다. 이러한 작업은 포리스트 전체에 대한 서비스 구성 요소를 설치하는 반면, 이 코드는 단일 컴퓨터에 서비스를 설치합니다.

void InstallServiceOnLocalComputer(
            LPTSTR szServiceAccountDN,  // Distinguished name of logon account.
            LPTSTR szServiceAccountSAM, // SAM name of logon account.
            LPTSTR szPassword)          // Password of logon account.
{
SC_HANDLE   schService = NULL;
SC_HANDLE   schSCManager = NULL;
TCHAR szPath[512];
LPTSTR lpFilePart;
TCHAR szDNofSCP[MAX_PATH];
TCHAR szServiceClass[]=TEXT("ADSockAuth");
 
DWORD dwStatus;
TCHAR **pspn=NULL;
ULONG ulSpn=1;
 
// Get the full path of the service's executable.
// The code example assumes that the executable is in the current directory.
dwStatus = GetFullPathName(TEXT("service.exe"), 512, szPath, &lpFilePart);
if (dwStatus == 0) {
    _tprintf(TEXT("Unable to install %s - %s\n"), 
            TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
    return;
}
_tprintf(TEXT("path of service.exe: %s\n"), szPath);
 
// Open the Service Control Manager on the local computer.
schSCManager = OpenSCManager(
                NULL,                   // Computer (NULL == local)
                NULL,                   // Database (NULL == default)
                SC_MANAGER_ALL_ACCESS   // Access required
                );
if (! schSCManager) {
    _tprintf(TEXT("OpenSCManager failed - %s\n"), 
                   GetLastErrorText(szErr,256));
    goto cleanup;
}
        
// Install the service in the SCM database.
schService = CreateService(
            schSCManager,               // SCManager database
            TEXT(SZSERVICENAME),        // Name of service
            TEXT(SZSERVICEDISPLAYNAME), // Name to display
            SERVICE_ALL_ACCESS,         // Desired access
            SERVICE_WIN32_OWN_PROCESS,  // Service type
            SERVICE_DEMAND_START,       // Start type
            SERVICE_ERROR_NORMAL,       // Error control type
            szPath,                     // Service binary
            NULL,                       // No load ordering group
            NULL,                       // No tag identifier
            TEXT(SZDEPENDENCIES),       // Dependencies
            szServiceAccountSAM,        // Service account
            szPassword);                // Account password
if (! schService) {
    _tprintf(TEXT("CreateService failed - %s\n"), 
                   GetLastErrorText(szErr,256));
    goto cleanup;
}
 
_tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
 
// Create the service's Service Connection Point (SCP).
dwStatus = ScpCreate(
        2000,                 // Service default port number
        szServiceClass,       // Specifies the service class string
        szServiceAccountSAM,  // SAM name of logon account for ACE
        szDNofSCP             // Buffer returns the DN of the SCP
        );
if (dwStatus != 0) {
    _tprintf(TEXT("ScpCreate failed: %d\n"), dwStatus );
    DeleteService(schService);
    goto cleanup;
}
 
// Compose and register a service principal name for this service.
// This is performed on the install path because this requires elevated
// privileges for updating the directory.
// If a local account of the format ".\user name", skip the SPN.
if ( szServiceAccountSAM[0] == '.' ) 
{
    _tprintf(TEXT("Do not register SPN for a local account.\n"));
    goto cleanup;
}
 
dwStatus = SpnCompose(
        &pspn,            // Receives pointer to the SPN array.
        &ulSpn,           // Receives number of SPNs returned.
        szDNofSCP,        // Input: DN of the SCP.
        szServiceClass);  // Input: the service's class string.
 
if (dwStatus == NO_ERROR) 
    dwStatus = SpnRegister(
        szServiceAccountDN,  // Account on which SPNs are registered.
        pspn,                // Array of SPNs to register.
        ulSpn,               // Number of SPNs in array.
        DS_SPN_ADD_SPN_OP);  // Operation code: Add SPNs.
 
if (dwStatus != NO_ERROR) 
{
    _tprintf(TEXT("Failed to compose SPN: Error was %X\n"), 
                  dwStatus);
    DeleteService(schService);
    ScpDelete(szDNofSCP, szServiceClass, szServiceAccountDN);
    goto cleanup;
}
 
cleanup:
if (schSCManager)
    CloseServiceHandle(schSCManager);
if (schService)
    CloseServiceHandle(schService);
DsFreeSpnArray(ulSpn, pspn);
return;
}

이전 코드 예제에 대한 자세한 내용은 SCP를 사용하여 서비스에 대한 SPN 작성서비스에 대한 SPN 등록을 참조하세요.