Alternatives to COM?

Started by
7 comments, last by iMalc 17 years, 5 months ago
Hey guys. Wanted to get some of your takes on this. Where I currently work, we have a few main applications. The applications are pretty much a small base, with a bunch of COM DLLs as separate projects. The excuse for using COM is that "if there's a change to just one tool, we can swap out a new DLL without breaking existing code" and "it can be shared across the different applications, and different apps would be able to basically take all the DLLs they need and make an app out of it, while another app would use a different subset". I don't think we leverage much of either of these. The big problem with COM is all the marshalling done back and forth is so incredibly unnecessary. There's huge code bloat from it, and a lot of compromises were made in order to be able to use this architecture, including some less than object oriented approaches. It also suffers terribly from DLL hell, as its very easy, from a development standpoint, to get the wrong COM object registered. Overall, I think, the majority of the COM code was unnecessary and there's a ton of code duplication due to the COM approach, with the marshalling. So I was wondering what alternatives there are. The main concerns is that any new approach would have to preserve: 1) Modularity of being able to add new components without building a new app (patching). 2) Being able to dynamically query what is available by name or ID (e.g.: Like the COM QueryInterface). Thanks.
Advertisement
It sounds like COM is being badly used.
I can't imagine how you could ever get the wrong COM object registered. How on earth could that be possible? Barely makes sense to me.
I've seldom had to do manual marshalling and trust me I work on a MUCH larger COM / DCOM project, and our server is free threaded.
COM has neither helped nor hindered things staying object orineted.
Code duplication is nothing related to using COM. If any code was duplicated instead of being made more generic and reusable then that was the programmers fault.
Do you use ATL? Or what besides COM do you use?

I honestly think that if the team can't make COM work, then they wont be able to make anything similiar work any better.

Don't get me wrong though, COM kinda sux sometimes.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Quote:Original post by iMalc
It sounds like COM is being badly used.
I can't imagine how you could ever get the wrong COM object registered. How on earth could that be possible? Barely makes sense to me.
I've seldom had to do manual marshalling and trust me I work on a MUCH larger COM / DCOM project, and our server is free threaded.
COM has neither helped nor hindered things staying object orineted.
Code duplication is nothing related to using COM. If any code was duplicated instead of being made more generic and reusable then that was the programmers fault.
Do you use ATL? Or what besides COM do you use?

I honestly think that if the team can't make COM work, then they wont be able to make anything similiar work any better.

Don't get me wrong though, COM kinda sux sometimes.


Fair arguments. The big problem is the EVERYTHING is used as a VARIANT. This makes it more usable with vbs scripts (which are rarely leveraged either). So there's a ton of marshalling to and from variants. Here's a pseudocoded example:

