在主電腦上安裝服務
下列程式代碼範例示範在主計算機上安裝已啟用目錄的服務的基本步驟。 它會執行下列作業:
- 呼叫 OpenSCManager 函式,以開啟本機電腦上的服務控制管理員 (SCM) 句柄。
- 呼叫 CreateService 函式,以在 SCM 資料庫中安裝服務。 此呼叫會指定服務的登入帳戶和密碼,以及服務的可執行文件和服務的其他相關信息。 如果指定的登入賬戶無效,CreateService 就會失敗。 不過, CreateService 不會檢查密碼的有效性。 它也不會驗證帳戶在本機計算機上具有登入即服務。 如需詳細資訊,請參閱 授與主計算機上的登入即服務許可權。
- 呼叫服務的 ScpCreate 子程式,以在目錄中建立服務連接點物件 (SCP) 來發佈此服務實例的位置。 如需詳細資訊,請參閱用戶端如何尋找和使用服務 連線 點。 此例程也會將服務的系結資訊儲存在 SCP 中、在 SCP 上設定 ACE,讓服務可以在運行時間存取它、在本機登錄中快取 SCP 的辨別名稱,並傳回新 SCP 的辨別名稱。
- 呼叫服務的 SpnCompose 子程式,該子程式會使用服務的類別字串和 SCP 的辨別名稱來撰寫服務主體名稱(SPN)。 如需詳細資訊,請參閱 使用 SCP 撰寫服務的 SPN。 SPN 可唯一識別服務的這個實例。
- 呼叫服務的 SpnRegister 子程式,在與服務登入帳戶相關聯的帳戶對象上註冊 SPN。 如需詳細資訊,請參閱 註冊服務的SPN。 SPN 的註冊可讓用戶端應用程式驗證服務。
不論登入帳戶是本機或網域用戶帳戶還是 LocalSystem 帳戶,此程式代碼範例都能正確運作。 針對網域用戶帳戶, szServiceAccountSAM 參數包含 帳戶的 Domain**\**UserName 名稱,而 szServiceAccountDN 參數則包含目錄中用戶帳戶對象的辨別名稱。 針對 LocalSystem 帳戶,szServiceAccountSAM 和 szPassword 為 NULL,而 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。