gpt4 book ai didi

c# - 如何更改 WinForms 中的 progressBar 以报告 native C++ 代码中的进度?

转载 作者:搜寻专家 更新时间:2023-10-31 02:16:14 27 4
gpt4 key购买 nike

我尝试使用来自 How to use C# BackgroundWorker to report progress in native C++ code? 的代码在 WinForms 中更改 progressBar。但是如果我创建一个表单,从 c++ dll 调用的函数不会启动 (CSharpReportProgressStatus())。无需创建表单即可正常工作。

CppLayer.h:

#define DLLAPI __declspec(dllexport)

extern "C"
{
typedef void (__stdcall *ReportProgressCallback)(int, char *);
typedef bool (__stdcall *CancellationPendingCallback)();

struct DLLAPI WorkProgressInteropNegotiator
{
ReportProgressCallback progressCallback;
CancellationPendingCallback cancellationPending;
bool cancel;
};

DLLAPI void __stdcall CppLongFunction(WorkProgressInteropNegotiator& negotiator);
}

CppLayer.cpp:

#include "CppLayer.h"
#include <iostream>
#include <windows.h> //Для sleep

typedef void (__stdcall * pfnCallback)(int progress, int* cancel);

extern "C"
{
DLLAPI void __stdcall CppLongFunction(WorkProgressInteropNegotiator& negotiator)
{
const int STEP_COUNT = 12;

char * messages[3] = {"ONE", "TWO", "THREE"};

for (int i = 0; i < STEP_COUNT; i++)
{
Sleep(100);

if (negotiator.cancellationPending()) {
negotiator.cancel = true;
break;
}

std::cout << "Calculate " << i << std::endl;
negotiator.progressCallback((i + 1) * 100 / STEP_COUNT, messages[i % 3]);
}
}
};

程序.cs:

using System;
using System.Windows.Forms;

namespace CallBackFromCpp
{
static class Program
{
[STAThread]
public static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Presenter p = new Presenter();
Application.Run(p.Init());
}
}
}

Form1.cs:

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace CallBackFromCpp
{
public partial class Form1 : Form
{
public event EventHandler StartEvent;

public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
if (StartEvent != null)
StartEvent(this, e);
}
}
}

Presenter.cs:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace CallBackFromCpp
{
public class Presenter
{
Form1 _view;

public ApplicationContext Init()
{
_view = new Form1();
_view.StartEvent += _view_StartEvent;

return new ApplicationContext(_view);
}

public delegate void ReportProgressCallback(int percentage, string message);

public delegate bool CancellationPendingCallback();

[StructLayout(LayoutKind.Sequential)]
public class WorkProgressInteropNegotiator
{
public ReportProgressCallback reportProgress;

public CancellationPendingCallback cancellationPending;

#pragma warning disable 0649
// C# does not see this member is set up in native code, we disable warning to avoid it.
public bool cancel;
#pragma warning restore 0649
}

[DllImport("CppLayer.dll")]
public static extern void CppLongFunction([In, Out] WorkProgressInteropNegotiator negotiator);

static EventWaitHandle resetEvent = null;

void _view_StartEvent(object sender, EventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.ProgressChanged += CSharpReportProgressStatus;
bw.DoWork += CSharpLongFunctionWrapper;
bw.RunWorkerCompleted += CSharpReportComplete;

resetEvent = new AutoResetEvent(false);

bw.RunWorkerAsync();

resetEvent.WaitOne();
}

void CSharpLongFunctionWrapper(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;

WorkProgressInteropNegotiator negotiator = new WorkProgressInteropNegotiator();

negotiator.reportProgress = new ReportProgressCallback(bw.ReportProgress);
negotiator.cancellationPending = new CancellationPendingCallback(() => bw.CancellationPending);

GCHandle gch = GCHandle.Alloc(negotiator);

CppLongFunction(negotiator);

gch.Free();

e.Cancel = negotiator.cancel;
}

void CSharpReportProgressStatus(object sender, ProgressChangedEventArgs e)
{
string message = e.UserState as string;
_view.richTextBox1.Text += String.Format("Report {0:00}% with message '{1}'", e.ProgressPercentage, message);
_view.progressBar1.PerformStep();

BackgroundWorker bw = sender as BackgroundWorker;
if (e.ProgressPercentage > 50)
bw.CancelAsync();
}

void CSharpReportComplete(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
_view.richTextBox1.Text = "Long operation canceled!";
}
else if (e.Error != null)
{
_view.richTextBox1.Text = String.Format("Long operation error: {0}", e.Error.Message);
}
else
{
_view.richTextBox1.Text += "Long operation complete!";
}
resetEvent.Set();
}
}
}

