|
Hi,
I have a client that (for example) does various effects on a given graphics file. My client needs to call a method called DoEffect(). What I want to do is provide one or more COM servers to provide the effects which would then allow others to add effects by simply building their own COM server with a DoEffect() method.
Using component categorisation, I can place my "effect servers" in a component category and my client can discover them at run-time - it will simply list a set of prog IDs or CLSIDs. Additionally, it would allow others to build their own effect servers and as long as they use the effect server categorisation, my client will list them as being able to be used.
What I want to know is how best to design my effect COM servers? Should I provide them all with an interface called (say) IEffect and then should I be able to call the DoEffect() method from the interface pointer? I guess the interface point can be derived easily enough from the PROGID/CLSID listed by component category...
Thanks for any info you can share on this
|
|
|
|
|
Yes, that's very easy - after you discover the PROGID/CLSID of the required effect component, you can call CoCreateInstance , passing the CLSID and asking for IEffect interface. Then, if the interface is known to the object you want to create, you will get directly IEffect pointer for using.
Then one simple call of the IEffect::DoEffect() method and you can release the object (or save it for later use...)
Later on, when you will need to extend your effect modules by changing the method or its parameters, you have to develop new interface, say IEffect2 . Then the steps will be the same as above, but after you will have the IEffect interface, you should use QueryInterface for retrieving the IEffect2 . If this step fails, it is simply some old effect object, without new features support, so you will use the old IEffect::DoEffect() way of calling.
Is that you are asking for? (frankly, the last sentence
Anonymous wrote:
I guess the interface point can be derived easily enough from the PROGID/CLSID listed by component category...
wasn't quite clear to me)
|
|
|
|
|
Thanks for the reply
Well, now I have a client (my effects browser). I have three effect COM servers, each declaring a "IMyEffect" interface, with a method "GetName()" which simply returns the name of the effect it provides.
In each COM server, I have over-ridden the default GetCategoryMap() method and added a category map to my coclass which specifies a single implemented category using a single defined CATID (in a header file accessible to all my COM servers).
In my effect browser client I've implemented code to simply list all the progIDs/clsIDs which it does successfully!
OK, now what I want to do is call the GetName() method on each and surely I should have enough information just from the CLSIDs but that's just my problem!
Normally, I'd call the CoCreateInstance method, specifying a CLSID (well I've got that, so that's OK), a reference to the interface identifier and an interface pointer in which to return a pointer to the interface on which I want to call the method!
That's now the problem though. Normally, I'd declare a pointer to the interface but I need access to the appropriate header file, likewise I also need the IID but again I need the header file. The problem is of course, I don't know which header file to include because the whole point of the browser is to select the COM server dynamically!
OK, I believe there's certain fundamentals of using COM which I'm just not grasping, but I'm close I hope
I'm wondering if:
a) there's some magical way of calling QueryInterface() to return what I need though from what I can tell, I still need an IID to specify (can I get the IID from the CLSID?). And what then about the IMyEffect pointer? I'd still have to have access to a header file!
b) I need to create a COM server defining the IMyEffect interface, then allow my effect COM servers to aggregate it and implement the actual methods themselves.
(b) sounds the most promising to me (what do you think or have I misunderstood even further how COM is supposed to work?). I could then have a header file which contined the single IID of IMyEffect and a declaration of the IMyEffect interface. Unfortunately, how do I then categorise the interfaces so that my browser can "discover" all the available effect servers at run-time?
|
|
|
|
|
Hi,
Normally I am using the other way around, as I consider the browser app to be the controlling part, the IMyEffect interface is defined in here (either directly in the exe's IDL if any or in some of the DLLs supplied directly with it - say default plugin). This normally produces the tlb file somewhere. You have to store this tlb - this is your header.
Then when you need to develop some plugin, you refers to the tlb file, receiving all required declarations:
in IDL file of the newly developed plugin:
<br />
library SOMEPLUGINLib<br />
{<br />
importlib("stdole32.tlb");<br />
importlib("stdole2.tlb");<br />
<br />
importlib("\Projects\Tlb\plugin.tlb");<br />
<br />
[uuid()<br />
]<br />
coclass SuperNewPlugin<br />
{<br />
[default] interface IMyPlugin;
}<br />
<br />
Be aware, that the IDL compiler uses different paths for searching the tlb files (at least it seemed that for me some versions later, since then I use absolute paths here)
When you need to generate the C++ part, there is very usefull directive #import "plugin.tlb" see documentation for more options with this directive (I consider some of them usefull). Then this generates the header for you and includes it into the compilation chain (In fact, it behaves as normal include)
Optionally you can refer directly the dll file instead of tlb, result is the same
This import creates some .tlh and optionally .tli files in your Debug (Release) folder, so you can inspect what actually is here. It should contain the interface definition and all IIDs and CLSIDs defined in the plugin.tlb
So then the usage is something like that:
<br />
#import "\Projects\Tlb\plugin.tlb" raw_interfaces_only, raw_native_types, no_namespace, named_guids <br />
<br />
class ATL_NO_VTABLE CSuperNewPlugin : <br />
...<br />
public IDispatchImpl<IMyPlugin, &IID_IMyPlugin, &LIBID_WHERE_THE_PLUGIN_IS_DEFINEDLib>,
...<br />
The rest remains the same...
Hope it is somehow clear, if not don't hesitate to ask more
|
|
|
|
|
Finally got my login from my other machine (I hate posting anonymously)! Glad to meet you Geo, phykell here!
OK, what I decided to do was have my browser client (EffectBrowser), and have a separate server, "Effects", to hold the IEffect interface (same as "IPlugin" in your example) and make it available to all plugins and the browser of course.
I created an ATL server and defined a new class as an "ATL Class". The class name is "Effect", the interface type is "custom" and there's a single interface associated with it, "IEffect". I then added a method to the interface, "GetName", returning me a *BSTR. That project builds with no problems, registers and gives me a type library "Effects.tlb"
So, I then create my plugin as an ATL server, "EffectServerMosaic". I add a new ATL class with the name "EffectMosaic". It has a custom interface called "IEffectMosaic". I then import the Effects.tlb into the EffectServerMosaic.idl file and I change the EffectMosaic coclass' default interface to "IEffect" (defined in my imported type library). I then add a category map to my EffectMosaic coclass. My ATL server builds and registers successfully.
Note at this point, I haven't implemented a method GetName() for my plugin which I guess is necessary to over-ride the one provided by my plugin's coclass' default interface "IEffect". Is that right because if so, how come the compiler hasn't generated an error *requiring* me to provide an implementation?
So far so good (I think)
Finally I create my EffectBrowser application. I place the necessary code to get a list of all coclasses with the required category map and I prove this by displaying the retrieved ProgIDs derived from the CLSIDs (I presume these CLSIDs/ProgIDs are of each coclass). For example, one ProgID I get is displayed as:
EffectServerMosaic.EffectMosaic.1
I then try and create an instance of one of these coclasses (so that I can try and call the GetName() method on them. Unfortunately, I get an error message "REGDB_E_CLASSNOTREG" class not registered.
Here's the code I use to try and create the instance:
CLSID myCLSID = clsid[0];<br />
IEffect *pMyEffect = NULL;<br />
hr = CoCreateInstance(myCLSID, NULL, CLSCTX_ALL, IID_IEffect, (void **)&pMyEffect);
Note that IEffect and IID_IEffect are pulled in from the following #includes:
#include "..\Effects\Effects_i.c" <br />
#include "..\Effects\Effects.h"
Clearly, if I'd tried to create an instance using a pointer to the IEffectMosaic interface and the IID_IEffectMosaic ID, it would've have succeeded. I thought I'd be able to call the plugin's methods by getting its ProgID and using it's default interface or do you think I've made a fundamental mistake somewhere (I certainly do!)...
Lastly, I didn't understand what you meant when you said "need to generate the C++ part" and what you meant when you recommended using the #import diective.
Thanks again for your help, and sorry this is taking so long
"The folly of man is that he dreams of what he can never achieve rather than dream of what he can."
|
|
|
|
|
Well, so first to your question,
phykell wrote:
Note at this point, I haven't implemented a method GetName() for my plugin which I guess is necessary to over-ride the one provided by my plugin's coclass' default interface "IEffect". Is that right because if so, how come the compiler hasn't generated an error *requiring* me to provide an implementation?
yeah, you need to implement all the methods specified in the interface. That's simply because in interface all methods are virtual with PURE specification. So you are right, compiler should generate an error. If he didn't do so, he probably found some implementation somewhere or it might be, you didn't change the class declaration for deriving the class from the interface.
But we can do it other way. Briefly - you have the tlb definig the interface IEffect and you have some object wanting to implement this interface.
So choose the class view in your devstudio, right click on the class you want to implement the IEffect interface (I guess CEffectMosaic ) and select Implement interface (in msvc.net it is Add/Implement interface). Here you have to select the IEffect somehow. I guess the wizard is self explanatory, but if you'll have difficulties, I am mostly allways here.
If gods will be with you and the wizard will not crash, you'll have all done - your class is derived from the IEffect interface, it should be written between BEGIN_COM_MAP and END_COM_MAP and all this work it have to do. You have now only to add the Category stuff.
So let's go on - why the CoCreateInstance returns the class not registered? Hmm, difficult to say - I would check, if you pass correct CLSID to it. Also here I would suggest to use the smart pointers - it will simplify your code and take care about addref/release things. Then the code will looks like somehow like that one:
<br />
CComPtr<IEffect> spEffect;<br />
hr = spEffect.CoCreateInstance( myCLSID );<br />
Then I would try to directly create one specified plugin - spEffect.CoCreateInstance( CLSID_EffectMosaic(?) ) this could help to find the problem...
#import part. well... you included the IEffect from the Effects.h. This is OK, but now imagine, that you have no access to it for any reason, not important now. So instead of
<br />
#include "..\effects\effects.h"<br />
you can use
<br />
#import "effects.tlb" + modifiers
and you'll achieve the same goal - in fact the effects.h and effects_i.c are generated again here (or something very similar). Then the choice is on the developer what he will prefer. The Add Interface wizard uses the #import part.
Hope this helps you a bit more.
|
|
|
|
|
It's working! Thanks very much for your help, it's greatly appreciated!
Perhaps I should write my first article on this very subject, with the appropriate credits of course
The "implement interface" wizard worked just fine and simply let me browse for the type library I wanted to import. Once I'd built the code I could see exactly where I was going wrong previously, and it turned out I was very close (but no cigar as they say).
Anyway, thanks again Geo
"The folly of man is that he dreams of what he can never achieve rather than dream of what he can."
|
|
|
|
|
If I try and create an interface called IInternet as part of my COM server, I get the following compiler error:
Error C2440: 'static_cast' : cannot convert from 'class CInternet *' to 'struct IDispatch *' - Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
I guess this means I can't just use any name I please for an interface - and I thought only IUnknown wasn't to be used!
Is there some way round this and is there also a definitive list of what interface names would cause a clash?
Thanks.
|
|
|
|
|
Heh, bad luck . There is no such a list of "Interface names you cannot use". For the system the name is actually represented by the IID - some big ugly number. For the system itself is no problem to have ten or whatever number of IInternet interfaces, as long as they will have different IID .(see OLE/COM object viewer, in the tree is a branch Interfaces, I'm sure you'll find here some duplicate interface names, but not IIDs).
Problem what you have, is in your compiler - because one IInternet is included in header urlmon.h (this is included through ATL as well as through MFC headers) and one is in header generated from your IDL file. Then, your compiler have 2 different IInternet definitions. But he doesn't care about some IID , for him it is simply the collision, but it is the same collision as having say two base classes with the same name.
Solution - you can simply rename your IInternet to something else, or you have to avoid the urlmon header. For this Microsoft guys use #define __urlmon_h__ . But don't know the dependencies in your code, so cannot say if the second option will work.
Maybe after some elaboration, there will be some possibility to place the names to different namespaces. But I am normally not using the namespace thingie, so I cannot help you here much.
Hope this helps
|
|
|
|
|
Hi, thanks for the reply
I totally see that now. Sure enough, it is clashing with IInternet declared in urlmon.h. Trouble is, in my COM server which is going to be providing internet services, it will definitely have to use the IInterent services (wrapping some of them up I suppose). I guess I'll just have to use a more specific name as you suggest.
Interestingly, I was led down the wrong path by the error I reported above. I tried creating another COM server with an interface defined in it called "IModule" and got the same error. I then wondered if there's an IModule interface declared in some header like IInterface, but I haven't found one. I then wondered if it's because I'd created another COM server with an IModule interface but as you correctly point out (and as I also believed) the system differentiates by CLSID (and IID) not interface name. Anyway, I then tried creating three COM servers as Effect objects (from my other post above) and defined a "IMyEffect" interface on each. The first one built with no problems but when I was building the second one, I got a dialog box saying that it was already defined in the registry and did I want to use the existing CLSID or not. I replied no of course. I thought then that yes, the system was aware of interface names and not just IIDs then! Stranger still was that when I repeated the creation of yet another effect COM server and created another ATL object declaring the same interface "IMyEffect" I didn't get a repeat of the message asking if I wanted to use the existing CLSID! Hope you followed all that!
The namespace idea is interesting, I'll follow this up, thanks.
|
|
|
|
|
I guess you probably have the same IID for the interfaces here. But why the compiler did not ask for the second time, I have no idea.
But anyway it made me unsure a bit so I duplicate a interface name I have surely registered from some of my older projects and create a new project containing that interface name, but with different IID and compiler had no complains. In OLE/COM view I saw both are registered...
So I think you simply copied the interface definitions, including the uuid . But when this number is equal, for the system the interfaces are equal too.
Maybe would be helpfull to state, that the interface is a independent on the CLSID - the system have no link between them. Then the comparing is not based on CLSID/IID comparsion, but only on the IID!
For the solution for your problem, I think my previous reply below (above?) will help you...
|
|
|
|
|
Dear Erik...
I have seen yout article abt plugging the controls in IE toolbar.
In that I have some doubt.Because I'm new to VC++ development concern.
My first task is to create an Active X control(like one listbox and image) and I plug in to I.E 5.5 tool bar with out using the ATL COM APPWizard.
U have given this link...
http://www.codeproject.com/atl/ietoolbartutorial.asp
It asks to install
1)RBDeskBand ATL object Wizard(version 2.0)
2)CWindowImplATL Object Wizard.
Really I don't know what are these two things.If I install the CWindowTempATL Object Wizard,it shows the message like that service is already available.
After that I searched and I found in the Visual Studio .NET folder.What to do now?.
And anotherone is In my Insert menu,New ATL wizard is not enabiling.
But my superior told me that Without installing these,I need to create..Could you help me?.I should finish within TWO DAYS.my mail id(if possible send to here also) : vcarivu@indiatimes.com
After all tries,I have hope only with our peoples in this site....Otherwise I can't survive here..
If this mail disturbs you,please forgive me...
If you wish ,give me your id also.I'm in very bad and critical situation without any help.
Thanks for viewing this.Please help me.
Bye,
Arivu
|
|
|
|
|
hi all,
I'm new to VC++ development company.I need to create an Active X control(like one listbox and image) and I plug in to I.E 5.5 tool bar with out using the ATL COM APPWizard.After searching in our website ,they gave the link
http://www.codeproject.com/atl/ietoolbartutorial.asp
It asks to install
1)RBDeskBand ATL object Wizard(version 2.0)
2)CWindowImplATL Object Wizard.
Without installing these,I need to create..Please send me the steps quickly.
I should finish within TWO DAYS.my mail id(if possible send to here also) : vcarivu@indiatimes.com
After all tries,I have hope only with our peoples in this site....Otherwise I can't survive here..
Expecting u're replies..
Thanks for viewing this.Please help me.
Everfriendly,
Arivu
|
|
|
|
|
I am confuse... can anyone tell me is OLE object also is called COM????
And does anyone know how to create OLE object in Visual C++??? thanks!!!
|
|
|
|
|
These are the Microsoft's naming standards :P. In general way, as it is understand now, I can say, yes, OLE object = COM.
What is correct I guess is:
COM = Component Object Model - some general way how to write modules and comunicate between them, even if they are written in different language.
OLE = Object Linking and Embedding - some features, founded I guess in times of old plain Win 3.1(?). This was intended for Word & other Microsoft products to embedd or link e.g. Excel table to the say Word document. On 32bit OS the COM was introduced, so since then the OLE uses COM as an underlying technology.
|
|
|
|
|
Does anyone know if its safe to have a map of _variant_t like the one below?
std::map<DWORD, _variant_t> VAPMAP;
Thanks.
|
|
|
|
|
I believe it is safe. I used it in few projects here around and I had no problem with.
|
|
|
|
|
Hello,
I have an ATL COM Server with one dual interface. I can instantiate
the interface via Javascript, VBScript, no problem. If one
instance of the interface exists, it registers into the ROT (Running Object Table). So far I could not figure out if I am doing the registration wrong
or my Javascript knowledge is too small to get my hands on the already running
interface via the ROT in JScript oder VBScript.
<br />
Var server;<br />
<br />
server = WScript.GetObject("","COMTest.Application");<br />
It always instantiates a new interface leading to two ROT entries!
The only method I know is:
<br />
Var server;<br />
<br />
server = WScript.GetObject(,"COMTest.Application");<br />
But this gives syntax error, but may work in Visual Basic (not script).
Which always useses the existing instance of the interface.
Any suggestions ?
Yours,
Alois Kraus
|
|
|
|
|
Hi,
I want to delete all the bvs and exe files as the come into exchanger server and before user can get those emails. How can i do that?
This is urgent and response will be very much appreciated.
Thank you!
Ammad
We learn by sharing knowledge
Ammad
|
|
|
|
|
sorry this is COM section....
u want to send mail when u click some button/something?
http://www.codeproject.com/staticctrl/CMyHyperLink.asp
~~~~Code the Dreams~~~~~
|
|
|
|
|
Please don't cross-post (post in multible forums), it makes people kinda mad, and then you don't get any answers.
BTW, I did answer your question in the ATL/WTL forum.
- Anders
Money talks, but all mine ever says is "Goodbye!"
|
|
|
|
|
I design a COM object by ATL, which has two IDispath. Default dispatch is IPlot2D, and the second is IMIDraw.
IMIDraw has a method Plot(), how do I launch Plot() method?
|
|
|
|
|
Define some method of default IPlot2D interface which will return the IMIDraw interface.
<br />
In idl:<br />
[propget] HRESULT DrawManager([out,retval]IMIDraw ** ppDraw)
In C++:<br />
STDMETHODIMP HRESULT CPlot2D::DrawManager(IMIDraw ** ppDraw)<br />
{<br />
return QueryInterface(IID_IMIDraw,(void**)ppDraw);<br />
}
<br />
In VB:<br />
Dim o As Plot2D, o2 As IMIDraw<br />
Set o = CreateObject("PlotLib.Plot2D") ' or = New Plot2D<br />
Set o2 = o.DrawManager<br />
o2.Plot<br />
' Or<br />
o.DrawManager.Plot
<br />
In VBScript:<br />
Dim o, o2<br />
Set o = CreateObject("PlotLib.Plot2D")<br />
Set o2 = o.DrawManager <br />
o2.Plot<br />
' Or<br />
o.DrawManager.Plot
With best wishes,
Vita
|
|
|
|
|
You can't have 2 IDispatch interface on one COM object.
--
Only in a world this sh*tty could you even try to say these were innocent people and keep a straight face.
|
|
|
|
|
QueryInterface
~~~~Code the Dreams~~~~~
|
|
|
|
|