The Delphi XML SAX2 Component & MSXML 3.0        
by Danny Heijl	

Example 1: 
(a) 
HRESULT _stdcall characters(
       [in] unsigned short* pwchChars, 
       [in] int cchChars);

(b) 
HRESULT characters(
       [in] const wchar_t * pwchChars, 
       [in] int cchChars);

(c) 
function  characters(
       var pwchChars: Word; // * WRONG * //
       cchChars: SYSINT): HResult; stdcall;

(d) 
function  characters(
       const pwchChars: pWideChar;
       cchChars: SYSINT): HResult; stdcall;

Example 2: 
 
(a)
type
  TSAXContentHandler = class(TInterfacedObject, ISAXContentHandler)
  public
    function  putDocumentLocator(const pLocator: ISAXLocator):
                                 HResult; stdcall;
    function  startDocument: HResult; stdcall;
    ... other methods ...
  end;

(b)
var
  ContentHandler: TSAXContentHandler;
begin
  Contenthandler := TSAXContentHandler.Create;

Example 3: 
constructor TSAXParser.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FLine := 0;
  FColumn := 0;
  FAttributeList := TSXPAttributeList.Create;
  // create an instance of the Reader COM object
  FReader := CreateComObject(CLASS_SAXXMLReader) as ISAXXMLReader;
  // instantiate the handler classes
  FErrorHandler   := TSAXErrorHandler.Create;
  FLexicalHandler := TSAXLexicalHandler.Create;
  FContenthandler := TSAXContentHandler.Create;
  FDTDHandler     := TSAXDTDHandler.Create;
  FDeclHandler    := TSAXDeclhandler.Create;
  FContentHandler.FDocumentLocator := Nil;
  // Handlers need a reference pointing back to us
  // because they will continually update
  // our Line and Column properties on each event
  FLexicalhandler.SAXParser := Self;
  FContenthandler.SAXParser := Self;
  FDTDHandler.SAXParser := Self;
  FDeclHandler.SAXParser := Self;
  // pass the handler implementations to the reader
  FReader.putErrorHandler(FErrorhandler);
  FReader.putContentHandler(FContentHandler);
  FReader.putDTDHandler(FDTDHandler);
  Freader.putProperty('http://xml.org/sax/properties/lexical-handler',
                      FLexicalhandler as ISAXLexicalHandler);
  Freader.putProperty('http://xml.org/sax/properties/declaration-handler',
                      FDeclhandler as ISAXdeclHandler);
end;

Example 4: 

// OnComment is handled by the SAXLexicalHandler
procedure TSAXParser.SetOnComment(const Value: TOnComment);
begin
  FLexicalHandler.OnComment := Value;
end;
function  TSAXParser.GetOnComment: TOnComment;
begin
  Result := FLexicalHandler.OnComment;
end;
// OnStartDocument is handled by the SAXContentHandler
procedure TSAXParser.SetOnStartDocument(const Value: TOnStartDocument);
begin
  FContentHandler.OnStartDocument := Value;
end;
function TSAXParser.GetOnStartDocument: TOnStartDocument;
begin
  Result := FContentHandler.OnStartDocument;
end;


Listing One
// *********************************************************************//
// Interface: ISAXContentHandler
// Flags:     (16) Hidden
// GUID:      {1545CDFA-9E4E-4497-A8A4-2BF7D0112C44}
// *********************************************************************//
ISAXContentHandler = interface(IUnknown)
['{1545CDFA-9E4E-4497-A8A4-2BF7D0112C44}']
function  putDocumentLocator(
          const pLocator: ISAXLocator): HResult; stdcall;
function  startDocument: HResult; stdcall;
function  endDocument: HResult; stdcall;
function  startPrefixMapping(
          const pwchPrefix: pWideChar;
          cchPrefix: SYSINT;
          const pwchUri: pWideChar;
          cchUri: SYSINT): HResult; stdcall;
function  endPrefixMapping(
          const pwchPrefix: pWideChar;
          cchPrefix: SYSINT): HResult; stdcall;
function  startElement(
          const pwchNamespaceUri: pWideChar;
          cchNamespaceUri: SYSINT;
          const pwchLocalName: pWideChar;
          cchLocalName: SYSINT;
          const pwchQName: pWideChar;
          cchQName: SYSINT;
          const pAttributes: ISAXAttributes): HResult; stdcall;
function  endElement(
          const pwchNamespaceUri: pWideChar;
          cchNamespaceUri: SYSINT;
          const pwchLocalName: pWideChar;
          cchLocalName: SYSINT;
          const pwchQName: pWideChar;
          cchQName: SYSINT): HResult; stdcall;
function  characters(
          const pwchChars: pWideChar;
          cchChars: SYSINT): HResult; stdcall;
function  ignorableWhitespace(
          const pwchChars: pWideChar;
          cchChars: SYSINT): HResult; stdcall;
