|
Hello Maverick,
We'll continue with part two of our discussion :
11. How do we resolve the problems ? In short, we must marshall VB's Event dispinterface so that when MyNetworkClass::OnRequestConnection() is called, the dispinterface is ready to be used. The following is a summary of how we can achieve this :
11.1 In the MyNetworkClass C++ class, create 2 STL vectors :
typedef vector<LPSTREAM> ISTREAM_VECTOR;
typedef vector<LPDISPATCH> IDISPATCH_VECTOR;
ISTREAM_VECTOR m_vectorStream;
IDISPATCH_VECTOR m_vectorDispatch;
The first vector is used to hold IStream pointers. When COM marshalls interface pointers, it uses IStreams to temporarily hold interface data. The same IStreams are used to unmarshall interface pointers in destination threads.
The second vector is used to hold IDispatch pointers. Each IDispatch pointer points to a dispinterface of a VB Event Object.
11.2 In the MyNetworkClass C++ class, instead of internally holding a pointer to an interface of your ATL COM Object, hold a pointer to the C++ class that implements the ATL COM Object, e.g. :
instead of :
IMyCOMObjectInterfacePtr m_spIMyCOMObjectInterface;
or
IMyCOMObjectInterface* m_pIMyCOMObjectInterface;
use :
CMyCOMObject* m_pCMyCOMObject;
11.3 In the MyNetworkClass C++ class, create a function like the following :
long MyNetworkClass::MarshalEventDispatchInterfaces ()
{
int nConnections = (m_pCMyCOMObject -> m_vec).GetSize();
int nConnectionIndex = 0;
HRESULT hrTemp = S_OK;
long lRet = 0;
for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
{
IStream* pIStreamTemp = NULL;
m_pCMyCOMObject -> Lock();
CComPtr<IUnknown> sp = (m_pCMyCOMObject -> m_vec).GetAt(nConnectionIndex);
m_pCMyCOMObject -> Unlock();
IDispatch* pIDispatchTemp = reinterpret_cast<IDispatch*>(sp.p);
if (pIDispatchTemp)
{
// Create stream for marshaling IDispatch to search thread.
hrTemp = ::CoMarshalInterThreadInterfaceInStream
(
IID_IDispatch, // interface ID to marshal
pIDispatchTemp, // ptr to interface to marshal
&pIStreamTemp // output variable
);
// Place stream in member variable where COM thread will look for it.
// No need to call Release() on pStream. They will be Release()'d
// when we later call CoGetInterfaceAndReleaseStream().
if (pIStreamTemp)
{
m_vectorStream.push_back(pIStreamTemp);
}
// Also no need to call pIDispatchTemp->Release(). pIDispatchTemp is a temporary pointer
// to the IUnknown pointer in sp. And sp will automatically call Release() on its
// internal IUnknown pointer.
}
}
return lRet;
}
This function iterates through the connection points contained in the connection points vector (m_vec) of the COM Object. Note that a COM Object can be connected to more than one connection point.
Each connection point is an implementation of a specific event set. If your COM Object only implements one event set, then each connection point is of the same dispinterface.
We get each connection point, cast it into an IDispatch pointer and then call the Win32 API CoMarshalInterThreadInterfaceInStream() on it. The CoMarshalInterThreadInterfaceInStream() function will take the IDispatch pointer and create an IStream object that stores data pertaining the the IDispatch interface pointer. We then store the output IStream pointer into our stream vector m_vectorStream.
Call this function early, e.g. when your COM Object is first created or before you call the Network SDK function that start the thread. The idea is to prepare the dispinterfaces for marshalling into the Network SDK thread.
11.4 In the MyNetworkClass C++ class, create another function like the following :
long MyNetworkClass::UnMarshallEventDispatchInterfaces ()
{
ISTREAM_VECTOR::iterator theIterator;
int iIndex = 0;
HRESULT hrTemp = S_OK;
long lRet = 0;
// Unmarshal interface pointers
for (theIterator = m_vectorStream.begin(); theIterator != m_vectorStream.end(); theIterator++)
{
IDispatch* pIDispatchTemp = NULL;
IStream* pIStreamTemp = NULL;
// Get stream pointer.
pIStreamTemp = (*theIterator);
if (pIStreamTemp)
{
// Use stream pointer to create IDispatch pointer that we can call from this thread.
hrTemp = ::CoGetInterfaceAndReleaseStream
(
pIStreamTemp, // stream containing marshaling info
IID_IDispatch, // interface desired
(void**)&pIDispatchTemp // output variable
);
// Note that at this time, pIStreamTemp will be Release()'d and will no longer
// be valid.
// Put resulting IDispatch pointer in IDispatch pointers vector.
if (pIDispatchTemp)
{
m_vectorDispatch.push_back(pIDispatchTemp);
pIDispatchTemp -> AddRef(); // Since we have added pIDispatchTemp into a collection, we have an additional reference to it.
}
}
}
return lRet;
}
This function iterates through each IStream pointer stored in MyNetworkClass::m_vectorStream and uses it in a call to the Win32 API CoGetInterfaceAndReleaseStream().
This call will return to us a pointer to our IDispatch pointer from the IStream object.
After obtaining this IDispatch pointer, we will store it into our IDispatch vector m_vectorDispatch.
Call this function in the MyNetworkClass::OnRequestConnection() function. But call it only once so that the IDispatch vector is filled up only one time. After calling this function, we can invoke the specific event of your event set (by calling the IDispatch::Invoke() method).
11.5 We invoke the specific event by iterating through the IDispatch pointers contained in the IDispatch vector :
HRESULT FireEvent()
{
IDISPATCH_VECTOR::iterator theIterator;
int iIndex = 0;
CComVariant varResult;
CComVariant* pvars = new CComVariant[1];
HRESULT hrRet = S_OK;
for (theIterator = m_vectorDispatch.begin(); theIterator != m_vectorDispatch.end(); theIterator++)
{
IDispatch* pIDispatch = (*theIterator);
if (pIDispatch)
{
VariantClear(&varResult);
pvars[0] = 100;
DISPPARAMS disp = { pvars, NULL, 1, 0 };
pIDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
}
}
delete[] pvars;
hrRet = varResult.scode;
return hrRet;
}
The above function is an example of invoking an event that takes a long parameter (value 100).
Modify the above examples to suit your particular case, Maverick. Contact me again if you need further clarifications. Please note that my examples are for illustration purposes. I have emphasised on principles and have not paid attention to details like syntax errors or memory considerations.
You will, of course need to do things like clearing the vectors and ensuring that the functions MarshalEventDispatchInterfaces() and UnMarshallEventDispatchInterfaces() are called appropriately at the right time and place.
Best of luck, Maverick,
Bio.
|
|
|
|
|
Hello!!!
Thank you very much for the explanation.. I did have an additional info
about COM...
Well for your information, what i did was I made a COM Object using ATL in VC++, and i think it already inhertis an IDispInterFace Interface...
<br />
class ATL_NO_VTABLE CCsSdp : <br />
public CComObjectRootEx<CComSingleThreadModel>,<br />
public CComCoClass<CCsSdp, &CLSID_CsSdp>,<br />
public ISupportErrorInfo,<br />
public IConnectionPointContainerImpl<CCsSdp>,<br />
public IDispatchImpl<ICsSdp, &IID_ICsSdp, &LIBID_BTRFCOMMLib>,<br />
public CProxy_ICsSdpEvents< CCsSdp >,<br />
public CNetwork
{<br />
....<br />
...<br />
...<br />
DECLARE_REGISTRY_RESOURCEID(IDR_CSSDP)<br />
<br />
DECLARE_PROTECT_FINAL_CONSTRUCT()<br />
<br />
BEGIN_COM_MAP(CCsSdp)<br />
COM_INTERFACE_ENTRY(ICsSdp)<br />
COM_INTERFACE_ENTRY(IDispatch)<br />
COM_INTERFACE_ENTRY(ISupportErrorInfo)<br />
COM_INTERFACE_ENTRY(IConnectionPointContainer)<br />
COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)<br />
END_COM_MAP()<br />
...<br />
...<br />
Is this ok?
i just added the Network Class to its base class of the Com Object.. Is this a nice idea? the multiple inheritance issue here? or do i have to make the Network Class/Object a member variable of the Com Class?
|
|
|
|
|
Hello Maverick,
I think adding CNetwork to the CCsSdp class is fine. I also think that it would be a better idea to send you a working example which contains concepts and constructs you mentioned (e.g. the base class with the callback function).
I'll try to come up with a good, simple and consise example and email it to you. Please give me some time.
Thanks, Maverick.
Bio.
|
|
|
|
|
Well thanks... that would be good...
First, what header files do i have to include so that i can include the definitions for
the vector.. I already included "include <vector>" but im still getting compiler errors and it points to this line:
typedef vector<lpstream> ISTREAM_VECTOR;
the error message is:
error C2143: syntax error : missing ';' before '<'
But actually im getting the idea...
i am incorporating your code MarshallInterface() and UnmarshallInterface() now...
so i hope that by incorporating your code, my program will work...
Now with regards to the Fire_Event() code you made, i think ATL has automatically done it for me so.. i don't have to deal with it..
And what about clearing the vectors? i can clear them on the destructor code ryt?...
And about calling the functions at the ryt time, i just have to follow your instructions on when to call because you said it on your explanation where to call it ryt?
One more thing, can you suggest a good book regarding COM? what r u reading ryt now?
thank you very much!!!
|
|
|
|
|
Hello Maverick,
To use the STL vector class, #include <vector> and declare the namespace you will be using, e.g. :
#include <vector>
using namespace std;
>> Now with regards to the Fire_Event() code you made, i think ATL has automatically done it for me so.. i don't have to deal with it..
A strong word of caution here, Maverick. You must not use the same wizard-generated code for the event firing function. The wizard-generated code will use the original non-marshalled IDispatch pointers contained in your COM Object. --This is the cause of the crash.--
You must use the marshalled IDispatch interfaces. I think an example program will best illustrate the importance of marshalling. I'll also put in some code to illustrate clearing of vectors. Just give me a few days' time.
Best Regards,
Bio.
|
|
|
|
|
Hi!!
I've been reading your explanations thoroughly... and after reading your explanations, i tried a different search keywords to search for a solution.. So I tried the keywords "interface marshalling".. and guess what? i found a solution in microsft knowledge base.... They made a custom class for CProxy_EVENT class... so basically they supplied a header file.. and then i used that Custom class in place of CProxy_EVENT class generated by ATL Wizard.. and that's it, my code is working now... I forgot to Knowledge Base number but i will post it here soon.. But I guess you know that already?
Well thanks for the info, and i would like to have more information about COM and threads, apartment as i will have to use multiple com Objects soon...
thanks very much
|
|
|
|
|
Hello Maverick,
That's great news, Maverick. By the way, I've also just finished my example program for you. But it's just as good that you have found a solution in MSDN.
However, if you should need my sample, please do not hesitate to email me directly. My email is :
bllim@singnet.com.sg
Best of Luck, Maverick,
Bio.
|
|
|
|
|
How to use the EnumConnectionPoints, IEnumConnectionPoints in connectable components
any example or sample which can help me out....
thanks in advance
kabir chugh
|
|
|
|
|
IConnectionPointContainer* pCont;
pUnk->QueryInterface(IID_IConnectionPointContainer, (void**)&pCont);
IEnumConnectionPoints pEnum;
pCont->EnumConnectionPoints(&pEnum);
IConnectionPoint* pConnPoint;
while(pEnum->Next(1, &pEnum, NULL) == S_OK) {
DoStuffWithConnectionPoint(pConnPoint);
pConnPoint->Release();
}
(error handling and irrelevant ->Release()'s omitted)
IEnumConnectionPoint follows the standard patterns as all IEnumXXX interfaces does. See the topic on IEnumXXX in the MSDN library.
--
<british-accent>Pass the jam, would you?
|
|
|
|
|
Hmmm,
I have read this chapter "Structured storage and compound files" from not soo good INSIDE OLE by kraig. I am not able to compile the source code given with this chapter, it needs inole.dll which i can't seem to find.
So kindly tell me how to compile it or direct me to some other link etc where i can see a working example of "Structured storage and compound files" in C++ (not using mfc or ATL).
Thank you.
|
|
|
|
|
|
I'm trying to implement the IProcessInitializer interface implemented in COM+ 1.5. I've implemented it in a COM+ DLL created in VC++ 6, using ATL objects, and included the comsvcs.h header file in my app.
When trying to run the object via ASP I get a 'No such interface supported' message. I'm running this on a Windows Server 2003 machine.
Any ideas?
|
|
|
|
|
I know nothing about COM, COM+, ActiveX, OLE or any of these technologies. I've never programed in Visual C++ before, and I have no intention in doing so. However, I have experience in Delphi and C#.
I need to know about COM, etc. Which book will help me grasp these technologies without delving into VC++?
Sammy
"A good friend, is like a good book: the inside is better than the cover..."
|
|
|
|
|
Essential COM by Don Box is a good book. But I guess C++ knowledge is kind of a prerequisite for that book. Inside COM by Dale Rogerson.
But still, knowing C++ doesn't do you any harm to fully understand COM.
--
<british-accent>Pass the jam, would you?
|
|
|
|
|
Jörgen Sigvardsson wrote:
But still, knowing C++ doesn't do you any harm to fully understand COM.
???
Tech.Support : Mam, is your pc running under windows?
Customer : No actually its close to the main door.
|
|
|
|
|
COM evolved from C++ virtual table pointers. If you already understand the vtable concept in C++, you can often skip the first 2-3 chapters in a COM-book, and you don't have to give "the interface concept" a second thought as it's as natural as air.
--
<british-accent>Pass the jam, would you?
|
|
|
|
|
Thanks to nice articles on the site I was able to get my COM project going nicely. I decided to do this without ATL/MFC as my needs are quite basic and I really want to understand what I'm doing. I'm not saying ATL/MFC are bad, I've worked with MFC for years so this really isn't about what's good and what's bad, I just chose this approach and try to live with my decision.
Enough of introduction, here's the problem. Being new to COM, I'm having some problems in adding support for every interface required by Internet Explorer to use it. IDispatch and invoke seem to work nicely, but do I need something more?
Do I need to mark the component safe for scripting if I sign the code?
Also, I need some interoperability with another ActiveX component, namely Macromedia Flash. I'd be happy to handle this via javascript functions, but how do I retrieve the scripting host and is there a solution which would work on other browsers than IE?
|
|
|
|
|
If all you need is to invoke few methods in your component, then there is no need to implement other interfaces, IDispatch is enough (although ISupportErroInfo could be useful for error reporting)
Whether to mark it "safe for scripting" or not is up to you. If you won't, the IE behavour will depend on IE security setting "SCript ActiveX controls marked safe for scripting" : "disable", "prompt", "enable"
Edward
|
|
|
|
|
Hi Folks,
If I declare an object tag with a class ID, I get statement completion in Interdev. If I use a ProgID I don't
I'd rather use PROGID's - any way around this?
Davy
My Personal Blog - Homepage. Scottish News - Angus Blog, Perth Blog and Dundee Blog
|
|
|
|
|
I've written a Word automation appilcation in C#, and I'd like to capture the OnClose Event....I know thats not the name but I don't know what else to cal it. Basically I open an instance of word and open a document, if the user tries to close the word window I'd like to create a small question dialog box, and thoughts ?
|
|
|
|
|
I'm developing a ActiveX in MFC Visual C++ 6.0 (Not ATL) and I have a serius problem and I can't find documentation in books or Internet.
How can I save a CArray into the persistence of a ActiveX????? (I mean in a PX_Blob function)
A Thousand Thanks.
Diego
|
|
|
|
|
Serialize it to a CArchive ?
You'd be better off asking in the Visual C++ forum.
|
|
|
|
|
Yes Serialize to a CArchive and the pass the handle (HGLOBAL) of that archive to the PX_Blob.
And of course recover that CArray later.
Thanks.
|
|
|
|
|
Hi,
I am currently using Visual Studio version 6.0 for C++/MFC development and I want to move to the new IDE Visual Studio .NET and continue learning COM and ATL C++ developlment.
QUESTION
Can I develop regular COM/ATL C++ code using the new VS.NET? I should say that I am new to COM programming so I don't want to make things more difficult.
So my question is really - I would like to continue the same C++/MFC/COM/ATL coding but using the new compiler and IDE. Is that possible, without too much grief?
Thanks
Mike
|
|
|
|
|
|