當前位置: 妍妍網 > 碼農

.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>

    希望這些介紹能幫助到大家。

    你學廢了嗎?

    👓都看到這了,還在乎點個贊嗎?

    👓都點贊了,還在乎一個收藏嗎?

    👓都收藏了,還在乎一個評論嗎?