C ++ Callback для отправки текста обратно на C # - PullRequest
3 голосов
/ 17 февраля 2011

Я новичок в C ++. Мне сказали, что использование «обратного вызова» с C ++ является лучшим решением для этого. Вот моя ситуация.

У меня есть DLL, написанная на C ++
в этой DLL есть метод для запуска службы, которая запускается с помощью кода C # (это прекрасно работает)
когда служба в DLL работает, я хочу, чтобы DLL возвращала текст в код C #, это просто код прогресса, такой как «запуск первого этапа» и «первый этап завершен»


Я осмотрелся и мне сказали, что лучший способ достичь этого - использовать обратные вызовы, я не знаю, как это реализовать. У кого-нибудь есть предложения или статьи, которые я могу проверить? Пожалуйста, включите C ++, поскольку у меня нет опыта работы с C ++.


Приветствия

Ответы [ 4 ]

4 голосов
/ 17 февраля 2011

Могут быть более чистые способы, но вот некоторые шаги, которые я использовал, чтобы заставить это работать.

Определите делегата и функцию для передачи его в вашу DLL.Параметры - это то, что будет отправлено обратно делегату C #:

  public delegate uint CallbackFn( uint param1, uint param2 );

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

Создайте переменную для хранения делегата.Убедитесь, что это не выходит за рамки.В ходе тестирования я обнаружил, что GC восстановит его (он не «знал», что моя DLL все еще использует его):

  CallbackFn mCmdCallback = null;

Затем инициализируйте его где-нибудь:

  mCmdCallback = new CallbackFn( YourCallback );

А затем передать его в свою DLL:

RegisterTheCallback( mCmdCallback );

И определить фактический метод, который будет принимать вызов:

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

Код в DLL может выглядеть следующим образом:

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

И тогда код в вашей DLL может вызывать его с соответствующими данными, когда это необходимо:

ret = (lpfnCallback)( 234, 456 );
3 голосов
/ 17 февраля 2011

Вы можете просто передать строку C # обратно в C ++ и строку C ++ в C #.Требование заключается в том, что строка является Unicode, а метод выделения - SysAllocString, а не malloc.Любую ASCII-строку, которую нужно преобразовать в Unicode.

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

И это для регистрации C # dll

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

И это для преобразования Unicode в ASCII и наоборот.

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

И код .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

Редактировать: Смотрите здесь для более подробной информации:

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

1 голос
/ 17 февраля 2011

Это сложно, но вот код - мне потребовалось некоторое время, чтобы разобраться - отзывы приветствуются Можете ли вы вызвать C # DLL из C DLL?

Этот пример - неуправляемый c ++ to C #.

0 голосов
/ 17 февраля 2011

Обратный вызов - это просто конкретное использование делегата. Нормальная модель выглядит примерно так:

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

Прелесть этой модели в том, что любой метод с определенной «сигнатурой» (параметры и тип возвращаемого значения) может использоваться в качестве обратного вызова. Это форма «слабой связи», когда код не зависит от знания того, чем является объект, а только от того, что он делает, так что этот объект может быть заменен другим объектом без кода, который знает разницу.

Приведенный выше пример все на C #. Когда вы используете функции extern из C ++ DLL, вы, вероятно, имеете дело с типом IntPtr, который является простым указателем на метод. Вы должны иметь возможность «маршалировать» этот указатель как делегат при определении интерфейса C # для этого метода extern, поэтому этот метод будет выглядеть как обычный метод C #.

...