为什么我的多线程C++ .NET 应用程序仅在可视化工作室之外执行时才崩溃



我创建了一个非常简单的C++.NET应用程序,使用托管和非托管代码来复制我的问题。

当用户单击按钮时,应该会生成一个新线程并执行一些耗时的任务,同时通过状态更新回调我的主线程。

此代码从 Visual Studios Express 2010 的 side 中编译并成功执行。也就是说,当我单击"播放"按钮时,我的项目可以构建和执行而不会崩溃。但是,如果我转到可执行文件所在的"发布"文件夹并运行它,则单击按钮后应用程序就会崩溃。我正在使用/clr 和发布模式进行编译。

我创建一个表单并添加一个按钮。以下是 Form1.h 的代码:

#pragma once
#include "core.h"
#include <Windows.h>
#include <process.h>
namespace RepErr {
    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    using namespace System::Runtime::InteropServices;
    int x;
    /// <summary>
    /// Summary for Form1
    /// </summary>
    public ref class Form1 : public System::Windows::Forms::Form
    {
    public:
        Form1(void)
        {
            InitializeComponent();
            //
            //TODO: Add the constructor code here
            //
        }
    protected:
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        ~Form1()
        {
            if (components)
            {
                delete components;
            }
        }
    private: System::Windows::Forms::Button^  button1;
    protected: 
    private:
        /// <summary>
        /// Required designer variable.
        /// </summary>
        System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        void InitializeComponent(void)
        {
            this->button1 = (gcnew System::Windows::Forms::Button());
            this->SuspendLayout();
            // 
            // button1
            // 
            this->button1->Location = System::Drawing::Point(104, 62);
            this->button1->Name = L"button1";
            this->button1->Size = System::Drawing::Size(75, 23);
            this->button1->TabIndex = 0;
            this->button1->Text = L"button1";
            this->button1->UseVisualStyleBackColor = true;
            this->button1->Click += gcnew System::EventHandler(this, &Form1::button1_Click);
            // 
            // Form1
            // 
            this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
            this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
            this->ClientSize = System::Drawing::Size(276, 160);
            this->Controls->Add(this->button1);
            this->Name = L"Form1";
            this->Text = L"Form1";
            this->ResumeLayout(false);
        }
#pragma endregion
    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
                 core *o1 = new core();
                 unsigned  uiThread1ID;
                 HANDLE hth1 = (HANDLE)_beginthreadex(NULL, 0, core::ThreadStaticEntryPoint, o1, CREATE_SUSPENDED, &uiThread1ID);
                 ResumeThread( hth1 );
             }
public:
    static void* callback(int smallIndex) {
        x = smallIndex;
        void* dtpage = NULL;
        return dtpage;
    }
    delegate void* myCALLBACKDelegate(int smallIndex);
    static GCHandle gch;
    //static constructor, initialize delegate here
    static Form1() {
        myCALLBACKDelegate^ fp=gcnew myCALLBACKDelegate(callback);
        gch = GCHandle::Alloc(fp);
        formCallback = static_cast<myCALLBACK>(Marshal::GetFunctionPointerForDelegate(fp).ToPointer());
    }
};
}

这是 core.h 的代码:

