2005. május 9., hétfő

Introduction to COM


Problem/Question/Abstract:

Introduction to COM

Answer:

Introduction

A few months ago, while I was looking for my first house, I spent quite a lot of time with a real estate agent which taught me the three most important elements in business marketing: location, location, location.

Well, in the Component Object Model (COM) this can be translated in integration, integration, integration while location is ideally the last thing you are interested in. Every Windows user deals with COM every day, knowing it or not. COM is used by Microsoft Office when we run the spell check utility, is used by many web sites running IIS and is also used by the underlying operating system for some of its mundane tasks. Many others instead choose COM to build complex, scalable and secure enterprise systems.

Component oriented integration is what COM is all about.

Intergration yesterday

The general meaning of the word integration is "to make into a whole by bringing all parts together; unify". In our field, this can be done in many ways and can be applied to many things: imagine you are developing a word processor application. You will create or inherit a custom memo box for editing purposes, you may include a spell checker and, if you want to get fancy, you may also want to include a set of custom routines that will allow your users to convert the document to HTML or RTF. The integration of all these parts will make your word processor.

Now, imagine that for some reasons, you want to be able to update any of these single elements without redeploying the whole application. You may also want to make those components available to a second application, maybe developed by somebody else in another language. These capabilities are very common today.

Todays applications are much bigger than they used to be few years ago and anything that can help managing this complexity is welcome.

The first approach you may try is using DLLs. Dynamic linking is the ability to bind and invoke executable code at runtime. In that hypothetical word processor, the DLL that includes the conversion routines may export functions such as:

function GetConverters: TConverterList;
  procedure Convert(anID: integer; aDocument, aFileName: string);

TConverter = record
  ID: integer;
  Name, FileExtension, Description: string;
end;

Any time we want to add a converter you'd only have to update the DLL and the word processor would automatically have access to the new functionality. This works fine and you will achieve what your objective. GetConverters returns a custom TList which may have some methods in pure Delphi style that make it very handy and easy to use (a TConverterList). Unfortunately this is not an optimal solution and, worst of all, it doesn't work unless you are using Delphi or Borland C++ Builder... In order to use the result of the function GetConverters pointer as a TConverterList, the client needs to know what a TConverterList is. In order to do this, we need to inject into our client that information but still, even if we do this we'd have a problem with non-Borland compilers. TList is a VCL class. It is not included for instance in Microsoft Visual C++ or Visual Basic. Those developers couldn't benefit in any way from the pointer we return.You could have structured your DLL differently, following for instance the approach of the Windows API EnumWindows which takes a pointer to a call back routine. Another solution could have been exporting more functions. Whichever approach you may chose you'd still be confined in a word of simple data types which is everything but object oriented and, in top of that, the DLL has to be run on the client's computer... COM is one of the technologies that help us to solving some of those issues.

Integration, today

COM has a long story. Officially the acronym COM was born somewhere around 1993. We can trace COM roots back to Windows 3.x where DDE and OLE were used in Microsoft Word and Excel as a sort of rudimentary communication and inter operability glue. Today COM is everywhere on the Windows platform. Small applications such as ICQ, CuteFTP or Allaire HomeSite are accessible through COM. Applications suites such as Microsoft Office are based on it. Windows based enterprise systems leverage COM and Microsoft Transaction Server for business critical operations. If you develop on Windows you will have to face COM sooner or later. The faster you'll do it, the better it will be. This article is about understanding COM and the reasons behind it rather than an providing another how-to tutorial. I will start with the basic principles behind COM and then I provide a concrete example. The first part won't take long but will definitely give you a better understanding of what happens in the example and why that happens. Make sure you download the sample by clicking here. The factors that lead to COM are the followings: Object Oriented language independence Dynamic Linking Location independence.

Object Oriented language independence

