一、整体思路
c#客户端不能直接调用c dll,需要做2个黏合层把它们连接起来,这里以trtc sdk的dll为例
二、实现步骤
1. 先写个从c#客户端调用c dll完成trtc初始化的功能
(1) 创建一个c#客户端(选择windowsform)项目
(2) 在程序入口main()函数中,加入InitWrapper()准备用来初始化trtc sdk
Program.cs
代码语言:txt复制//初始化TRTC SDK
//调用链路:WindowsFormsApp1.exe(c#) -> RTCManager.dll(c#) -> TRTCWrapper.dll(c ) -> liteav.dll(c ,要调用的目标dll)
int nRet = RTCWrapper.InitWrapper();
(3) 创建RTCManager.dll(选择.netframework库)c# 项目
(4) 创建RTCWrapper c#类,并用DllImport映射c dll中的接口InitWrapper()
TRTCWrapper.cs
代码语言:txt复制public static class RTCWrapper
{
/// <summary>
/// 初始化TRTC
/// </summary>
/// <returns>1成功,0失败</returns>
[DllImport("TRTCWrapper.dll", EntryPoint = "InitWrapper")]
public static extern int InitWrapper();
}
(5) 创建TRTCWrapper.dll(选择动态链接库) c 项目
(6) 实现InitWrapper接口
TRTCWrapper.h
代码语言:txt复制//初始化TRTC
extern "C" __declspec(dllexport) int _stdcall InitWrapper();
TRTCWrapper.cpp
代码语言:txt复制_declspec(dllexport) int _stdcall InitWrapper()
{
trtcInstance = TRTCCloudCore::GetInstance();
if (trtcInstance)
{
trtcInstance->Init();
return 1;
}
else
return 0;
}
(7) 在GetInstance()中加载trtc sdk的dll库,注意这里要用动态加载dll的方式,不然编译不过。Init()有其他作用,可以用来设置回调
TRTCCloudCore.cpp
代码语言:txt复制TRTCCloudCore* TRTCCloudCore::GetInstance()
{
if (m_instance == NULL) {
engine_mex.lock();
if (m_instance == NULL)
{
m_instance = new TRTCCloudCore();
}
engine_mex.unlock();
}
return m_instance;
}
TRTCCloudCore::TRTCCloudCore()
{
trtc_module_ = nullptr;
if (trtc_module_ != nullptr) return;
HMODULE hmodule = NULL;
GetModuleHandleEx(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
L"TRTCModule", &hmodule);
char module_path[MAX_PATH] = { 0 };
::GetModuleFileNameA(hmodule, module_path, _countof(module_path));
std::string module_dir = GetPathNoExt(module_path);
if (module_dir.length() == 0) {
LINFO(L"TRTC GetModule Path Error");
return;
}
std::string module_full_path = module_dir "liteav.dll";
trtc_module_ =
::LoadLibraryExA(module_full_path.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
}
注意:
1.设置项目之间的依赖关系
2.添加trtc对应的include,lib目录
3.设置dll的生成目录,跟c# exe在一起,这样方便调试,不用写脚本copy
2. 接着实现sdk的回调通知给主程序
(1) 在程序入口main()函数中,加入AddTRTCMsgCallBack()准备用来注册trtc的回调
Program.cs
代码语言:txt复制//注册SDK的callback
RTCDelegate rtcDelegate = new RTCDelegate();
RTCWrapper.AddTRTCMsgCallBack(rtcDelegate.rtcCallBack);
(2) 在RTCManager.dll中实现RTCDelegate c#类,用来接收trtc sdk的回调
RTCDelegate.cs
代码语言:txt复制enum TRTC_MSG
{
TRTC_MSG_onEnterRoom = 1,
TRTC_MSG_onExitRoom,
TRTC_MSG_onRemoteUserEnterRoom,
TRTC_MSG_onRemoteUserLeaveRoom,
TRTC_MSG_onError,
TRTC_MSG_onWarning
//TODO 补齐TRTC的回调
};
namespace RTCManager
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void RTCWrapperCallback(int trtcModule, int errCode, [In, MarshalAs(UnmanagedType.LPStr)] string errMsg);
public class RTCDelegate
{
public RTCWrapperCallback rtcCallBack = null;
public RTCDelegate()
{
Setcallback();
}
private void RTCWrapperCallbackFun(int trtcModule, int errCode, [In, MarshalAs(UnmanagedType.LPStr)] string errMsg)
{
TRTC_MSG trtcMsg = (TRTC_MSG)trtcModule;
switch (trtcMsg)
{
case TRTC_MSG.TRTC_MSG_onEnterRoom:
break;
default:
break;
}
}
public void Setcallback()
{
rtcCallBack = RTCWrapperCallbackFun;
}
}
}
(3) 用DllImport映射c dll中的接口AddTRTCMsgCallBack()
TRTCWrapper.cs
代码语言:txt复制/// <summary>
/// 注册TRTC通用回调
/// </summary>
/// <param name="callback">通用回调</param>
/// <returns></returns>
[DllImport("TRTCWrapper.dll", EntryPoint = "AddTRTCMsgCallBack")]
public static extern void AddTRTCMsgCallBack(RTCWrapperCallback callback);
(4) 在TRTCWrapper.dll中实现AddTRTCMsgCallBack()接口
TRTCWrapper.h
代码语言:txt复制//注册TRTC通用回调
extern "C" __declspec(dllexport) void _stdcall AddTRTCMsgCallBack(TRTCMsgCallback messageCallback);
TRTCWrapper.cpp
代码语言:txt复制_declspec(dllexport) void _stdcall AddTRTCMsgCallBack(TRTCMsgCallback messageCallback)
{
if(trtcInstance)
trtcInstance->MessageCallBack = messageCallback;
//else 输出错误日志
}
(5) 把messageCallback设置到trtc sdk中
TRTCCloudCore.h
代码语言:txt复制typedef void(*TRTCMsgCallback)(int trtcModule, int errCode, const wchar_t* errMsg);
TRTCMsgCallback MessageCallBack = nullptr;
(6) 在trtc sdk的回调接口onEnterRoom中调用messageCallback
TRTCCloudCore.cpp
代码语言:txt复制void TRTCCloudCore::onEnterRoom(int result)
{
if (MessageCallBack)
MessageCallBack(TRTC_MSG_onEnterRoom, result, L"");
}
(7) Debug一下看看能不能回调到c#中
3. 让trtc sdk的视频在c#程序中显示
(1) 在c#程序窗口中,添加个button用来测试
Form1.cs
代码语言:txt复制private void button1_Click(object sender, EventArgs e)
{
IntPtr hwnd = this.Handle;
RTCWrapper.EnterRoom(0,
"",
"",
0);
RTCWrapper.StartLocalPreview(hwnd);
RTCWrapper.StartLocalAudio();
}
(2) 用DllImport映射c dll中的接口StartLocalPreview()
TRTCWrapper.cs
代码语言:txt复制[DllImport("TRTCWrapper.dll", EntryPoint = "StartLocalPreview")]
public static extern void StartLocalPreview(IntPtr hwnd);
(3) 在TRTCWrapper.dll中实现StartLocalPreview()接口
TRTCWrapper.h
代码语言:txt复制//开启本地预览
extern "C" __declspec(dllexport) void _stdcall StartLocalPreview(HWND hwnd);
TRTCWrapper.cpp
代码语言:txt复制_declspec(dllexport) void _stdcall StartLocalPreview(HWND hwnd)
{
if (trtcInstance)
trtcInstance->startPreview(hwnd);
//else 输出错误日志
}
(4) 调用trtc sdk本地视频预览接口
TRTCCloudCore.cpp
代码语言:txt复制void TRTCCloudCore::startPreview(HWND hwnd)
{
if (m_pCloud == nullptr)
return;
if (m_bStartLocalPreview)
return;
if (!hwnd)
return;
m_pCloud->startLocalPreview(hwnd);
m_bStartLocalPreview = true;
}
(5) 用相同的方法实现enterRoom、startLocalAudio
注意:
c#调用c dll,因为是DllImport动态引用,所以debug时进不了c 代码;
可以写个简单的mfc exe,用静态引用的方式引用TRTCWrapper.lib和liteav.lib,就可以debug c 的代码了。
需要源码可以发邮件到tomas7571@qq.com