我是C ++的新手。我被告知使用C ++的“回调”是最好的解决方案。这是我的情况。
我有一个用C ++编写的DLL 这个DLL有一个方法来启动通过C#代码运行的服务(这很好) 当DLL中的服务运行时,我希望DLL将文本传回C#代码,这只是进度代码,例如“第一阶段开始”和“第一阶段完成”
我环顾四周,并被告知,实现这一目标的最佳方法是使用回调,我真的不知道如何实现这一点。有没有人有任何建议或文章我可以看看?请包含C ++,因为我没有C ++经验。
干杯
答案 0 :(得分:4)
可能有更简洁的方法,但这里有一些我用来使其工作的步骤。
定义委托和将其传递给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 );
答案 1 :(得分:3)
您只需将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
答案 2 :(得分:1)
这很棘手,但这是代码 - 花了我一段时间才弄明白 - 赞成赞成。 Can you call a C# DLL from a C DLL?
此示例是非托管c ++到C#。
答案 3 :(得分:0)
回调只是委托的特定用途。普通模型看起来像这样:
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中的extern函数时,您可能正在处理IntPtr类型,该类型是指向该方法的简单指针。在定义该extern方法的C#接口时,您应该能够将此指针“编组”为委托,因此该方法看起来像普通的C#方法。