活动目录是 Windows NT 4.0 和 Windows 2000 使用的目录服务 。要想使用活动目录服务,需要调用 ADSI( 活动目录服务接口 )。ADSI 是一组以 COM 接口的形式提供目录服务的,程序员可以通过 ADSI 存取四种网络目录结构: WinNT (Microsoft SAM 数据库 ) 、 LDAP ( 轻量目录存取协议 ) 、 NDS (NetWare 目录服务 ) 和 NWCOMPAT (Novell NetWare 3.x)。
ADSI 可以使 Windows NT 管理员的工作变得轻松 。ADSI 支持管理员执行一些一般的管理任务,比如添加新用户、管理打印机、安全设定和控制 NT 域 。因为 ADSI 使用 COM 接口,任何支持 COM 的编程语言像 Delphi 、 BCB 、 VB 、 VC 等都可以调用 ADSI。
图 1.111
活动目录运行在 Windows NT 4.0 和 Windows 2000 上 。客户端程序可以运行在 Windows 95 、 Windows 98 、 Windows NT 4.0 和 Windows 2000 上 。为了使用 ADSI,必须安装 ADSI COM 接口 。ADSI 2.5 SDK 可以从 Microsoft ADSI 网址 http://www.microsoft.com/adsi 下载。SDK 包括文档、在线帮助和很多例子,不过不幸的是这些例子都是针对 VB 和 VC 的,这里我们将演示如何使用 Delphi 调用 ADSI。
程序演示
图 1.111 所示的程序演示了如何调用 WinNT provider 提供的功能 。演示程序用来连接到一个域,一旦连接到域,程序将会列出在 PDC 上找到的 NT 的用户和组以及域中的计算机 。同时这个程序还演示了如何察看域中计算机上的服务和察看、添加、删除 NT 组中的用户 。
使用 ADSI 控制 Windows NT/2000
ADSI 可以使我们控制用户、组、计算机、文件共享、打印任务、打印队列和服务等系统资源 。要想在 Delphi 中调用 ADSI,需要引入活动目录类型库,调用菜单 Project | Import Type Library 命令,选择 ActiveDs (Version 1.0) 点确认,Delphi 会生成相应的封装文件 。
绑定 Win NT 目录服务
连接 Win NT 目录服务就是找到域控制器然后绑定到相应的对象上 。绑定可以通过 ADsGetObject 或 ADsOpenObject 函数来实现 。ADsGetObject 函数声明如下:
function ADsGetObject(lpszPathName: PWideChar; const riid: TIID; out obj): HResult; stdcall; external "activeds.dll";
第一个参数是对象的路径名,第二个参数是对象的接口标识符,第三个参数用于返回得到的被请求的接口指针 。缺省条件下,函数根据当前用户进行安全认证 。
ADsOpenObject 函数在不同的安全认证机制下绑定 ADSI 对象,它主要是通过调用参数返回的用户名和口令来认证的 。函数声明如下:
function ADsOpenObject(lpszPathName: PWideChar; lpszUserName: PWideChar; lpszPassWord: PWideChar; dwReserved: LongInt; const riid: TIID; out obj): HResult; stdcall; external "activeds.dll";
第一个参数意义同上,第二、三个参数是调用者提供的用户名和口令,第四个参数是一个保留的 provider 标识,用来确定绑定的认证方法,第五个参数是请求接口的接口标识符,最后一个参数用来返回请求的接口指针 。
第一个函数使用登录用户缺省的信任级别,而第二个函数允许开发者指定特殊的安全信任机制来绑定 ADSI 对象 。下面代码演示了两种不同的绑定方式:
procedure TMainFrm.actOpenWinNTExecute(Sender: TObject);
var
UnknownObject: IUnknown;
DomainPath: WideString;
Domain: IADsContainer;
begin
// 指定域路径
DomainPath := "WinNT://"ADSIDomainName.Text;
// 如果使用用户登录了信息
if cbUseLogin.Checked then
// 使用用户登录的信息创建域对象
OleCheck(AdsOpenObject(PWideChar(DomainPath),
PWideChar(ADSIUsername.Text),
PWideChar(ADSIPassword.Text), 0, IID_IADsContainer,
UnknownObject));
else
OleCheck(ADsGetObject(PWideChar(DomainPath),
IID_IADsContainer, UnknownObject));
// 设定域对象
Domain := UnknownObject as IADsContainer;
// 从域中获得信息列表
GetDomainInformation(Domain);
end;
下面我们需要声明三个变量 :
第一个是接口变量 , 用来绑定由指定的对象路径返回的函数 。
UnknownObject: IUnknown;
第二个参数是 WideString 类型的变量,用来在绑定函数中产生一个对象路径 。
DomainPath: WideString;
第三是一个 IADsContainer 接口类型变量,用来保存返回的接口变量 。
Domain: IADsContainer;
IADsContainer 变量将被用来从指定的 ADSI 对象中获得全部用户、组和计算机 。当然也可以使用 IADsDomain 类型的变量,但它不适合枚举域中的子对象 。
下面指定想要获得的对象路径,如果域名是 "PRISMA",要想获得 ADSI 对象,就需要指定路径为 "WinNT://PRISMA" :
// 设定域名路径
DomainPath := "WinNT://"ADSIDomainName.Text;
下面代码使用不同的安全认证方式:
// 如果使用登录信息
if cbUseLogin.Checked then
// 使用登录并创建域对象
OleCheck(AdsOpenObject(PWideChar(DomainPath),
PWideChar(ADSIUsername.Text),
PWideChar(ADSIPassword.Text), 0, IID_IADsContainer,
UnknownObject));
else
// 创建域对象
OleCheck(ADsGetObject(PWideChar(DomainPath),
IID_IADsContainer, UnknownObject));
然后我们获取 IADsContainer 指针,以便查询域中的子对象:
// 获取域对象
Domain := UnknownObject as IADsContainer;
最后,把 IADsContainer 接口指针作为参数调用来获得域中的子对象 :
GetDomainInformation(Domain);
在域中查找
要想在域中查找子对象,可以使用 GetDomainInformation 过程,代码如下 :
// 获取域信息
procedure TMainFrm.GetDomainInformation(
Domain: IADsContainer);
var
Enum: IEnumVariant;
ADsTempObj: OLEVariant;
ADsObj: IADs;
Value: LongWord;
begin
// 清空用户、组和计算机列表
UserListView.Items.Clear;
GroupListView.Items.Clear;
ComputerListView.Items.Clear;
// 获取枚举对象
Enum := (Domain._NewEnum) as IEnumVariant;
// 利用枚举对象查找
while (Enum.Next(1, ADsTempObj, Value) = S_OK) do begin
// 获得临时对象
ADsObj := IUnknown(ADsTempObj) as IADs;
// 如果是用户对象
if AdsObj.Class_ = "User" then
AddUserToList(ADsObj);
// 如果是组对象
if AdsObj.Class_ = "Group" then
AddGroupToList(ADsObj);
// 如果是计算机对象
if AdsObj.Class_ = "Computer" then
AddComputerToList(ADsObj);
end;
end;
下面是对程序流程的详细说明,首先获得枚举对象,并赋值给 Enum 变量:
Enum := (Domain._NewEnum) as IEnumVariant;
然后利用枚举变量进行查找并把每个子对象赋值给临时的 OLEVariant 对象:
【Delphi深度探索-活动目录开发】while (Enum.Next(1, ADsTempObj, Value) = S_OK) do begin
OLEVariant 变量赋值给 ADSI 对象
ADsObj := IUnknown(ADsTempObj) as IADs;
获得 ADSI 对象后,开始检查对象类,根据对象类别把 ADSI 分别处理,并把不同 ADSI 对象的属性添加到各自的列表视图中去:
// 如果是用户对象
if AdsObj.Class_ = "User" then
AddUserToList(ADsObj);
// 如果是组对象
if AdsObj.Class_ = "Group" then
AddGroupToList(ADsObj);
// 如果是计算机对象
图 1.112
if AdsObj.Class_ = "Computer" then AddComputerToList(ADsObj);
运行结果如 图 1.112 所示 。
创建或删除计算机用户
通过 WinNT provider,可以创建或删除域中任意一台计算机的用户,要想创建一个域中指定的计算机的用户,需要绑定到指定的计算机上 。一旦要绑定到计算机对应的 ADSI 容器对象,就需要调用 Create 方法 。容器对象的 Create 方法需要两个参数,一个是要创建的 ADSI 对象的类别,一个是要用来描述 ADSI 对象的名字 。调用 Create 方法后会返回新的 ADSI 对象的引用参考,下面代码演示了如何创建一个用户:
var
ComputerObj: IADsContainer;
TempUserObj: IUnknown;
UserObj: IADsUser;
PDCName: WideString;
NewUserName: WideString;
AdsPath: WideString;
begin
// 获取用户信息
PDCName := InputBox(" 创建新用户 ", " 请输入域名 : ", "");
NewUserName := InputBox(" 创建新用户 ", " 请输入用户名 : ", "");
// 指定域名路径
AdsPath := "WinNT://"PDCName",computer";
// 创建计算机对象
OleCheck(AdsGetObject(PWideChar(AdsPath),
IID_IADsContainer, ComputerObj));
// 创建新用户
TempUserObj := ComputerObj.Create("user", NewUserName);
UserObj := TempUserObj as IADsUser;
// 设定目录信息
UserObj.SetInfo;
// 刷新列表
actOpenWinNT.Execute;
删除用户也非常类似,只不过不需要创建任何的用户对象,可直接调用容器对象 IADsContainer 接口的 Delete 方法,Delete 方法需要两个参数,第一个是要删除的对象类别,第二个是对象名称,代码示意如下 :
var
ComputerObj: IADsContainer;
PDCName: WideString;
UserName: WideString;
AdsPath: WideString;
begin
// 获取用户信息
PDCName := InputBox(" 删除用户 ", " 请输入域名 ", "");
UserName := InputBox(" 删除用户 ", " 请输入要删除的用户名 : ", "");
if MessageDlg(" 你是否确信要删除用户 : "UserName" ?", mtConfirmation, [mbYes, mbNo], 0) = mrYes then
begin
// 指定域名路径
AdsPath := "WinNT://"PDCName",computer";
// 创建计算机对象
OleCheck(AdsGetObject(PWideChar(AdsPath),
IID_IADsContainer, ComputerObj));
// 删除用户
ComputerObj.Delete("user", UserName);
// 刷新列表
actOpenWinNT.Execute;
end;
end
查看组中的用户
通过 WinNT provider,我们还可以执行各种用户组的维护任务,比如从组中添加或删除用户 。下面就来演示如何枚举组中的用户并察看用户相关信息 。首先让下面代码获得一个 IADsGroup 对象,然后使用 IADsMember 对象来枚举 IADsGroup 对象中所有用户对象,列出不同组中的所有用户:
var
GroupObj: IADsGroup;
Members: IADsMembers;
AdsPath: WideString;
Enum: IEnumVariant;
TempUserObj: OLEVariant;
UserObj: IADsUser;
TempListObj: TListItem;
Value: LongWord;
begin
// 清空列表
GroupListView.Items.Clear;
// 指定域名路径
AdsPath := "WinNT://"MainFrm.ADSIDomainName.Text"/"GroupName;
// 创建组对象
OLECheck(AdsGetObject(PWideChar(AdsPath), IID_IADsGroup,
GroupObj));
// 获得 members 对象
Members := GroupObj.Members;
// 获取枚举对象
Enum := (Members._NewEnum) as IEnumVariant;
// 使用枚举对象进行查找
while (Enum.Next(1, TempUserObj, Value) = S_OK) do
try
// 保存临时对象
UserObj := IUnknown(TempUserObj) as IADsUser;
// 创建新的列表项
TempListObj := GroupListView.Items.Add;
// 指定属性
TempListObj.Caption := UserObj.Name;
except
on E:Exception do
图 1.113
end;
IADsGroup 接口是用来管理 NT 组信息的接口,而它的 Members 属性是一个管理组中用户或 ADSI 对象列表的接口 。最后使用 IADsUser 接口变量 UserObj 来获得 NT 组中的用户信息 。
程序运行结果如图 1.113 所示 。
控制 NT 服务
WinNT provider 还可以用来控制 NT 服务、服务器和工作站,下面的代码遍历 NT 服务,并根据相应的计算机添加服务到相应列表中 :
var
UnknownObject: IUnknown;
Computer: IADsContainer;
ComputerPath: WideString;
Enum: IEnumVariant;
AdsTempObj: OLEVariant;
AdsObj: IADs;
Value: LongWord;
begin
if Item.Caption = "" then
Exit;
// 指定域名路径
ComputerPath := "WinNT://"ADSIDomainName.Text"/"Item.Caption;
// 创建计算机对象
OleCheck(ADsGetObject(PWideChar(ComputerPath),
IID_IADsComputer, UnknownObject));
// 获得计算机容器接口
Computer := UnknownObject as IADsContainer;
// 清空服务视图列表
ServiceListView.Items.Clear;
// 获取枚举对象用于遍历
Enum := (Computer._NewEnum) as IEnumVariant;
// 用枚举对象进行查找
while (Enum.Next(1, ADsTempObj, Value) = S_OK) do begin
// 保存临时对象
ADsObj := IUnknown(ADsTempObj) as IADs;
// 如果对象为服务的话,添加到视图中
if AdsObj.Class_ = "Service" then
AddServiceToList(ADsObj);
end;
end.
要想获得 NT 服务的信息,需要绑定服务对应的 ADSI 对象,ADSI 提供了 IADsService 对象来维护 NT 服务的信息,下面代码演示了如何绑定 NT 服务,并显示服务名:
var
ServiceObj: IADsService;
AdsPath: WideString;
begin
// 指定域名路径
AdsPath := "WinNT://"ComputerName"/"ServiceName;
// 获得服务对象
OLECheck(ADsGetObject(PWideChar(AdsPath),
IID_IADsService, ServiceObj));
// 获得服务名
lblServiceName.Caption := " 服务名 : "ServiceName;
lblDisplayName.Caption := " 服务显示名称 : "
ServiceObj.Get_DisplayName;
要想启动或停止 NT 服务,可以使用 IADsServiceOperations ADSI 对象 。同上面一样需要先绑定到 NT 服务上去,而同前面不一样的是,这回不是获得 IADsService ADSI 对象,而是获得 IADsServiceOperations ADSI 对象:
// 创建计算机对象
OleCheck(AdsGetObject(PWideChar(AdsPath),
IID_IADsServiceOperations, Result));
然后使用 GetServiceObj 函数来绑定到 IADsServiceOperations ADSI 对象上,并返回 IADsServiceOperations 接口 :
// 获得服务对象
ServiceObj := GetServiceObj;
启动服务需要调用 IADsServiceOperations 接口的 Start 方法:
图 1.114
// 启动服务,Get_Status = 1 表明服务正处于停止状态
if ServiceObj.Get_Status = 1 then
ServiceObj.Start;
要停止服务需要调用 IADsServiceOperations ADSI 对象的 Stop 方法 , 运行结果如图 1.114 所示 。
结论
毫无疑问,通过 ADSI 目录服务,调用 WinNT provider 可以更容易实现 NT 的管理功能,但它也是一个庞大的系统,光 Windows 的活动目录编程参考就有上千页,说明我们必须花费大量时间才能真正掌握它的精髓 。
- Windows Vista系统下IE7.0深度测试
- 阴阳师狐妖哪里打
- 凤仪之诏场景探索羽毛怎么解锁
- Vista破解方法深度总结
- 目前共7种方法 Vista破解方法深度总结
- 深度剖析:为什么Windows Vista不受青睐
- 深度剖析:为什么Vista不受青睐?
- 探索个人技能的方法有哪些
- 探索性格的方法有哪些
- 海上钢琴师的深度意义
