|
Unfortunately, I don't use MFC, because it hides the underlying implementation, often making it difficult to make alterations without problems.
But, I'll try to explain clearly. If your 'My Program' is instantiated by a user, and your 'My Program' calls CoRegisterClassObject (this is the default, and your MFC implemenatation may actually do this), you can locate this running instance with the creation of a Class Moniker (presumably, implemented in your 'MyManager'). The Class Moniker must be initialized with a Binding Context to work properly. And, you pass the CLSID of a COM class exported by 'MyProgam' to IMoniker->BindToObject (which is where all the work is done), also, passing the IID_IUnknown Interface ID (this is a system IID, and you must link to uuid.lib, for the symbol to be exported correctly) as the REFIID parameter, the function should return the IUnknown interface pointer to the CLSID from 'MyProgram' that you supplied to the CreateClassMoniker function.
(From Matt Pietrek, "If you've used OLE, COM, or ActiveX, you probably remember that there are .LIB files that are used for predefined class IDs (CLSIDs) and interface IDs (IIDs). Both CLSIDs and IIDs are forms of GUIDs, which are 16-byte unique values".)
IMoniker->BindToObject returns a COM typed interface pointer (to the IUnknown of whatever interface you passed to CreateClassMoniker. This can then be used with QueryInterface.) You can then call, IMoniker->GetObject to determine if the instance is already running. That's the basic idea.
To intialize the Binding Context, call, CreateBindCtx, and then, pass it a BIND_OPTS2 structure (which must also be initialized). You must call, GetBindOptions and then, SetBindOptions to do this correctly.
Here's what the code might look like:
IUnknown *ppvUnknown = NULL ;
CLSID YourClassCLSID ;
BIND_OPTS2 BindOptns ;
IBindCtx *BindContx = NULL ;
IMoniker *ClsMoniker = NULL ;
ZeroMemory (BindOptns, sizeof (BIND_OPTS2)) ;
HRESULT hrBnd = CreateBindCtx (0, &BindContx) ;
if (hrBnd != NULL)
{
BindContx->GetBindOptions (&BindOptns) ;
BindOptions.cbStruct = sizeof (BIND_OPTS2) ;
BindOptions.dwClassContext = CLSCTX_ALL ;
BindContx->SetBindOptions (&BindOptns) ;
HRESULT hrMonikr = CreateClassMoniker (YourClassCLSID, &ClsMoniker) ;
if (FAILED(hrMonikr))
{
}
HRESULT hrBndObj = ClsMoniker->BindToObject (BindContx, NULL, IID_IUnknown, reinterpret_cast<void**>(&ppvUnknown)) ;
if ((SUCCEEDED(hrBndObj) && (ppvUnknown != NULL))
{
HRESULT hrRunning = ClsMoniker->IsRunning (BindContx, NULL, NULL) ;
}
else
{
ClsMoniker->Release () ;
BindContx ->Release () ;
ppvUnknown ->Release () ;
}
}
'MyManager' can use this technique to determine if an instance of 'MyProgram' is already running, and instantiate another one for it's exclusive use. You should read up on exactly what is the default COM procedure for the MFC class that you are using.
However, COM does NOT allow you to implement IUnknown, QueryInterface to return a NULL interface pointer, conditionally, if it has already returned a valid interface pointer. In other words, you cannot rewrite the QueryInterface implementation of 'MyProgram' to return a NULL pointer based on some determination, unless it ALWAYS does that.
Also, you might want to read this article: The COM Macro-Architecture Topology[^], which explains the default COM behavior with activation requests.
|
|
|
|
|
Thank you for the information. One thing that I found was that 'ClsMoniker->IsRunning' is not supported on a class Moniker. I believe I understand the default COM behavior, but I am looking at a way to change that behavior. I think I understand the code you provided, but I am still not sure how it can help me.
I have more then 1 instance of 'MyProgram' running. I want to be able to have 'MyManager' connect to an instance, that I select, of already running 'MyProgram'. I am not sure how to tell COM which instance to use. It always uses the instance that was started first, but that may not be the one I want. I have tried getting the Runable object, but that does not seem to work.
You talked about having 'MyManager' instantiate a copy of 'MyProgram' for it's exclusive use. That would work, but I am not sure how to do that. It always connects to the instance of 'MyProgram' that was started first. I need 'MyManager' to instantiate 'MyProgram' and connect to that instance, even if there is already other 'MyProgram's running.
Thanks again for you help.
|
|
|
|
|
hi to all,
i have implemented an activex control which works fine on iis server. however, when i deployed my project under tomcat and called the page with ie activex is not displayed. moreover when the page is loading, i am getting the info message about loading the activex. i see the object under C:\WINDOWS\Downloaded Program Files on some machines and on some machines i do not. I both copied the .ocx and .cab files on the same folder of my .html file. The object definition in the html is as follows;
<object id="CSTimer1" width="456" height="49<br" mode="hold"> CODEBASE="CSTimer.ocx"
CLASSID="CLSID:01B5BC0F-A51D-4C47-B3E9-3DA1099750CE">
<param name="_Version" value="65536" />
<param name="_ExtentX" value="12060" />
<param name="_ExtentY" value="1291" />
<param name="_StockProps" value="0" />
is it related with tomcat? any suggestions??
thanx in advance...
|
|
|
|
|
Hi all,
I wonder if someone here can help me, I did not have much success on msdn forums with this question.
I have the Microsoft's WebBrowser control embedded in my application and I am having trouble with the IDocHostUIHandler::GetHostInfo ( http://msdn.microsoft.com/en-us/library/aa753257(VS.85).aspx ) method. According to documentation, I should be able to pass a custom CSS file to the WebBrowser. And it works fine with IE6 and IE7, but it is ignored when IE8 is installed.
Has anyone else encountered this behavior?
Thanks for any suggestions. I even tried an example code from codeproject.com and added the css piece, but it did not worked. I'll be grateful for any idea.
Have a nice day,
Vlasta
|
|
|
|
|
I wanted to find out if it was possible to use the COM in VB .NET to receive a cell that is being changed (based on time, user values, etc) into a VB .Net program to then log the changes every few msecs into a database that I can then analise at a later date. Any help would be greatly appreciated as I cannot find anything of this type anywhere else.
Thanks
|
|
|
|
|
hello,
i have successfully created a button in a toolbar of Outlook Express.
on the click of this button i should be able to get a email address of
the sender from the selected message.
i am getting the name of the sender but not the "Email-Address".
thanks,
Nikhil
|
|
|
|
|
Hi all,
I'm quite new in COM but unfortunately I have to talk to a Delphi Application which supports a COM interface to other applications.
Invoking Methods of this COM Server or reading some attributes works quite well in my test application, in pseudo code it looks like
<br />
CoInitialize( NULL );<br />
...<br />
CLSIDFromProgID( L"DVBViewerServer.DVBViewer", & clsid );<br />
...<br />
GetActiveObject( clsid, NULL, & pIUnknown );<br />
...<br />
pIUnknown->QueryInterface( IID_IDispatch, ( void * * ) & pIDispatch );<br />
...<br />
pDispatch->GetIDsOfNames( IID_NULL, & szProperty, 1, LOCALE_SYSTEM_DEFAULT, & DispId );<br />
pDispatch->Invoke(DispId, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dp, &vReturn, NULL, NULL);<br />
...<br />
vReturn.pdispVal->GetIDsOfNames( IID_NULL, & szMeth, 1, LOCALE_SYSTEM_DEFAULT, & dispID );<br />
...<br />
vReturn.pdispVal->Invoke( dispID, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, & dp20, & varRetVal, NULL, NULL );<br />
<br />
On the other hand, it doesn't seem so easy to get an own c++ function registered as a COM Eventhandler.
I've read all articles and tutorials, but i didn't found a small and simple tutorial how to get from the COM/Delphi interface description
<br />
type ICOMServerEvents = dispinterface(IDispInterface)<br />
procedure onAddRecord(ID: Integer);<br />
procedure onClose;<br />
...<br />
to something like
<br />
void CALLBACK onAddRecord( int iID) {}<br />
Do I have do define an interface
<br />
interface ICOMServerEvents : IUnknown<br />
{<br />
virtual void __stdcall onAddRecord( int iID) = 0;<br />
...<br />
}<br />
then derive a class
<br />
class CCOMServerEvent : public ICOMServerEvents<br />
{<br />
virtual void __stdcall onAddRecord( int iID) { TRACE("AddRecord"); }<br />
....<br />
}<br />
and use something like createinstance to get this connected to the server ?
What is the simplest way ?
BR,
Andi
|
|
|
|
|
Hi,
From the use of the word 'Events' in 'ICOMServerEvents' I'd think you need to look up Conection Points, otherwise known in as Events. From a quick search in the CP Articles I picked this [^] at random. There are lots of other tutorials and explanations of Connection Points around.
|
|
|
|
|
Hi,
thank you for your quick answer. I've read this article before, and I've implemented a Event Sink in that way, how it was described in Microsofts AtlEvent[^] Example. One another good article I've found in another site Dispinterface vs. Events and Runtime Sinks[^] has brought me on the idea with the special MT CoInitialize flags.
But the reality is, if I follow the steps to create the event handler as shown in the MS or guru example, everything goes o.k. (Connection is built, every function gives S_OK). Server is found, etc., even DispEventAdvise gives S_OK.
- But I didn't receive any event - muahhh
The events are really fired from the source, because some Delphi Demo Client App shows me the events.
So I came onto the idea that DELPI COM is something special. Do you know a method to deep TRACE the COM event handling ?
<br />
1. Create a new class derived from IDispEventImpl. <br />
class CSinkObj : public IDispEventImpl<IDC_OBJ, CSinkObj><br />
{<br />
...<br />
}<br />
<br />
2. Specify the source interface ID, ...<br />
<br />
class CSinkObj : public IDispEventImpl<IDC_OBJ, CSinkObj,<br />
&DIID__EventSink,
&LIBID_COMOBJLib,
1,
0>
{<br />
...<br />
}<br />
<br />
3. Add a sink map ... SINK_ENTRY():<br />
<br />
class CSinkObj : public IDispEventImpl<IDC_OBJ, CSinkObj,<br />
&DIID__EventSink, &LIBID_COMOBJLib, 1, 0><br />
{<br />
public:<br />
BEGIN_SINK_MAP(CSinkObj)<br />
SINK_ENTRY_EX(IDC_SRCOBJ, DIID__EventSink, 1, OnTick)<br />
END_SINK_MAP()<br />
...<br />
}<br />
<br />
4. Add event handler methods to your class ( __stdcall calling convention):<br />
<br />
class CSinkObj : public IDispEventImpl<IDC_SRCOBJ, CSinkObj3><br />
{<br />
public:<br />
BEGIN_SINK_MAP(CSinkObj)<br />
SINK_ENTRY(IDC_SRCOBJ, 1 , OnTick)<br />
END_SINK_MAP()<br />
<br />
HRESULT __stdcall OnTick(long tickcnt)<br />
{<br />
ATLTRACE("CSinkObj::OnTick\n");<br />
return S_OK;<br />
}<br />
...<br />
}<br />
<br />
5. Connect the sink to the COM object :<br />
pSinkObj->DispEventAdvise(pUnk);<br />
<br />
6. Disconnect the sink by calling DispEventUnadvise():<br />
<br />
pSinkObj->DispEventUnadvise(pUnk);<br />
BR,
Andreas
|
|
|
|
|
I've found my fault by my self, using the Rubber Duck method below (thanks your help Jonathan ):
We called it the Rubber Duck method of debugging. It goes like this: 1) Beg, borrow, steal, buy, fabricate or otherwise obtain a rubber duck (bathtub variety) 2) Place rubber duck on desk and inform it you are just going to go over some code with it, if that’s all right. 3) Explain to the duck what you code is supposed to do, and then go into detail and explain things line by line 4) At some point you will tell the duck what you are doing next and then realise that that is not in fact what you are actually doing. The duck will sit there serenely, happy in the knowledge that it has helped you on your way. Works every time. Actually, if you don’t have a rubber duck you could at a pinch ask a fellow programmer or engineer to sit in.
My fault was, that I have used the VS generated UUIDs for coclass & its library, because I thought, they must be different from the source's TLB, because I took a specialization from the servers interface.
But if I use the server IDs it works.
Happy greetings,
Andreas
|
|
|
|
|
I am having a text file as a resource in my project. Now when i run the exe, i want to read a text file from anywhere on the file system and update my resource. I have tried UpdateResource, it succeeds, but does not reflect the changes in the resource file of my binary.I have used EndUpdateResource also. Please help me out....
|
|
|
|
|
Hi all...I have a server Component which is a inprocess exe and a dialog based client... Server has 3 methods ..One method returning CPU usuage , other method returning the memory usuage and the third method just returns a BSTR String... In client code ,when I try to call the third method(returns the BSTR srting) I get Access Denied error.. How can I overcome this issue...
Thank u..
Here's my sample code..(Client).
CoInitialize(NULL);
HRESULT hr = NULL,hr1=NULL;
IGetStatus* pGetStatus;
hr=CoCreateInstance(CLSID_GetStatus,NULL,CLSCTX_LOCAL_SERVER,IID_IGetStatus,(void **)&pGetStatus);
if(SUCCEEDED(hr))
{
try
{
_bstr_t str1 ;
str1 = pGetStatus->GetBeat();
char* Text = _com_util::ConvertBSTRToString(str1);
AfxMessageBox(Text);
::SysFreeString(str1);
UINT CPU_Info = pGetStatus->Get_CPU();
int Memory_Info = pGetStatus->Get_Memory();
}
catch(_com_error e)
{
AfxMessageBox(e.ErrorMessage(),MB_ICONSTOP);
}
}
|
|
|
|
|
RevathiRamakumar wrote: I have a server Component which is a inprocess exe
Ummm, I think not... If it's an EXE it is out-of-process.
Try and change CLSCTX_LOCAL_SERVER in the call to ::CoCreateInstance() into CLSCTX_INPROC_SERVER and the call will most likely fail.
RevathiRamakumar wrote:
IGetStatus* pGetStatus;
....
str1 = pGetStatus->GetBeat();
It looks like you're using the raw COM interface and not a wrapper for dispatch interfaces. If this is the case you're violating the COM rule that says every function must return a HRESULT . Thus you cannot return a string, it has to be an output parameter.
How is your IGetStatus::GetBeat() interface function declared in the IDL-file?
What attributes have you added to your IGetStatus interface? "oleautomation"?
Is your IGetStatus interface declared as a "dispinterface" or just "interface" in the IDL-file?
I would suggest that you declare your IGetStatus interface as an ordinary interface using the "interface" keyword in the IDL-file, but add the "oleautomation" attribute in order to use typelib-marshalling. The benefit is that you don't have to worry about how the interface is marshalled, but the downside is that you're restricted to use automation compatible data types such as BSTR (any type that can be contained in a VARIANT ).
Then your interface function declaration should look something like this:
HRESULT GetBeat( [out] BSTR* pTheBeat ); and you should call it some way similar to this:
BSTR* pbstrTheBeat = NULL;
hr = pGetStatus->GetStatus( pbstrTheBeat );
if( SUCCEEDED( hr ) )
{
::SysFreeString( pbstrTheBeat );
}
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|
Hi.. Thanks for the help extended...I have added oleautomation...its an ordinary Interface.. I tried ur suggestion.. But,in the following code I get error ...
BSTR* pbstrTheBeat = NULL;
hr = pGetStatus->GetBeat(pbstrTheBeat);
C2660: 'Get_Beat' : function does not take 1 parameters
The prototype and the definition are similar and they just return a single value only..And also the intelligence shows me two methods ie
1.HRESULT raw_Get_Beat(BSTR*) and
2._bstr_t GetBeat().
When I use hr = pGetStatus->raw_Get_Beat(pbstrTheBeat) I dont get any error..But, HRESULT fails..
Why is that so?I dont understand...Can u please help me..
Thank u..
modified on Tuesday, March 31, 2009 2:33 AM
|
|
|
|
|
You probably have the "dual" attribute added to the interface.
If this is the case you should remove it.
The dual attribute mean that a client can use the interface as an ordinary COM virtual table based interface and as a dispatch (automation) interface.
Read more here[^].
When the typelib is imported and the interface is a "dual" interface, two functions will be declared for each interface function; one for the dispinterface and one for the "raw" virtual table based interface.
If you add the "raw_interfaces_only" attribute to the #import you tell the preprocessor to declare functions only for the vtable based part of the interface.
Read more here[^].
RevathiRamakumar wrote: When I use hr = pGetStatus->raw_Get_Beat(pbstrTheBeat) I dont get any error..But, HRESULT fails..
If you add the "raw_interfaces_only" attribute, the "raw" prefix will be removed from the name of this function.
So, the call fails....
What is the error code and what do you suspect may be wrong?
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|
Thank u..Ho it seems I need to know alot...I tried the following...
BSTR* pbstrTheBeat = NULL;
hr1 = pGetStatus->raw_Get_Beat(pbstrTheBeat);
if( SUCCEEDED( hr1 ) ) //-2147023116
{
AfxMessageBox("Text");
}
else
{
_com_error e(hr1);
AfxMessageBox(e.ErrorMessage());
}
And I got the error message ...A null pointer reference was passed to stub... hr1=-2147023116
|
|
|
|
|
RevathiRamakumar wrote: And I got the error message ...A null pointer reference was passed to stub...
Oops... Sorry about that, must have been a temporary brain hiccup.
The error makes perfectly sense if you think about it, how is the caller suppose to get the result....
Of course it should be:
BSTR bstrTheBeat = NULL;
hr = pGetStatus->GetBeat( &bstrTheBeat );
if( SUCCEEDED( hr ) )
{
::SysFreeString( bstrTheBeat );
bstrTheBeat = NULL;
}
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|
Thank u...
NO It doesn't work...
error C2664: 'raw_GetBeat' : cannot convert parameter 1 from 'unsigned short *** ' to 'unsigned short ** '
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast.
So, I tried this one..
hr1 = pGetStatus->raw_GetBeat((BSTR*)&pbstrTheBeat);
But, still I get ACCESS DENIED...
|
|
|
|
|
RevathiRamakumar wrote: cannot convert parameter 1 from 'unsigned short *** ' to 'unsigned short **
Read my code snippet again. The type has changed from BSTR* to a simple BSTR compared to the code snippet in my previous post.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|
Ho extremely sorry...Now no errors..But y is the access denied...sorry to bug u alot..Thank u..
|
|
|
|
|
Is there something to do with the firewall settings...I'm not sure if firewall settings has to something with COM..Thank u in advance..
|
|
|
|
|
RevathiRamakumar wrote: Is there something to do with the firewall settings...I'm not sure if firewall settings has to something with COM.
If you would be using DCOM, Distributed COM, where the server and client resides on different machines, a network firewall may present some troubles.
Some firewalls, such as Comodo, could prevent the client process from accessing the server process or ask the user for action.
However, I very much doubt any firewall would cause any troubles in your case since you're able to successfully call other functions of the same interface of the same server.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|
RevathiRamakumar wrote: Ho extremely sorry...
No worries, you were not the only one who made a mistake.
RevathiRamakumar wrote: Now no errors..
Good.
RevathiRamakumar wrote: But y is the access denied
What are you trying to do when do you experience this?
Have you tried to debug it and step though the code?
Does the BSTR variable point to a valid string?
What does the source code look like where you get this error?
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|
Thanks for ur reply..Actually I couldn't call any of the methods in the interface..Whenever I try to call any of these methods I get Access denied exception...
HRESULT hr1;
hr1 = pGetStatus->raw_Get_Beat(&pbstrTheBeat);//This raises Access Denied
/*BSTR does not point to a valid String..It shows 0xc000000 */
if( SUCCEEDED( hr1 ) )
{
AfxMessageBox("Text");
}
else
{
_com_error e(hr1);
AfxMessageBox(e.ErrorMessage());
}
}
catch(_com_error e)
{
AfxMessageBox(e.ErrorMessage(),MB_ICONSTOP);//This tells me ACCESS IS DENIED
}
I tried to debug..Actually the purpose is that I need to get the CPU and Memory usuage of a Server machine which is remote..I wanted to try it on a local machine first and then to implement on a remote machine..But, in the local machine itself I get Access Denied..Can u please tell me how to solve this...Thank u in advance...
|
|
|
|
|
RevathiRamakumar wrote: Actually I couldn't call any of the methods in the interface..Whenever I try to call any of these methods I get Access denied exception...
I assumed you were able to call the two other functions successfully since you wrote
RevathiRamakumar wrote: when I try to call the third method(returns the BSTR srting) I get Access Denied error.
in your original post.
I suggest you unregister the server, rebuild the complete server and register it.
Clean up the client project, and especially the files generated when #importing the typelib of the server.
Rebuild the client, debug it and verify that the server can be successfully created, i.e. make sure that the HRESULT returned from ::CoCreateInstance() equals zero and your interface pointer points to a valid address.
Also make sure you have the named_guids attribute when #importing since this will declare the CLSID and IIDs correctly for you.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|