function  processingInstruction(
          const pwchTarget: pWideChar;
          cchTarget: SYSINT;
          const pwchData: pWideChar;
          cchData: SYSINT): HResult; stdcall;
function  skippedEntity(
          const pwchName: pWideChar;
          cchName: SYSINT): HResult; stdcall;
end;


Listing Two
// ISAXContentHandler.StartElement callback function
function  TSAXContenthandler.startElement(
                         const pwchNamespaceUri: pWideChar;
                         cchNamespaceUri: SYSINT;
                         const pwchLocalName: pWideChar;
                         cchLocalName: SYSINT;
                         const pwchQName: pWideChar;
                         cchQName: SYSINT;
                         const pAttributes: ISAXAttributes): HResult; stdcall;
var
  strNamespaceURI: string;
  strLocalName: string;
  strQName: string;
  Attribute: TSXPAttribute;
  i: integer;
  nAttributes : integer;
  pURI, pLocalName, pQname, pValue, pType: pWideChar;
  URIsize, Localsize, Qsize, Typesize, size: integer;
begin
  if Assigned(FOnStartElement) then begin
    GetLineColumn;
    // convert element name and URI to string
    try
      if pwchNamespaceUri <> Nil then
       strNamespaceURI :=WideCharLenToString(pwchNamespaceUri,cchNamespaceUri)
      else
        strNamespaceURI := '';
      if pwchLocalName <> Nil then
        strLocalName := WideCharLenToString(pwchLocalName, cchLocalName)
      else
        strLocalName := '';
      if pwchQName <> Nil then
        strQName := WideCharLenToString(pwchQName, cchQName)
      else
        strQName := '';
      // build the attribute list
      try
        if pAttributes <> Nil then begin
          with pAttributes do begin
            getLength(nAttributes);
            FAttributeList.Capacity := nAttributes;
            for i := 0 to nAttributes - 1 do begin
              getName(i, pURI, URIsize, pLocalName, Localsize, pQName, Qsize);
              getValue(i, pValue, size);
              getType(i, pType, Typesize);
              Attribute := TSXPAttribute.Create;
              with Attribute do begin
                URI       := WideCharLenToString(pURI, URIsize);
                LocalName := WideCharLenToString(pLocalName, Localsize);
                QName     := WideCharLenToString(pQName, Qsize);
                Value     := WideCharLenToString(pValue, size);
                AttType   := WideCharLenToString(pType, Typesize);
              end;
              FAttributeList.Add(Attribute);
            end; // for
          end; // with
        end; // if pAttributes <> Nil
        // now call the application event handler
        FOnStartElement(strNamespaceUri,strLocalName,strQName,FAttributeList);
      finally
        // clean up the attributes list
        for i := 0 to FAttributeList.Count - 1 do begin
          TSXPAttribute(FAttributeList.Items[i]).Free;
        end;
        FAttributeList.Clear;
      end;
      Result := S_OK;
    except
      Result := E_Fail;
    end;
  end else begin
    Result := S_OK;
  end;
end;


Listing Three
unit filterform;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, MSSAXParser;
type
  TfrmFilter = class(TForm)
    SXP: TSAXParser;
    Memo1: TMemo;
    procedure SXPStartElement(const NamespaceURI, Localname,
      QName: String; const Attributes: TSXPAttributeList);
    procedure SXPEndElement(const NamespaceURI, Localname,
      QName: String);
    procedure SXPCharacters(const Chars: String);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    bWanted: boolean;
    strElement: string;
  end;
var
  frmFilter: TfrmFilter;
implementation
{$R *.DFM}
procedure TfrmFilter.SXPStartElement(const NamespaceURI, Localname,
  QName: String; const Attributes: TSXPAttributeList);
var
  attr: TSXPAttribute;
begin
  if bWanted then begin
    strElement := LocalName;
    exit;
  end;
  if CompareText(Localname, 'book') <> 0 then exit;
  if not Assigned (Attributes) then exit;
  attr := Attributes.GetItem('genre');
  if (attr = Nil) or (CompareText(attr.Value, 'fiction') <> 0) then exit;
  attr := Attributes.GetItem('in_stock');
  if (attr = Nil) or (CompareText(attr.Value, 'yes') <> 0) then exit;
  bWanted := true;
end;
procedure TfrmFilter.SXPEndElement(const NamespaceURI, Localname,
  QName: String);
begin
  if CompareText(LocalName, 'book') = 0 then begin
    if bWanted then
      Memo1.Lines.Add('--------------------------------------------');
    bWanted := false;
  end;
end;
procedure TfrmFilter.SXPCharacters(const Chars: String);
begin
  if bWanted then
    if Trim(Chars) <> '' then
      Memo1.Lines.Add(Format('%-20s %s', [strElement, Chars]));
end;
procedure TfrmFilter.FormCreate(Sender: TObject);
begin
  Memo1.Lines.Add('Fiction books currently in stock :');
  Memo1.Lines.Add('==================================');
  SXP.Parse;
end;
end.


