C++ Callback to send text back to C#

前端 未结 4 1851
有刺的猬
有刺的猬 2021-01-03 09:14

I\'m new to C++. I have been told using a \"callback\" with C++ is the best solution for this. Here is my situation.

I have a DLL written in C++
this DLL has

相关标签:
4条回答
  • 2021-01-03 10:05

    It's tricky but here's the code -- took me a while to figure out -- upvotes appreciated. Can you call a C# DLL from a C DLL?

    This example is unmanaged c++ to C#.

    0 讨论(0)
  • 2021-01-03 10:05

    A callback is simply a particular use of a delegate. The normal model looks something like this:

    public class MyCaller
    {
       public OtherClass otherClassInstance;
    
       public void CallbackMethod() {...}
    
       public void UsesTheCallback()
       {
          //The callback method itself is being passed
          otherClassInstance.MethodWithCallback(CallbackMethod);
       }
    }
    
    public class OtherClass
    {
       public delegate void CallbackDelegate()
       public void MethodWithCallback(CallbackDelegate callback)
       {
          //do some work, then...
          callback(); //invoke the delegate, "calling back" to MyCaller.CallbackMethod()
       }
    }
    

    The beauty of this model is that any method with the defined "signature" (parameters and return type) can be used as the callback. It's a form of "loose coupling", where code isn't dependent on knowing what an object IS, only what it DOES, so that object can be swapped out for another object without the code knowing the difference.

    The above example is all in C#. When you're using extern functions from a C++ DLL, you'll probably be dealing with an IntPtr type that is a simple pointer to the method. You should be able to "marshal" this pointer as a delegate when defining the C# interface to that extern method, so the method will look like a normal C# method.

    0 讨论(0)
  • 2021-01-03 10:06

    You can simply pass a C# string back to C++ and a C++ string to C#. The requirement is that the string is unicode and the allocation method is SysAllocString and not malloc. Any ASCII string you need to convert to unicode.

    const wchar_t* theString = L"hello";
    BSTR bstr = SysAllocString(theString);
    DoSomething(bstr);
    SysFreeString(bstr);
    

    And this to register the C# dll

    Assembly asm = Assembly.LoadFile (@"c:\temp\ImageConverter.dll");
    RegistrationServices regAsm = new RegistrationServices();
    bool bResult = regAsm.RegisterAssembly(asm, AssemblyRegistrationFlags.SetCodeBase);
    

    And this to convert Unicode to ASCII and vice-versa.

    inline BSTR Cstring2VBstring(char *szString)
    {
        WCHAR* res = NULL;
        BSTR bs;
        DWORD n;
        char *sz = NULL;
        if (*szString && szString)
        {
            sz = strdup(szString);
            n = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, sz, -1, NULL, 0);
    
            if (n)
            {
                res = (WCHAR*) malloc(n * sizeof(char) );
                MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, sz, -1, res, n);
            }
    
        }
    
        bs = SysAllocString( (const OLECHAR*) res);
        free(sz);
        return bs;
    }
    
    
    
    // C String to BSTR conversion (2)
    BSTR Cstringn2VBstring(char *szString, int dwSize)
    {
        WCHAR* res = NULL;
        BSTR bs;
        DWORD n = (DWORD) dwSize;
        char *sz = NULL;
        if (*szString)
        {
            sz = (char*) malloc(dwSize);
            memcpy(sz, szString, dwSize);
            n = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, sz, n, NULL, 0);
            if(n)
            {
                res = (WCHAR*) malloc(n * sizeof(char) );
                MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, sz, -1, res, n);
            }
        }
        bs = SysAllocStringLen( (const OLECHAR*) res, n);
    
        free(sz);
        return bs;
    }
    

    And the .NET code:

    Namespace TestLibrary2
        ' Interface declaration. '
        Public Interface ICalculator
            Function Add(ByVal Number1 As Integer, ByVal Number2 As Integer) As Integer
            Function Subtract(ByVal Number1 As Long, ByVal Number2 As Long) As Long
            Function ReturnValue() As String
            Function Concat(ByVal Number1 As String, ByVal Number2 As String) As String
    
            Sub Concat2(ByVal Number1 As String, ByVal Number2 As String)
    
            Function isTrue(ByVal bInputvalue As Boolean) As Boolean
            Function isTrue2(ByRef bInputvalue As Boolean) As Boolean
        End Interface
    
    
    
        ' Interface implementation. '
        Public Class ManagedClass
            Implements ICalculator
    
    
            Public Function Add(ByVal Number1 As Integer, ByVal Number2 As Integer) As Integer Implements ICalculator.Add
                Return Number1 + Number2
            End Function
    
    
            Public Function Subtract(ByVal Number1 As Long, ByVal Number2 As Long) As Long Implements ICalculator.Subtract
                Try
                    System.IO.File.WriteAllText("c:\temp\subtract.txt", "Subtracted: ")
                Catch ex As Exception
                    MsgBox(ex.Message)
                End Try
    
                Return Number1 - Number2
            End Function
    
    
            Public Function Concat(ByVal Number1 As String, ByVal Number2 As String) As String Implements ICalculator.Concat
                Try
                    System.IO.File.WriteAllText("c:\temp\Concat.txt", "Nummer1: " + Number1 + vbCrLf + "Nummer2:" + Number2)
                Catch ex As Exception
                    MsgBox(ex.Message)
                End Try
    
                Dim strReturnValue As String = Number1 + Number2
                Return strReturnValue
            End Function
    
    
            Public Sub Concat2(ByVal Number1 As String, ByVal Number2 As String) Implements ICalculator.Concat2
                Console.WriteLine("moo")
            End Sub
    
    
            Public Function ReturnValue() As String Implements ICalculator.ReturnValue
                Dim x As String = "moooooo"
                Return x
            End Function
    
    
            Public Function isTrue(ByVal bInputvalue As Boolean) As Boolean Implements ICalculator.isTrue
                If bInputvalue = True Then
                    Return True
                End If
                Return False
            End Function
    
    
            Public Function isTrue2(ByRef bInputvalue As Boolean) As Boolean Implements ICalculator.isTrue2
                If bInputvalue = True Then
                    Return True
                End If
                Return False
            End Function
    
        End Class
    
    
    End Namespace
    

    Edit:
    See here for closer information:

    http://support.microsoft.com/kb/828736
    http://msdn.microsoft.com/en-us/library/ms734686.aspx

    0 讨论(0)
  • 2021-01-03 10:18

    There may be cleaner ways, but here are some of the steps I used to make it work.

    Define the delegate and the function to pass it to your DLL. The parameters are what will be sent back to the C# delegate:

      public delegate uint CallbackFn( uint param1, uint param2 );
    
      [DllImport("yourdll.dll",  CallingConvention=CallingConvention.Winapi, EntryPoint="RegisterTheCallback" )]
      private static extern uint RegisterTheCallback( CallbackFn pfn );
    

    Create a variable to store the delegate. Make sure this doesn't go out of scope. In my testing, I found that the GC would reclaim it (it wasn't "aware" that my DLL was still using it):

      CallbackFn mCmdCallback = null;
    

    Then initialize it somewhere:

      mCmdCallback = new CallbackFn( YourCallback );
    

    And then pass it to your DLL:

    RegisterTheCallback( mCmdCallback );
    

    And define the actual method that will receive the call:

      private uint YourCallback( uint param1, uint param2 )
      {
        // report progress etc.
      }
    

    The code in the DLL might look like this:

    DWORD _declspec( dllexport ) WINAPI RegisterTheCallback
    (
       DWORD (WINAPI *lpfnCallback)( DWORD param1, DWORD param2 )
    )
    {
    // Store lpfnCallback somewhere so that it can be called later
    ...
    }
    

    And then the code in your DLL can call it with the appropriate data when it needs:

    ret = (lpfnCallback)( 234, 456 );
    
    0 讨论(0)
提交回复
热议问题