|
COM is Microsoft's binary object model. It is an interface based programming architecture along with a set of runtime services.
|
|
|
|
|
|
I'm calling an delphi ocx control from a visual c++ application (not MFC).
(CoInitialize, CoCreateInstance,...and so on). ActiveX functions are complete available.
I try now to connect my c++ window to this activeX, so that all activeX routines will paint there.
How can I do this ? (any sample code ?)
Do I have to write a complete OleContainer or will some only simple routines work as well ?
PS:
The ocx does some polyline paint actions, which I will save via activex routines.
Thanks
|
|
|
|
|
I was making a program which shows u the streams and storages within a compound file. Can anyone tell me how to delete the root storage object. Since normally you delete any substorage using its parents pointer.....but how to delete the root storage object which has no parent.
|
|
|
|
|
Simple. You delete the entire stg file. It's analogous to deleting the root directory on a hard drive. The only way to delete it is to remove the partition on which the root directory is located.
--
<british-accent>Pass the jam, would you?
|
|
|
|
|
Hello everyone. Before being handed this project I have never worked with COM besides as an ActiveX user in VB, but thankfully I had a good MFC/C++, SQL Server background. Now I am responsible for the maintenance of a very large 3 tier application. After complaining to my boss I was not the right guy for the job (and he not caring very much), read a couple of books, did the tutorials, and done already quite a bit of debugging and fixes to the application. Backend is SQL Server 2000 (140 tables exposed through 400+ stored procedures, app never does SQL queries), middle tier is 28 components in a COM+ application and a MFC GUI, also some ATL services and device drivers involved, all which talk to the middle tier.
The software shop that originally did this product closed down and left us the latest version with many issues. I have solved most of them. We where having performance problems (very slow GUI, some deadlocks) so I ran some traces and found out that stored procedures where taking forever to execute - there where way too many indexes – about on everything. Doing things right (in my opinion) I was able to take the execution of the 100 longest running queries down from over a minute to 11 seconds. Watching the execution plan of the most commonly executed stored procedures, the indexes created where very badly chosen. I dropped around 100 non-clustered indexes and created a few, hand picked ones. Of course insert and updates are working very well now. Also the GUI is very usable now, from taking 10 or more seconds to change a screen or fill up some text boxes to almost instantaneous response, as it should be and was before this version.
Everything seemed fine until I ran it in a test computer. Then hell broke loose .
What I found out is that, just by dropping the non-clustered indexes, a call to a method in a component which calls a stored procedure is causing an ADO time out (ADO error -2147217871 ‘Execution aborted because a resource limit has been reached; no results have been returned’). Before dropping the indexes the call ran fine. If I go to the Com+ application, choose that component and change transactions from required to supported, the problem ‘goes away’ and calling the same method on the same component takes a few seconds. Changing back transactions being to required brings back the problem. I’ve already debugged and jumped to the component where the call is done and the call fails when the stored procedure is executed in ADO, so it is not a problem of the code of the component but somewhat related to Com+ and MTS configuration (or the component use of them), from what I can guess with my limited knowledge and experience.
The stored procedure is quite a complex one which creates a few temporary tables and then builds the returned records from them. In both situations, running the query the component runs in query analyzer, with the same user and against the same server, executes in a few seconds as it should. With the indexes dropped, the query runs FASTER than before.
In brief: before dropping the indexes, the call to the method ran with no problem both with transactions required and supported. After dropping the non-clustered indexes, the call to the method times out when transactions are required, and runs correctly when admited.
My MTS/COM+ and transaction knowledge is very limited, as you can guess – I have no practical knowledge in the field, just checked some books and debugged/modified the COM objects in this application. From what I have said above I can suspect some deadlock/race situation happening when COM+ transaction is happening, but I do not see how this is happening – this is the only call happening at the time, and it calls just one stored procedure. If I change the ADO command timeout, the time until I get the error just changes to reflect this – tried leaving the query running for as long as 10 minutes (which of course, anything over 30 seconds is very unacceptable).
Hope I explained myself well enough. Any help suggestions on how to tackle this problem would be very appreciated.
Thanks,
Juan Miguel Venturello
Edit: after a few hours of playing around, there is just A FEW indexes which I drop and causes the problem why is this????
|
|
|
|
|
Hi,
I am using a third party COM component that fires events when a certain operation has occured, like operation complete event is fired.
now i want to continue the work after the operation has been completed. i.e after the event has occured.
so i did this way... i put a bool value in the sink that is set to false when the completion event occurs.
and in the clin
CSink::OnCompleteTask(long code)
{
..
bTaskComplete = FALSE;
}
and at the place where i am calling the method.
pInterface->CopyFile(....); //this is asyncronouse operation.
while(CSink::bTaskComplete);
... it come here after the copy is complete
but the problem is that this thread is getting locked.
no events comes.???
why and how do i avoid it.
thanx.
Tech.Support : Mam, is your pc running under windows?
Customer : No actually its close to the main door.
|
|
|
|
|
Hello Prakash,
Your problem sounds like a typical Apartment-Threaded-Model COM Server/Client issue. I have provided a possible solution (jump straight to point 11 for the solution if my write-up below gets too boring) as well as an analysis of the situation :
1. Your application as well as the 3rd Party COM component is most probably Apartment-Threaded-Model based. Please let me know if this is the case.
2. When the following code is executed :
pInterface->CopyFile(....); //this is asyncronouse operation.
the 3rd party component probably starts or goes into another thread to perform its lengthy operation.
3. When the component has completed its operation, it must fire an event to your app to indicate completion of task. You have set this up by defining the CSink::OnCompleteTask() function and hooking this function to the component's connection point.
4. Everything is fine up to here. But why does your application (more precisely, the thread that calls the CopyFile() method) hang up ? Why does the CompleteTask event not fire ?
5. Quite simply, it's the endless while loop that is causing the problem :
while(CSink::bTaskComplete);
I'll explain this next.
6. When the 3rd Party COM component has completed its task, it will need to fire the CompleteTask event. This is done by CALLING the appropriate event handling function (in your case, this is the CSink::OnCompleteTask() function). The 3rd Party COM Component will already have a pointer to this function earlier on when event handling are setup.
7. Now, the very important thing is that the 3rd Party COM component will not be able to call your event handler function (CSink::OnCompleteTask()) directly. The call to the event handler function must be SERIALIZED. That is, the call request must be put into the message queue of the client application.
8. This serialization is done to ensure thread-safety and that the private data of the 3rd Party COM Component is always intact and not subject to random modification by threads running in your application.
9. Now, when the call request (to your event handler CSink::OnCompleteTask()) is put into the message queue of your app, the message queue must continue to pump in order that the call request gets executed.
10. Due to the fact that you are in a constant while loop, not only will the call request never get through, your application's GUI will also freeze up (if the thread that calls CopyFile() is also the main GUI thread).
11. To resolve the problem, try putting in a simple "make-shift" message pump after your call to pInterface->CopyFile(...) as follows :
MSG msg;
// Dispatch all windows messages in queue.
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage(&msg);
if (CSink::bTaskComplete == FALSE)
break;
}
The above message pump will ensure that the 3rd Party COM Component's call request to your event handling function will get through. Because of this, the CSink::OnCompleteTask() function will be executed and CSink::bTaskComplete will be set to FALSE.
Once CSink::bTaskComplete is FALSE, we will break out of our while loop.
12. Give the above code a try, Prakash. And let me know how things turn out.
Best Regards,
Bio.
|
|
|
|
|
Wow your answer is just greate, and thanx for takeing so much time to anwer my query.
I dont thing there will be any problem with this solution.
Today i am not working so will let you know about the result tomorrow.
Regards,
Prakash.
Tech.Support : Mam, is your pc running under windows?
Customer : No actually its close to the main door.
|
|
|
|
|
Works like a charm.
thank you.
Tech.Support : Mam, is your pc running under windows?
Customer : No actually its close to the main door.
|
|
|
|
|
Hello Prakash,
Congratulations, Prakash . Please note that not all COM Servers and Clients will face this issue. It all depends on the threading model of the COM Server.
Some COM Servers are designed and developed using the Free-Threaded-Model. In this model, the COM Server itself will take care of thread synchronisation issues and will ensure well-synchronised access to its internal data.
The Apartment-Threaded-Model is Microsoft's default way of helping COM Server developers overcome threading issues.
Take Care and Best Wishes, Prakash,
Bio.
|
|
|
|
|
Thanx, i will keep that in mind and atleast for now i am very much satisfied with STA model.
Thanx Again.
Toughest Steel Comes From Hottest Furnance
|
|
|
|
|
Hello!
I browsed this topic and i thought you maybe the one to help me with a COM problem..
I'm making an ATL DLL from VC6.. supporting Connection Points.. and its client is a VB Program..
Now i have a function from a third party SDK...
<br />
void OnFunctionEvent()<br />
{<br />
Fire_AtlEvent()<br />
}<br />
Now, this code fragment generates an exception so the program stops here...
Now this maybe related to what you have explained in the previous messages..
Can you help why is this generating an exception, and how could i fix this?
tnx in advance
|
|
|
|
|
Hello Maverick,
Thanks very much for the question. I did some background research before writing this reply and I do anticipate more correspondence between us.
First, Maverick, some questions to help me understand your 3rd party SDK :
1. Your description for your OnFunctionEvent() function is :
//this is a derived function (pure virtual function)
//according to the documentation of the SDK,
//derived functions run on a different context from the application
//so operations inside the function must be made thread safe
This sounds to me like OnFunctionEvent() is a method that is part of a CALLBACK interface.
2. My guess is that you have to implement a class that implements the methods of this CALLBACK interface so that your 3rd party SDK can notify you of certain events.
3. If this is the case, their documentation makes sense as the callback methods (e.g. OnFunctionEvent()) can be invoked anytime and within the context of some thread not created by your application.
Please let me know if the above analysis is correct, Maverick. We'll proceed again after that.
Thanks, Maverick,
Bio.
|
|
|
|
|
Bio,
Hi,
Well you got all things right... The OnFunctionEvent is CallBack function... Actually its from a Network API, this function gets called when a maching requests a connection from the program...
2. Its not a CALLBACK interface per se, but the API has a Base Class with a mix of functions and callbacks... it just happened that the base class has a pure-virtual function OnFunctionEvent() so i have to SubClass from that base class so i can implement what to do when That Function is called...
3. Well actually the function is really called "OnRequestConnection()", it gets invoked everytime there is a request for connection... And yea, i think it creates a thread in the background not created by me...
hope to hear from you soon.. and help me soon.. pls
|
|
|
|
|
Hello Maverick,
Thanks for the reply, Maverick, and the confirmations. I have two more question to ask of you in order to confirm some other important points :
1. Can I assume that your COM Object is dual interfaced ?
2. Can I also assume that the event interface of your COM Object is a pure dispinterface ?
The above questions (esp question 2) are quite important to establish that you are using OLE-Automation-Compatible types. If this is confirmed, the solution to your problem will be much simplified.
I look forward to hearing from you again.
Thanks, Maverick,
Bio.
|
|
|
|
|
Hello Maverick,
I understand that you may be currently short of time, so I will assume that the answers to my two questions posed earlier are "YES" each (which is the typical case for ATL COM Objects which are used by VB Clients).
Now, I will further assume that you have also defined a C++ class derived from the Base Class (which contains the OnRequestConnection() virtual function) supplied by the 3rd Party Network SDK.
I certainly hope that this C++ class is NOT the same class as your ATL COM Object. Otherwise, things will get a little more complicated
Let's say the name of this class is "MyNetworkClass" and that you have instantiated this class with an object named "networkClass". I also assume that MyNetworkClass internally holds a pointer to your ATL COM Object.
Based on this scenario, my analysis of the problem is : interface marshalling. Let's explore this carefully :
1. We know that when the Network SDK calls MyNetworkClass::OnRequestConnection(), you are most likely in a thread different from any threads created by your application. The important thing is that this thread is not the thread in which your COM object is created.
2. The thread in which a COM Object is created is known as the owning thread. Your COM Object is created by your VB client and so the owning thread is most likely the VB client's main (GUI) thread.
3. Note that the interfaces of an Apartment-Threaded-Model COM Object cannot be used directly in non-owning threads (i.e. in threads other than the one which created the COM Object).
4. You can, however, use a pointer to your C++ class that implements the COM Object in any thread (as long as you can ensure thread safety). This pointer can be used to call any public functions of your C++ class. This is pure C++ programming and is not related to COM (this fact will be useful to us as you will see later on).
5. In order to use an interface of a COM Object in a non-owning thread, the interface must be marshalled to the target thread.
6. A twist to the story is that not only is your COM Object involved, but the COM Object behind the events interface is also involved. Note well that the COM Object that implements the events interface is NOT your COM Object but an object created by the VB Program !
7. Yes, that's right. When your COM Object fires an event to VB, you are actually calling a function living right inside VB code and this VB code is actually a method of a dispinterface (IDispatch Interface).
8. Your COM Object obtains a pointer to this dispinterface right at the beginning of the VB client code when your COM Object is created. At that time, alot of handshaking activities take place and one of these is the passing of a dispinterface pointer from the VB client to your COM Object.
9. This dispinterface pointer, like all other COM Interface pointers, cannot be used directly in non-owning threads. The owning thread of this dispinterface is most likely the main (GUI) thread of the VB client.
10. As of right now, we have the following situation :
10.1 When MyNetworkClass::OnRequestConnection() is called, you are in a non-owning thread.
10.2 You cannot use both your COM Object's interfaces (not too bad a problem) and the VB Event Functions' dispinterface (a bad problem).
10.3 The fact that your COM Object's interfaces cannot be used directly within the thread is not too bad a problem. In fact we do not need to use it.
10.4 The VB Event Functions dispinterface, however, -must- be marshalled to the currently executing thread (that called MyNetworkClass::OnRequestConnection()) in order to use it.
My reply is getting a little too long at this time, Maverick. I'll post another reply with the solution to the problem.
See You Later,
Bio.
|
|
|
|
|
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
|
|
|
|
|