Нарушение доступа при статической инициализации

Я работаю над приложением с Visual Studio 2015 в Windows 7. Приложение имеет интерфейс С#, С++ CLR-оболочку и собственный код С++.

Мое приложение выходит из строя с нарушением доступа при инициализации статической переменной в области возможностей с помощью кода С++. Но только на Windows Server 2003 Enterprise SP2, а не на Windows 7 или Windows Server 2012. Я знаю, что Windows Server 2003 не поддерживается, но мне нужно настроить таргетинг на эту платформу еще на несколько месяцев, а Visual Studio 2015 предоставляет набор инструментов платформы для таргетинга он.

Я создал небольшой воспроизводимый пример, который вы найдете в конце.

Сбой происходит только со всеми тремя задействованными компонентами (С#, С++ CLR, С++). Если я удалю его, авария исчезнет.

Сбой происходит только с определенным пользовательским конструктором. Если я удалю конструктор, авария исчезнет.

Я не эксперт по сборке, но для меня это похоже на то, что авария вызвана кодом, который проверяет, требуется ли статическая инициализация. Конструктор даже не вызывается.

Мой вопрос: почему он падает на Windows Server 2003? Я пропустил некоторые важные настройки проекта?

Сообщение об ошибке

Unhandled exception at 0x1000167E (Native.dll) in Managed.exe.dmp: 0xC0000005: Access violation reading location 0x00000000.

Консольное приложение Visual С# "Managed.exe"

Program.cs

// Target framework: .NET Framework 4
// Platform target: x86

using System;

namespace Managed
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Write("Press enter to start test...");
            Console.ReadLine();

            Native.Wrapper wrapper = new Native.Wrapper();
            Console.WriteLine("Test was successful");

            Console.Write("Press enter to exit...");
            Console.ReadLine();
        }
    }
}

Библиотека классов CLR Visual С++ "Native.dll"

Wrapper.hpp

#pragma once

namespace Native
{

public ref class Wrapper
{
public:
    Wrapper();

}; // public ref class Wrapper

} // namespace Native

Wrapper.cpp

// Platform Toolset: Visual Studio 2015 - Windows XP (v140_xp)
// Common Language Runtime Support: Common Language Runtime Support (/clr)
// .NET Target Framework Version: v4.0
// Warning Level: Level4
// Treat Warnings As Errors: Yes (/WX)
// Precompiled Header: Not Using Precompiled Headers
// SubSystem: Console (/SUBSYSTEM:CONSOLE)
// Optimization: Disabled (/Od)

#pragma once

#include "Wrapper.hpp"
#include "Caller.hpp"

namespace Native
{

Wrapper::Wrapper()
{
    Caller* caller = new Caller();
    delete caller;
}

} // namespace Native

Caller.hpp

#pragma once

namespace Native
{

class Caller
{
public:
    Caller();

}; // class Caller

} // namespace Native

Caller.cpp

// Platform Toolset: Visual Studio 2015 - Windows XP (v140_xp)
// Common Language Runtime Support: No Common Language RunTime Support
// Warning Level: Level4
// Treat Warnings As Errors: Yes (/WX)
// Precompiled Header: Not Using Precompiled Headers
// SubSystem: Console (/SUBSYSTEM:CONSOLE)
// Optimization: Disabled (/Od)

#include "Caller.hpp"
#include "Singleton.hpp"

namespace Native
{

Caller::Caller()
{
    Singleton::GetInstance()->DoSomething();
}

} // namespace Native

Singleton.hpp

#pragma once

#include <iostream>

namespace Native
{

class Singleton
{
public:
    Singleton() // !!! remove constructor to prevent crash !!!
    { }

    static Singleton* GetInstance()
    {
        static Singleton Instance; // !!! crashes here !!!
        return &Instance;
    }

    void DoSomething()
    {
        std::wcout << L"Doing something...\n";
    }

}; // class Singleton

} // namespace Native

