My server is a DLL with proxy/stub merged, with a dispatch interface. The server is always local and in-process. This is the fastest possible configuration, right?
The client-server interaction is fairly high frequency... i think this is my problem. For example, to populate a dialog before it is displayed, the client queries the server to Find() the appropriate record (ADO to an Access DB), then the client uses separate calls to retrieve each field (about 10 fields). The server is not doing much work here, just returning global variables that were set during the previous Find() method.
My customer requires this to run on a pre-pentium machine, 100MHz, 24Mb RAM, Win 98. Of course, on my development beast, the screen updates instantly, but the customer machine takes about 7 seconds (yuk )
Do you think it's the COM component slowing it down?
The solution for this is that you should club separate calls into a single call. For example:
Suppose you have a collection of 100 items, and collection's object has 4 attributes each. To display all the properties, your client is doing something like this:
For each item of collection:
get first prop of object
get second prop of object
get third prop of object
get fourth prop of object
So, you are making 5 *100 = 500 calls to COM server. Instead you should define prototype like this:
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.
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.
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.
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?
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?
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.
I want to register a component to regitry(the component is not realized,I just want to practise the procedure ).my code is bellow:
char szKeyBuf="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)
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?
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
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.
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).
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.