... some com method ...HRESULT doStuff(VARIANT inInt){   if (inInt.vt == VT_INT)   {      ... marshall from variant, process ... // 3 -5 lines of code         }   else if (inInt.vt == VT_BSTR)   {      .. convert to int, proceed ... // 5-7 lines of code   }   else   {      ... set error condition, set hRes ...      hRes = E_INVALIDARG; // ugh OR my favorite:      hRes = MAKE_HRESULT(...,...,...);   }   hRes = e.ErrorCode();}... then where it's used ...try{// use com:hRes = CoInitialize(...)if (FAILED(hRes)){   throw _com_error(hRes);}hRes = IComInterface->QueryInterface(...)if (FAILED(hRes)){   throw _com_error(hRes);}... I have an int, convert it into a variant -- 3-5 lines of codeIComInterface->DoStuff(var);CoUnitialize(...)}catch(_com_error& e){   hRes = ...   AfxMessageBox(e.ErrorMessage());}return hRes;


So, in all it's COM hell because there's tons and tons of marshalling done, it's the same code every time, and all we did with all the pseudo code that I mentioned is 20-30 lines of code (for every place used) which basically calls a method and converts and int into a variant and back to an int.
Oh, and to answer your question, there is some use of the ATL and COM classes, like _variant_t, _com_error, CComSafeArray, CAtlSafeArray, etc... but a lot of the time there's just a copy and paste job and the marshalling is just fugly. Have 40 methods and you turned a 500 line source file into 2000 line source file. fugly.
Your objection isn't with COM, it's with OLE Automation. I worked for years on COM projects and rarely, if ever, touch VARIANT or BSTR or any that stuff.

There are tools to make all the oleaut boilerplate easier I believe. Doesn't C# and more generally CLR stuff interface relatively painlessly? I don't really know though since it's been a long time since I had to care.
-Mike
Quote:Original post by Anon Mike
Your objection isn't with COM, it's with OLE Automation. I worked for years on COM projects and rarely, if ever, touch VARIANT or BSTR or any that stuff.

There are tools to make all the oleaut boilerplate easier I believe. Doesn't C# and more generally CLR stuff interface relatively painlessly? I don't really know though since it's been a long time since I had to care.


This is all C++ code that uses it (and rarely VBS), so whether C# or CLR integrates painlessly is irrelevant, atm. I'm relatively new to COM, I just started using it within the last two months. I guess you're right. The issue isn't so much COM, but how COM is being used with the VARIANT and BSTRs is just insane and seems to be very un-object-oriented.
I thought that the variant/bstr stuff was only required if you want to support IDispatch for scripting/vb integration, or a dual interface which supports both IDispatch and direct dispatch.

Also, I thought that marshalling was only performed over network or process boundaries so isn't actually an issue if you implemen your COM control as an in-proc server (i.e. a DLL/OCX).

It's a long time since I did any COM stuff though. It's pretty daunting if I recall corrctly. I'd stick to ATL rather than any other method though (e.g. MFC).
"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley
Quote:Original post by Paradigm Shifter
I thought that the variant/bstr stuff was only required if you want to support IDispatch for scripting/vb integration, or a dual interface which supports both IDispatch and direct dispatch.


Right. They're doing this in every case, when in most cases it's not needed. I'm agreeing with the previous posters, that this is more a symptom of how COM is being used than a COM-specific issue. Using all this variant/bstr stuff so we can conform to VBS, when we have like 5 VBS scripts in the entire system is silly. I'll bring the issue up with my manager again. He's already gathering my feelings on it.
Quote:Original post by Paradigm Shifter
I thought that the variant/bstr stuff was only required if you want to support IDispatch for scripting/vb integration, or a dual interface which supports both IDispatch and direct dispatch.
I guess they went a bit overboard with using variants.
Quote:Also, I thought that marshalling was only performed over network or process boundaries so isn't actually an issue if you implemen your COM control as an in-proc server (i.e. a DLL/OCX).
Marshalling is also required between thread apartments.

At my work we make thorough use of a number of useful macros similiar to:
#define THROW_IF_FAILED(hr) //if failed throw a _com_error#define COM_CATCH(hr) //catch a com error and assign it to hr, and log error to the log etc.
These things are good for shortening the code.

You might also consider writing a class that takes the VARIANT in the constructor and provides methods like getAsInt(), getAsBSTR(), getAsBool(), getAsIUnknown() etc which do all the checking and converting and throwing if necessary.

Now your code would look like:
HRESULT doStuff(VARIANT inInt){   HRESULT hr;   VarHelper myVar(inInt);   try {      int value = myVar.getAsInt();      // do stuff with value   } COM_CATCH(hr);   return hr;}... then where it's used ...HRESULT hr;try {   // use com:   THROW_IF_FAILED(CoInitialize(...));   THROW_IF_FAILED(IComInterface->QueryInterface(...));   ... I have an int, convert it into a variant -- 3-5 lines of code   IComInterface->DoStuff(var);   CoUnitialize(...);} COM_CATCH(hr)return hr;
This can be improved furthur mind you.

CComBSTR is your friend too!

Note that you should only be calling CoInitialise when the thread is created, at most, not for each function that uses COM. You really only need to call it for each apartment.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

This topic is closed to new replies.

Advertisement