Here's an interesting way to build a COM-ish program without having to make one of those nasty proxy/stub DLL thingies. Instead of placing your code into an EXE and turning it into an out-proc server, make a minimally sized EXE and statically link it with a DLL which contains everything.

That way, if you're making an Automation program (for example), you can put all your real application code in a DLL which can be loaded as an in-proc server. Tell me what you think about it.

By the way, I am working on a framework for easily designing COM interfaces, objects, and servers. I've looked "CoLib" that comes with the MASM32 package, but I've noticed that it uses too much run-time information for my taste, and defining interfaces doesn't look as clean and pretty as it could be. My COM framework will rely on compile-time (or is it assembler-time?) information using elaborate macros. MASM has a ridiculously powerful preprocessor system, and my system strives to exploit it to the furthest extent :grin: (haven't you noticed that you can define preprocessor constants in macros using parameters?). It will also help with implementing servers, (un)registering components, and (of course) interface calls. The good thing about my system is that everything doesn't rely on the presence of everything else (if you use VC++, then you know MFC). For example, you can link the implementation of the 'standard' class factory with your specially designed object, or you can write a custom class factory with my macros to easily define objects, and use the registration lib functions by not the un-registration functions.

(By the way, one of the poll options should be "pain in the assembler.")
Posted on 2002-07-10 23:50:22 by Paulicles the Philosopher
COM is a natural for in-process servers (ie, a dll), since the process memory space is all the same, and interface pointers are really pointers to the interface.

They do not. will not ever require proxy/stub code.

Proxy/stub code (P/S) only becomes necessary when you go out of process, so 'just pretend' interfaces may be created in each process. The P/S puts some code in each process to allow communications between process (via the magic of the operating system itself).

Incidentally, you need not write P/S code if you restrict yourself to automation (ie, VB) types, as the universal proxy stub marshaller will handle all of these types.

An update to CoLib is way overdue, good luck in your work.
Posted on 2002-07-11 21:46:49 by Ernie
I know that a Local (or even worse, a Remote) server includes a lot of shadowy tricks with fake interface pointers and RPC calls, but the fact that professionals actually make those things despite the obvious drawbacks confuses me, even if their program supports Automation with "automatic" default marshalling.

Just putting all your code in a DLL and linking it to a tiny EXE just makes more sense. I wonder if anyone else has ever thought of it.

And thanks for the encouragement for my project. I'm working really fast (since I have way too much free time), and the debugging is a lot less worse than I had imagined. The hard part will probably be documenting everything in my library so that it is easy to understand and use (which just reminds me of how much I hate writing HTML).
Posted on 2002-07-12 14:11:12 by Paulicles the Philosopher
Just putting all your code in a DLL and linking it to a tiny EXE just makes more sense. I wonder if anyone else has ever thought of it.


I'm confused by what you mean. Lots of COM servers are built inside DLLs. All the .OCX ones are, .OCX is just an alternative spelling of DLL, they are the same thing.

Yes, going out of process is a heavy price, but sometimes even professionals want one process to bump inside another. I did it myself in the dark days of VB5 to get multiple threads.
Posted on 2002-07-13 16:32:45 by Ernie
The attached pic will explain what I mean. I know that there are plenty of DLL servers out there (including OCX servers for controls), and there are EXE servers too. I'm only saying that making an EXE which links statically with a DLL server with all the real application code is better than using RegisterActiveObject() and local and remote marshalling. (I know marshalling is sometimes necessary for some types of threading models, but COM multithreading is something I just need to read more about to fully understand.)
Posted on 2002-07-14 09:53:58 by Paulicles the Philosopher
To change the subject, I have two small questions for you. In my COM library, I am adding support for objects that either 1) support aggregation 2) don't support aggregation and return CLASS_E_NOAGGREGATION if the pUnkOuter parameter (in functions like CoCreateInstance()) is not NULL 3) requires aggregation and return an error if pUnkOuter is NULL. This is similar to one of the features in VC++ ATL library. An object is similar to an abstract base class in C++ and Java (not that I am some sort of Java fan). Small question #1 is that I don't know if COM allows such a thing as an abstract base class.

Small question #2 is that I don't know what error value to return if an object that requires aggregation is not created correctly (that is, if pUnkOuter is NULL). All I can think about is E_FAIL, but do you think that there is a better HRESULT, in case other clients that aren't aware of my COM library and are totally confused by E_FAIL. I checked Inside OLE, InsideCOM, and the rest of MSDN butI found nothing.
Posted on 2002-07-14 10:39:01 by Paulicles the Philosopher
PTP,
to your 2. question: accordung to code sample below CLASS_E_NOAGGREGATION would be best error code.



CreateInstance PROC pThis:ptr CClassFactory, pUnkOuter:LPUNKNOWN,
riid:ptr IID,ppObject:ptr LPUNKNOWN

