本机 C++ 程序在使用 C++/CLI 和 C# 互操作 DLL 启动时崩溃

Posted

技术标签:

【中文标题】本机 C++ 程序在使用 C++/CLI 和 C# 互操作 DLL 启动时崩溃【英文标题】:Native C++ program crashes on startup with C++/CLI & C# interop DLL 【发布时间】:2015-11-10 00:36:01 【问题描述】:

我需要将 C# WPF UserControls 包装在 C++/CLI 包装器中以在本机 C++ 程序中使用。本机程序大量使用MFC,并在VS2010中编译。我已经在单独的解决方案中正确生成了 C++/CLI .lib 和 .dll 文件,并从 MFC 应用程序链接到它们,该应用程序成功编译和链接,像往常一样生成可执行文件。但是,当我启动 MFC 应用程序时,我在到达 main() 之前就收到了访问冲突错误 0xc0000005。当我注释掉包装代码时,程序正常启动。

为了尝试隔离问题,我尝试创建一个完全独立的本机 C++ 项目,其唯一目的是使用虚拟数据调用包装类。该项目到达 main() 但在我调用包装类的构造函数时抛出异常(0xe0434352)。

我已经尝试寻找任何可能的解决方案,但与我的情况最匹配的解决方案是检查函数签名以确保我传递了正确数量的参数(我是)。我还从独立项目中查找了 0xe0434352 异常,它似乎是一个通用的 .NET 异常。

我还看到this 发布的问题,但希望解决方案比这更简单。我一直按照this 博客上的说明创建包装类。

我将包括除 MFC 应用程序代码之外的所有代码,因为它甚至没有加载到 main()。我希望如果我能设法解决全新项目中的问题,MFC 应用程序将启动。

C# WPF:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ManagedWPFLink

    public struct TestResultData
    
        public string inspectionType;
        public string testResult;
        public string measuredValue;
        public string passCondition;
        public string comments;
    

    /// <summary>
    /// Interaction logic for TestResultsDialog.xaml
    /// </summary>
    public partial class TestResultsDialog : UserControl
    
        private Window window;

        public TestResultsDialog()
        
            InitializeComponent();

            DataGridTextColumn col1 = new DataGridTextColumn();
            DataGridTextColumn col2 = new DataGridTextColumn();
            DataGridTextColumn col3 = new DataGridTextColumn();
            DataGridTextColumn col4 = new DataGridTextColumn();
            DataGridTextColumn col5 = new DataGridTextColumn();

            this.testResultsGrid.Columns.Add(col1);
            this.testResultsGrid.Columns.Add(col2);
            this.testResultsGrid.Columns.Add(col3);
            this.testResultsGrid.Columns.Add(col4);
            this.testResultsGrid.Columns.Add(col5);

            col1.Binding = new Binding("inspectionType");
            col2.Binding = new Binding("testResult");
            col3.Binding = new Binding("measuredValue");
            col4.Binding = new Binding("passCondition");
            col5.Binding = new Binding("comments");

            col1.Header = "Inspection Type";
            col2.Header = "Test Result";
            col3.Header = "Measured Value";
            col4.Header = "Pass Condition";
            col5.Header = "Comments";

            this.window = new Window
            
                Title = "Inspection Results",
                Content = this,
                SizeToContent = SizeToContent.WidthAndHeight,
                ResizeMode = ResizeMode.NoResize
            ;
        

        public void addRow(string inspectionType, string testResult, string measuredValue, string passCondition, string comments)
        
            TestResultData data = new TestResultData();

            data.inspectionType = inspectionType;
            data.testResult = testResult;
            data.measuredValue = measuredValue;
            data.passCondition = passCondition;
            data.comments = comments;

            this.testResultsGrid.Items.Add(data);
        

        public void deleteAllRows()
        
            this.testResultsGrid.Items.Clear();
        

        public void showDialog()
        
            this.window.ShowDialog();
        
    

C# XAML:

<UserControl x:Class="ManagedWPFLink.TestResultsDialog"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="400" d:DesignWidth="600">
    <Grid>
        <DataGrid AutoGenerateColumns="True" Height="376" HorizontalAlignment="Left" Margin="12,12,0,0" Name="testResultsGrid" VerticalAlignment="Top" Width="576" ItemsSource="Binding" MinColumnWidth="40">
        </DataGrid>
    </Grid>
</UserControl>

C++/CLI 头文件:

// ManagedWPFLink.h

#pragma once

namespace WPFLink 

    class WPFPrivate;

    class __declspec(dllexport) WPFWrapper
    
    private:
        WPFPrivate *internalWrapper;

    public:
        WPFWrapper();
        ~WPFWrapper();

        void addItems(const char **data);
        void deleteAllRows();
        void showDialog();
    ;

