Share via


Installieren eines Diensts auf einem Hostcomputer

Das folgende Codebeispiel zeigt die grundlegenden Schritte zum Installieren eines verzeichnisfähigen Diensts auf einem Hostcomputer. Es führt die folgenden Vorgänge aus:

  1. Ruft die OpenSCManager-Funktion auf, um ein Handle für den Dienststeuerungs-Manager (SCM) auf dem lokalen Computer zu öffnen.
  2. Ruft die CreateService-Funktion auf, um den Dienst in der SCM-Datenbank zu installieren. Dieser Aufruf gibt das Anmeldekonto und das Kennwort des Diensts sowie die ausführbare Datei des Diensts und andere Informationen zum Dienst an. CreateService schlägt fehl, wenn das angegebene Anmeldekonto ungültig ist. CreateService überprüft jedoch nicht die Gültigkeit des Kennworts. Außerdem wird nicht überprüft, ob das Konto über das Anmelde-als-Dienst-Recht auf dem lokalen Computer verfügt. Weitere Informationen finden Sie unter Gewähren der Anmeldung als Dienstrecht auf dem Hostcomputer.
  3. Ruft die ScpCreate-Unterroutine des Diensts auf, die ein Dienstverbindungspunktobjekt (Service Connection Point Object, SCP) im Verzeichnis erstellt, um den Speicherort dieses instance des Diensts zu veröffentlichen. Weitere Informationen finden Sie unter Suchen und Verwenden eines Dienstverbindungspunkts durch Clients. Diese Routine speichert auch die Bindungsinformationen des Diensts im SCP, legt eine ACE für den SCP fest, damit der Dienst zur Laufzeit darauf zugreifen kann, speichert den distinguished Name des SCP in der lokalen Registrierung zwischen und gibt den distinguished Name des neuen SCP zurück.
  4. Ruft die SpnCompose-Unterroutine des Diensts auf, die die Klassenzeichenfolge des Diensts und den distinguished Name des SCP verwendet, um einen Dienstprinzipalnamen (Service Principal Name, SPN) zu erstellen. Weitere Informationen finden Sie unter Erstellen der SPNs für einen Dienst mit einem SCP. Der SPN identifiziert diese instance des Diensts eindeutig.
  5. Ruft die SpnRegister-Unterroutine des Diensts auf, die den SPN für das Kontoobjekt registriert, das dem Anmeldekonto des Diensts zugeordnet ist. Weitere Informationen finden Sie unter Registrieren der SPNs für einen Dienst. Durch die Registrierung des SPN können Clientanwendungen den Dienst authentifizieren.

Dieses Codebeispiel funktioniert ordnungsgemäß, unabhängig davon, ob es sich bei dem Anmeldekonto um ein lokales Benutzerkonto oder ein Domänenbenutzerkonto oder um das LocalSystem-Konto handelt. Für ein Domänenbenutzerkonto enthält der Parameter szServiceAccountSAM denDomänenbenutzernamen**\** des Kontos, und der Parameter szServiceAccountDN enthält den distinguished Name des Benutzerkontoobjekts im Verzeichnis. Für das LocalSystem-Konto sind szServiceAccountSAM und szPasswordNULL, und szServiceAccountSN ist der distinguished Name des Kontoobjekts des lokalen Computers im Verzeichnis. Wenn szServiceAccountSAM ein lokales Benutzerkonto angibt (Namensformat ist ".\UserName"), überspringt das Codebeispiel die SPN-Registrierung, da die gegenseitige Authentifizierung für lokale Benutzerkonten nicht unterstützt wird.

Beachten Sie, dass die Standardsicherheitskonfiguration es nur Domänenadministratoren ermöglicht, diesen Code auszuführen.

Beachten Sie außerdem, dass dieses Codebeispiel wie geschrieben auf dem Computer ausgeführt werden muss, auf dem der Dienst installiert wird. Daher befindet es sich in der Regel in einer separaten ausführbaren Installationsdatei von Ihrem Dienstinstallationscode, sofern vorhanden, das Schema erweitert, die Benutzeroberfläche erweitert oder eine Gruppenrichtlinie einrichtest. Bei diesen Vorgängen werden Dienstkomponenten für eine gesamte Gesamtstruktur installiert, während mit diesem Code der Dienst auf einem einzelnen Computer installiert wird.

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;
}

Weitere Informationen zum vorherigen Codebeispiel finden Sie unter Erstellen der SPNs für einen Dienst mit einem SCP und Registrieren der SPNs für einen Dienst.