[Bug 17902] New: ole32.CoGetClassObject: threads without apartment should use existing MTA

wine-bugs at winehq.org wine-bugs at winehq.org
Mon Mar 30 17:43:13 CDT 2009


           Summary: ole32.CoGetClassObject: threads without apartment should
                    use existing MTA
           Product: Wine
           Version: 1.1.18
          Platform: Other
               URL: http://www.microsoft.com/downloads/details.aspx?FamilyID
        OS/Version: other
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: ole32
        AssignedTo: wine-bugs at winehq.org
        ReportedBy: focht at gmx.net


there are apps which use free threaded COM (inproc) servers and call
CoGetClassObject() from (new) threads that do not explicitly initialize COM.
This is valid behaviour when one of the existing threads already created the
MTA by calling CoInitializeEx(NULL, COINIT_MULTITHREADED).

See: http://support.microsoft.com/kb/150777

--- quote ---
Current implementations of COM allow a thread that does not explicitly
initialize COM to be a part of the MTA. A thread that does not initialize COM
is part of the MTA only if it starts using COM after at least one other thread
in the process has previously called CoInitializeEx(NULL,
COINIT_MULTITHREADED). (It is even possible that COM itself may have
initialized the MTA when no client thread has explicitly done so; for example,
a thread associated with an STA calls CoGetClassObject/CoCreateInstance[Ex] on
a CLSID that is marked "ThreadingModel=Free" and COM implicitly creates an MTA
into which the class object is loaded.) See the information on threading model
interoperability below.

However, this is a configuration that might cause problems, such as access
violations, under certain circumstances. Therefore, it is strongly recommended
that each thread that needs to do COM work initialize COM by calling
CoInitializeEx and then, on completion of COM work, call CoUninitialize. The
cost of "unnecessarily" initializing an MTA is minimal.
--- quote ---

An example app suffering from this is the managed console debugger from .NET
2.0 SDK "cordbg.exe":

When trying to debug a simple console C# app created with symbols (.pdb

--- snip ---
wine "C:\\windows\\Microsoft.NET\\Framework\\v2.0.50727\\csc.exe" /debug+
--- snip ---

Start debugger:

--- snip ---
wine "C:\\Program Files\\Microsoft.NET\\SDK\\v2.0\\Bin\\cordbg.exe foo.exe" 
--- snip ---

The debugger will immediately begin to execute the app - not halting in Main()
method (source).
This is because symbol information can't be loaded using COR symbol binder
which lives in a free threaded COM inproc server.
0A29FF9E-7F9C-4437-8B11-F424491E3931 -> CLSID_CorSymBinder

Threads 0x38 (main) and 0x36 call "CoInitializeEx" with COINIT_MULTITHREADED,
creating the MTA.
Thread 0x3b is created later as pooled thread - part of MTA - to serve CLR
debugger requests (work items).
That thread never makes any init calls to COM (verified).