local pAsmCtrl:ptr CAsmCtrl

DebugOut "IClassFactory::CreateInstance"

mov eax, ppObject
mov DWORD PTR [eax], 0

if ?AGGREGABLE
.if (pUnkOuter != NULL)
invoke IsEqualGUID, riid, addr IID_IUnknown
.if (eax == FALSE)
DebugOut "IClassFactory::CreateInstance failed (riid != IID_IUnknown)"
return CLASS_E_NOAGGREGATION
.endif
.endif
else
.if (pUnkOuter != NULL)
DebugOut "IClassFactory::CreateInstance failed (pUnkOuter != Null)"
return CLASS_E_NOAGGREGATION
.endif
endif


To your 1. question: What is the difference between an interface and an abtract base class? dont see any.
Posted on 2002-07-14 15:01:53 by japheth
3) requires aggregation and return an error if pUnkOuter is NULL.


I can't see how this is usefull. I don't think it violates the COM contract, so you could do it, but why?

An object may be usefull only when contained within another object (such as a visual control), but by demanding it be aggregated, you cut short how it may be reused, leaving the hapless programmer no option to contain it.

And after all, COM is about reusing programmable objects.
Posted on 2002-07-14 22:03:58 by Ernie
To Ernie:
Just use your imagination! There must be lots of ways to use abstract base classes for COM if you think 'bout it. You could use such a component to display info about the outer component (which may not make sense if the component was not aggregated).

Besides, why not support it? The code took me 4 minutes to write and no time to debug since all it requires is a simple preprocessor statement to generate the code. Besides, I already added support 'support' and 'no support' already. :grin:

To japheth:
I wouldn't return because CLASS_E_NOAGGREGATION is traditionally used when aggregation is not supported. Returning that code might make Ole32.dll go into a state of denial and crash. The problem is that if an object is created using IClassFactory (in my humble, macro-macho system) a custom code can be returned, but if CoCreateInstance is used, the return value will not equal the value of IClassFactory::CreateInstance (as far as I know).

E_INVALIDARG or E_UNEXPECTED are close enough. Or I could make a custom HRESULT (like CLASS_E_MUSTAGGREGATE). If you are using a component that must be aggregated, you probably would know about it anyway.
Posted on 2002-07-15 00:15:09 by Paulicles the Philosopher

You could use such a component to display info about the outer component (which may not make sense if the component was not aggregated).



Yikes! And violate containment? <g>

"Gentle degredation" would dictate a non-aggregated 'aggregator sniffer' would, of course, return a reasonable error result if asked to query it's (nonexistant) aggregator.

I'm not agruing with you, I'm just asking what is the usefullness you seek.

BTW, "abstract base class" is too C++ for me (I don't do C++). Can you put it in other terms so I get what you mean?

Also, as you correctly stated, a multithreaded enviroment also needs interface marshalling, even for in-process DLLs. I tend to forget this case cause I avoid multithreading (having only personally used it once).
Posted on 2002-07-16 19:46:52 by Ernie
An "abstract base class" is a class (or object, or type, or whatever) that has one or more "pure virtual functions" - which are the equivilent of COM vtables with members that point to NULL.

For example, interfaces in C++ are declared as abstract base classes inheriting from IUnknown and having only "pure virtual functions." A class object in C++ "inherits" from these interfaces and fills up the vtables by implementing all functions. This is NOT what a COM abstract base could be used for.

(By the way, sorry for spilling C++ stuff in the conversation.)

Let me just make a really messy and simple example. You could use an aggregation-only class to automate IDispatch (no put intended.) Let's call it's only interface IAutoDispatch, with two methods: LoadDispatch, which takes a pointer to the outer components ITypeInfo interface, and UnloadDispatch, which takes no parameters. The outer component calls InitDispatch when it initializes and if doesn't return S_OK, it returns E_UNEXPECTED to the class factory*. The outer component can return pointers to IAutoDispatch and...... no, wait..... :confused: that doesn't make sense. Forget what I just said! My example doesn't make sense anymore since the client wound't want IAutoDispatch, even if IAutoDispatch is useful if it is used by a component. I guess Ernie is right: containment is better (to hide IAutoDispatch). I can't come up with a good example because it could take some thought. I still think an aggregation-only object is somehow useful and there would be no harm in including it into my library. But I am a humble programmer and Ernie wins this round. :alright: Now let's all play "Spear Toss!" ::Everyone runs to The Heap::

* I know that the MSDN COM reference states that IClassFactory's CreateInstance method should only return interfaces to uninitialized components, but who follows that rule anyway? I don't think someone is fond of that idea. Why did they make it so? Why?!?
Posted on 2002-07-16 23:15:59 by Paulicles the Philosopher