开发者

Writing C# GUI over a C++ dll or C++ exe

I have a C++ console Exe which does some progamming. Now i wanted to write a C# GUI which does some of the programming that the C++ exe does. I was thinking of few approaches,

  1. Write the C# GUI with all programming in C++ done from scratch.(I do not want to do this for the amount of rework it entails)
  2. Build a C++ dll which does the programming and have it imported i开发者_开发知识库n GUI app.(Now here i have a concern. How do i capture the output of the routines in c++ dll and display it in GUI? Should i return the output as string for every routine that the app calls.? Since i dont know managed c++ iam going to build an unmanaged C++ dll. )


Building a C++/CLI dll really isn't that hard. You basically use unmanaged C++ code except that you define a "public ref class" which hosts the functions you want the C# code to see.

What kind of data are you returning? Single numbers, matrices of numbers, complex objects?

UPDATE: Since it has been clarified that the "output" is iostreams, here is a project demonstrating redirection of cout to a .NET application calling the library. Redirecting clog or cerr would just require a handful of additional lines in DllMain patterned after the existing redirect.

The zip file includes VS2010 project files, but the source code should also work in 2005 or 2008.

The iostreams capture functionality is contained in the following code:

// compile this part without /clr
class capturebuf : public std::stringbuf
{
protected:
    virtual int sync()
    {
        // ensure NUL termination
        overflow(0);
        // send to .NET trace listeners
        loghelper(pbase());
        // clear buffer
        str(std::string());
        return __super::sync();
    }
};

BOOL WINAPI DllMain(_In_ HANDLE _HDllHandle, _In_ DWORD _Reason, _In_opt_ LPVOID _Reserved)
{
    static std::streambuf* origbuf;
    static capturebuf* altbuf;
    switch (_Reason)
    {
    case DLL_PROCESS_ATTACH:
        origbuf = std::cout.rdbuf();
        std::cout.rdbuf(altbuf = new capturebuf());
        break;
    case DLL_PROCESS_DETACH:
        std::cout.rdbuf(origbuf);
        delete altbuf;
        break;
    }

    return TRUE;
}

// compile this helper function with /clr
void loghelper(char* msg) { Trace::Write(gcnew System::String(msg)); }


So you just want to call c++ library from managed .net code?

Then you would need to either build a COM object or a p-invokable library in c++. Each approach has it's own pros and cons depending on your business need. You would have to marshall data in your consumer. There are tons and tons of material on both concepts.


Possibly relevant: Possible to call C++ code from C#?


You could just write a C# GUI wrapper (as you suggest in option 2) and spawn the C++ process; however, this will be a little slow (I don't know if that matters).

To run your C++ exe and capture the output you can use the ProcessRunner I put together. Here is the basic usage:

using CSharpTest.Net.Processes;
partial class Program
{
    static int Main(string[] args)
    {
        ProcessRunner run = new ProcessRunner("svn.exe", "update");
        run.OutputReceived += new ProcessOutputEventHandler(run_OutputReceived);
        return run.Run();
    }

    static void run_OutputReceived(object sender, ProcessOutputEventArgs args)
    {
        Console.WriteLine("{0}: {1}", args.Error ? "Error" : "Output", args.Data);
    }
}


See the first comment in this page to redirect stderr


Probably the best way to go here is use P/Invoke or Platform Invoke. Depending on the structure or your C++ dll interface you may want to wrap it in a pure C interface; it is easiest if your interface uses only blittable types. If you limit your dll interface to blittable types (Int32, Single, Boolean, Int32[], Single[], Double[] - the basics) you will not need to do any complex marshaling of data between the managed (C#) and unmanaged (C) memory spaces.

For example, in your c# code you define the available calls in your C/C++ dll using the DllImport attribute.

[DllImport, "ExactDllName.dll"]  
static extern boolean OneOfMyCoolCRoutines([In] Double[] x, [In] Double[] y, [Out] Double result)

The little [In]'s and [Out]'s are not strictly required, but they can speed things along. Now having added your "ExactDllName.dll" as a reference to your C# project, you can call your C/C++ function from your C# code.

fixed(Double *x = &x[0], *y = &y[0] )  
{  
   Boolean returnValue = OneOfMyCoolCRoutines(x, y, r);  
}  

Note that I'm essentially passing pointers back and fourth between my dll and C# code. That can cause memory bugs because the locations of those arrays may be changed by the CLR garbage collector, but the C/C++ dll will know nothing of it. So to guard against that, I've simply fixed those pointers in my C#, and now they won't move in memory while my dll is operating on those arrays. This is now unsafe code and I'll need to compile my C# code w/ that flag.

There are many fine details to language interop, but that should get you rolling. Sticking to a stateless C interface of blittable types is a great policy if possible. That will keep your language interop code the cleanest.

Good luck,

Paul

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