--- snip ---
0038:Call KERNEL32.GetProcAddress(60970000,7a37e2ae "CoInitializeEx")
0038:Ret  KERNEL32.GetProcAddress() retval=609750c8 ret=79e9d311
0038:Call ole32.CoInitializeEx(00000000,00000000) ret=79ebfba8
0038:Call ntdll.RtlAllocateHeap(00110000,00000008,000000fc) ret=60986499
0038:Ret  ntdll.RtlAllocateHeap() retval=0014f9c0 ret=60986499
0038:Call ntdll.RtlAllocateHeap(00110000,00000000,00000028) ret=609a750c
0038:Ret  ntdll.RtlAllocateHeap() retval=001500e8 ret=609a750c
0038:Call KERNEL32.InitializeCriticalSection(001500f8) ret=609a7543
0038:Ret  KERNEL32.InitializeCriticalSection() retval=00000001 ret=609a7543
0038:Call ntdll.RtlAllocateHeap(00110000,00000008,0000007c) ret=60983f54
0038:Ret  ntdll.RtlAllocateHeap() retval=00151df0 ret=60983f54
0038:Call KERNEL32.InitializeCriticalSection(00151e10) ret=60983fb4
0038:Ret  KERNEL32.InitializeCriticalSection() retval=00000001 ret=60983fb4
0038:Ret  ole32.CoInitializeEx() retval=00000000 ret=79ebfba8 
0036:Call ole32.CoInitializeEx(00000000,00000000) ret=79ebfba8
0036:Call ntdll.RtlAllocateHeap(00110000,00000008,000000fc) ret=60986499
0036:Ret  ntdll.RtlAllocateHeap() retval=00151e78 ret=60986499
0036:Ret  ole32.CoInitializeEx() retval=00000000 ret=79ebfba8 
003b:Call advapi32.RegOpenKeyExW(80000000,0310d170
003b:Ret  advapi32.RegOpenKeyExW() retval=00000000 ret=79ea5b8a
003b:Call advapi32.RegOpenKeyExW(00000238,0310d270
L"2.0.50727",00000000,00020019,0310d0ec) ret=79ea5b8a
003b:Ret  advapi32.RegOpenKeyExW() retval=00000000 ret=79ea5b8a
003b:Call advapi32.RegQueryValueExW(0000023c,7a136a40
L"ImplementedInThisVersion",00000000,0310d0e4,00000000,0310d0e8) ret=79ea5bd0
003b:Ret  advapi32.RegQueryValueExW() retval=00000000 ret=79ea5bd0
003b:Call advapi32.RegCloseKey(0000023c) ret=7a1369f2
003b:Ret  advapi32.RegCloseKey() retval=00000000 ret=7a1369f2
003b:Call advapi32.RegOpenKeyExW(80000000,0310d318
003b:Ret  advapi32.RegOpenKeyExW() retval=00000000 ret=79ea5b8a
003b:Call advapi32.RegQueryValueExW(00000238,79f873d0
L"Class",00000000,0310d2f8,00000000,0310d310) ret=79ea5bd0
003b:Ret  advapi32.RegQueryValueExW() retval=00000002 ret=79ea5bd0
003b:Call advapi32.RegCloseKey(00000238) ret=79f88946
003b:Ret  advapi32.RegCloseKey() retval=00000000 ret=79f88946
003b:Call KERNEL32.GetProcAddress(60970000,7a37e222 "CoGetClassObject")
003b:Ret  KERNEL32.GetProcAddress() retval=60974f78 ret=79e9d311
003b:Call ole32.CoGetClassObject(00175d3c,00000015,00000000,79f83a7c,0310d5e8)
003b:err:ole:CoGetClassObject apartment not initialised
003b:Ret  ole32.CoGetClassObject() retval=800401f0 ret=7a041335
003b:Call KERNEL32.GetLastError() ret=79e74ab4
003b:Ret  KERNEL32.GetLastError() retval=0000007f ret=79e74ab4
000007f ret=78132d14
003b:Ret  KERNEL32.FormatMessageW() retval=00000000 ret=7a127c38
--- quote ---

The failure is due to Wine's CoGetClassObject() rejecting requests when no
apartment in current thread (oletls) exists.

--- snip dlls/ole32/compobj.c ---
    REFCLSID rclsid, DWORD dwClsContext, COSERVERINFO *pServerInfo,
    REFIID iid, LPVOID *ppv)
    LPUNKNOWN   regClassObject;
    HRESULT     hres = E_UNEXPECTED;
    APARTMENT  *apt;

    TRACE("\n\tCLSID:\t%s,\n\tIID:\t%s\n", debugstr_guid(rclsid),

    if (!ppv)
        return E_INVALIDARG;

    *ppv = NULL;

    apt = COM_CurrentApt();
    if (!apt)
        ERR("apartment not initialised\n");
        return CO_E_NOTINITIALIZED;
--- snip dlls/ole32/compobj.c ---

Rather than failing CoGetClassObject() without apartment, it should look for an
existing MTA and let the current thread use it.

With the MTA being used, the inproc server is created and the app works as
intended, e.g. symbols get loaded, source can be stepped ...


Configure bugmail: http://bugs.winehq.org/userprefs.cgi?tab=email
Do not reply to this email, post in Bugzilla using the
above URL to reply.
------- You are receiving this mail because: -------
You are watching all bug changes.

More information about the wine-bugs mailing list