The DLL example above had a serious problem: in order for functions to return an object, the client has to know its interface. An interface is a very important concept in both object oriented programming and COM. An interface is the declaration of all the public methods and properties of a class. Without knowing it, that pointer could be anything and the compiler wouldn't know how to find the correct method addresses, the parameters and result types of them, etc. Does COM allow me to return objects without knowing their interface? No, although it may look that way in some cases. The concept of interface is the heart of COM. In order to be language independent, COM defines a binary standard for interface definition and introduces the concept of type libraries. Type libraries are binary files that contain information about a numbers of interfaces (you define how many you want to declare and what you want them to look like). From inside Delphi, open the type library COMConverter.tlb contained in the COMConverter directory. You will see the following window opening:  



This is the Borland type library editor which allows us to look and edit COM type libraries. As you can see, this type library defines the interface IConverterList which contains the properties Count and Items in perfect Delphi style. Delphi knows how to interpret type libraries and through the type libray editor, presents them in a user- friendly fashion. Visual Basic, Borland C++ Builder or Visual C++ do the same. They all agreed to support the COM binary standard and to play according to its rules. Now, from within the type library editor, press F12. Delphi will create a unit named COMConverter_TLB.pas  



Through COM I can define an interface that I am sure other COM enabled languages can understand. Delphi will use that information to generate interfaces that it can understand and use, as we just saw. It's all there and ready to be used now, almost as it was a regular Delphi object. There are some key differences but for now, let's continue with the principles.

Dynamic Linking

Similarly to DLLs, COM allows (and actually only works through) dynamic linking. You can choose to take advantage of this in two ways: early binding or late binding. Before we continue there's an important thing you need to do: register your COM library. Registration is the process through which Windows becomes aware of a COM object and learns how to instantiate it. In order to do this you need to use a special tool called RegSvr32.exe (contained in Windows\System32) or the Borland's equivalent TRegSvr.exe (contained in Program Files\Borland\Delphi5\Bin). Another way of doing it, when you have the Delphi source code, is to open the COM project (in our case COMConverter.dpr) and press Run\Register ActiveX Server. Registering a COM server means inserting special keys into the Windows registry. The information you will store include the name of the DLL or EXE file that hosts your COM object, the identifiers that uniquely identify it (see the yellow on green code above) and a few extra things. If you don't do this Windows won't be able to instantiate your COM object. By continuing our analogy with DLLs, early binding is similar to importing routines from a DLL by using the external directive. When you do that, you embed in your client the definition of those routines and you expect them to match exactly that definition when you connect to them at runtime. If the name, the parameters or the result type is changed, you will have an error as soon as the application starts. Late binding instead is similar to the GetProcAddress API call, where you specify the name of the function you want to connect to using a string and you get back a pointer to it. When you do that, your client runs fine unless you try to use that function passing wrong parameters. Invoking methods of a COM object through early binding is faster than doing it using late binding. Every time you use late binding, you are asking Windows to look for a method called with a certain name, return a pointer to it and then, finally, invoke it. By using early binding you immediately call it, without any additional overhead because you already know where that method's entry point is. On the other side instead, using late binding allows much more flexibility and make things such as scripting possible. This is the content of the file VBTest.vbs contained in the WordProcessor directory:

dim MyObj, i, s

set MyObj = CreateObject("COMConverter.ConverterList")
s = ""
for i = 0 to (MyObj.Count - 1)
  s = s & MyObj.Items(i).Description & ", "

next

msgbox("You can save as " & s)

Double click on it and see what happens. Our ConverterList object will be created and the names of all supported converters will be displayed. All this without Delphi, VB or anything else. This is done using a late bound call to the methods Get_Items and Get_Count. The Active Scripting Engine embedded in Windows (which, by the way is also accessible through COM) took care of parsing the text file and asking to find and invoke them. You can do the same in Delphi too but how do you make sure you are using one instead of the other? It is very easy. The way you can do late binding in Delphi is generally by using OleVariant variables. By using typed variables you are using early binding. This is a snippet of code from the unit fMainForm.pas in the WordProcessor directory:

implementation

uses ComObj;

{$R *.DFM}

procedure TForm1.bLateBindingClick(Sender: TObject);
var
  myobj: OleVariant;
begin
  myobj := CreateOLEObject('COMCOnverter.ConverterList');
  ShowMessage('There are ' + IntToStr(myobj.Count) + ' converters available');
end;