`#pragma once
#include <vcclr.h>
#include "Form1.h"
extern "C" { 
    typedef void* (__stdcall *myCALLBACK)(int smallIndex);
}
// static pointer to managed function
myCALLBACK formCallback;
public class core {
public:
    core() {}
    static unsigned __stdcall ThreadStaticEntryPoint(void *pThis) {
        core *pCr = (core*)pThis;
        pCr->doCall();
        return 1;
    }
    void doCall() {
        formCallback(1);
    }
};
#pragma endregion

为什么此应用程序在Visual Studios之外崩溃?我是否需要将某些 dll 或 .NET 文件与可执行文件放在同一个目录中?

谢谢威廉

如果我将警告级别更改为最高详细级别,编译器输出:

1>c:usersbilldocumentsvisual studio 2010projectsreperrreperrForm1.h(107): warning C4434: a static constructor must have private accessibility; changing to private access
1>c:usersbilldocumentsvisual studio 2010projectsreperrreperrForm1.h(87): warning C4100: 'e' : unreferenced formal parameter
1>c:usersbilldocumentsvisual studio 2010projectsreperrreperrForm1.h(87): warning C4100: 'sender' : unreferenced formal parameter
1>RepErr.cpp(9): warning C4100: 'args' : unreferenced formal parameter
1>RepErr.cpp(19): warning C4339: '_TP_POOL' : use of undefined type detected in CLR meta-data - use of this type may lead to a runtime exception
1>RepErr.cpp(19): warning C4339: '_TP_CLEANUP_GROUP' : use of undefined type detected in CLR meta-data - use of this type may lead to a runtime exception
1>RepErr.cpp(19): warning C4339: '_TP_CALLBACK_INSTANCE' : use of undefined type detected in CLR meta-data - use of this type may lead to a runtime exception
1>RepErr.cpp(19): warning C4339: '_ACTIVATION_CONTEXT' : use of undefined type detected in CLR meta-data - use of this type may lead to a runtime exception
1>  Generating Code...
1>c:UsersBilldocumentsvisual studio 2010ProjectsRepErrRepErrRepErr.cpp : warning C4710: '__clrcall RepErr::Form1::~Form1(void)' : function not inlined
1>c:usersbilldocumentsvisual studio 2010projectsreperrreperrform1.h(28): warning C4710: 'void __clrcall RepErr::Form1::InitializeComponent(void)' : function not inlined
1>c:usersbilldocumentsvisual studio 2010projectsreperrreperrreperr.cpp(16): warning C4710: '__clrcall RepErr::Form1::Form1(void)' : function not inlined
1>  .NETFramework,Version=v4.0.AssemblyAttributes.cpp
1>  RepErr.vcxproj -> c:usersbilldocumentsvisual studio 2010ProjectsRepErrReleaseRepErr.exe
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

这是 RepErr.cpp:

// RepErr.cpp : main project file.
#include "stdafx.h"
#include "Form1.h"
using namespace RepErr;
[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
    // Enabling Windows XP visual effects before any controls are created
    Application::EnableVisualStyles();
    Application::SetCompatibleTextRenderingDefault(false); 
    // Create the main window and run it
    Application::Run(gcnew Form1());
    return 0;
}

程序崩溃,因为formCallback为 NULL(因此core::doCall取消引用 NULL 指针)。 formCallback为 NULL,因为初始化它的 Form1 静态构造函数永远不会运行。

可以通过向 Form1 静态构造函数添加以下行来演示这一点:

static Form1() {
    // ... other initialisation ...
    MessageBox::Show(((int) formCallback).ToString("x8"));
}

在调试版本(或在 VS 调试器下运行的发布)中,将显示一个包含函数指针值的消息框。在发布版本(不在调试器下)中,不会显示此对话框,因为静态构造函数未运行。

静态构造函数不会运行,因为类型标记为 BeforeFieldInit。这意味着"类型的初始值设定项方法在首次访问为该类型定义的任何静态字段时或之前执行"。如果无法访问任何静态字段,则不需要运行静态构造函数(在发布版本中,它不需要)。

根据此连接问题,这是设计使然。解决方法是在实例Form1构造函数中访问您类型的静态字段,这将强制静态构造函数运行,这将正确初始化formCallback

static int s_dummy;
public:
    Form1()
    {
        // force static constructor to run now
        s_dummy = 0;
        InitializeComponent();
    }

或者(也是我的建议),使用 Thread 类创建一个新的托管线程;这将避免对托管委托、GCHandle、formCallback全局函数指针和静态构造函数的需求。如果需要执行非托管代码,可以从该托管线程调用本机C++。

最新更新