C++/CLI 源码:

// This is the main DLL file.

#include "stdafx.h"

#using <WindowsBase.dll>
#using <PresentationCore.dll>
#using <PresentationFramework.dll>

#using "ManagedWPFLink.dll"

#include "WPFLink.h"

using namespace System::Runtime::InteropServices;

namespace WPFLink

    public class WPFPrivate
    
    public:
        msclr::auto_gcroot<System::String^> inspectionType;
        msclr::auto_gcroot<System::String^> testResult;
        msclr::auto_gcroot<System::String^> measuredValue;
        msclr::auto_gcroot<System::String^> passCondition;
        msclr::auto_gcroot<System::String^> comments;

        msclr::auto_gcroot<ManagedWPFLink::TestResultsDialog^> testResultDialog;

    public:
        WPFPrivate()
        
        

        ~WPFPrivate()
        
            delete testResultDialog;
        
    ;

    WPFWrapper::WPFWrapper()
    
        this->internalWrapper = new WPFLink::WPFPrivate();
    

    WPFWrapper::~WPFWrapper()
    
        delete this->internalWrapper;
    

    void WPFWrapper::addItems(const char **data)
    
        this->internalWrapper->inspectionType = gcnew System::String(data[0]);
        this->internalWrapper->testResult = gcnew System::String(data[1]);
        this->internalWrapper->measuredValue = gcnew System::String(data[2]);
        this->internalWrapper->passCondition = gcnew System::String(data[3]);
        this->internalWrapper->comments = gcnew System::String(data[4]);

        this->internalWrapper->testResultDialog->addRow(this->internalWrapper->inspectionType, this->internalWrapper->testResult,
            this->internalWrapper->measuredValue, this->internalWrapper->passCondition, this->internalWrapper->comments);
    

    void WPFWrapper::deleteAllRows()
    
        this->internalWrapper->testResultDialog->deleteAllRows();
    

    void WPFWrapper::showDialog()
    
        this->internalWrapper->testResultDialog->showDialog();
    

原生 C++ 代码:

#include "..\WPFLink\WPFLink.h"

int main()

    WPFLink::WPFWrapper wrapper; // Generates 0xe0434352 error
    return 0;

【问题讨论】:

使用 VS 调试器单步调试本机版本没有提供有用的信息? 我认为您不应该像在 ~WPFPrivate() 中那样删除 auto_gcroot 对象... auto_gcroot 进行自己的资源管理。我还会尝试在 WPFPrivate 中注释掉那个花哨的 TestResultsDialog ,看看这是否导致了问题,或者它是否是更根本的问题。 @user4581301 使用 VS 调试器单步执行本机版本会给出评论中提到的错误,无论我是“跳过”还是“进入”该行。没有其他用处。 @MarkWaterman 我会尝试删除删除语句,看看是否有帮助。注释掉 TestResultsDialog 可能没有用,因为这是我试图与这个互操作层交互的对象。 确认注释掉 WPFPrivate 中的 TestResultsDialog 可以防止系统崩溃。我需要弄清楚它为什么会崩溃或如何正确包装 WPF 以在 MFC 中使用。 【参考方案1】:

扯了这么多毛,我在这里使用了这篇帖子中的方法:http://www.codeguru.com/cpp/cpp/cpp_managed/nfc/article.php/c14589/Hosting-WPF-Content-in-an-MFC-Application.htm

我使用相关代码生成了一个 HWND,该程序与我自己的原生 C++ 代码完美配合。但是,在我们的主 MFC 应用程序启动时,我仍然遇到访问冲突,我根本没有设法缩小范围。我拥有的唯一信息是,如果我尝试链接到我的 DLL,程序就会崩溃。我将不得不放弃这种链接到存根 .lib 文件并加载必要的 DLL 的方法,而是重做接口以仅使用 extern "C" 函数并在我的 DLL 上手动调用 LoadLibrary。

感谢所有试图提供帮助的人。

【讨论】:

以上是关于本机 C++ 程序在使用 C++/CLI 和 C# 互操作 DLL 启动时崩溃的主要内容,如果未能解决你的问题,请参考以下文章

尝试从 C++/CLI 调用非托管 C++ 时解决错误

是否可以在 C# 中从本机 C++ 类创建变量?

从 c# 访问 C++ .lib 库

C++/CLI + C++ Native 会提高性能吗? [关闭]

我可以使用 C++/CLI (.NET Winforms/WPF) 为用本机 C 和 C++ 编写的应用程序提供 GUI

C++ 本机 Vs C++/Cli 性能(用于 OpenCV 项目)