|
As long as the threading model of the thread matches that of the component, there shouldn't be any proxy/stub work going on, and the call into the component is just a virtual function call, which isn't much more costly than a non-virtual call.
If both parts (client and component) are written in C++, or if the client is VB6, try to ensure that your component exposes a vtable interface as well as the dispatch interface - a dual interface, as it's known. This is basically a custom interface derived from IDispatch, so that callers which can early bind do so, while late-binding callers aren't shut out.
If the threading model of the component doesn't match the threading model of the caller, COM will introduce a proxy and stub to marshal between the caller and the component within the thread. This is less costly than having the component as a local server process, but more costly than a plain call.
This might happen if your component is marked 'Free' but your client is either VB6, or calls CoInitialize or CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ); to initialise COM from C++. If the component is marked 'Both', it will behave as if you'd specified 'Apartment' if the thread that created it runs in the Single-Threaded Apartment (conditions as above), or 'Free' if the creator runs in the Multi-Threaded Apartment.
I suspect, though, that your database accesses are what's killing you. Measure the actual performance of Access on that system configuration. It's going to be sluggish - indeed with only 24Mb of RAM, it's likely to be constantly swapping.
Generally it's a good idea to compile your app with the 'Minimize Size' compiler options - in VB, this is Project Properties > Compile tab > select Compile to Native Code and Optimize for Small Code.
In VC 6.0, I use the /Oxs option - full optimization, favor small code - and the /Og option - global optimizations. I also use /Gy (enable function-level linking) and specify /OPT:REF to the linker (Remove unreferenced functions). This last is the default, but it gets turned off if you generate debugging information for the release build, which I do. Specifying /OPT:WIN98 might improve your startup time on Windows 98.
Doing this can reduce your code size, which can reduce the number of page faults you take.
You could also try using a profiler to find out where the hotspots are. The Win32 SDK contains a tool called the Call Attributed Profiler, or CAP.
--
Mike Dimmick
|
|
|
|
|
Thanks Mike, for such a detailed response!
I have been working through each of your points.
My COM objects and the exe use apartment threading, and a dual interface.
(All of the components are written in VC++).
The exe initialises COM with ...
struct InitOle {
InitOle() { ::CoInitialize(NULL); }
~InitOle() { ::CoUninitialize(); }
} _init_InitOle_;
And then creates the COM objects with ...
CoCreateInstance( CLSID_SubstrateMgr,
NULL,
CLSCTX_INPROC_SERVER,
IID_ISubstrateMgr,
(void **) &g_pSubstrateMgr);
Is this the fastest method?
I have used all of the optimisation settings you suggested, although I can't see any noticable difference.
I believe you are right about the database access being the trouble spot. If the COM calls aren't that much worse than a function call, I can leave my COM interfaces alone and focus on the database access.
Thanks again for your help.
Jo
|
|
|
|
|
Keep in mind that ADO is dog slow. Use OLEDB directly and you will see 2-3 times perfomance increase. Example: I created an ASP page that used ADO to return a simple recordset(SELECT TOP 50 FROM TABLE1) and loop through the recordset to generate an HTML table. Then I wrote a C++ COM object that that accessed OLEDB directly instead of ADO from my ASP page. Then I used ATL Server and my C++ COM object.
Here's what I found: 1) ASP/ADO - 70 requests/sec 2) ASP/C++ COM/OLEDB - 290 request/sec 3) ATL Server/C++ COM/OLEDB - 370 request/sec.
The problem with ADO is that everything is a VARIANT so it can be easily used by scripting clients. OLEDB is meant to be used by compiled/early bound languages.
|
|
|
|
|
I ported Mario Zucca's grid control to WinCE using eVC++ 3.0 compiler, but making it a Full Control to that it can be easily used in c++ MFC programs like any other ActiveX control. All that went without very many problems -- except being able to fire events. So I just copied the code to do that into my project and it seemed to work.
Then I created a test C++ MFC Dialog-based program, added the control and using ClassWizzard created the events that were exposed and explained above. That too seemed to work. However, When the control attempts to fire the event, it cannot because it doesn't realize that it has any client containers!
For example: m_vec.GetSize(); always returns 0. Does anyone know how to fix this problem?
<br />
HRESULT Fire_BeforeEdit(INT Row, INT Col, VARIANT_BOOL * Cancel)<br />
{<br />
CComVariant varResult = VT_EMPTY;<br />
T* pT = static_cast<T*>(this);<br />
int nConnectionIndex;<br />
CComVariant* pvars = new CComVariant[3];<br />
<br />
int nConnections = m_vec.GetSize();<br />
<br />
<br />
for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)<br />
{<br />
pT->Lock();<br />
|
|
|
|
|
Hello!
Can I access COM object from java?
Thanks
|
|
|
|
|
EZ JCom lets you call COM objects from Java. Check http://www.ezjcom.com/
|
|
|
|
|
I would like to create a COM object with all my ADO methods. I then would like to have Visual Basic pass a recordset pointer to my COM DLL. Is this possible? If so how would I go about it?
Thanks
|
|
|
|
|
Hi itz possible,
you implement all the ADO classes into your class. Take a template
Implements {ADOCLASSNAME}
then... call the ado object.
I dont why you want to call all this.
Best of Luck
|
|
|
|
|
We have written an ou-of-proc, Free threaded, ATL server.
When 4 web users call this server for 5-6 hours, this server hangs, and a new instance of the server is launched in the memory. And the new server also not responds to user calls.
Can somebody tell me the possible cause of this trouble?
|
|
|
|
|
I have soem queries regarding component versioning.
I had a component named "Old_Component" exposed as an STA, out-of proc COM server, developed in VC++. This is wrapped by another component "Old_Component_Wrapper" which is in-proc COM server and exposed to client.
Now we are planning to provide new version of our product.
In new version, we are thinking of removing the wrapper layer. That is we want to expose "Old_Component" to client instead of "Old_Component_Wrapper". Can you tell me is this possible? Or what can be the possible issues in doing this?
|
|
|
|
|
Yes, it is possible. All you are doing is taking a contained or aggregated server and making it an independent out-proc server.
Kuphryn
|
|
|
|
|
Thanks for your reply. I have a further query:
What shall happen to the clients who were using "Old_Component_Wrapper(DLL)" server? Shall they be able to use new "Old_Component(EXE)" without any change in their code??
|
|
|
|
|
I am not familiar with out-proc COM server. My thought is that the out-proc COM server will be a new version or even an entirely different server from the old one. Yes, the clients will have to change their code.
Kuphryn
|
|
|
|
|
hi..
can someone plz tell me from where can i get a sample OLE control with automation supported..
thanks..
kindest regards
|
|
|
|
|
I want to register a component to regitry(the component is not realized,I just want to practise the procedure ).my code is bellow:
HKEY hKey;
char szKeyBuf[512]="first key";
const char* szValue="first key";
LPTSTR class_name="first class";
long lResult = ::RegCreateKeyEx(HKEY_CLASSES_ROOT, szKeyBuf, 0, class_name,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL);
if(lResult != ERROR_SUCCESS)
{
cout<<"Error Happened"<<"\n";
cout<
|
|
|
|
|
hello,
i wrote a EXE server using ATLCOMwizard in VC++6.0.the server was successfully registered
in my machine. here i used custom interfaces of mine.
now i have to create client to test that. help me to create client.
And also remote clients could able to instanceiate the server and use the interfaces.for that what should i do to fullfill the requirements.
|
|
|
|
|
I've encountered a probably setting a default property on a COM interface I am generating. This interface, IField, is generating dynamically from my application, and extends another of my interfaces, IMessageComponent (also generated dynamically, which inherits from IDispatch). I want to make the Value property (which is read/write) the default of the IField interface, so i've the the ID to zero.
When using it from VB6, it sometimes works. It always works if I am reading the value from the default property.
e.g. str = msg.Segment.Field This always works
However, when setting the value it does not work if there the object is set indirectly. For example:
Dim fld As Test.Field
fld = msg.Segment.Field
fld = "blah" This always works
msg.Segment.Field = "blah" This never works works - VB always gives a compile-time type mismatch error.
Has anyone encountered this previously and know of a workaround?
Cheers
Dave
http://www.cloudsofheaven.org
|
|
|
|
|
Hi Dave,
Dim fld As Test.Field<br />
fld = msg.Segment.Field<br />
fld = "blah" This always works
I couldn't understand how the code above works without error.
Because 'fld' is just a variable of type IField, not an instance of IField. So the statement 'fld = msg.Segment.Field' shouldn't work. Even if u r calling
CComObject<CField>* pFld;<br />
hRes = CComObject<CField>::CreateInstance(&pFld); in Segment , that line should be set fld = msg.Segment.Field . Then fld = "blah" is ok. Because now field is refering to an active instance of IField in 'msg.Segment'
Sameway if msg.Segment.Field = "blahh" gives a type mismatch error, then msg.Segment.Field might not be an active instance, make sure u had called hRes = CComObject<CField>::CreateInstance(&pFld);
does this make sense...?
best wishes ... mil10.
|
|
|
|
|
You're right, the line should be "set fld = msg.Segment.Field" - it was that in my VB code, but as I am a C++ programmer, not a VB 'programmer', i forgot it when i wrote that message.
However, i can guarrantee that the object does always exist - that's why I don't understand why it doesn't work...
Anyway, I'm just looking for ideas...
Dave
http://www.cloudsofheaven.org
|
|
|
|
|
Try to debug into CField::put_Value from vb exe and see what is happening inside CField::put_Value . Since u r not a vb prgmr , i donno whether u familier with debugging into an atl dll from vb.
anyway FYI -
Just create the vb app with the code msg.Segment.Field = "blahh" into an EXE.
Then change ATL project build setting to Win32Debug
Goto "Project->Settings->Debug->Executable for debug session:" and browse the VB exe path into the text field.
Put break point in CField::put_Value , then run the ATL project by F5.
best wishes..mil10
|
|
|
|
|
I do know how to debug from VB, but unfortunately it's not going to help here. It's a compile error, not a runtime error.
Dave
http://www.cloudsofheaven.org
|
|
|
|
|
Seems very strange..if u mind to send me the source code I am ok to debug. otherwise try unregister/reregister or more safely try on another mechine. Because I had faced almost simialr prblm early. Initialy the DISP_ID was 1. After registration with 1, I changed it to 0 to set it as default. Again registred the component but, showed err in my mechine but worked fine in another mechine( with fresh registry entries).
best wishes..mil10
|
|
|
|
|
Thanks for the offer, but don't worry about it. I'll give it a go on a clean machine and see what happens. If that doesn't work, then i'll just leave it as is - the default property would be a nice extra to have, but it can still be used fine without it.
Thanks a lot for your help.
Dave
http://www.cloudsofheaven.org
|
|
|
|
|
Because "Field" property gives an object (some interface like IField or IDispatch), the VB makes some additional action.
fld = "blah"
This always works because the VB cannot assign the string value to the object variable. He should call some method(property) for this. And this action is a calling of property-by-default (with dispid=0) if this property is presented by the object.
msg.Segment.Field = "blah"
This never works because the VB will call the "Field" property of msg.Segment object with parameter which equals to "blah". It gives the compilation error because probably the "Field" property does not have the parameters.
Try the following construction
msg.Segment.Field() = "blah"
With best wishes,
Vita
|
|
|
|
|
I am working in a framework in my COM server where I have a thread . From this thread I need to send events out of the Server which I am not able to do normally . What should I be doing in order to be able to send out events !
Related article in MSDN is
Events and threads
The good doctor alluded to problems he was having with events and threads earlier.
Here's the basic rule: Unless you do something special (more on this later), you always have to fire your events from the same thread that called IConnectionPoint::Advise. You may not normally start another thread and fire events from that thread. Why? Well, to fire an event means that you call into another object using an interface pointer.
But, in COM, you are never allowed to pass interface pointers between threads; you must marshall them instead. This is not terribly difficult to do, even though it involves calling the API with the world's longest name: CoMarshalInterThreadInterfaceInStream. (Perhaps it's named for a small town in Wales.)
You pass the stream pointer to the other thread (guaranteed to work in this special case) and call CoGetInterfaceAndReleaseStream in the new thread to get a marshalled interface pointer. Unfortunately, ATL's event firing implementation doesn't marshall the interface pointers, so it'll take some work to fire events from another thread.
In two common cases, this isn't an issue. If you're firing the event in response to a Windows message, such as a mouse button down message, you'll be okay. You'll be running on the same thread you were created on when you received the message.
Or, in our example, we fire an event in response to a method call. Again, this is safe, because the method will be called on the same thread you were created on.
It's only when you explicitly create a thread yourself (such as to sleep, or do some background processing) that firing events from the newly created thread becomes difficult.
So Do I have to Marshall the Interface to the COM server or all the IConnectionPoint Interfaces ?
|
|
|
|
|