Freigeben über


Gotcha with STAThreadAttribute and Managed C++

Managed thread objects have an ApartmentState property that can be set to STA or MTA.  But setting this property on the main thread doesn't work reliably because the CLR might set the apartment state to MTA (by calling CoInitializeEx(NULL, COINIT_MULTITHREADED)) before your first line of code executes.  And once a thread's apartment state has been set, it can't be changed (without calling CoUninitialize first).

Therefore, to reliably set the main thread's apartment state, you must place the STAThreadAttribute or MTAThreadAttribute on your entry point.  (MTA is the CLR's default, but using MTAThreadAttribute can be necessary in VB.NET since the compiler emits STAThreadAttribute on Main by default for backwards compatibility with VB6.)  This is important to understand even if you don't use Interop directly.  For example, using Windows Forms requires that your entry point is marked with STAThreadAttribute, and the code emitted when creating a new Visual C++ Windows Forms Application project in VS.NET 7.1 doesn't handle this requirement correctly.

Using these attributes is easy in any language except Managed C++.  You'd probably expect the following code to print "0x1":

  #include <objbase.h>
#include <stdio.h>
#using <mscorlib.dll>
using namespace System;

  [STAThread]

  int main()

  {

    printf("0x%x\n", CoInitialize(NULL));

    return 0;

  }

That's because the STAThreadAttribute should force the CLR to initialize the thread to STA, causing the explicit call to CoInitialize to return S_FALSE.  But if you run it, you'll see that it prints either "0x0" (S_OK, indicating that the apartment state was not previously set) or "0x80010106" (RPC_E_CHANGED_MODE, indicating that the apartment state was already set to MTA).  This means that the attribute didn't work.

The reason for this can be discovered by opening the assembly in ILDASM.  The main function isn't the assembly's true entry point.  The C++ compiler emits an entry point called _mainCRTStartup that initializes the C runtime library before calling your main method.  Furthermore, the v7.0 and v7.1 compilers do not propagate attributes on your main method to the true entry point.  This should be addressed in a future version of the product, but in the meantime you can do the following:

  #include <objbase.h>

  #include <stdio.h>

  #using <mscorlib.dll>

  using namespace System;

  extern "C" int mainCRTStartup();

  // Called by mainCRTStartup

  int main() {}

  // The true entry point

  [STAThread]

  int myMain()

  {

    // Initialize the CRT

    mainCRTStartup();

    printf("0x%x\n", CoInitialize(NULL));

    return 0;

  }

You also need to use the linker option /ENTRY:myMain to make myMain your entry point.  In Visual Studio .NET, this is the Entry Point setting under Configuration Properties -> Linker -> Advanced in the project's Property dialog.  This new code prints "0x1" every time.

This example used mainCRTStartup with main, but you could also use wMainCRTStartup with wmain.  Or for a Windows application (/SUBSYSTEM:WINDOWS), you can use WinMainCRTStartup with WinMain, or wWinMainCRTStartup with wWinMain.

Comments

  • Anonymous
    July 18, 2003
    Comment to your blog rss feed file: it is very hard to read your rss feed with a aggregator. Your used editor (assume Word :) generates some HTML elements with a Word internal namespace (e.g. &lt;o:p&gt;) that is not taken over to the feed and will always fail to render with a Xslt transformation we use. This post is such a non-working example :-( Would really like it to read your blog without such problems, it's great!
  • Anonymous
    July 18, 2003
    Thanks for the feedback! I'll try to avoid that in the future...
  • Anonymous
    July 29, 2003
    So, Adam:If I use /ENTRY:myMain shouldn't I forget about manged code security?Or assumption is: If we are planning to use COM we shouldn't run in "sandbox" and securiy is off?Anyway: if you have time (which I doubt) you may check this:http://members.cox.net/igor.tebelev/ActiveXDeployer.htmThanks for the great book.
  • Anonymous
    July 27, 2004
    The comment has been removed
  • Anonymous
    February 27, 2006
    >This should be addressed in a future version of the product

    In Whidbey, the Microsoft Linker added the /CLRTHREADATTRIBUTE switch to handle this situation.

    Here's the link to the switch documentation:
    http://msdn2.microsoft.com/en-US/library/s6bz81ya.aspx

  • Anonymous
    April 04, 2008
    PingBack from http://blueonionsoftware.com/Blog.aspx?p=f9a9d878-b088-4a1e-8399-df23bc03d192
  • Anonymous
    January 21, 2009
    PingBack from http://www.keyongtech.com/398670-dao-deprecation
  • Anonymous
    June 07, 2009
    PingBack from http://besteyecreamsite.info/story.php?id=2315