Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

RE: CoInitialize failure with many processes

840 views
Skip to first unread message

Dan

unread,
Apr 1, 2009, 4:28:05 PM4/1/09
to
Follow up to my own post. I've tried the following code in the main function
of the C++ process to place each instance into its own Windows Station before
initializing COM. The function calls succeed, but I'm not sure how to tell
if the processes are really running in their own station or not.
Unfortunitely, it does not solve my problem. COM is still failing in the
same manner as before.

Thanks,
Dan


HWINSTA hOldWinSta = GetProcessWindowStation();

// Run process in its own Windows Station
HWINSTA hWinSta = CreateWindowStation(
NULL, // Create new station name
0, // Reserved, must be zero
STANDARD_RIGHTS_REQUIRED, // Desired Windows Station Security Access
NULL); // Windows Station cannot be inherited by child processes

if (hWinSta != NULL)
{
// Set this process to run in newly created station (we cannot close
handle while assigned to process)
if (!SetProcessWindowStation(hWinSta))
{
DWORD dwErrorCode = GetLastError();
LogWindowsEventLog(EVENTLOG_ERROR_TYPE, "Error setting process to
created Windows Station: %1!d!", dwErrorCode);
CloseWindowStation(hWinSta);
}
}
else
{
DWORD dwErrorCode = GetLastError();
LogWindowsEventLog(EVENTLOG_ERROR_TYPE, "Error occured creating Windows
Station for Process: %1!d!", dwErrorCode);
}

// Execute Process
ProcessMain();

if (hWinSta != NULL && hOldWinSta != NULL)
{
// Set process back to original Windows Station so we can clean up
if (SetProcessWindowStation(hOldWinSta))
{
CloseWindowStation(hWinSta);
hWinSta = NULL;
}
}

"Dan" wrote:

> I have very limited experience with COM, so hopefully someone can point me in
> the right direction here. I'm working with a large pile of legacy code that
> uses COM, but no one here knows COM anymore. And there is no time available
> to rewrite this code using .NET.
>
> Here's the setup: We have a .NET window service that manages hundreds of C++
> processes on a server. Each C++ process handles a single connection and has
> to be in its own process because of everything using globals (ick, I know).
> In the past, each process was it's own Windows Service, but due to
> limitations on the number of services (and it's a major pain to manage), we
> wrote a wrapper service to handle all these sub-processes.
>
> Here's the problem: When we create a large number of these C++ processes
> (>65 on the server, >35 on my development machine), the CoInitialize()
> function returns an error indicating "The class does not exist". The
> existing processes work just fine, only the new ones fail. If I kill working
> processes, the new ones start working equal to the number of working ones
> that I killed. If I create a new process myself and not from the service, it
> works fine.
>
> It was suggested that this may have something to do with Window Stations and
> a limit within COM per station. Since each service runs in it's own station,
> the previous one process per service implementation apparently worked, but
> when all are in the same station we have this problem.
>
> I have several questions probing for possible solutions. Is there a way to
> kick off processes in their own station? If so, would I still have access to
> kill them programmatically as needed? Is there a way to increase COM limits
> to prevent this problem? If so, any issues that could result from this
> change? Is there any other method for modifying how COM operates to prevent
> this limit from being reached without having to rewrite all the legacy code?
>
> We're using WinXP Pro SP2 and Server 2003, .NET Framework 2.0, VS2005. Not
> upgrading to .NET 3.5 or VS2008 until after we release.
>
> Thanks,
> Dan

Paul Baker [MVP, Windows Desktop Experience]

unread,
Apr 1, 2009, 4:53:48 PM4/1/09
to
How many threads are there in a process? Which thread is/threads are calling
CoInitializeEx? What is it/are they passing for the parameters (pvReserved,
dwCoInit). What is the result returned?

Paul

"Dan" <D...@discussions.microsoft.com> wrote in message
news:F65A9B10-30CC-464A...@microsoft.com...

Dan

unread,
Apr 1, 2009, 5:07:03 PM4/1/09
to
Paul,

There are only two threads per process. The main thread and a read event
handler thread that doesn't make any COM calls, just handling serial port
events. COM is used to communicate with the server service and eventually
the backend database.

The call to CoInitialize (not CoInitializeEx) is just passing in NULL

CoInitialize(NULL);

The HRESULT return value from this call is 0x80070583 in the instances that
fail.

Thanks,
Dan

Brian Muth

unread,
Apr 1, 2009, 5:18:26 PM4/1/09
to
My instincts tell me that you are likely running out of a resource of some
type. You need to use performance monitor to see what's going on.

Why are you trying to run each process in its own winstation? I can't make
sense of why you are doing that.

Brian


Paul Baker [MVP, Windows Desktop Experience]

unread,
Apr 1, 2009, 5:32:33 PM4/1/09
to
I agree that running it in its own Windows Station doesn't make sense. But I
see that you were just using it as an attempt at a workaround that was
unsuccessful so I didn't comment on it :)

Paul

"Dan" <D...@discussions.microsoft.com> wrote in message

news:BD5E2A95-8AB8-4D13...@microsoft.com...
> Brian,
>
> I was trying to place them in their own Windows Station as it was
> recommended to me as a potential solution by a co-worker. My real problem
> is
> COM not working. If I can get that fixed without dealing with Windows
> Station, then great! They can all run in the same station for all I care.
>
> On my development machine running WinXP SP2 with 4GBs RAM, at the time of
> failure, my memory usage is around 2 GB to 3.5 GBs based on what else I
> have
> running. And the point of failure, the number of instances running before
> the next one fails, doesn't change depending on what else is running.
> Processors are mostly idle.
>
> Thanks,
> Dan

Paul Baker [MVP, Windows Desktop Experience]

unread,
Apr 1, 2009, 5:40:37 PM4/1/09
to
The value 0x80070583 is the Win32 error code ERROR_CLASS_DOES_NOT_EXIST
wrapped as an HRESULT.

I always try to figure out the symbolic name of an error code as I find it
usually turns up much more useful information in searches.

I think this error code actually refers to a window class, not a COM class.
Indeed, calling CoInitialize should not relate to any particular class, just
initializing COM in general.

I concur with the theory that it is running out of resources and would add
that it is likely User resources that it is running out of. Perhaps it can't
register the class of the window that is used to synchronize calls in an
STA. It sounds like these processes are non-interactive but do they create
unecessary windows?

Paul

"Dan" <D...@discussions.microsoft.com> wrote in message

news:532FCC1E-A721-4FB7...@microsoft.com...

Brian Muth

unread,
Apr 1, 2009, 5:45:04 PM4/1/09
to
Ok, forget trying to create winstations. Kick your co-worker in the pants
for wasting your time.

Can you please clarify the picture for me. You are trying to create hundreds
of processes. Each process is instantiating a COM object. So, is the COM
server a DLL or a separate executable? Is it a simple COM object or a
full-blown ActiveX control? Was it developed in MFC or ATL? What resources
does it use?

Please try to answer as many of these questions as you can. And did you run
performance monitor yet as advised?


Brian

Paul Baker [MVP, Windows Desktop Experience]

unread,
Apr 2, 2009, 10:29:09 AM4/2/09
to
Now please look at Brian's questions :)

Paul

"Paul Baker [MVP, Windows Desktop Experience]"
<paulrich...@community.nospam> wrote in message
news:u1W89Jxs...@TK2MSFTNGP03.phx.gbl...

Dan

unread,
Apr 2, 2009, 3:05:03 PM4/2/09
to
Brian,

As I continue to dig through this code, it appears that it is using COM to
directly communicate with the SQL server. I'm learning as I go here.

Prior to calling CoInitialize(NULL), there is nothing done of interest.
(e.g. Setting thread priority, parsing command line arguments, etc) After
the CoInitialize, the connection is made using a msado15.tlh file and a
_connection object (apparently an ADO object, or ActiveX Data Object).
CreateInstance is called on a _ConnectionPtr object, then Open is called on
this object with the following connection string:

"Provider=sqloledb;Server=%*.*S%S%*.*S;Database=%*.*S%SSystem;Trusted_Connection=Yes;"

The resulting object is returned as a _variant_t object casted from an
(IUnknown*) pointer. This variant object is passed into a _RecordsetPtr
object's Open function along with an SQL statement casted as a _variant_t
object as well.

Regarding the Performance Monitor, I haven't seen anything out of the
ordinary. What performance objects and counters should I be looking at?

Thanks,
Dan

Paul Baker [MVP, Windows Desktop Experience]

unread,
Apr 2, 2009, 3:40:41 PM4/2/09
to
Is this typical of each of the hundreds of processes? So it is using ADO to
connect to a SQL Server?

I could not find a good reference about ADO connection pooling, but I
believe the pool is process specific. In other words, each process will get
its own connection to the SQL Server. I can see how having hundreds of
simultaneous connections to a SQL Server might be a bad idea.

"netstat -o" at the Command Prompt will reveal what is going on with TCP
connections in practice. You can try different parameters to get whatever
information you want.

I would think some more about how you might reengineer this a bit without
using up too much time that you don't have.

Paul

"Dan" <D...@discussions.microsoft.com> wrote in message

news:BE92EC61-A5B2-4D2F...@microsoft.com...

Dan

unread,
Apr 2, 2009, 5:13:01 PM4/2/09
to
Paul,

Each process is identical, just configured via command line arguments to
establish a different connection (e.g. different modem in a large modem
bank). So each one is establishing a connection to the database. When
starting the service that starts these processes (currently starting 34 in my
test environment), I see the number of SQL connections increase from 17 to
88, then drop back to 54. So it looks like each is making 2 connections,
then closing one of them.

Checked the SQL configuration. It is still set to the default maximum
allowed number of user connections of unlimited (which is 32K). No errors
recorded in SQL log about reaching the maximum number of connections. And I
can still connect from other sources.

No major increase in the number of TCP connections established as indicated
from netstat. Still can connect via TCP after reaching my problem's maximum.

I'll definately have to look into how to refactor this code to extract the
database access code into a .NET via Interop or something. Then I could
eliminate COM altogether. If I can pull the connection code back to the main
service, I could reduce the connections to one. But then I need to find a
good interprocess communication method (other than COM) that can handle
numerous clients.

Thanks for your help,
Dan

Brian Muth

unread,
Apr 2, 2009, 5:40:51 PM4/2/09
to

"Dan" <D...@discussions.microsoft.com> wrote in message
news:BE92EC61-A5B2-4D2F...@microsoft.com...
> Brian,
>
> As I continue to dig through this code, it appears that it is using COM to
> directly communicate with the SQL server. I'm learning as I go here.
>
> Prior to calling CoInitialize(NULL), there is nothing done of interest.
> (e.g. Setting thread priority, parsing command line arguments, etc) After
> the CoInitialize, the connection is made using a msado15.tlh file and a
> _connection object (apparently an ADO object, or ActiveX Data Object).
> CreateInstance is called on a _ConnectionPtr object, then Open is called
> on
> this object with the following connection string:
>
> "Provider=sqloledb;Server=%*.*S%S%*.*S;Database=%*.*S%SSystem;Trusted_Connection=Yes;"
>
> The resulting object is returned as a _variant_t object casted from an
> (IUnknown*) pointer. This variant object is passed into a _RecordsetPtr
> object's Open function along with an SQL statement casted as a _variant_t
> object as well.
>
> Regarding the Performance Monitor, I haven't seen anything out of the
> ordinary. What performance objects and counters should I be looking at?
>

This was a very helpful post. I wish in fact that you provided this very
relevant information at the beginning.

I'm surmising that you have a simple application that is accessing SQL
Server using classic ADO. This is a simple client-server type of
relationship. Scaling is definitely going to be an issue here, since SQL
Server can only handle so many connections at one time. Double-check the
code to ensure that any ADO connection is closed explicitly. This is
especially important if you are opening and closing connections more than
once, as leaving out the call to Close() will accelerate exhaustion of SQL
resources.

You can use the SQL Server Profiler to examine the number of connections
while you start launching. This is an extremely important tool for you, so
if you are unfamiliar with it, now is the time to do your research and learn
how to use it properly. Also, use your favourite search engine and enter
"ADO SQL Connection Pooling" and you will find lots of hits relevant to this
problem.

You will need to fully leverage SQL Server connection pooling. There are
probably several ways of tackling it but I would recommend that a very
simple COM DLL be written that calls the SQL Server on behalf of any client
process. Then install this COM DLL into Component Services to change it to a
COM+ application. This will enable connection pooling, and you should be
able to easily launch hundreds of processes without running into the problem
you are currently facing. Your application is no longer a client-server
architecture, but now an n-tier architecture, and will scale much, much
better.

This simple COM+ DLL could be written in VB6, although C++ of course could
also be used.

Good luck with your project.

Regards,

Brian

Dan

unread,
Apr 2, 2009, 5:51:01 PM4/2/09
to
Brian,

I would of loved to have provided that information earlier, but I did not
write this code and I'm learning both this part of the code and COM at the
same time. The original authors no longer work here. Prior to today, I
couldn't of told you what ADO was, let alone how it is used.

Your suggestion regarding creation of a COM+ DLL installed into Component
Services with pooling sounds promising. I'll will dig in that direction.

Thanks for your help,
Dan

Dan

unread,
Apr 8, 2009, 5:26:10 PM4/8/09
to
I've determined the root cause of my problem. Posting the result here for
anyone else reading this with a similiar problem.

Each process that I'm creating is being created on the default desktop of
the windows station for the parent service. Monitoring heap usage using the
Microsoft Desktop Heap Monitor, it appears that each of these processes is
allocating about 14k of desktop heap space. Once the usage reached 99.x%,
all future processes that are created generate the error condition as
described.

Output from desktop heap monitor when fully allocated:

Windowstation: (Service-0x0-c0d52c$) SessionID: 0
Desktop: (Default) Addr: be180000

Desktop Heap 524288 (0x 80000) Bytes
Committed 520192 (0x 7f000) Bytes
Uncommitted 4096 (0x 1000) Bytes
Allocated 520112 (0x 7efb0) Bytes
Total Freed 80 (0x 50) Bytes
Unused 4176 (0x 1050) Bytes
Used Rate ( 99.2) %

I'm not sure yet why a non-UI process that performs serial communications
needs to allocate 14k of desktop heap space. It doesn't have a desktop.
More digging required...

Dan

Paul Baker [MVP, Windows Desktop Experience]

unread,
Apr 9, 2009, 10:09:30 AM4/9/09
to
Dan,

Thanks for sharing. I think readers would be interested to hear anything
else you discover from your digging.

So, your processes are not only wasting database resources, they are also
wasting desktop resources.

At first I thought "what is a desktop heap anyway?" but I did some research
and found this article.

Ntdebugging Blog : Desktop Heap Overview.
http://blogs.msdn.com/ntdebugging/archive/2007/01/04/desktop-heap-overview.aspx

"I'm not sure yet why a non-UI process that performs serial communications

needs to allocate 14k of desktop heap space. It doesn't have a desktop.".

I think my theory of a week ago was an over-simplification but along the
right lines. I told you then why a Single-Threaded Apartment (STAs) have UI.
This is true even if it is non-interactive. It is described in more detail
here.

INFO: OLE Threads Must Dispatch Messages:
http://support.microsoft.com/kb/136885

Desktop heap exhaustion is causing your ERROR_CLASS_DOES_NOT_EXIST. If a
process has an STA, and it sounds like all of them do, COM will register a
window class and create a window behind the scenes. This is eating up some
desktop heap. It doesn't sound like a lot, but maybe it adds up. I don't
know howe much of your 14 Kb this accounts for but you see how it adds up.

You do have a desktop, you just don't know it because you didn't intend to
use it. It is associated with a non-interactive window station but it exists
nonetheless.

Thread Connection to a Desktop:
http://msdn.microsoft.com/en-us/library/ms686744(VS.85).aspx

Besides monitoring the wasting of database resources as Brian mentioned, you
could monitor desktop resources.

Performance Monitor can be used. Now you have some idea what you are looking
for. Brian mentioned this before. Did you do it?
You can see how many USER Objects you are using by adding the column to Task
Manager. I mentioned this before. Did you do it?
You can see if you are creating any windows, either using a tool like
Microsoft Spy++ or examining source code. I am not sure what tool will
organize it by processes. You could write one :P

Perhaps your colleague who suggested creating separate window stations per
process was starting to think along these lines. Perhaps he thought that by
creating a window station and desktop you would get a "fresh" desktop heap
but this is a really bad way to go. All you're doing is wasting different
resources and introducing issues communicating between the processes.

So you say the people who wrote these processes are long gone? Shame,
because they deserve far more of the scrutinity than your colleague who we
were critical of but who was only trying to help.

Paul

"Dan" <D...@discussions.microsoft.com> wrote in message

news:4D0C6F31-699A-4770...@microsoft.com...

0 new messages