procedure TForm1.bEarlyBindingClick(Sender: TObject);
var
  myobj: IConverterList;
begin
  myobj := CoConverterList.Create;
  ShowMessage('There are ' + IntToStr(myobj.Count) + ' converters available');
end;

As you can see, the only differences between the two are the type of myobj and the instantiation of it. The fact that you declared myobj as an OleVariant is the key here. That tells Delphi how you invoke the methods of a COM object. Anytime you use an OleVariant you can specify any method name. The compiler won't complain. Try putting myobj.XYZ in the first event handler. Delphi will successfully compile it but at runtime will raise an exception as soon as you hit that line of code. Late binding . In the second case you wouldn't be able to compile it, because IConverterList doesn't define have a method called XYZ. Early binding .

Location independence

Not to many years ago the terms "distributed" and "thin client" became very popular. The two terms are often used together when discussing about systems physically split into presentation, business and data storage tiers (multi-tier or 3-tier systems). By physically split I mean that each of those tiers can be running on the same machine or on separate ones. The reasons behind this type or architecture have to do with both a need for cleaner designs and scalability. Since this is not an article about multi tier design, I won't go any deeper in this discussion. In the next articles on COM I will discuss about this topic in detail. The things we said so far showed how COM lets us use objects embedded in DLLs. Wouldn't be nice if, on top of that, those DLLs could be located and actually executed on a more powerful machine? Wouldn't be nice not to have to worry about TCP/IP communication and sockets? Well, this is all possible using distributed COM (DCOM). DCOM is an extension of COM that allows us to do inter process communication across machine boundaries. The real nice thing behind DCOM is that the only thing that changes for the developer is the way you instantiate your COM object. Instead of calling CoCreate you would now call CoCreateRemote() passing either an IP address or the name of the machine that executes the COM object. When you do this, Windows creates an object (proxy) on the client machine that looks exactly like the real object. When you call a method on the proxy, it takes care of delivering your call and the parameters you specified to the other machine where a listener (stub) is waiting. The stub then invokes the real method and packages back the result. All this is done transparently for you. Code wise, the only thing difference for you is to specify CoCreate or CoCreateRemote when creating your COM object.

Conclusion

COM is the ideal technology to develop flexible, expandable and open application on Windows. It defines a standard, object oriented way of exposing functionality and promotes integration between them. By embracing COM you can make your application more open, expandable and controllable (whenever needed) from the outside world. You will get access to a wide set of tools and functionality embedded in your operating system and other applications such as Microsoft Office which will enanche the functionality you can provide. If you need to develop enterprise systems you will be able to leverage your investment in this technology and get access to another set of tools and servers (i.e. Microsoft Transaction Server, BizTalk, Application Center) that won't require a switch in language or approach. If you were not familiar with COM, I hope this article provided some interesting information to get you started. If instead, you are already using COM, I hope it helped you understanding a little better why COM exists and when you can benefit from using it. Understanding the reasons behind a technology instead of jumping immediately into some step- by-step code example is a much more rewarding approach in both short and long run.

What's coming next...

COM is a very large topic. ActiveX, OLE, OLE/DB, ADO, MTS and other acronyms have been created to separate the COM world into smaller, specific areas or categories. COM has to do with networking, security, data storage and many, many other things... Many books have been written on these topics but still COM is considered very complex or obscure. Well, COM is everything but very complex or obscure. It is all about finding the right information at the right time. The key is understanding the whats and whys behind it as in any other technology. In the next articles I will try to get a little more technical and I will go into real code. I will also write other generic articles like this before approaching any of the sub categories mentioned above but I will try to keep a balance between theoretical and practical.

Resources

If you want to read more I recommend the following books:

Understanding COM+, David Platt, Microsoft Press
Inside COM, Dale Rogerson, Microsoft Press
COM and DCOM: Microsoft's Vision for Distributed Objects, Roger Sessions, John Wiley Sons

You may find some more informations online at:

Microsoft's COM pages
Binh Ly's website
Dan Miser's Distribucon site
Deborah Pate's "Rudimentary" home page

Nincsenek megjegyzések:

Megjegyzés küldése