VC 2005 Beta 2: How to set STA ApartmentState

  • Thread starter Carl Daniel [VC++ MVP]
  • Start date
C

Carl Daniel [VC++ MVP]

Adriano said:
Hello.

I'm moving an application VC 2003 to VC 2005 Beta2. I need to set STA
ApartmentState model so the drag & drop registration can work.

I used to do
System::Threading::Thread::CurrentThread->ApartmentState =
System::Threading::ApartmentState::STA;
as the first statment of _tWinMain and also in the DllMain of my
mixed mode assemblies.

On VC 2005 I can't do this statment in DllMain, so I was oriented by
Carl Daniel and Kapil to create a static instance of a class that makes
the
statment on its constructor. I included a class like the following in
all mixed mode DLLs, hoping the static instances would be initialized on
the .cctors.

__gc class QIVCadCctor
{
public:
QIVCadCctor()
{
System::Threading::Thread::CurrentThread->ApartmentState =
System::Threading::ApartmentState::STA;
}

static QIVCadCctor* cadCctor = new QIVCadCctor();
};

Even doing this, when the program tries to execute

System::Threading::Thread::CurrentThread->ApartmentState =
System::Threading::ApartmentState::STA;

on _tWinMain, I get the following error message on my debug output
window:
Managed Debugging Assistant 'InvalidApartmentStateChange' has
detected a problem in 'c:\Fontes\QiCad.NET\QiCad.NET.exe'.
Additional Information: Thread is attempting to set the apartment
state to STA, but it has already been set to MTA.
See
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50215\sdk\bin\mdaBoilerplate.exe.mda.config
for documentation.

Does anyone know what is missing?

I can't tell you anything very specific, but clearly - something else is
running, either in DllMain itself, or in the .cctor that has already called
CoInitializeEx and set the current thread to be part of the MTA. You might
be able to debug it by setting a breakpoint at CoInitializeEx(), and
examining the call stack.

-cd
 
A

Adriano Coser

Hello.

I'm moving an application VC 2003 to VC 2005 Beta2. I need to set STA
ApartmentState model so the drag & drop registration can work.

I used to do
System::Threading::Thread::CurrentThread->ApartmentState =
System::Threading::ApartmentState::STA;
as the first statment of _tWinMain and also in the DllMain of my mixed mode
assemblies.

On VC 2005 I can't do this statment in DllMain, so I was oriented by Carl
Daniel and Kapil to create a static instance of a class that makes the
statment on its constructor. I included a class like the following in all
mixed mode DLLs, hoping the static instances would be initialized on the
..cctors.

__gc class QIVCadCctor
{
public:
QIVCadCctor()
{
System::Threading::Thread::CurrentThread->ApartmentState =
System::Threading::ApartmentState::STA;
}

static QIVCadCctor* cadCctor = new QIVCadCctor();
};

Even doing this, when the program tries to execute

System::Threading::Thread::CurrentThread->ApartmentState =
System::Threading::ApartmentState::STA;

on _tWinMain, I get the following error message on my debug output window:

Managed Debugging Assistant 'InvalidApartmentStateChange' has detected a
problem in 'c:\Fontes\QiCad.NET\QiCad.NET.exe'.
Additional Information: Thread is attempting to set the apartment state
to STA, but it has already been set to MTA.
See
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50215\sdk\bin\mdaBoilerplate.exe.mda.config
for documentation.

Does anyone know what is missing?

Thanks in advance.

Regards,
Adriano.
 
A

Adriano Coser

Hello Carl.

I didn't find any calls to CoInitialize or CoInitializeEx in my code. Also,
I don't know how to set a breakpoint over this function.

Is there any other cause to ApartmentState to set to MTA before _tWinMain
entry point?

Thanks for your help.

Adriano.


AltoQi - Tecnologia Aplicada à Engenharia Adriano Coser Departamento de
Desenvolvimento Tel.: (48) 239-7000 ramal: 7069 e-mail: (e-mail address removed)
website: www.altoqi.com.br
 
A

Adriano Coser

Hi Carl.

I've just realized that the static instances I created to set the apartment
state on my DLLs (like the following) are not initialized before _tWinMain.

I put breakpoints on DllMain and on the constructor of QIVCadCctor and the
program only stops on DllMain. Could this be the problem? What else can I do
to call managed code on a mixed mode DLL initialization?

__gc class QIVCadCctor
{
public:
QIVCadCctor()
{
System::Threading::Thread::CurrentThread->ApartmentState =
System::Threading::ApartmentState::STA;
}
static QIVCadCctor* cadCctor = new QIVCadCctor();
};

Thanks for your help.
Adriano.
 
C

Carl Daniel [VC++ MVP]

Adriano said:
Hi Carl.

I've just realized that the static instances I created to set the
apartment state on my DLLs (like the following) are not initialized
before _tWinMain.
I put breakpoints on DllMain and on the constructor of QIVCadCctor
and the program only stops on DllMain. Could this be the problem?
What else can I do to call managed code on a mixed mode DLL
initialization?

Why not simply set the apartment to the STA in native code instead of
managed? I would assume that a simple call to CoInitialize() in DllMain()
would be sufficient (I hope I'm not getting myself in trouble by not
verifying that it's actually safe to call CoInitialize() from DllMain()).

-cd
 
A

Adriano Coser

Carl, I think it's OK now.

I called CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); on DllMain of one
of my DLLs when reason == DLL_PROCESS_ATTACH. Then I called CoUninitialize()
when read == DLL_PROCESS_DETACH.

Now the program is running! Thanks a lot, you made my weekend.

But in the output window I'm getting the inverse message that I had before:

Managed Debugging Assistant 'InvalidApartmentStateChange' has detected a
problem in 'c:\Fontes\QiCad.NET\QiCad.NET.exe'.
Additional Information: Thread is attempting to set the apartment state to
MTA, but it has already been set to STA.

Do you believe I should call CoInitializeEx in all my mixed mode DLLs?

Thanks again for your help.

Adriano.

AltoQi - Tecnologia Aplicada à Engenharia Adriano Coser Departamento de
Desenvolvimento Tel.: (48) 239-7000 ramal: 7069 e-mail: (e-mail address removed)
website: www.altoqi.com.br
 
C

Carl Daniel [VC++ MVP]

Adriano said:
Carl, I think it's OK now.

I called CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); on DllMain
of one of my DLLs when reason == DLL_PROCESS_ATTACH. Then I called
CoUninitialize() when read == DLL_PROCESS_DETACH.

Now the program is running! Thanks a lot, you made my weekend.

But in the output window I'm getting the inverse message that I had
before:
Managed Debugging Assistant 'InvalidApartmentStateChange' has
detected a problem in 'c:\Fontes\QiCad.NET\QiCad.NET.exe'.
Additional Information: Thread is attempting to set the apartment
state to MTA, but it has already been set to STA.

Do you believe I should call CoInitializeEx in all my mixed mode DLLs?

Actually, as a general principle I don't believe you should ever try to
enter an apartment in a DLL unless you're doing it in a thread that your DLL
"owns" (creates and managed the lifetime of). Perhaps there's something
unique about your situation that requires it, but I'd try to get rid of the
requirement rather than tilting at windmills trying to find a way to honor
it.

-cd
 
G

Guest

Adriano Coser said:
Carl, I think it's OK now.

I called CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); on DllMain of one
of my DLLs when reason == DLL_PROCESS_ATTACH. Then I called CoUninitialize()
when read == DLL_PROCESS_DETACH.

Now the program is running! Thanks a lot, you made my weekend.

But in the output window I'm getting the inverse message that I had before:

Managed Debugging Assistant 'InvalidApartmentStateChange' has detected a
problem in 'c:\Fontes\QiCad.NET\QiCad.NET.exe'.
Additional Information: Thread is attempting to set the apartment state to
MTA, but it has already been set to STA.

Do you believe I should call CoInitializeEx in all my mixed mode DLLs?

Thanks again for your help.

Adriano.

AltoQi - Tecnologia Aplicada à Engenharia Adriano Coser Departamento de
Desenvolvimento Tel.: (48) 239-7000 ramal: 7069 e-mail: (e-mail address removed)
website: www.altoqi.com.br


No, you should never call a method from DllMain which has managed code in it
or calls another method with managed code. You can easily get into a loader
lock problem.

The recommended way to move code out of DllMain is to declare a global
managed object. In the constructor of that object you should call the code
which you were initially calling in DllMain. The module constructor will
initialize see this global and call the constructor.
Note: A managed object here is not a ref/value class object. You cannot
have global ref objects. Here managed object means a native class compiled
with /clr or under #pragma managed.

The reason is this works is because in mixed assemblies, the initializations
are done by the module constructor. It is very important to remember that the
module constructor for an assembly is called only if some managed code is
first executed either in the host assembly or in your Dll itself. If you dont
"use" that managed object you will never invoke the module constructor, thus
your constructor will never be called and data will be uninitialized.

Let me give you an example,
///////////////////////
cl /clr /LD /Zi 1.cpp
cl /clr /EHa t.cpp /Zi /link 1.lib
/////////////////////

/////////////////
//1.cpp

#pragma once
#include <stdio.h>
#include <windows.h>
class __declspec(dllexport) A
{
public:
A()
{
System::Console::WriteLine("Module constructor doing initialization based
on the global instance of the class A\n");
System::Console::WriteLine("Code from DllMain, now in constructor
executed\n");
}
void foo()
{
printf("foo called so that the linker knows not to throw away an unuse
object in t.cpp \n");
}
};

#pragma unmanaged
// Global instance of object
A obj;

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
// REMOVE ALL MANAGED CODE FROM DLLMAIN.
return true;
}



///////////////////////////////
//t.cpp

#include <windows.h>
using namespace System;
#include <stdio.h>
#using "1.dll"
class __declspec(dllimport) A
{
public:
void foo();
};
int main()
{
LoadLibrary("1.dll");
A obj;
obj.foo();
}
 
C

Carl Daniel [VC++ MVP]

Kapil said:
No, you should never call a method from DllMain which has managed
code in it or calls another method with managed code. You can easily
get into a loader lock problem.

Note that he's calling CoInitializeEx, which, last I checked, has no managed
code in it. Of course, that alone does not mean it's safe to call it from
DllMain, and I make no claim that it is.

-cd
 
G

Guest

Carl Daniel said:
Note that he's calling CoInitializeEx, which, last I checked, has no managed
code in it. Of course, that alone does not mean it's safe to call it from
DllMain, and I make no claim that it is.

-cd
You are right. But it always makes me nervous when someone calls a method in
DllMain and that method potentially could have managed code which tries to
load the CLR.

The original method you mention of having code in the managed object's
constructor is much better and should work. Although I think the
interpretation of Adriano was not quite correct. He created a static object
inside the managed class but it should have been a global like the example I
posted so that the module constructor can do the initialization when the
assembly is loaded.

Thanks Carl.
Kapil
 
A

Adriano Coser

Hello.

I got another solution (from Willy) to the apartment state problem: to set
it on the linker command line of the main thread (application). The statment
is: /CLRTHREADATTRIBUTE:STA

It worked fine and now I don't have to do nothing special on my DLLs.

Thanks a lot for all the answers.

Regards,
Adriano.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top