我使用托管代码和非托管代码创建了一个非常简单的C .NET应用程序来复制我的问题.
当用户单击一个按钮时,新的线程应该生成并执行一些耗时的任务,同时通过状态更新回调我的主线程.
此代码在Visual Studios Express 2010中编译并成功执行.也就是说,当我单击“播放”按钮时,我的项目构建并执行而不会崩溃.但是,如果我转到可执行文件所在的Release文件夹并运行它,则单击该按钮后应用程序将崩溃.我正在使用/ clr和Release模式进行编译.
我创建一个表单并添加一个按钮.这就是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:\users\bill\documents\visual studio 2010\projects\reperr\reperr\Form1.h(107): warning C4434: a static constructor must have private accessibility; changing to private access
1>c:\users\bill\documents\visual studio 2010\projects\reperr\reperr\Form1.h(87): warning C4100: 'e' : unreferenced formal parameter
1>c:\users\bill\documents\visual studio 2010\projects\reperr\reperr\Form1.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:\Users\Bill\documents\visual studio 2010\Projects\RepErr\RepErr\RepErr.cpp : warning C4710: '__clrcall RepErr::Form1::~Form1(void)' : function not inlined
1>c:\users\bill\documents\visual studio 2010\projects\reperr\reperr\form1.h(28): warning C4710: 'void __clrcall RepErr::Form1::InitializeComponent(void)' : function not inlined
1>c:\users\bill\documents\visual studio 2010\projects\reperr\reperr\reperr.cpp(16): warning C4710: '__clrcall RepErr::Form1::Form1(void)' : function not inlined
1> .NETFramework,Version=v4.0.AssemblyAttributes.cpp
1> RepErr.vcxproj -> c:\users\bill\documents\visual studio 2010\Projects\RepErr\Release\RepErr.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"));
}
在Debug构建(或在VS调试器下运行Release)中,将显示一个MessageBox,其中包含函数指针的值.在发布版本中(不在调试器下),不显示此对话框,因为未运行静态构造函数.
静态构造函数未运行,因为类型标记为BeforeFieldInit.这意味着“类型的初始化方法是在首次访问为该类型定义的任何静态字段之前或之前执行的”.如果没有任何静态字段的访问权限,则不需要运行静态构造函数(并且在发布版本中,它不会运行).
根据this Connect issue,这是设计的.解决方法是在实例Form1构造函数中访问您的类型的静态字段,这将强制静态构造函数运行,这将正确初始化formCallback:
static int s_dummy;
public:
Form1()
{
// force static constructor to run now
s_dummy = 0;
InitializeComponent();
}
或者(以及我建议的),使用Thread类创建一个新的托管线程;这将避免需要托管委托,GCHandle,formCallback全局函数指针和静态构造函数.从该托管线程,如果需要执行非托管代码,可以调用本机C.