If you are going to use the Active Directory Service Interface (ADSI) in Delphi, the first thing you will need is the typelibrary (TLB). This TLB is in the windows\system32 folder and has the name activeds.tlb.
We can import this tlb in Delphi (the procedure differs somewhat, depending on the Delphi version), but there are quite some problems with the resulting pas file of this import:
- Ugly and non meaningfull names such as __MIDL___MIDL_itf_ads_0000_0000_001.
- Record sizes are sometimes (read: usually) wrong due to alignment errors.
- Some Interfaces have wrong declarations resulting in Access Violations or just hard to use (eg using var for input parameters).
- Delphi works with Typed Pointer but since it has no clue on the proper name it uses PUserTypexx (eg PUserType1 = ^_ADS_CASEIGNORE_LIST). It’s hard to recognise later on what the real type is.
A version of the imported tlb is also in the Jedi Apilib (JwaAdsTLB) and basically it had the same errors. Because I was wondering how this would work in c++ I checked the SDK and found the header file Iads.h.
If you open Iads.h you can see that this header file is generated by the MIDL compiler from a file called ads.odl with the following settings:
/* Compiler settings for ads.odl:
Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 7.00.0555
Zp8 means alignment on 8 byte boundaries which Delphi does not seem to support.
I decided to convert Iads.h and merge this with the imported tlb.
Here are some examples of the corrections:
Imported Tlb:
// *********************************************************************//
// Interface: IDirectoryObject
// Flags: (0)
// GUID: {E798DE2C-22E4-11D0-84FE-00C04FD8D503}
// *********************************************************************//
IDirectoryObject = interface(IUnknown)
[‘{E798DE2C-22E4-11D0-84FE-00C04FD8D503}’]
function GetObjectInformation(out ppObjInfo: PUserType11): HResult; stdcall;
function GetObjectAttributes(var pAttributeNames: PWideChar; dwNumberAttributes: LongWord;
out ppAttributeEntries: PUserType12;
out pdwNumAttributesReturned: LongWord): HResult; stdcall;
function SetObjectAttributes(var pAttributeEntries: _ads_attr_info; dwNumAttributes: LongWord;
out pdwNumAttributesModified: LongWord): HResult; stdcall;
function CreateDSObject(pszRDNName: PWideChar; var pAttributeEntries: _ads_attr_info;
dwNumAttributes: LongWord; out ppObject: IDispatch): HResult; stdcall;
function DeleteDSObject(pszRDNName: PWideChar): HResult; stdcall;
end;
JwaAdsTLB:
// *********************************************************************//
// Interface: IDirectoryObject
// Flags: (0)
// GUID: {E798DE2C-22E4-11D0-84FE-00C04FD8D503}
// *********************************************************************//
IDirectoryObject = interface(IUnknown)
[‘{E798DE2C-22E4-11D0-84FE-00C04FD8D503}’]
function GetObjectInformation(var ppObjInfo: PADS_OBJECT_INFO): HResult; stdcall;
function GetObjectAttributes(pAttributeNames: PWideChar; dwNumberAttributes: LongWord;
out ppAttributeEntries: PADS_ATTR_INFO;
out pdwNumAttributesReturned: LongWord): HResult; stdcall;
function SetObjectAttributes(pAttributeEntries: PADS_ATTR_INFO; dwNumAttributes: LongWord;
out pdwNumAttributesModified: DWORD): HResult; stdcall;
function CreateDSObject(pszRDNName: PWideChar; pAttributeEntries: PADS_ATTR_INFO;
dwNumAttributes: LongWord; out ppObject: IDispatch): HResult; stdcall;
function DeleteDSObject(pszRDNName: PWideChar): HResult; stdcall;
end;
Imported Tlb:
__MIDL___MIDL_itf_ads_0000_0000_0017 = record
case Integer of
0: (DNString: PWideChar);
1: (CaseExactString: PWideChar);
2: (CaseIgnoreString: PWideChar);
3: (PrintableString: PWideChar);
4: (NumericString: PWideChar);
5: (Boolean: LongWord);
6: (Integer: LongWord);
7: (OctetString: ADS_OCTET_STRING);
8: (UTCTime: _SYSTEMTIME);
9: (LargeInteger: _LARGE_INTEGER);
10: (_className: PWideChar);
11: (ProviderSpecific: ADS_PROV_SPECIFIC);
12: (pCaseIgnoreList: ^_ADS_CASEIGNORE_LIST);
13: (pOctetList: ^_ADS_OCTET_LIST);
14: (pPath: ^__MIDL___MIDL_itf_ads_0000_0000_0005);
15: (pPostalAddress: ^__MIDL___MIDL_itf_ads_0000_0000_0006);
16: (Timestamp: ADS_TIMESTAMP);
17: (BackLink: ADS_BACKLINK);
18: (pTypedName: ^__MIDL___MIDL_itf_ads_0000_0000_0009);
19: (Hold: ADS_HOLD);
20: (pNetAddress: ^__MIDL___MIDL_itf_ads_0000_0000_0011);
21: (pReplicaPointer: ^__MIDL___MIDL_itf_ads_0000_0000_0012);
22: (pFaxNumber: ^__MIDL___MIDL_itf_ads_0000_0000_0013);
23: (Email: ADS_EMAIL);
24: (SecurityDescriptor: ADS_NT_SECURITY_DESCRIPTOR);
25: (pDNWithBinary: ^__MIDL___MIDL_itf_ads_0000_0000_0015);
26: (pDNWithString: ^__MIDL___MIDL_itf_ads_0000_0000_0016);
end;
JwaAdsTLB:
ADSVALUE = packed record
case Byte of // Padding
0: (
dwType: ADSTYPE;
case _Padding1: ADSTYPE of // Padding
ADSTYPE_DN_STRING: (DNString: ADS_DN_STRING);
ADSTYPE_CASE_EXACT_STRING: (CaseExactString: ADS_CASE_EXACT_STRING);
ADSTYPE_CASE_IGNORE_STRING: (CaseIgnoreString: ADS_CASE_IGNORE_STRING);
ADSTYPE_PRINTABLE_STRING: (PrintableString: ADS_PRINTABLE_STRING);
ADSTYPE_NUMERIC_STRING: (NumericString: ADS_NUMERIC_STRING);
ADSTYPE_BOOLEAN: (Boolean: ADS_BOOLEAN);
ADSTYPE_INTEGER: (Integer: ADS_INTEGER);
ADSTYPE_OCTET_STRING: (OctetString: ADS_OCTET_STRING);
ADSTYPE_UTC_TIME: (UTCTime: ADS_UTC_TIME);
ADSTYPE_LARGE_INTEGER: (LargeInteger: ADS_LARGE_INTEGER);
ADSTYPE_OBJECT_CLASS: (ClassName: ADS_PROV_SPECIFIC);
ADSTYPE_PROV_SPECIFIC: (ProviderSpecific: ADS_PROV_SPECIFIC);
ADSTYPE_CASEIGNORE_LIST: (pCaseIgnoreList: PADS_CASEIGNORE_LIST);
ADSTYPE_OCTET_LIST: (pOctetList: PADS_OCTET_LIST);
ADSTYPE_PATH: (pPath: PADS_PATH);
ADSTYPE_POSTALADDRESS: (pPostalAddress: PADS_POSTALADDRESS);
ADSTYPE_TIMESTAMP: (Timestamp: ADS_TIMESTAMP);
ADSTYPE_BACKLINK: (BackLink: ADS_BACKLINK);
ADSTYPE_TYPEDNAME: (pTypedName: PADS_TYPEDNAME);
ADSTYPE_HOLD: (Hold: ADS_HOLD);
ADSTYPE_NETADDRESS: (pNetAddress: PADS_NETADDRESS);
ADSTYPE_REPLICAPOINTER: (pReplicaPointer: PADS_REPLICAPOINTER);
ADSTYPE_FAXNUMBER: (pFaxNumber: PADS_FAXNUMBER);
ADSTYPE_EMAIL: (Email: ADS_EMAIL);
ADSTYPE_NT_SECURITY_DESCRIPTOR: (SecurityDescriptor: ADS_NT_SECURITY_DESCRIPTOR);
ADSTYPE_DN_WITH_BINARY: (pDNWithBinary: PADS_DN_WITH_BINARY);
ADSTYPE_DN_WITH_STRING: (pDNWithString: PADS_DN_WITH_STRING)
);
end;
_adsvalue = ADSVALUE;
PADSVALUE = ^ADSVALUE;
LPADSVALUE = PADSVALUE;
If you are looking for the file: I commited it to the trunk as JwaAdsTLB.pas.
Related posts:
- Query Active Directory from Excel
- Executing a Fast User Switch programmatically – part 1
- Executing a Fast User Switch programmatically – part 2
- Locking a workstation – part 1
- Reading accountExpires attribute from Active Directory (in Delphi)
Please consider donating something (even a small amount is ok) to support this site and my work:
Filed under: Active Directory, Delphi, Programming
One Response for "Random Active Directory Notes #1"
Just wondering if you had got any further with this?
Leave a reply