2009. szeptember 5., szombat
Reading the IIS Meta Base
Problem/Question/Abstract:
In this article I am going to show you how to how to access to IIS Meta Base (in read-only mode). You can simply take this further to use the techniques shown here for administrative purposes. The IIS Meta Base is used to install web, ftp and gopher sites on your MS Windows server. This feature may be interesting for your installer applications.
You need MS Windows 2000 with IIS or MS Windows NT 4 SP 6a with IIS installed.
Answer:
IN THE BEGINNING
Many of you develop, just as I do, Internet server applications. After weeks of planning and testing your applications you are ready to deploy it. Now you have to write a detailed explaination of how to install and administrate your application. At this point this article will give you a little head start. Windows has the Registry, which is a great tool for administrating many aspects of the computer, however, not all aspects are administratable through the Registry.
The IIS has to be administrated through the IIS Metabase. In your {system32}\inetsrv\iisadmin folder there are many ASP examples on how to access the IIS Metabase, however, these are not easily taken to Delphi. Starting with the GetObject function, that does not exist in Delphi, going to enumarations and so on.
VB SCRIPTS GetObject
Scanning through the ASP files in the IISAdmin folder you will hit the GetObject function quite a few times. The GetObject will return the interface to an object already loaded into the computers memory. The object is named by a string similar to 'IIS://localhost'. GetObject will allow you to get access to objects running on other computers, too. In one of the D3K articles I have found a method that will compromise for this VB Script function. I have named the function VBGetObject, because as a function with this name already exists in Delphi.
function VBGetObject(const Name: string): IDispatch;
var
BindContext: IBindCtx;
Moniker: IMoniker;
Eaten: Integer;
begin
OleCheck(CreateBindCtx(0, BindContext));
OleCheck(MkParseDisplayName(
BindContext, PWideChar(WideString(Name)), Eaten, Moniker
));
OleCheck(Moniker.BindToObject(BindContext, nil, IDispatch, Result));
end;
ENUMERATIONS (COLLECTIONS) IN DELPHI
VBScript has the nice construct for each ... in ..., a simple and fast way to access all objects (items) in an list ("array") of those. There is no similar construct in Delphi to use, well you want need it anyway, we'll work around it. :)
Usually all enumerations have a count property and an items property as well, however Micrsoft decided to NOT implement these in these ADSI classes properly. Therefore, we cannot us them in an for I := 0 to Pred(Count) do type of construction. We rather have to access the enumeration object and simulate the for...each loop ourselves.
procedure DoEnum(Cont: IADsContainer[...]);
var
I: Cardinal;
Enum: IEnumVariant;
rgvar: OleVariant;
[...]
begin
try
// get a hold on the variant collection
Enum := Cont._NewEnum as IEnumVariant;
Enum.Reset;
Enum.Next(1, rgvar, I);
// enumerate the variant collection
while I > 0 do
begin
[...]
Enum.Next(1, rgvar, I);
end;
except
end;
end;
The function above is taken from the source below with a few parts omitted to show the basic idea of enumerations. First we get the Enumeration object and cast it as IEnumVariant, the default VB Script enumeration type. Next, we reset the enumeration, just in case and then we get the first item for the enumeration. We loop through the enumeration until no item is returned anymore. That's all.
CREATING THE APPLICATION
Start Delphi and create a new application (in case another is still open). The following code will assume a few component names, please add them accordingly:
your main form: NAME=frmMain
TTreeView: NAME=trvMBStructure ALIGN=alLeft
TListView: NAME=lstMBItems ALIGN=alClient VIEWSTYLE=vsReport, add three Columns to the list CAPTIONS=(Property,Type,Value)
TStatusBar: NAME=sttInfo SIMPLEPANEL=True
Save the Unit1 as uMainForm.pas.
Next go to the menu "Project|Import Type Library...". (NOTE: This step may not work on Delphi 6 properly - sorry, you will have to wait for the first service pack. :( ) Click the "Add.." button and select the "activeds.tlb" from your "Winnt\System32" directory. Select a unit directory and click the "Create Unit" button. The file "ActiveDs_TLB.pas" will be created.
Next paste the code from below and run your application. I hope the comments will give you all information you need.
THE CODE
unit uMainForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, ComCtrls, ActiveDs_TLB, Contnrs;
type
TADsContainer = class
private
FIntf: IADsContainer;
FPath: string;
FProperties: TStringList;
function GetADsClass: IADsClass;
procedure LoadProperties;
protected
public
constructor Create(aIntf: IADsContainer);
destructor Destroy; override;
property Path: string read FPath;
property Intf: IADsContainer read FIntf;
property ADsClass: IADsClass read GetADsClass;
property Properties: TStringList read FProperties;
end;
TfrmMain = class(TForm)
trvMBStructure: TTreeView;
splDummy: TSplitter;
lstMBItems: TListView;
sttInfo: TStatusBar;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure trvMBStructureChange(Sender: TObject; Node: TTreeNode);
private
FADsContainer: TObjectList;
procedure EnumIISMetaBase;
procedure ShowItemInfo(ADsContainer: TADsContainer);
public
end;
var
frmMain: TfrmMain;
implementation
uses
ActiveX, ComObj;
{$R *.DFM}
// simulates VB Scripts GetObject - a method to get an instance to an already
// loaded object in memory
function VBGetObject(const Name: string): IDispatch;
var
BindContext: IBindCtx;
Moniker: IMoniker;
Eaten: Integer;
begin
OleCheck(CreateBindCtx(0, BindContext));
OleCheck(MkParseDisplayName(
BindContext, PWideChar(WideString(Name)), Eaten, Moniker
));
OleCheck(Moniker.BindToObject(BindContext, nil, IDispatch, Result));
end;
{ TADsContainer }
constructor TADsContainer.Create(aIntf: IADsContainer);
begin
inherited Create;
FIntf := aIntf;
FProperties := TStringList.Create;
FPath := IADsSyntaxDisp(FIntf).ADsPath;
LoadProperties;
end;
destructor TADsContainer.Destroy;
begin
FreeAndNil(FProperties);
FIntf := nil;
inherited Destroy;
end;
function TADsContainer.GetADsClass: IADsClass;
begin
Result := VBGetObject(IADsODisp(FIntf).Schema) as IADsClass;
end;
procedure TADsContainer.LoadProperties;
var
I: Integer;
Props: OleVariant;
begin
// iis objects can have mandatory and optional properties
// the must be loaded seperately
// the IADS objects will return a safe-array if there are more than one
// properties, a OleString will be returned if there is just one property
FProperties.Clear;
// load mandatory properties
Props := ADsClass.MandatoryProperties;
if VarType(Props) and varArray = varArray then
for I := VarArrayLowBound(Props, 1) to VarArrayHighBound(Props, 1)
do
FProperties.Add(Props[I])
else
FProperties.Add(Props);
// load optional properties
Props := ADsClass.OptionalProperties;
if VarType(Props) and varArray = varArray then
for I := VarArrayLowBound(Props, 1) to VarArrayHighBound(Props, 1)
do
FProperties.Add(Props[I])
else
FProperties.Add(Props);
end;
{ TfrmMain }
procedure TfrmMain.EnumIISMetaBase;
procedure DoEnum(Cont: IADsContainer; Parent: TTreeNode; Path: string);
var
I: Cardinal;
Enum: IEnumVariant;
rgvar: OleVariant;
Node: TTreeNode;
ADsContainer: TADsContainer;
begin
try
// get a hold on the variant collection
Enum := Cont._NewEnum as IEnumVariant;
Enum.Reset;
Enum.Next(1, rgvar, I);
// enumerate the variant collection
while I > 0 do
begin
// create a tree node for every item in the collection
Node := trvMBStructure.Items.AddChild(Parent, rgvar.Name);
ADsContainer := TADsContainer.Create(IDispatch(rgvar) as IADsContainer);
FADsContainer.Add(ADsContainer);
Node.Data := ADsContainer;
// enumerate sub-items
DoEnum(ADsContainer.Intf, Node, ADsContainer.Path);
Enum.Next(1, rgvar, I);
end;
except
end;
end;
var
Root: string;
begin
trvMBStructure.Items.BeginUpdate;
try
// clear previous
trvMBStructure.Items.Clear;
FADsContainer.Clear;
// you could enumerate other objects, like LDAP, too
Root := 'IIS://LocalHost';
DoEnum(VBGetObject(Root) as IADsContainer, nil, Root);
finally
trvMBStructure.Items.EndUpdate;
end;
end;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
FADsContainer := TObjectList.Create;
// load the iis meta base
EnumIISMetaBase;
end;
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
FreeAndNil(FADsContainer);
end;
procedure TfrmMain.ShowItemInfo(ADsContainer: TADsContainer);
var
I: Integer;
PropName: string;
LI: TListItem;
begin
lstMBItems.Items.BeginUpdate;
try
lstMBItems.Items.Clear;
if ADsContainer <> nil then
begin
// show current iis path
sttInfo.SimpleText := ADsContainer.Path;
// iterate all properties, skip the first ('')
for I := 1 to Pred(ADsContainer.Properties.Count) do
begin
LI := lstMBItems.Items.Add;
// get the property name
PropName := ADsContainer.Properties.Strings[I];
// load property name
LI.Caption := PropName;
// get property type
LI.SubItems.Add('0x' + IntToHex(VarType(
IADsDisp(ADsContainer.Intf).Get(PropName)
), 8));
// get property value
case VarType(IADsDisp(ADsContainer.Intf).Get(PropName)) of
varEmpty:
LI.SubItems.Add('(value is empty)');
varNull:
LI.SubItems.Add('(value is null)');
varSmallint, varInteger, varSingle, varDouble, varCurrency,
varDate, varOleStr, varBoolean:
LI.SubItems.Add(IADsDisp(ADsContainer.Intf).Get(PropName));
else
LI.SubItems.Add('(data type not handled)');
end;
end;
end
else
begin
sttInfo.SimpleText := '';
end;
finally
lstMBItems.Items.EndUpdate;
end;
end;
procedure TfrmMain.trvMBStructureChange(Sender: TObject; Node: TTreeNode);
begin
if Node = nil then
ShowItemInfo(nil)
else
ShowItemInfo(TADsContainer(Node.Data));
end;
end.
Feliratkozás:
Megjegyzések küldése (Atom)
Nincsenek megjegyzések:
Megjegyzés küldése