【问题标题】:C++ Callback to send text back to C#C++ 回调将文本发送回 C#
【发布时间】:2011-02-17 15:27:35
【问题描述】:

我是 C++ 新手。有人告诉我,使用 C++ 的“回调”是最好的解决方案。这是我的情况。

我有一个用 C++ 编写的 DLL
这个 DLL 有一个方法来启动通过 C# 代码运行的服务(这工作正常)
当 DLL 中的服务运行时,我希望 DLL 将文本传回 C# 代码,这只是进度代码,例如“第一阶段开始”和“第一阶段完成”


我环顾四周,被告知实现这一目标的最佳方法是使用回调,我真的不知道如何实现这一点。有没有人有任何建议或文章可以查看?请包括 C++,因为我在 C++ 方面的经验为零。


干杯

【问题讨论】:

  • ehi @Funky 您是否成功实施了解决此问题的有效解决方案?你用回调了吗?也许这个线程中的一些建议的解决方案?我看到另一个有类似问题的有希望的线程:stackoverflow.com/questions/38521509/… 在此先感谢,并对多个问题表示抱歉:D

标签: c# c++ dll callback


【解决方案1】:

可能有更简洁的方法,但这里有一些我用来使它工作的步骤。

定义委托和函数以将其传递给您的 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 );

【讨论】:

  • 嗨,你能包括任何 C++ 吗?
  • @Funky:添加了更多内容以尝试澄清它的非托管端。
  • c++ 代码导致许多错误,是否需要添加其他内容才能使其正常工作?
  • @Funky:我无意中留下了一个特定于我的项目的类型,但刚刚将其更改为 DWORD(它是 UNSIGNED32)。你看到了什么错误?
  • @Mark Wilkins:是的!那行得通,...好吧,C++ 已经构建好了,我还没有完成 C# 部分。我可以通过使用 MethodName(data); 在我的 C++ 中调用这个函数吗?
【解决方案2】:

您可以简单地将 C# 字符串传回 C++,并将 C++ 字符串传回 C#。要求是字符串是unicode,分配方法是SysAllocString而不是malloc。您需要转换为 unicode 的任何 ASCII 字符串。

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

【讨论】:

  • 这对 OP 一点帮助都没有。
  • 我认为确实如此。他想做的就是从 C++/C# 调用一个 C#/C++ 函数。这甚至不需要回调。有一个MS支持示例怎么做,他可以用google找到它,上面只是他需要的字符串信息,因为该示例只处理整数。
  • 我添加了 C++ 代码并构建了它,我添加了 C# 并构建了它但是如何从 C# 访问字符串?
【解决方案3】:

这很棘手,但这是代码——我花了一段时间才弄清楚——感谢支持。 Can you call a C# DLL from a C DLL?

这个例子是非托管 c++ 到 C#。

【讨论】:

  • 我在我的 C++ 中尝试了这一部分并得到了一些错误: typedef void (__stdcall *Callback)(PCWSTR);静态回调界面; //将委托分配给函数指针 __declspec(dllexport) void __stdcall CSharp_GuiWriter(void * jarg1) { Callback arg1 = (Callback) 0 ; arg1 = (回调)jarg1; gui = arg1; } //调用 (*gui)(_T("make C# print this text"));
  • 对不起,这是我的错误:错误 C2065:'PCWSTR':未声明的标识符错误 C2165:'left-side modifier':无法修改指向数据的指针错误 C2513:'void *':没有声明变量在“=”之前错误 C3861:“_T”:找不到标识符错误 C4430:缺少类型说明符 - 假定为 int。注意:C++ 不支持 default-int 错误 C2371: 'gui' : redefinition;不同的基本类型
  • @Ben Voigt:谢谢你消除了一半的错误,我剩下的唯一错误是:错误 C3861:'_T':找不到标识符错误 C4430:缺少类型说明符 - 假定为 int。注意:C++ 不支持 default-int 错误 C2371: 'gui' : redefinition;不同的基本类型
  • 这行:(*gui)(_T("make C# print this text"));需要任何额外的东西,因为我得到三个错误:错误 C4430:缺少类型说明符 - 假定为 int。注意:C++ 不支持 default-int 错误 C2373: 'gui' : redefinition;不同类型修饰符错误 C2440:“正在初始化”:无法从“const char [24]”转换为“int *”
  • 它在顶部定义: typedef void (__stdcall *Callback)(PCWSTR);静态回调界面; ...您当然需要将该调用放在一个函数中。
【解决方案4】:

回调只是委托的一种特殊用途。普通模型如下所示:

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# 编写的。当您使用 C++ DLL 中的外部函数时,您可能会处理一个 IntPtr 类型,它是指向该方法的简单指针。在为该 extern 方法定义 C# 接口时,您应该能够将此指针“编组”为委托,因此该方法看起来像一个普通的 C# 方法。

【讨论】:

  • 嗨,感谢您的回复,但正如我所提到的,我没有 C++ 经验(嗯....4 天!)您是否有一个网站可以显示 C++ 代码?
  • 如标题所述,它是用于使用C++的。这不显示任何 C++。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-04
  • 1970-01-01
  • 2015-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多