How to use C # BackgroundWorker to report progress in native C ++ code?

In my Visual Studio solution, I have an interface implemented in C # and some code implemented in C ++.

I am using the BackgroundWorker class to report the duration of an operation.

How can I use BackgroundWorker to report the progress of my native C ++ code?

In other words, how can I rewrite the C # code below to my own C ++ and call the resulting C ++ code from C #? If it is not possible to rewrite the code below, one could learn about other equivalent solutions. Thanks.

 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"); } } } 
-1
source share
2 answers

Example. It has been tested on x86 C # and native 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 <iostream> 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 # class that interacts with C ++ code:

 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(); } } } 

The following links may be helpful:

0
source

In C # code, declare your own C ++ method using the DLLImport attribute and call this method from the BackgroundWorker.ProgressChanged handler. Strike>

DISCLAIMER: I have not tested this code, and it may not be the best approach, but, at least in theory, I think it will work. Hopefully one of the more experienced members here can check if this is really correct.

It is assumed that you are running a background worker from C #, and you want the ProgressChanged event in C # (I assume that it is, since your user interface is in C #).

You can still use BackgroundWorker in C #, but just call your native method using the DLLImport mentioned above. You can also change the signature of your method to take a pointer to a function that matches the signature for ReportProgress , and then call that delegate from your own code.

MSDN has articles on Routing Delegates and Function Pointers (although all examples use C ++ / CLI). You can also see the documentation for DLLImport and the MarshalAs and UnmanagedType listing.

For example, if your own method was

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

you define the type of function pointer in your native code as

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

and change your own signature to

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

Your DLLImport for foo will look like

 // 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); 

Then your worker will look like

 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); } 

Hope this made some sense (and hope it is right!)

+1
source

Source: https://habr.com/ru/post/892153/


All Articles