Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / Managed C++ / May 2005

Tip: Looking for answers? Try searching our database.

VC 2005 Beta 2: How to set STA ApartmentState

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Adriano Coser - 29 Apr 2005 15:55 GMT
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.
Carl Daniel [VC++ MVP] - 29 Apr 2005 15:56 GMT
> Hello.
>
[quoted text clipped - 42 lines]
>
> 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
Adriano Coser - 29 Apr 2005 19:56 GMT
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: coser@altoqi.com.br
website: www.altoqi.com.br
>> Hello.
>>
[quoted text clipped - 50 lines]
>
> -cd
Adriano Coser - 29 Apr 2005 21:02 GMT
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.
Carl Daniel [VC++ MVP] - 29 Apr 2005 21:54 GMT
> Hi Carl.
>
[quoted text clipped - 5 lines]
> 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
Adriano Coser - 29 Apr 2005 23:03 GMT
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: coser@altoqi.com.br
website: www.altoqi.com.br
>> Hi Carl.
>>
[quoted text clipped - 12 lines]
>
> -cd
Carl Daniel [VC++ MVP] - 30 Apr 2005 01:30 GMT
> Carl, I think it's OK now.
>
[quoted text clipped - 12 lines]
>
> 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
Kapil Khosla [MSFT] - 30 Apr 2005 19:09 GMT
> Carl, I think it's OK now.
>
[quoted text clipped - 36 lines]
> >
> > -cd

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();
}
Carl Daniel [VC++ MVP] - 01 May 2005 01:48 GMT
> 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
Kapil Khosla [MSFT] - 01 May 2005 08:01 GMT
> > 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
[quoted text clipped - 5 lines]
>
> -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
Adriano Coser - 03 May 2005 16:00 GMT
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.

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.