我正在用C#编写托管C#中的回调方法。调试时,我可以看到调用了非托管函数(我什至已经看到指针地址被发送出去了),但是回调方法本身似乎从未触发。我想念什么吗?
此外,我没有DLL的源,只有头文件。它可以连接到USB音频接口。
在标题中,我尝试使用此方法:
// Sends a signal to outputs 1-2 and measures how long it takes to return to inputs 1-2
typedef void LatencyTestResultCallback(MSeriesDevRef,ULONG samplesLatency,ULONG sampleRate,ULONG reportedLatency);
MOTU_FactORY_LIB_EXPORT MSeriesLatencyTest(MSeriesDevRef,LatencyTestResultCallback*);
对。关于这个标题,我了解了几件事。在我希望在托管代码中使用的任何方法中,我都会从枚举中接收结果代码(请参见下面的完整标题)。知道这一点后,我会寻找结果“ 0”以确保可以接收和执行我的呼叫。这很重要,因为在尝试使它起作用的过程中,我收到的结果为0!此外,我必须使用MSeriesInit()连接到设备/ DLL
好的,我已经编写了以下测试代码:
[DllImport("MSeriesLib.dll")] // Getting our DLL and assigning both the method import and callback.
unsafe static extern int MSeriesLatencyTest(ulong MSeriesDevRef,LatencyTestDelegate callback);
public unsafe delegate void LatencyTestDelegate(ulong MSeriesDevRef,ulong samplesLatency,ulong sampleRate,ulong reportedLatency);
// Garbo collection,you need to stop!
public static unsafe LatencyTestDelegate latencyTestInstance;
public static unsafe void LatencyTestResultCallback(ulong MSeriesDevRef,ulong reportedLatency) {
//This never runs,which is where the core of my problem lies.
Console.WriteLine("Callback executed.");
}
我现在已经花了几天时间,并且在此途中学到了一些东西。对我来说,最明显的例子之一就是垃圾回收有可能在甚至没有调用回调委托之前就将其清除。我启用了本机调试以及“本机兼容性”,然后逐步查看了反汇编程序,向我展示了,是的,该方法正在被垃圾回收。
因此,我实例化了委托(作为副作用阅读),它还使您可以避免使用IntPtr并通过Marshal.GetFunctionPointerForDelegate()进行分配。 (我也尝试过此操作,分配了指针并传递了该指针,并实现了没有回调触发的相同行为。)
MSeriesLatencyTest(ulong MSeriesDevRef,IntPtr callback);
IntPtr latencyTestPtr = Marshal.GetFunctionPointerForDelegate(LatencyTestResultCallback);
当我运行程序时,输出变为:
欢迎测试控制台应用程序。键入“帮助”作为命令。
测试
已连接到MSeriesLib。
延迟测试返回:0
我再次知道,没有异常,因为它返回了kSuccess(0)的结果。
我还在代表实例的分配处添加一个断点,该断点显示:
latencyTestInstance | {Method = {Void LatencyTestResultCallback(UInt64,UInt64,UInt64)}}
我真的不知道为什么此时没有触发回调。类型应该是正确的(ULONG,非指针),我可以查看被分配并传递给外部方法的委托,但是一旦转到DLL,响应将返回0,一切正常,然后...保持沉默。
请帮助!
=======================================
UPDATE 2019-12-03T21:32:56Z:我能够在我最喜欢的平台之一上收集some help:Discord!对我建议的更改是要注意32位和64位的无符号类型,
不收集委托仍然很重要,但是还有一个细节:委托本身的调用约定。
[您应该在委托声明上使用]
UnmanagedFunctionPointer(CallingConvention.Cdecl)
并使调用约定在本地进行清理。
__ stdcall是被呼叫者清理,而__cdecl是呼叫者清理
(由milleniumbug 贡献)
因此,我已经更新了我的代码,如下所示:
[DllImport("MSeriesLib.dll",CallingConvention = CallingConvention.Cdecl)]
unsafe static extern int MSeriesLatencyTest(uint MSeriesDevRef,LatencyTestDelegate callback);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate void LatencyTestDelegate(uint MSeriesDevRef,uint samplesLatency,uint sampleRate,uint reportedLatency);
public static unsafe LatencyTestDelegate latencyTestInstance = LatencyTestResultCallback;
在类主体中:
public static unsafe void LatencyTestResultCallback(uint MSeriesDevRef,uint reportedLatency) {
Console.WriteLine($"Callback executed. Device: {MSeriesDevRef},Sample Latency: {samplesLatency},Sample Rate: {sampleRate},Reported Latency: {reportedLatency}");
}
不幸的是,在进行调试时,似乎仍未调用LatencyTestResultCallback
。下面是完整的课程更改。
完整的Program.cs类(old version)
using System;
using System.Runtime.InteropServices;
namespace AudioTest {
class Program {
[DllImport("MSeriesLib.dll")]
public static extern int MSeriesInit();
[DllImport("MSeriesLib.dll")]
public static extern int MSeriesFree();
[DllImport("MSeriesLib.dll",CallingConvention = CallingConvention.Cdecl)]
unsafe static extern int MSeriesLatencyTest(uint MSeriesDevRef,LatencyTestDelegate callback);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate void LatencyTestDelegate(uint MSeriesDevRef,uint reportedLatency);
public static unsafe LatencyTestDelegate latencyTestInstance = LatencyTestResultCallback;
static bool isConnected;
static void Main(string[] args) {
bool exit = false;
Console.WriteLine("Welcome to test console app. Type 'help' for comands.");
while (!exit) {
string input = Console.ReadLine();
if (input.Equals("help")) Console.WriteLine("test,exit");
else if (input.Equals("exit")) quit();
else if (input.Equals("test")) testCallback();
}
}
public static void quit() {
if (isConnected) MSeriesFree();
Environment.Exit(0);
}
public static unsafe void testCallback() {
if (MSeriesInit() == 0) {
isConnected = true;
Console.WriteLine("Connected to MSeriesLib.");
int res = MSeriesLatencyTest(1,latencyTestInstance);
Console.WriteLine("Latency test returned: " + res);
}
}
public static unsafe void LatencyTestResultCallback(uint MSeriesDevRef,uint reportedLatency) {
Console.WriteLine($"Callback executed. Device: {MSeriesDevRef},Reported Latency: {reportedLatency}");
}
}
}
C ++标头(MSeriesLib.h)
#ifndef MSeriesLib_H_CWM
#define MSeriesLib_H_CWM
#ifdef BUILDING_DLL
#define MOTU_FactORY_LIB_EXPORT __declspec(dllexport) MSeriesLibResult __stdcall
#else
#define MOTU_FactORY_LIB_EXPORT __declspec(dllimport) MSeriesLibResult __stdcall
#endif
#ifdef __cplusplus
extern "C" {
#endif
// I'm only supporting one MSeries at a time. If there are multiple MSeriess attached,// these functions will only apply to the first one it finds.
// Result codes
enum MSeriesLibResult
{
kSuccess = 0,kUnsuccessful,kNoDevice,kBadParameter
};
MOTU_FactORY_LIB_EXPORT MSeriesInit();
MOTU_FactORY_LIB_EXPORT MSeriesFree();
struct WithMSeriesLib
{
WithMSeriesLib() { MSeriesInit(); }
~WithMSeriesLib() { MSeriesFree(); }
};
typedef ULONG MSeriesDevRef;
// Call to get the nth available device ref (pass n=0 for first device)
MOTU_FactORY_LIB_EXPORT MSeriesGetDevice(ULONG n,MSeriesDevRef* outDev);
// Skipping to the method we actually want...
// ...
// ...
// Sends a signal to outputs 1-2 and measures how long it takes to return to inputs 1-2
typedef void LatencyTestResultCallback(MSeriesDevRef,ULONG reportedLatency);
MOTU_FactORY_LIB_EXPORT MSeriesLatencyTest(MSeriesDevRef,LatencyTestResultCallback*);
// Insert various other methods...
// ...
// ...
#ifdef __cplusplus
}
#endif /* __cplusplus */
// Reset the alignment
// #pragma pack(pop)
#endif //MSeriesLib_H_CWM
EDIT 2019-12-03T19:04:39Z-小写案例类型和拼写更正。
EDIT 2019-12-03T21:32:56Z-获得了关于不一致和更新代码,更新格式,添加内容的帮助。