|
I assume that MediaEvent and DeliveredEvent are COM interfaces.
To cast a COM interface from one interface into another use QueryInterface.
I.e.
void f(IMediaEvent * p)
{
if( p != NULL )
{
IDeliveredEvent * l_p(NULL);
HRESULT const l_hr(p->QueryInterface(
IID_IDeliveredEvent,
reinterpret_cast<void**>(&l_p)));
if( SUCCEEDED(l_hr) )
{
l_p->Release();
}
}
}
Using some sort of smart pointer (for instance CComQIPtr) is preferrable.
|
|
|
|
|
Thank you very much. It seems to work fine!
Stefan
|
|
|
|
|
Thanks for taking time to read this.
I have built a C# DLL full of business objects. Initially these objects were going to be used by a C# wizard only, but as development went on we determined that these objects and their methods could be used by an MFC DLL as well. I figured COM Interop...no problem.
Well I ran into problems after adding in all of the interfaces, adding a strong name to the DLL, creating a Type Library using the typexp.exe to create the type library and using regasm with the /codebase option to register the interfaces and coClasses.
The problems revolve around the client. The client trys to import the type library, generating the tli and tlh files. Inside of these files the GetType method defined by the base object in .NET has arguments of _Type and _TypePtr. The _Type and _TypePtr are not defined in any of the files that I can find.
I have a work around of substituting long for _Type and _TypePtr. Really since we are not calling GetType, I figured sc#$w it! Also since we are using interfaces here shouldn't GetType be banned from the interface. Everything compiled and ran.
I don't want to redefine the _TypePtr and _Type within the client as a long, nor do I want to have to parse through each tli and tlh file to substitute the hacked GetType method definition and declaration everytime I add to the type library.
Has anybody run into this problem before?
Is there a Header that I could add to the client to allow for the proper defninition of the _Type and _TypePtr?
Once again thanks for reading this.
All the best
Ward
Frag
Try, Fail..Fail, Succeed
Tool Developer
Michigan, USA
|
|
|
|
|
I have created solutions to these problems. If you would like the code please let me know.
Ward
;-D
Frag
Try, Fail..Fail, Succeed
Tool Developer
Michigan, USA
|
|
|
|
|
What did you change to accomodate this?
|
|
|
|
|
Hi all,
I have downloaded dsoframer.ocx ..... It is full of OLE / COM code. and i am newer to these interfaces and such activXcontrols.....
What i am doing.
i am trying to load office document ( from local file / from memory HGLOBAL ) into DsoFramer control placed in dialog based MFC application. Then after loading document , i want to disable keydown,on char,key up and right mouse button down. so that user cannt modify content. ( like viewer only.) I have debugged and put messagebox in source code but with no luck.
I want to know where can i handle such win messages.?
Does any other interface i need to add or implement?
I am able to get such message in CDsoFramerControl::ControlWindowProc() but when NO Document is loaded.? Why?
I have looked to windows with SPY++ i found that there are more windows handle underbeneth so is that i problem why i dont get any aforesaid messages.?
Any help is greatly appreciated.
Thank in advance.
Regards
Jetli
Jetli
conclusion means Coming to wrong Decision with confidence
|
|
|
|
|
Hi Jetli,
What you are missing here is OLE automation, once you have loaded the document inside DSOFramer activex control. You should query for IDispatch pointer of embeeded active document. Once you have IDispatch you can start automating that active document. (OLE automation is meaning the same, control the functionality of the application !)
hmmm... to disable the right click and typing etc see the methods expose by that active document IDispatch interface. You can use the utility provided by MS "OLE/COM Object Viewer" to see all the methods expose by the that application.
As I remember for "MS-Word" you can disable the typing using the "protect" method expose by word. And for right click search on "CommandBars".
I think its sufficient information !
Enjoy !
Cheers,
Vishal
|
|
|
|
|
Hi Vishal
Many thanks to you.
vishalmore wrote:
What you are missing here is OLE automation, once you have loaded the document inside DSOFramer activex control. You should query for IDispatch pointer of embeeded active document. Once you have IDispatch you can start automating that active document. (OLE automation is meaning the same, control the functionality of the application !)
above is new for me.... THANKS A LOT ... i have tried to get IDispatch and also played with some of methods by looking in OLE viewer. I would like to show how i have done this :
LPDISPATCH lpDisp = m_shControl.GetActiveDocument();<br />
DISPID dispid;<br />
OLECHAR FAR* szFunction;<br />
szFunction = OLESTR("Protect");<br />
DISPPARAMS dispparams;<br />
DISPID mydispid[1] = { DISP_PROPERTYPUT };<br />
VARIANTARG vararg[1];<br />
dispparams.rgvarg = vararg;
VariantInit(&dispparams.rgvarg[0]);<br />
dispparams.rgvarg[0].vt = VT_I4;
dispparams.rgvarg[0].iVal =2 ; <br />
dispparams.rgdispidNamedArgs = mydispid;
dispparams.cArgs = 1;
dispparams.cNamedArgs =1;
<br />
HRESULT hr = lpDisp->GetIDsOfNames (IID_NULL, &szFunction, 1, <br />
LOCALE_USER_DEFAULT, &dispid);<br />
<br />
hr = lpDisp->Invoke (dispid, IID_NULL, LOCALE_USER_DEFAULT,<br />
DISPATCH_METHOD, &dispparams, NULL, NULL, <br />
NULL);
"protect" property doesnot disable typing..
"Commandbars" is for toolbard floating in word window. ( which i can control via Dsoframer control.
Is there any property / suggestion for doing this?
Again Thanks In advance
Jetli
conclusion means Coming to wrong Decision with confidence
|
|
|
|
|
Hi Jetli,
Your code sounds gud to be gud to me.
Jetli Quoted :
"protect" property doesnot disable typing..
One possible mistake you have done is this
dispparams.rgvarg[0].iVal = 2 ;
It should be dispparams.rgvarg[0].lVal = 1 ;
You are saying that dispparams.rgvarg[0].vt = VT_I4; and storing short value in long ?
Well "Protect" method disables the typing in word document(I am very sure on this), have you check the return code in your automation routine for
"protect" method ?
Jetli Quoted :
"Commandbars" is for toolbard floating in word window. ( which i can control via Dsoframer control.
Is there any property / suggestion for doing this?
Word context menus are comes under the category of "CommandBars" only, you can disable the context menus in following manner ...
1.)Get IDispatch for embeeded Active Document.
2.)Ask IDispatch for DISPATCH_PROPERTYGET - "CommandBars", it will return disp ptr for CommandBars. This property takes one param i.e. nothing but id of that command bar (in word id for context menus starts from 38)
(you can use VBA to check the codes for these context menus for different office version )
3.)Now invoke the "Enabled" method using this disp ptr. This method takes 1 param of type BOOL.
My suggestion is that put all the above three steps in one for/while loop.
That's Context menus are disabled !
All the very best !
Cheers,
Vishal
|
|
|
|
|
Hi Vishal,
Many Thanks To You.....
But unfortunately i am still not able to do.... I need further help
I have done what u have said and tried things tooo .. but with no luck...
I have placed code as per your last reply...
>>One possible mistake you have done is this
>>dispparams.rgvarg[0].iVal = 2 ;
>>It should be dispparams.rgvarg[0].lVal = 1 ;
>>You are saying that dispparams.rgvarg[0].vt = VT_I4; and storing short value in long ?
i have corrected it......but still having same problem
// Again my revised code to look at
LPDISPATCH lpDisp = m_shControl.GetActiveDocument();
DISPID dispid;
OLECHAR FAR* szFunction;
szFunction = OLESTR("Protect");
DISPPARAMS dispparams;
DISPID mydispid = { DISPID_PROPERTYPUT };
// wdAllowOnlyComments=1// wdAllowOnlyRevisions=0,// wdAllowOnlyFormFields=2
//CComVariant vrProtecttionType((long)0);//1
VARIANTARG vararg[1];
dispparams.rgvarg = vararg; // 1-element array
VariantInit(&dispparams.rgvarg[0]);
dispparams.rgvarg[0].vt = VT_I4;
dispparams.rgvarg[0].lVal=1 ;
//dispparams.rgvarg=&vrProtecttionType ;
dispparams.cArgs=1;
dispparams.rgvarg=vararg ;
dispparams.cNamedArgs=1;
dispparams.rgdispidNamedArgs=&mydispid;
HRESULT hr = lpDisp->GetIDsOfNames (IID_NULL, &szFunction, 1,LOCALE_USER_DEFAULT, &dispid);
//dispid = 0x00000078
hr = lpDisp->Invoke (dispid, IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYPUT |DISPATCH_METHOD, &dispparams, NULL, NULL,
NULL);
//hr=0x80020005: Type Mismatch
>>have you check the return code in your automation routine for "protect" method ?
//hr=0x80020005: Type Mismatch
you can disable the context menus in following manner ...
>>1.)Get IDispatch for embeeded Active Document.
LPDISPATCH lpDisp = m_shControl.GetActiveDocument();
>>2.)Ask IDispatch for DISPATCH_PROPERTYGET - "CommandBars", it will return disp ptr for CommandBars. This property takes one
param i.e. nothing but id of that command bar (in word id for context menus starts from 38)
(you can use VBA to check the codes for these context menus for different office version )
DISPID dispid;
OLECHAR FAR* szFunction;
szFunction = OLESTR("CommandBars");
// parameter structure
DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
CComVariant vrTrue = (long)38;
dispparamsNoArgs.cArgs = 1;
dispparamsNoArgs.rgvarg = &vrTrue;
dispparamsNoArgs.cNamedArgs = 0;
dispparamsNoArgs.rgdispidNamedArgs = NULL;
HRESULT hr = lpDisp->GetIDsOfNames (IID_NULL, &szFunction, 1, LOCALE_USER_DEFAULT, &dispid);
//dispid 0x39
VARIANT vOut;
VariantInit(&vOut);
LPDISPATCH pCommandBarDispatch;
hr = lpDisp->Invoke (dispid, IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vOut, NULL,
NULL);
//hr=S_OK
>>3.)Now invoke the "Enabled" method using this disp ptr. This method takes 1 param of type BOOL.
pCommandBarDispatch = vOut.pdispVal;
OLECHAR FAR* szStr;
szStr = OLESTR("Enabled");
DISPID dispid1;
hr = pCommandBarDispatch->GetIDsOfNames(IID_NULL,&szStr,1,LOCALE_USER_DEFAULT,&dispid1);
//dispid1=0x60040005
DISPPARAMS dispParams = { NULL, NULL, 0, 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
//CComVariant vrId(VARIANT_FALSE); (also tried)
VARIANTARG vararg[1];
dispParams.rgvarg = vararg; // 1-element array
VariantInit(&dispParams.rgvarg[0]);
dispParams.rgvarg[0].vt = VT_BOOL;
dispParams.rgvarg[0].boolVal =VARIANT_FALSE;
dispParams.cArgs = 1;
dispParams.rgvarg = &vrId;
dispParams.cNamedArgs = 1;
dispParams.rgdispidNamedArgs = &dispidNamed;
hr = pCommandBarDispatch->Invoke(dispid,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_PROPERTYGET
|DISPATCH_METHOD,&dispparamsNoArgs,NULL,NULL,NULL);
// hr=0x80020003:Member Not found
Once Again Many thanks To You
Thanks In Advance...
Jetli
conclusion means Coming to wrong Decision with confidence
|
|
|
|
|
Hi Jetli,
hr = lpDisp->Invoke (dispid, IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYPUT | DISPATCH_METHOD, &dispparams, NULL, NULL, NULL);
Look at the code closly, the 4th parameter. It should be either
DISPATCH_PROPERTYPUT OR DISPATCH_METHOD OR DISPATCH_PROPERTYGET
In your case it should be DISPATCH_METHOD
One more sugestion you should always use the 2nd last parameter i.e EXCEPINFO. Its ease the life ...
Hope things are settled down ...
Cheers,
Vishal
|
|
|
|
|
Hi Vishal,
Thanks for prompt reply.
I have tried with Only DISPATCH_METHOD before and also now after you have said.
And also
Put EXCEPINFO ex; and &ex in 2nd last param.... but it contain nothing after failure....
hr=0x80020003:Member Not found for CommandBar/Enable case while
hr=TypeMismatch errror for protect method is still commingg
I am really thankful for yr help and yr patience.
But bcoz of not haveing proper base i am asking bit more from you.
Once Again Thanks
Jetli
conclusion means Coming to wrong Decision with confidence
|
|
|
|
|
Hmmm...
I think you should read more on OLE automation ...
As far as this problem is consern,
I am out of ideas !
Cheers,
Vishal
|
|
|
|
|
Hi Vishal
I am able to excute protect
Here Is Working Code....
LPDISPATCH lpDisp = m_shControl.GetActiveDocument();
EXCEPINFO ex;
OLECHAR FAR* szFunction;
DISPID dispid;
szFunction = OLESTR("Protect");
HRESULT hr = lpDisp->GetIDsOfNames (IID_NULL, &szFunction, 1,LOCALE_USER_DEFAULT, &dispid);
//dispid = 0x00000078
// Protect type,noreset,pass ,,,,,, Pass Ole in Reverse Order
VARIANT vArgs[3];
DISPPARAMS dpP;
dpP.cArgs = 3;
dpP.cNamedArgs = 0;
dpP.rgvarg = vArgsSaveAs;
VARIANT vOpt;
vOpt.vt = VT_ERROR;
vOpt.scode = DISP_E_PARAMNOTFOUND;
vArgsSaveAs[2].vt = VT_I4;
vArgsSaveAs[2].lVal = 1;
vArgsSaveAs[1] = vOpt;
vArgsSaveAs[0] = vOpt;
hr = lpDisp->Invoke(dispid, IID_NULL,
LOCALE_USER_DEFAULT, DISPATCH_METHOD,
&dpP, NULL, &ex, NULL);
And I am able to eat keystrokes via protection....
As Far As CommandBars
I have looked a bit.... I found Commandbar Property returns commandbar collection and error i was getting becoz "member not found" i guess it was becoz of not having appropriate Dispatch ptr.
Another thing i am not able to find Commandbars ( and also CommandBars Collection) Interface and its properties.. In OLe Viewer..... Any Ideas?
If you can give me some MORE info on 3 steps for disabling context menu... I will really appericiate.....
But Once Again Many Many Thanks To You.
Jetli
conclusion means Coming to wrong Decision with confidence
|
|
|
|
|
Damm... I was aware of this thing, that parameters should passed in reverse
order. Nways good to hear that u finaly done it !
See the pseduo code for context menus prob:
VARIANT v1,v2,v3;
While( i > 88)
{
v1.vt = VT_I4; v1.lVal = i;
Invoke(pDispWord,"CommandBars",0,DISPATCH_PROPERTYGET,1,v1,v2)
v3.vt = VT_BOOL; v3.boolVal = 0;
Invoke(v2.pdispVal,"Enabled",0,DISPATCH_PROPERTYPUT,1,v3,NULL);
i++;
}
That's al !
Gud luck !
Cheers,
Vishal
|
|
|
|
|
Hie,
I've created:
- an ATL/COM DLL containing an ActiveX sending events;
- an HTML page instantiating this ActiveX and receiving its events;
- an MFC executable instantiating this ActiveX also.
BUT when I launch both clients, I have 2 instances of the DLL in memory!
How to have only one instance?
Thanks
pinch'
|
|
|
|
|
The browser and the MFC executable run in completely separate processes. Each process runs independently and runs in it's own memory space. DLLs are always loaded directly into a given process's memory space. That's why you get two instances of the DLL.
You cannot do what you are trying to do with a DLL, period.
Instead you will have to create a threadsafe ActiveX exe singleton to make sure both applications get a reference to the same instance. That's about all I could tell you based on the information given.
Robert
|
|
|
|
|
Robert, thanks a lot for your answer.
In fact, I had first created an ActiveX exe (loaded only once in memory) but then the HTML page did not receive any ActiveX events. Is it a Microsoft bug or a question of DCOM configuration or what?
Pascal
|
|
|
|
|
Well by their nature, ActiveX components instantiated from an HTML page ultimately run on the client, not the webserver. Assuming your MFC application is also running on the same client it should not be a DCOM issue.
If I understand what you are trying to do, basically you have two applications that you wish to subscribe to, and respond to, the same events raised from some class in an ActiveX component.
The fact that an ActiveX exe creates only one instance of the server itself does not entirely solve your problem. A single ActiveX exe server component can still create multiple instances of a given class.
For example, suppose your HTML page initially creates some object, "MyEventsClass" from the ActiveX componet (and thereby causes an instance of the ActiveX EXE to be launched). When your MFC application is launched, it requests an instance of the same "MyEventsClass" object. Since the server alrready exists, the same, single instance of the ActiveX EXE server will supply the MFC app with the class instance requested HOWEVER, it will not be the same instance of the class already supplied to the HTML page.
So with an ActiveX EXE you will no longer have two instances of the same component, but you will still have two instances of the class which both applications request - one instance for each application.
Assuming you want both applications to have a reference to a shared, single instance of the class created by the AtiveX component, first you will have to add code to ensure the class is created as singleton. If you code the object as a singleton, the first application to request the class from the ActiveX EXE will cause an instance of "MyEventsClass" to be created and the first app will receive a reference to the new instance. When the second application requests an instance of the same class, you have to have code that prevents the ActiveX EXE from creating a new instance. Instead the ActiveX EXE should just return a reference to the same instance the first app has already created. (I hope that makes sense).
The net result is that only the first application to request the class will receive a reference to a class that it instantiates. All subsequent requests for instances of the class will receive a reference to the same, single class instance that has already been created by the first request.
If this is what you want to happen, coding a singleton class is not difficult - you just create a mutex, and check in the class constructor if the mutex already exists. If so, the constructor returns a reference to the already existing class instance to the second client app that requests the object.
Now, getting both apps to sink the same events from a single, shared instance of a class may be a different story, because ATL is essentially templates that hide a lot of the COM architecture in macros.
My understanding of the intricacies of what happens in the ATL COM wrapper framework is not extensive enough to speculate on what is required to sink the same set of events from a ActiveX EXE in two different apps.
I would suggest that you refine your question and post it on the ATL forum and see if some ATL guru can provide you with the details.
Sorry, that is about as much info as I can provide ...
Good Luck!
Robert
|
|
|
|
|
Robert,
Regarding to your complete explanation and to my ATL/COM book, I've just implemented a solution that works!
Thanks a lot
Pascal
|
|
|
|
|
Cool! What did you have to do? Just brief descript will be fine ...
|
|
|
|
|
Sure, here is the architecture of the solution:
- an ATL/COM executable server which transfers messages from a client (an MFC executable) to another client (an ActiveX) via the method and the event of an interface;
- a standard MFC executable (dialog based) connected to the COM server and sending messages to it via its interface method;
- an ATL/COM ActiveX connected to the (same!) COM server and receiving events from it (then messages from the MFC executable client) via its interface event sink;
So, both clients communicate together via this COM server loaded only once in memory!
"Et voilà"
Thanks again Robert
Pascal
|
|
|
|
|
Hi Pascal,
I am wondering if you could explain a little bit more.
As I understand Client1 creates an intance of the server calss. Client2 shares the referance to the single instance of the server class. How do you stop Client2 from creating a second instance of the server class.
Thanks
|
|
|
|
|
I have been trying to write a Method on my COM interface that takes an [in, out] parameter. I expected that this would act like a I was passing in a reference.
In the idl file the function is:
[id(1), helpstring("method Select")] HRESULT Select([in, out] System** System);
In the appropiate hear file the function is defined as:
STDMETHOD(Select)(System** System);
I use the function as below:
//In some cases the pSystem object is created other times it will be null
ISystemPtr pSystem(CLSID_System);
//or
ISystemPtr pSystem = NULL;
//The Select Method will display a dialog with a set of options, if the object being passed in is //NOT NULL the dialog should have the correct / relavent options selected. If it is NULL the //pSystem object will be created with the selected options and passed back to the client.
TheObjectWithTheMethod->Select(&pSystem);
The problem is that when an valid (not NULL) pointer is passed into the function the & operator actually releases the pointer to the pSystem and returns a NULL interface pointer. So inside the implimentation of the Select function we always get a NULL pointer.
Does anubody have any ideas?
Cheers
Andy
|
|
|
|
|
Use a ** when you want to pass a pointer to an object and you might want it to return a pointer to a different object. Consider:
HRESULT Select( System** pSystem )
{
if ( pSystem != NULL )
(*pSystem)->Release();
*pSystem = new System();
}
pSystem = new System();
HRESULT hr = Select( &pSystem );
If you don't want to change the value of the pointer you pass in, but instead change the value of the object, use a single *.
I think that you actually do want to pass the pointer by reference, but your ISystemPtr smart pointer class has an override of operator & which I assume is releasing the held pointer. Without seeing this class, or what the typedef is, it's difficult to help.
The documentation for _com_ptr_t , the environment's own smart pointer, suggests that operator&[^] releases the pointer then NULLs it. IIRC, using #import generates smart pointer typedefs of _com_ptr_t .
If this is what you're using, I think all you can do is Detach the pointer, call Select, then Attach the resulting pointer to the smart pointer object.
ATL's CComPtr allows direct access to the raw pointer through the public p member. Its operator& ASSERTs if the contained pointer is not NULL.
Stability. What an interesting concept. -- Chris Maunder
|
|
|
|
|