C + + Callback para enviar texto a C #

Soy nuevo en C ++. Me han dicho que usar una “callback” con C ++ es la mejor solución para esto. Aquí está mi situación.

Tengo un DLL escrito en C ++
esta DLL tiene un método para iniciar el servicio que se ejecuta a través del código C # (esto funciona bien)
cuando se ejecuta el servicio en la DLL, quiero que la DLL devuelva el texto al código C #, esto es solo un código de progreso como “inicio de una etapa” y “etapa uno completada”

He mirado a mi alrededor y me han dicho que la mejor manera de lograrlo es usar devoluciones de llamada, realmente no tengo ni idea de cómo implementar esto. ¿Alguien tiene alguna sugerencia o artículo que pueda revisar? Por favor incluya C ++ ya que tengo cero experiencia en C ++.

Aclamaciones

Simplemente puede pasar una cadena de C # a C ++ y una cadena de C ++ a C #. El requisito es que la cadena sea unicode y el método de asignación sea SysAllocString y no malloc. Cualquier cadena ASCII que necesite convertir a Unicode.

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

Y esto para registrar el C # dll.

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

Y esto para convertir Unicode a ASCII y viceversa.

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

Y el código .NET:

 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 

Editar:
Vea aquí para más información:

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

Puede haber formas más limpias, pero estos son algunos de los pasos que seguí para hacer que funcione.

Defina el delegado y la función para pasarlo a su DLL. Los parámetros son los que se enviarán de vuelta al delegado de C #:

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

Crear una variable para almacenar el delegado. Asegúrese de que esto no quede fuera del scope. En mis pruebas, encontré que el GC lo recuperaría (no era “consciente” de que mi DLL todavía lo estaba usando):

  CallbackFn mCmdCallback = null; 

Luego inicialízalo en algún lugar:

  mCmdCallback = new CallbackFn( YourCallback ); 

Y luego pásalo a tu DLL:

 RegisterTheCallback( mCmdCallback ); 

Y define el método real que recibirá la llamada:

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

El código en la DLL podría verse así:

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

Y luego el código en su DLL puede llamarlo con los datos apropiados cuando sea necesario:

 ret = (lpfnCallback)( 234, 456 ); 

Es complicado, pero aquí está el código, me tomó un tiempo descifrarlo, se agradecieron los votos. ¿Se puede llamar a una DLL de C # desde una DLL de C?

Este ejemplo es no administrado de C ++ a C #.

Una callback es simplemente un uso particular de un delegado. El modelo normal se ve algo así:

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

La belleza de este modelo es que cualquier método con la “firma” definida (parámetros y tipo de retorno) se puede usar como callback. Es una forma de “acoplamiento suelto”, donde el código no depende de saber qué es un objeto, solo lo que HACE, por lo que ese objeto se puede intercambiar por otro objeto sin que el código sepa la diferencia.

El ejemplo anterior es todo en C #. Cuando está utilizando funciones externas de una DLL de C ++, probablemente estará tratando con un tipo IntPtr que es un simple puntero al método. Debería ser capaz de “calcular” este puntero como un delegado al definir la interfaz de C # para ese método externo, de modo que el método se verá como un método de C # normal.