COM: All You Need to Know to Get Started
First off, do yourself a favour and get the code for this lesson: Ok with .NET and C# coming out now I figure it's time to write an article on COM. I've been meaning to write this for a long time now and hence I've a number of different ideas in my head. Let's start by getting those out to give you all an idea of what I will and won't cover in this article. a) What's COM?
What's COM?COM is an acronym for Component Object Model. What's that? Well it's a specification which describes how to to write code. Ok so that's great but I already know how to write code. Why should I follow a spec? Well there's a number of reasons. If you've made the transition from C to C++ then you'll understand some of the benefits you got when you made that transition. Similarly there are a number of benefits from taking your code from C++ and implementing it in a fashion that complies with COM. Said a different way, there's a number of benefits you get when you write COM objects instead of straight C++ objects. Some of those benefits are as follows:
Microsoft tells us: "COM is the fundamental "object model" on which ActiveX Controls and OLE are built. COM allows an object to expose its functionality to other components and to host applications. It defines both how the object exposes itself and how this exposure works across processes and across networks. COM also defines the object's life cycle." A COM object must sport a number of features, the good news is that 99% of this is done FOR you by Visual C++/VBasic/VJ++ . The language which gives the most control is definitely Visual C++ and so that's the one I'll use for this article. From the MSDN:
I like to keep my brain devoid of all useless information. In keeping with that philosophy instead of describing each of these topics, we're going to jump right into an example. If you do feel the burning desire to find out more of the theory I recommend you take a click over to msdn.microsoft.com and search on any of those topics. On to the ExampleThink of an interface as the way one object tells another about itself. It's like saying "Hey, look what I can do!". Every object must know the basic way to ask another object about itself, kind of like "Hi there, how are you doing?". This is made possible by the standard interface which all COM objects implement: IUnkown. IUnknown defines a few methods, my favourite being "QueryInterface()". QueryInterface is literally the function that says "What can YOU do?". It allows an object to ask another object for a pointer to one of its interfaces. Dizzy yet? Keep reading it'll all start to make sense in a second or two. Interfaces are implemented with pointers, tables and vtables and IDL (interface definition language). The GREAT news is VC++ does the hard work of generating ALL the code you need. You just need to tell VC++ what to generate and it does the work for you, ain't technology grand? We'll create and implement an interface on our COM object in a minute. Reference Counting, what's that?Microsoft tells us that "COM itself does not automatically try to remove an object from memory when it thinks the object is no longer being used. Instead, the object programmer must remove the unused object. The programmer determines whether an object can be removed based on a reference count. ". So how do you know when to let go of an object? Reference counting is one of the ways in which you can determine if anything's using the object. When an interface is used, a counter is incremented (through a function on IUnknown called AddRef). When it's done being used, a counter is decremented (through a function called Release). When the counter = 0, no one's using the object and it'll be freed up. In reality, the programmer (you) doesn't need to worry about this. They've made it VERY easy for programmers to use COM and as a result so much of it is taken care of for us. -===============================- Alright, let's setup the project and get cracking with some code. When you're done your workspace should look something like this:
-===============================- The following is from MSDN and tells you what you get when you generate your project: Files Generated by the ATL COM AppWizard
Test.cpp Test.def Test.idl Test.rc Resource.h StdAfx.cpp StdAfx.h -===============================- Alright, so we've got an ATL project. Let's create a class in that project. I'm going to call my class Chelloworld. Right click on the project and select add new class. In the window that pops up make sure you pick ATL class from the drop down as opposed to generic class. When you're done you should have a workspace with some files that look like this: Notice the funny looking thing called Ihelloworld. You're probably wondering where that guy came from. That's the object's interface and was generated for you when you created the class on the project. There's two instances of it in the workspace, the first is the iimplementation of it in C++ and the second is the actual interface definition in IDL (interface definition language). We'll add a method to the interface in a second so keep an eye on him as that's the key to this whole thing. Alright now let's implement our function "helloworld" on that class. Right click and select "Add Member Function". I'm going to call my function "helloworld" and give it a char* as a parameter, it'll take another char * which we'll fill in inside the function. Here's the definition. int helloworld(char *mystring, char *retmsg); You can probably guess where I'm going but if you don't this application is going to take a string A as input, it's going to alter it and place the result in B which will get returned to the calling function. Just about as basic as you can get. Here's the implementation of the function. int Chelloworld::helloworld(char *mystring, char *retmsg) { char tmp[200]; sprintf(tmp, "hello there %s, how are you today",mystring); strcpy(retmsg, tmp); return 1; } Now let's add the interface method : Right click on the Ihelloworld and select "Add Method", you'll get a box like this one.
Fill it in as I've done. You'll notice we're using BSTR instead of char* this is because char * is not a COM safe datatype. BSTR is. There's only certain types which can be used to exchange data between objects and each one maps to one of the more common data types. Check MSDN for more info on these COM safe data types.
At this point the workspace will look like this: Now let's implement the interface method. Here's how I did that: STDMETHODIMP Chelloworld::sayhello(BSTR input, BSTR *output) { // TODO: Add your implementation code here return S_OK; } was generated for me. I wrote the following: STDMETHODIMP Chelloworld::sayhello(BSTR input, BSTR *output) { char strtmp[200]; char char_output[200]; // Here we convert the BSTR to a char * WideCharToMultiByte(CP_ACP, 0, input, // actual Message lstrlenW(input), // sizeof bstrMsg strtmp, // recieving variable lstrlenW(input), // sizeof(strMsg), NULL, NULL); // Tack on an EOL strtmp[lstrlenW(input)] = ''; // here we call the internal COM functions. if (helloworld(strtmp, char_output) ) { // Create a temporary CComBSTR and place the strRetMsg in it. CComBSTR bstrTemp(char_output); // Return the output string *output = bstrTemp.Detach(); } return S_OK; } Here I just convert my input BSTR to a char * to make it easier for me to deal with, then I pass that to my other functions and return the result. Notice the lack of error checking to make this code a little easier for you to understand. Again notice the S_Ok which get's returned to COM internally. Go ahead and compile this, we're done with the C++ part of the tutorial. Next up we're going to use this COM object from VB. Open up Visual Basic and create a new form. You'll want to add a button and two textboxes to it. We're going to call the COM object when we hit the button, giving the text1 text as input and print the result in the second textbox. First we need to tell VB that we're using this COM object. Do this by adding our COM DLL to the project as a reference. Hit Project in the menu, then references and find your COM project name in the References window (you might have to scroll down the list to find it). Click the checkbox as I've done when you find it. How did VB know about this Library you ask? Well when you compiled in VC++ the last thing the compiler did was register the COM object in the registery (go and check if you like). VB simply pulls back the list of all registered objects. Note if you change the compiled DLL's located you'll have to re-register to get things to work.
Here's the code for the form: NOTE, and this is HUGE: we Dim the object as an Interface (Ihelloworld) NOT as the object itself. Does it ALL make sense now? We then create the object and it's "plugged into" via the interface. Take a look at the code for the entire form: Dim myObj As Ihelloworld Private Sub Command1_Click() Text2.Text = myObj.sayhello(Text1.Text) End Sub Private Sub Form_Load() Set myObj = CreateObject("cflake_hello_world.helloworld.1") End Sub Ok so we run the darn thing and here's what we get when I hit the button:
I hope you've learned something from this article. It's been a long time coming and took a while to get it to the level of detail I wanted. COM is a useful thing to know and can make your engine code EXTREMELY reusable WITHOUT performance degredation. It's also a lot of fun in a geeky type of way. Anyway what you've learned here will hopefully set you up for some serious .NET learning. Maybe you'll start to enjoy coding as much as I do. Regards,
Discuss this article in the forums
See Also: © 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
|