Почему мое многопоточное приложение C ++ .NET падает только при выполнении за пределами визуальных студий? - PullRequest
3 голосов
/ 06 мая 2011

Я создал очень простое приложение C ++ .NET, использующее как управляемый, так и неуправляемый код для репликации моей проблемы.

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

Этот код компилируется и успешно выполняется изнутри Visual Studio Studios Express 2010. То есть, когда я нажимаю кнопку «Play», мой проект собирается и выполняется без сбоев.Однако, если я перехожу в папку 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;
}

1 Ответ

5 голосов
/ 06 мая 2011

Программа аварийно завершает работу, поскольку formCallback имеет значение NULL (и, следовательно, core::doCall разыменовывает указатель NULL).formCallback равен NULL, потому что статический конструктор Form1, который его инициализирует, никогда не запускается.

Это можно продемонстрировать, добавив следующую строку в статический конструктор Form1:

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

В сборках Debug (или Release, запускаемых под отладчиком VS), MessageBox будет отображаться со значением указателя функции.В сборке выпуска (не в отладчике) этот диалог не отображается, потому что статический конструктор не запускается.

Статический конструктор не запускается, потому что тип помечен BeforeFieldInit .Это означает, что «метод инициализатора типа выполняется во время первого доступа к любому статическому полю, определенному для этого типа, или когда-то раньше».Если нет доступа к какому-либо статическому полю, тогда запуск статического конструктора не требуется (а в сборке выпуска - нет).

В соответствии с этой проблемой Connect ,это по замыслу.Обходной путь должен получить доступ к статическому полю вашего типа в конструкторе Form1 экземпляра, который заставит статический конструктор работать, что правильно инициализирует formCallback:

static int s_dummy;

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

        InitializeComponent();
    }

В качестве альтернативы (и что ярекомендую), используйте класс Thread для создания нового управляемого потока;это исключит необходимость в управляемом делегате, GCHandle, глобальном указателе функции formCallback и статическом конструкторе.Из этого управляемого потока вы можете вызывать нативный C ++, если вам нужно выполнить неуправляемый код.

...