如何使用C#BackgroundWorker报告本机C ++代码的进度?

在我的Visual Studio解决方案中,我使用C#实现了UI,并且在本机C ++中实现了一些代码。

我使用BackgroundWorker类来实现报告执行长操作的进度。

如何使用BackgroundWorker报告本机C ++代码的进度?

换句话说,如何将C#代码重写为本机C ++并从C#调用获得的C ++代码? 如果无法直接重写下面的代码,那么了解其他等效解决方案可能会很好。 谢谢。

 class MyClass { public void Calculate(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; for (int i = 0; i < StepCount; i++) { if (worker.CancellationPending) { e.Cancel = true; break; } // code which handles current iteration here worker.ReportProgress((i + 1) * 100 / StepCount, "Report progress message"); } } } 

在C#代码中,使用DLLImport属性声明本机C ++方法,并从BackgroundWorker.ProgressChanged处理程序中调用此方法。

免责声明:我没有测试过这些代码,这可能不是最好的方法,但至少在理论上我觉得这样可行。 希望这里经验丰富的成员之一可以validation这是否真的正确。

这假设您是从C#启动后台工作程序,并且您希望在C#中使用ProgressChanged事件(我认为这是因为您的UI在C#中)。

您仍然可以在C#中使用BackgroundWorker ,但只需使用我上面提到的DLLImport调用您的本机方法。 您还可以修改方法的签名,以获取与ReportProgress的签名匹配的函数指针,然后从您的本机代码中调用该委托。

MSDN上有一些关于编组委托和函数指针的文章(尽管这些示例都使用C ++ / CLI)。 您可能还想查看DLLImport和MarshalAs属性的文档以及UnmanagedType枚举。

例如,如果您的原生方法是

 void foo(int arg1, BOOL arg2) { // Your code here } 

你可以在你的本机代码中定义一个函数指针类型

 // Corresponds to void BackgroundWorker.ReportProgress(int progress, object state) typedef void (*NativeReportProgress) (int, void*); 

并将您的原生签名更改为

 void foo(int arg1, BOOL arg2, NativeReportProgress progressPtr) { // Some code. progressPtr(progressValue, stateVar); } 

你的foo DLLImport看起来像

 // Delegate type for BackgroundWorker.ReportProgress delegate void ReportProgressDelegate(int progress, object state); // The MarshalAs attribute should handle the conversion from the .NET // delegate to a native C/C++ function pointer. [DLLImport] void foo([MarshalAs(UnmanagedType.I4)] Int32 arg1, [MarshalAs(UnmanagedType.Bool)] bool arg2, [MarshalAs(UnmanagedType.FunctionPointer)] ReportProgressDelegate progressDel); 

然后你的工人看起来像

 void DoWork(object sender, DoWorkEventArgs e) { var worker = (BackgroundWorker)sender; // Notice that worker.ReportProgress is not followed the by (). // We're not actually calling the method here, we're just passing // a function pointer to that method into foo. foo(intArg, boolArg, worker.ReportProgress); } 

希望这有点意义(希望它也是正确的!)

示例如下。 它在x86 C#和原生Visual C ++上进行了测试:

CppLayer.h:

  #ifdef CPPLAYER_EXPORTS #define CPPLAYER_API __declspec(dllexport) #else #define CPPLAYER_API __declspec(dllimport) #endif extern "C" { typedef void (__stdcall *ReportProgressCallback)(int, char *); typedef bool (__stdcall *CancellationPendingCallback)(); struct CPPLAYER_API WorkProgressInteropNegotiator { ReportProgressCallback progressCallback; CancellationPendingCallback cancellationPending; bool cancel; }; CPPLAYER_API void __stdcall CppLongFunction(WorkProgressInteropNegotiator& negotiator); } 

CppLayer.cpp:

 #include "stdafx.h" #include "CppLayer.h" #include  extern "C" { // This is an example of an exported function. CPPLAYER_API 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]); } } }; 

与C ++代码互操作的C#类:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using System.ComponentModel; using System.Threading; namespace CSharpLayer { class SandboxCppProgress { 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 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); // 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(); e.Cancel = negotiator.cancel; } static EventWaitHandle resetEvent = null; static void CSharpReportProgressStatus(object sender, ProgressChangedEventArgs e) { string message = e.UserState as string; Console.WriteLine("Report {0:00}% with message '{1}'", e.ProgressPercentage, message); BackgroundWorker bw = sender as BackgroundWorker; if (e.ProgressPercentage > 50) bw.CancelAsync(); } static void CSharpReportComplete(object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) { Console.WriteLine("Long operation canceled!"); } else if (e.Error != null) { Console.WriteLine("Long operation error: {0}", e.Error.Message); } else { Console.WriteLine("Long operation complete!"); } resetEvent.Set(); } public static void Main(string[] args) { 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(); } } } 

以下链接可能有用:

  • 与本机库互操作(单声道)
  • 如何:使用C ++ Interop编组回调和代理
  • 如何:使用PInvoke的Marshal函数指针