Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
-Delphi/Pascal
-CBuilder/C++
-C#Builder/C#
-JBuilder/Java
-Kylix
Member Area
-Home
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Login/Logout
-Become a Member
-Why sign up!
-Newsletter
-Chat Online!
-Indexes NEW!!
Employment
-Build your resume
-Find a job
-Post a job
-Resume Search
Contacts
-Contacts
-Feedbacks
-Link to us
-Privacy/Disclaimer
Embarcadero
Visit Embarcadero
Embarcadero Community
JEDI
Links
Streaming COM Objects as XML Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
12-Nov-02
Category
COM+
Language
Delphi All Versions
Views
171
User Rating
No Votes
# Votes
0
Replies
0
Publisher:
DSP, Administrator
Reference URL:
DKB
			Author: Alessandro Federici

Streaming COM Objects as XML

Answer:

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 
websidehttp://www.msdn.microsoft.com/downloads/default.asp?url=/downloads/sample.asp
?url=/msdn-files/027/000/255/msdncompositedoc.xml. If you don't have Visual Studio 
you can download the DLL from the website 
Compiled.orghttp://www.compiled.org/showcontent.php?cid=118

TlbInf32 includes a set of classes that can help you reading type information for 
both type libaries and COM objects. Now, download my example by clicking 
herehttp://www.msdelphi.com/Articles/COM/Streaming_COM_Objects_to_XML/Streaming_COM_
Objects_to_XML.ZIP. 

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: 

1   ISimpleObject = interface(IDispatch)
2     function Name: WideString[propget, dispid $00000001]; safecall;
3     procedure Name(Value: WideString)[propput, dispid $00000001]; safecall;
4     function Age: Integer[propget, dispid $00000002]; safecall;
5     procedure Age(Value: Integer)[propput, dispid $00000002]; safecall;
6   end;
7   
8   //Then there's a client application that is able to persist and restore the state 
9   of it by using an auxiliary class called TTypeInfoStreamer which is defined as: 
10  
11  TTypeInfoStreamer = class
12  private
13    fTLI: _TLIApplication;
14  public
15    constructor Create;
16  
17    function GetObjectAsXML(const anObject: IDispatch): widestring;
18    procedure SetObjectAsXML(const anObject: IDispatch; aString: widestring);
19  end;
20  
21  //The two methods GetObjectAsXML and SetObjectAsXMl are what interest us. Please, 
22  don't make an example of the code. I put this sample togheter in 10 minutes to 
23  answer a question of a guy in a newsgroup. Take a look at the lines I highlighted: 
24  
25  function TTypeInfoStreamer.GetObjectAsXML(const anObject: IDispatch): widestring;
26  var
27    xml: DOMDocument30;
28    intfinfo: InterfaceInfo;
29    root,
30      node: IXMLDOMNode;
31    i: integer;
32    val: OleVariant;
33    p: PSafeArray;
34  begin
35    p := MakeEmptyParmsArray;
36  
37    try
38      intfinfo := fTLI.InterfaceInfoFromObject(anObject);
39  
40      xml := CoDOMDocument30.Create;
41      xml.async := FALSE;
42  
43      root := xml.createNode(' '');
44      xml.appendChild(root);
45  
46      with intfinfo do
47        for i := 1 to (Members.Count) do
48        begin
49          if not (Members[i].InvokeKind = INVOKE_PROPERTYGET) then
50            Continue;
51  
52          val := fTLI.InvokeHook(anObject, Members[i].Get_MemberId, 
53  INVOKE_PROPERTYGET,
54            p);
55  
56          node := xml.createNode('element', Members[i].Name, '');
57          node.text := VarToStr(val);
58          root.appendChild(node);
59        end;
60  
61    finally
62      result := root.xml;
63      SafeArrayDestroy(p);
64    end;
65  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: 


  Alessandro Federici
  25


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

66  procedure TTypeInfoStreamer.SetObjectAsXML(const anObject: IDispatch;
67    aString: widestring);
68  var
69    xml: DOMDocument30;
70    intfinfo: InterfaceInfo;
71    root,
72      node: IXMLDOMNode;
73    i: integer;
74    val: OleVariant;
75    p: PSafeArray;
76    s: string;
77  begin
78    p := MakeOneElementArray;
79  
80    try
81      intfinfo := fTLI.InterfaceInfoFromObject(anObject);
82  
83      xml := CoDOMDocument30.Create;
84      xml.async := FALSE;
85      xml.loadXML(aString);
86      root := xml.documentElement;
87  
88      with root do
89        for i := 0 to (childNodes.length - 1) do
90        begin
91          s := childNodes[i].nodeName;
92          SetOneElementArray(p, childNodes[i].Text);
93          fTLI.InvokeHook(anObject, s, INVOKE_PROPERTYPUT, p);
94        end;
95  
96    finally
97      SafeArrayDestroy(p);
98    end;
99  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!

			
Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10

 

Advertisement
Share this page
Advertisement
Download from Google

Copyright © Mendozi Enterprises LLC