当前位置: 欣欣网 > 码农

.net 调用海康SDK的跨平台解决方案

2024-07-07码农
  • 📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!

  • 📢本文作者:由webmote 原创

  • 📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔

  • 序言

    上2篇海康SDK使用以及常见的坑受到了许多网友的喜爱,这也说明了在工控领域内,使用.net开发还是非常便捷省事的。针对海康的SDK进行进一步封装,第一版Net Framework版本代码发在github上,供大家测试和使用。

    这次主要讲解在.net core /6/7/8下进行跨平台调用时怎么封装海康的跨平台库,毕竟很多的研发类库已经都迁移到.net core 跨平台的系统上,以便适应时代的潮流,支持和兼容linux系统,国产操作系统或者华为鸿蒙系统等。

    声明下 ,海康威视没有给赞助费,希望厂家能够看到,给点打赏,哈哈~~~

    1. 跨平台类库支持

    目前.net Core 支持两种模式对类库进行加载,一种是传统方式,使用 DllImport 属性模式定义;另外一种是新的方式,采用 NativeLibrary 类库进行高级解析和加载,这种方式需要你确认你的.net 类库版本,需要在支持范围内(.net core 3.1 以及.net 5+)。

    2. 采用DllImport默认方式加载

    如果你使用这种模式,那么大部分情形需要限定dll的路径,因此我们先看下默认采用的规则是什么?

    [DllImport("HCNetSDK")]
    publicstaticexternboolNET_DVR_Init();

    上面的代码,就隐含了下列规则:
    在 Windows 上运行时,将按以下顺序搜索 DLL:

  • HCNetSDK

  • HCNetSDK.dll(如果库名称尚未以 .dll 或 .exe 结尾)

  • 在 Linux 或 macOS 上运行时,运行时将尝试在前添加 lib ,并追加扩展名。在这些 OS 上,按以下顺序尝试库名:

  • HCNetSDK.so / - HCNetSDK.dylib

  • libHCNetSDK.so / libHCNetSDK.dylib1

  • HCNetSDK

  • libHCNetSDK1

  • 注意,在这些系统上,名称是区分大小写的。

    当然,如果类库名称以 .so 结尾或包含 .so. ,则搜索顺序会有所不同。

  • HCNetSDK.so.6

  • libHCNetSDK.so.61

  • HCNetSDK.so.6.so

  • libHCNetSDK.so.6.so1

  • 3.采用NativeLibrary进行高级解析

    在新版本的.net框架中,其提供了一系列函数对库加载的支持:

    voidFree(IntPtr handle)
    IntPtrGetExport(IntPtr handle,String name)
    IntPtrLoad(String libraryPath)
    IntPtrLoad(String libraryName, Assembly, DllImportSearchPath?)
    voidSetDllImportResolver(Assembly, DllImportResolver)
    IntPtrTryGetExport(IntPtr handle,String name,outIntPtr address)
    IntPtrTryLoad(String libraryPath,outIntPtr handle)
    IntPtrTryLoad(String libraryName, Assembly, DllImportSearchPath?,outIntPtr handle)

    其中仍然利用 DllImport 界定类库名称,然后再利用 DllImportResolver 进行高级解析。

  • 参数 assembly :已为其注册解析程序的程序集。

  • 参数 resolver :要注册的解析程序回调。

  • 此回调函数是第一次尝试解析程序集时启动的本机库加载函数。此方法的调用方应仅为自己的程序集注册解析程序。每个程序集只能注册一个解析程序。尝试注册第二个解析程序失败,并显示 InvalidOperationException

    以下是封装海康SDK的跨平台核心代码:

    public classHcNetSdk
    {
    privateconststring HCNetSDK ="HCNetSDK";
    //静态构造,保调用时,一次加载。
    staticHcNetSdk()
    {
    NativeLibrary.SetDllImportResolver(typeof(HcNetSdk).Assembly, DllImportResolver);
    }
    privatestaticIntPtrDllImportResolver(string libraryName,Assembly assembly,DllImportSearchPath? searchPath)
    {
    // windows下加载dll
    if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
    {
    if(Environment.Is64BitProcess)
    {
    if(libraryName == HCNetSDK)
    {
    return NativeLibrary.Load("./HCNetSDK/x64/HCNetSDK.dll", assembly, searchPath);
    }
    elseif(libraryName == AnalyzeData)
    {
    return NativeLibrary.Load("./HCNetSDK/x64/HCNetSDKCom/AnalyzeData.dll", assembly, searchPath);
    }
    }
    else
    {
    if(libraryName == HCNetSDK)
    {
    return NativeLibrary.Load("./HCNetSDK/x86/HCNetSDK.dll", assembly, searchPath);
    }
    elseif(libraryName == AnalyzeData)
    {
    return NativeLibrary.Load("./HCNetSDK/x86/HCNetSDKCom/AnalyzeData.dll", assembly, searchPath);
    }
    }
    }
    else//linux操作系统下,此处没有细分,如果有更多系统需求,可以自行增加
    {
    if(libraryName == HCNetSDK)
    {
    return NativeLibrary.Load("./HCNetSDK/linux/libhcnetsdk.so", assembly, searchPath);
    }
    elseif(libraryName == AnalyzeData)
    {
    return NativeLibrary.Load("./HCNetSDK/linux/HCNetSDKCom/libanalyzedata.so", assembly, searchPath);
    }
    }

    // Otherwise, fallback to default import resolver.
    return IntPtr.Zero;
    }
    publicHcNetSdk()
    {
    //
    // TODO: 在此处添加构造函数逻辑
    //
    }
    [DllImport(HCNetSDK)]
    publicstaticexternintNET_DVR_SendWithRecvRemoteConfig(int lHandle,IntPtr lpInBuff,uint dwInBuffSize,IntPtr lpOutBuff,uint dwOutBuffSize,refuint dwOutDataLen);
    [DllImport(HCNetSDK)]
    publicstaticexternintNET_DVR_SendWithRecvRemoteConfig(int lHandle,refHcNetSdk.NET_DVR_FACE_RECORD lpInBuff,int dwInBuffSize,refHcNetSdk.NET_DVR_FACE_STATUS lpOutBuff,int dwOutBuffSize,IntPtr dwOutDataLen);
    [DllImport(HCNetSDK)]
    publicstaticexternintNET_DVR_SendWithRecvRemoteConfig(int lHandle,refHcNetSdk.NET_DVR_FINGERPRINT_RECORD lpInBuff,int dwInBuffSize,refHcNetSdk.NET_DVR_FINGERPRINT_STATUS lpOutBuff,int dwOutBuffSize,IntPtr dwOutDataLen);
    ...
    }


    4. 海康dll的路径组织

    按照上面的解析定义,我们自定义了路径进行海康dll的加载,因此需要按照代码内的内容对dll及放好,放置方式如下所示。

    类库的项目文件中,需要增加对这些类库的处理定义:

    <ItemGroup>
    <NoneUpdate="HCNetSDK\**">
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
    </ItemGroup>

    编译的dll 和海康的dll目录和文件不要搞丢了。

    5. 总结

    这里以海康sdk的dll加载为例,介绍了两种方案进行跨平台的封装方式,如果你加载的dll文件比较少,可以使用第一种方案简单的进行标记和部署即可;如果涉及的dll比较复杂,需要放在不同的目录下进行调用支撑,那么采用第二种方式自定义控制更方便灵活。

    据我查看微软文档,还有一种方式是DllMap方式,再Mono下用的非常多,采用XML方式对dll的映射进行定义,即可实现类似2的方案,xml类似如下定义,这种方式我没有试过,有兴趣的朋友可以试一试。

    <configuration>
    <dllmapdll="OldLib"target="NewLib"/>
    </configuration>

    希望这些介绍能帮助到大家。

    你学废了吗?

    👓都看到这了,还在乎点个赞吗?

    👓都点赞了,还在乎一个收藏吗?

    👓都收藏了,还在乎一个评论吗?