最佳答案

问题已解决,在单独的线程中传输 dll 调用,并通过从该线程调用来更新 GUI。

Form1.cs:

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace CallBackFromCpp
{
public partial class Form1 : Form
{
public event EventHandler StartEvent;

public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
if (StartEvent != null)
StartEvent(this, e);
}

private void button2_Click(object sender, EventArgs e)
{
if (StopEvent != null)
StopEvent(this, e);
}
}
}

Presenter.cs:

using System;
using System.Threading;
using System.Windows.Forms;

namespace CallBackFromCpp
{
public class Presenter
{
Form1 _view;
WorkerClass _wc;

public ApplicationContext Init()
{
_view = new Form1();
_view.StartEvent += _view_StartEvent;
_view.StopEvent += _view_StopEvent;

return new ApplicationContext(_view);
}

void _view_StopEvent(object sender, EventArgs e)
{
if (_wc != null)
_wc.Stop = true;
}

delegate void ShowProgressDelegate(int progressPercentage, string message);

private void ShowProgress(int progressPercentage, string message)
{
_view.richTextBox1.Text += String.Format("Report {0:00}% with message '{1}'\n", progressPercentage, message);
_view.progressBar1.PerformStep();
}

void _view_StartEvent(object sender, EventArgs e)
{
ShowProgressDelegate showProgress = new ShowProgressDelegate(ShowProgress);
_wc = new WorkerClass(_view, showProgress);
Thread t = new Thread(new ThreadStart(_wc.RunProcess));
t.IsBackground = true; //make them a daemon - prevent thread callback issues
t.Start();
}
}
}

WorkerClass.cs:

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace CallBackFromCpp
{
public class WorkerClass
{
/// <summary>
/// Usually a form or a winform control that implements "Invoke/BeginInvode"
/// </summary>
ContainerControl _sender = null;

/// <summary>
/// The delegate method (callback) on the sender to call
/// </summary>
Delegate _senderDelegate = null;

public delegate void ReportProgressCallback(int percentage, string message);

public delegate bool CancellationPendingCallback();

[StructLayout(LayoutKind.Sequential)]
public class WorkProgressInteropNegotiator
{
public ReportProgressCallback reportProgress;

public CancellationPendingCallback cancellationPending;

#pragma warning disable 0649
// C# does not see this member is set up in native code, we disable warning to avoid it.
public bool cancel;
#pragma warning restore 0649
}

[DllImport("CppLayer.dll")]
public static extern void CppLongFunction([In, Out] WorkProgressInteropNegotiator negotiator);

/// <summary>
/// Constructor called by calle using ThreadPool OR ThreadStart
/// </summary>
public WorkerClass(ContainerControl sender, Delegate senderDelegate)
{
_sender = sender;
_senderDelegate = senderDelegate;
}

/// <summary>
/// Method for ThreadStart delegate
/// </summary>
public void RunProcess()
{
Thread.CurrentThread.IsBackground = true; //make them a daemon

WorkProgressInteropNegotiator negotiator = new WorkProgressInteropNegotiator();
negotiator.reportProgress = new ReportProgressCallback(ReportProgress);
negotiator.cancellationPending = new CancellationPendingCallback(() => { return Stop; });

// Refer for details to
// "How to: Marshal Callbacks and Delegates Using C++ Interop"
// http://msdn.microsoft.com/en-us/library/367eeye0%28v=vs.100%29.aspx
GCHandle gch = GCHandle.Alloc(negotiator);

CppLongFunction(negotiator);

gch.Free();
}

private void ReportProgress(int progressPercentage, string message)
{
_sender.BeginInvoke(_senderDelegate, new object[] { progressPercentage, message });
}

volatile bool _stop = false;
public bool Stop
{
set
{
_stop = value;
}
get
{
return _stop;
}
}
}
}

关于c# - 如何更改 WinForms 中的 progressBar 以报告 native C++ 代码中的进度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37207309/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com