Разборка

    static Singleton* GetInstance()
    {
10001650  push        ebp  
10001651  mov         ebp,esp  
10001653  push        0FFFFFFFFh  
10001655  push        10006A8Ch  
1000165A  mov         eax,dword ptr fs:[00000000h]  
10001660  push        eax  
10001661  mov         eax,dword ptr ds:[1001B334h]  
10001666  xor         eax,ebp  
10001668  push        eax  
10001669  lea         eax,[ebp-0Ch]  
1000166C  mov         dword ptr fs:[00000000h],eax  
        static Singleton Instance;
10001672  mov         eax,dword ptr ds:[1001B5D0h]  
10001677  mov         ecx,dword ptr fs:[2Ch]  
1000167E  mov         edx,dword ptr [ecx+eax*4] // !!! access violation here !!!
10001681  mov         eax,dword ptr ds:[1001B5A4h]  
10001686  cmp         eax,dword ptr [edx+4]  
1000168C  jle         Native::Singleton::GetInstance+79h (100016C9h)  

Регистры

EAX = 00000000 EBX = 00000000 ECX = 00000000 EDX = 006A0003 ESI = 001647C8
EDI = 0012F3BC EIP = 1000167E ESP = 0012F394 EBP = 0012F3A4 EFL = 00010282 

Изменить 1

При отладке локально, где произошел сбой, в сборке видны еще несколько символов:

    static Singleton* GetInstance()
    {
0FBD1650  push        ebp  
0FBD1651  mov         ebp,esp  
0FBD1653  push        0FFFFFFFFh  
0FBD1655  push        offset [email protected]@[email protected]@[email protected] (0FBD86BCh)  
0FBD165A  mov         eax,dword ptr fs:[00000000h]  
0FBD1660  push        eax  
0FBD1661  mov         eax,dword ptr [___security_cookie (0FBF03CCh)]  
0FBD1666  xor         eax,ebp  
0FBD1668  push        eax  
0FBD1669  lea         eax,[ebp-0Ch]  
0FBD166C  mov         dword ptr fs:[00000000h],eax  
        static Singleton Instance;
0FBD1672  mov         eax,dword ptr [__tls_index (0FBF0668h)]  
0FBD1677  mov         ecx,dword ptr fs:[2Ch]  
0FBD167E  mov         edx,dword ptr [ecx+eax*4]  
0FBD1681  mov         eax,dword ptr [TSS0<`template-parameter-2',Native::Singleton::tInstance,Native::Singleton * * const volatile,void,int, ?? &> (0FBF063Ch)]  
0FBD1686  cmp         eax,dword ptr [edx+4]  
0FBD168C  jle         Native::Singleton::GetInstance+79h (0FBD16C9h)  

Символ __tls_index, по-видимому, принадлежит к некоторому локальному хранилищу нитей (догадался от имени). Это соответствует Magic statics, который использует локальное хранилище потоков как оптимизацию производительности в эталонной реализации. При сбое локальное хранилище потоков возвращает 0.

Может ли это быть ошибкой среды выполнения Windows Server 2003, которая управляет и инициализирует локальный магазин потока?

Изменить 2

Сообщено как ошибка через Microsoft Connect: Отчет об ошибке

Ответ 1

Это ответ Microsoft, который был отправлен в мой отчет отчет об ошибке в Microsoft Connect:

В Windows Server 2003 и Windows XP возникают проблемы с динамической загрузкой библиотеки DLL (через LoadLibrary), которая использует потоковое локальное хранилище, которое использует потокобезопасную статику, чтобы обеспечить эффективное выполнение, когда статический локаль уже был инициализирован. Поскольку эти системы не поддерживаются, крайне маловероятно, чтобы патч был создан для этих систем, чтобы добавить эту поддержку, как это присутствует в Vista и более новых ОС, и мы неохотно наказываем производительность в операциях поддержки, чтобы обеспечить это функциональность для старых внекорпоративных.

Чтобы обойти проблему, вы можете использовать /Zc: threadSafeInit-, чтобы отключить безопасный код инициализации, и это позволит избежать локальной переменной потока. Конечно, при этом код инициализации возвращается в режим VS2013 и не является потокобезопасным, поэтому этот параметр является жизнеспособным, если вы не полагаетесь на безопасность потоков локальной статистики.

Ответ 2

ваш конструктор НЕ удаляется с момента его публикации. переместите его в частную часть объявления вашего класса.