delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback



Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi








Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (0)


Streaming COM objects as XMLGo to Alessandro Federici's websiteFormat this article printer-friendly!Bookmark function is only available for registered users!
Streaming COM objects as XML
Product:
Delphi 3.x (or higher)
Category:
OLE
Skill Level:
Scoring:
Last Update:
12/17/2001
Search Keys:
delphi delphi3000 article borland vcl code-snippet COM, XML, type library, tlbinf32.dll
Times Scored:
6
Visits:
5591
Uploader: Alessandro Federici
Company: MSDelphi.com
Reference: msdelphi.com
 
Question/Problem/Abstract:
Quick and dirty example on how to read Type Library information and generate XML files out of a COM object
Answer:



Streaming COM Objects as XML

Introduction

When I was making my first COM objects I used to think that I could give my COM classes the same shape of regular Delphi ones. I am specifically talking about properties and a "stateful life-style" in which you keep using its methods similarily to what you'd do with a DataSet (i.e. Open, Next, Next, Close). Well yes, COM allows you to do that and if the objects lives on the client's computer everything works fast and efficiently. The problem araises if and when you move your object to another machine. In that situation then your application suddenly starts to slow down and your client becomes dependent on the network condition. Each time you access a property you are invoking a method that executes on another machine. If your code makes such calls continuosly you will be in big trouble. The ideal solution would be to redesign such objects making them "stateless". With "stateless object" I mean those whose methods do all they say they will do (i.e. ExecuteMoneyTransfer) and don't depend on other methods (i.e. ExecuteMoneyTransfer doesn't expect a Commit or Rollback method to be called by the client after it is completion). But often the amount of legacy code makes a resedign not practical. Is it possible then to do anything to improve performance and makes stateful objects stateless? As you would expect it is possible (no point in writing this article otherwise ;-) ). By persisting the object's state in some kind of intermediary format (I choose XML) and streaming it in one shot you can achieve the goal. Remember that this is not an optimal solution and the code I am presenting here is not optimized either. If you are starting from scratch you should design stateless objects.

TlbInf32.dll

Included in Visual Studio 6 and Visual Basic 6.0 CDs you can find a very handy DLL called TlbInf32. You can download the documentation for this file on the MSDN webside. If you don't have Visual Studio you can download the DLL from the website Compiled.org

TlbInf32 includes a set of classes that can help you reading type information for both type libaries and COM objects.

The code

The example is very straightforward. I won't spend too much time on it. There's a simple COM library with an object which implements the following interface:

ISimpleObject = interface(IDispatch)
  function Name: WideString [propget, dispid $00000001]; safecall;
  procedure Name(Value: WideString) [propput, dispid $00000001]; safecall;
  function Age: Integer [propget, dispid $00000002]; safecall;
  procedure Age(Value: Integer) [propput, dispid $00000002]; safecall;
end;
Then there's a client application that is able to persist and restore the state of it by using an auxiliary class called TTypeInfoStreamer which is defined as:

TTypeInfoStreamer = class
private
       fTLI : _TLIApplication;
public
    constructor Create;

    function GetObjectAsXML(const anObject : IDispatch) : widestring;
    procedure SetObjectAsXML(const anObject : IDispatch; aString : widestring);
end;
The two methods GetObjectAsXML and SetObjectAsXMl are what interest us. Please, don't make an example of the code. I put this sample togheter in 10 minutes to answer a question of a guy in a newsgroup. Take a look at the lines I highlighted:

function TTypeInfoStreamer.GetObjectAsXML(const anObject: IDispatch): widestring;
var xml      : DOMDocument30;
    intfinfo : InterfaceInfo;
    root,
    node     : IXMLDOMNode;
    i        : integer;
    val      : OleVariant;
    p        : PSafeArray;
begin
  p := MakeEmptyParmsArray;
  
  try
    intfinfo := fTLI.InterfaceInfoFromObject(anObject);

    xml := CoDOMDocument30.Create;
    xml.async := FALSE;

    root := xml.createNode('element', intfinfo.Get_Name, '');
    xml.appendChild(root);

    with intfinfo do
      for i := 1 to (Members.Count) do begin
        if not (Members[i].InvokeKind=INVOKE_PROPERTYGET) then Continue;

        val := fTLI.InvokeHook(anObject, Members[i].Get_MemberId, INVOKE_PROPERTYGET, p);

        node := xml.createNode('element', Members[i].Name, '');
        node.text := VarToStr(val);
        root.appendChild(node);
      end;

  finally
    result := root.xml;
    SafeArrayDestroy(p);
  end;
end;
As you can see we created an instance of the TLIApplication object (included in TlbInf32.dll), passed a pointer to the object we want to stream and then looped tough its Members collections. The members collection is the list of methods implemented by the object. What we want to read is the value of the object's properties so we will only stop on the methods that return the value of a property (Members[i].InvokeKind=INVOKE_PROPERTYGET) . In order to invoke the method we need to call the method TLIApplication.InvokeHook which is defined as:

function  InvokeHook(const Object_: IDispatch; ID: OleVariant; InvokeKind: InvokeKinds;
                         var ReverseArgList: PSafeArray): OleVariant; safecall;
It's interesting to note how the ID parameter could be either the name of the method or its DispID. So, in case you have the DispID already, you wouldn't need to use late bound calls (which first invoke the IDispatch.GetIDOfNames method slowing things down a *lot*). InvokeKind tells the TLIApplication *how* to invoke it and finally the ReverseArgList is a safe array that in our case only contains no values. See the rest of the code to find out how I build one. The result is something like this:

<ISimpleObject>

  <Name>Alessandro Federici</Name>
  <Age>25</Age>
</ISimpleObject>


Et voila'! We have our COM object persisted into XML! Now we need to set back these values. See the code below.

procedure TTypeInfoStreamer.SetObjectAsXML(const anObject: IDispatch;
  aString: widestring);
var xml      : DOMDocument30;
    intfinfo : InterfaceInfo;
    root,
    node     : IXMLDOMNode;
    i        : integer;
    val      : OleVariant;
    p        : PSafeArray;
    s : string;
begin
  p := MakeOneElementArray;

  try
    intfinfo := fTLI.InterfaceInfoFromObject(anObject);

    xml := CoDOMDocument30.Create;
    xml.async := FALSE;
    xml.loadXML(aString);
    root := xml.documentElement;

    with root do
      for i := 0 to (childNodes.length-1) do begin
        s := childNodes[i].nodeName;
        SetOneElementArray(p, childNodes[i].Text);
        fTLI.InvokeHook(anObject, s, INVOKE_PROPERTYPUT, p);
      end;

  finally
    SafeArrayDestroy(p);
  end;
end;
As you can see we did the exact opposite of what the had done before except that in this case we invoked the method as a property writer. I hope this will demistify a little how to read COM type information and stream its contents in an arbitraty format. The TTypeInfoStreamer class is far from being a complete class but feel free to use the code as a start. Happy coding!


Alessandro Federici

System Architect
Borland Certified Consultant
-----
http://www.msdelphi.com  (home of the DSOAP Toolkit 2.0)

alef@msdelphi.com (primary)
alef@bigfoot.com (routes to the first)
kingalef@hotmail.com (if you have troubles with the others)





Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment













 
Sign up to consume product discounts for Bronze memberships !

read more


   


  Community Ad of
S. Carter
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)