<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      .NET 7 AOT 的使用以及 .NET 與 Go 互相調(diào)用

      背景

      其實(shí),規(guī)劃這篇文章有一段時(shí)間了,但是比較懶,所以一直拖著沒(méi)寫(xiě)。

      最近時(shí)總更新太快了,太卷了,所以借著 .NET 7 正式版發(fā)布,熬夜寫(xiě)完這篇文章,希望能夠追上時(shí)總的一點(diǎn)距離。

      本文主要介紹如何在 .NET 和 Go 語(yǔ)言中如何生成系統(tǒng)(Windows)動(dòng)態(tài)鏈接庫(kù),又如何從代碼中引用這些庫(kù)中的函數(shù)。

      在 .NET 部分,介紹如何使用 AOT、減少二進(jìn)制文件大小、使用最新的 [LibraryImport] 導(dǎo)入庫(kù)函數(shù);

      在 Go 語(yǔ)言部分,介紹如何使用 GCC 編譯 Go 代碼、如何通過(guò) syscall 導(dǎo)入庫(kù)函數(shù)。

      在文章中會(huì)演示 .NET 和 Go 相互調(diào)用各自生成的動(dòng)態(tài)鏈接庫(kù),以及對(duì)比兩者之間的差異。

      本文文章內(nèi)容以及源代碼,可以 https://github.com/whuanle/csharp_aot_golang 中找到,如果本文可以給你帶來(lái)幫助,可以到 Github 點(diǎn)個(gè)星星嘛。

      C# 部分

      環(huán)境要求

      SDK:.NET 7 SDKDesktop development with C++ workload

      IDE:Visual Studio 2022

      Desktop development with C++ workload 是一個(gè)工具集,里面包含 C++ 開(kāi)發(fā)工具,需要在 Visual Studio Installer 中安裝,如下圖紅框中所示。

      image-20221109182246338

      創(chuàng)建一個(gè)控制臺(tái)項(xiàng)目

      首先創(chuàng)建一個(gè) .NET 7 控制臺(tái)項(xiàng)目,名稱(chēng)為 CsharpAot

      打開(kāi)項(xiàng)目之后,基本代碼如圖所示:

      image-20221109184702539

      我們使用下面的代碼做測(cè)試:

      public class Program
      {
          static void Main()
          {
              Console.WriteLine("C# Aot!");
              Console.ReadKey();
          }
      }
      

      體驗(yàn) AOT 編譯

      這一步,可以參考官方網(wǎng)站的更多說(shuō)明:

      https://learn.microsoft.com/zh-cn/dotnet/core/deploying/native-aot/

      為了能夠讓項(xiàng)目發(fā)布時(shí)使用 AOT 模式,需要在項(xiàng)目文件中加上 <PublishAot>true</PublishAot> 選項(xiàng)。

      image-20221109184850615

      然后使用 Visual Studio 發(fā)布項(xiàng)目。

      發(fā)布項(xiàng)目的配置文件設(shè)置,需要按照下圖進(jìn)行配置。

      image-20221109201612226

      AOT 跟 生成單個(gè)文件 兩個(gè)選項(xiàng)不能同時(shí)使用,因?yàn)?AOT 本身就是單個(gè)文件。

      配置完成后,點(diǎn)擊 發(fā)布,然后打開(kāi) Release 目錄,會(huì)看到如圖所示的文件。

      image-20221109194100927

      .exe 是獨(dú)立的可執(zhí)行文件,不需要再依賴(lài) .NET Runtime 環(huán)境,這個(gè)程序可以放到其他沒(méi)有安裝 .NET 環(huán)境的機(jī)器中運(yùn)行。

      然后刪除以下三個(gè)文件:

          CsharpAot.exp
          CsharpAot.lib
          CsharpAot.pdb
      

      光用 .exe 即可運(yùn)行,其他是調(diào)試符號(hào)等文件,不是必需的。

      剩下 CsharpAot.exe 文件后,啟動(dòng)這個(gè)程序:

      image-20221109194207563

      C# 調(diào)用庫(kù)函數(shù)

      這一部分的代碼示例,是從筆者的一個(gè)開(kāi)源項(xiàng)目中抽取出來(lái)的,這個(gè)項(xiàng)目封裝了一些獲取系統(tǒng)資源的接口,以及快速接入 Prometheus 監(jiān)控。

      不過(guò)很久沒(méi)有更新了,最近沒(méi)啥動(dòng)力更新,讀者可以點(diǎn)擊這里了解一下這個(gè)項(xiàng)目:

      https://github.com/whuanle/CZGL.SystemInfo/tree/net6.0/src/CZGL.SystemInfo/Memory

      因?yàn)楹罄m(xù)代碼需要,所以現(xiàn)在請(qǐng)開(kāi)啟 “允許不安全代碼”。

      本小節(jié)的示例是通過(guò)使用 kernel32.dll 去調(diào)用 Windows 的內(nèi)核 API(Win32 API),調(diào)用 GlobalMemoryStatusEx 函數(shù) 檢索有關(guān)系統(tǒng)當(dāng)前使用物理內(nèi)存和虛擬內(nèi)存的信息

      使用到的 Win32 函數(shù)可參考:https://learn.microsoft.com/zh-cn/windows/win32/api/sysinfoapi/nf-sysinfoapi-globalmemorystatusex

      關(guān)于 .NET 調(diào)用動(dòng)態(tài)鏈接庫(kù)的方式,在 .NET 7 之前,通過(guò)這樣調(diào)用:

          [DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
          [return: MarshalAs(UnmanagedType.Bool)]
          internal static extern Boolean GlobalMemoryStatusEx(ref MemoryStatusExE lpBuffer);
      

      在 .NET 7 中,出現(xiàn)了新的操作方式 [LibraryImport]

      文檔是這樣介紹的:

      Indicates that a source generator should create a function for marshalling arguments instead of relying on the runtime to generate an equivalent marshalling function at run time.
      
      指示源生成器應(yīng)創(chuàng)建用于編組參數(shù)的函數(shù),而不是依賴(lài)運(yùn)行庫(kù)在運(yùn)行時(shí)生成等效的編組函數(shù)。
      

      簡(jiǎn)單來(lái)說(shuō),就是我們要使用 AOT 寫(xiě)代碼,然后代碼中引用到別的動(dòng)態(tài)鏈接庫(kù)時(shí),需要使用 [LibraryImport] 引入這些函數(shù)。

      筆者沒(méi)有在 AOT 下測(cè)試過(guò) [DllImport],讀者感興趣可以試試。

      新建兩個(gè)結(jié)構(gòu)體 MEMORYSTATUS.csMemoryStatusExE.cs

      MEMORYSTATUS.cs

      public struct MEMORYSTATUS
      {
          internal UInt32 dwLength;
          internal UInt32 dwMemoryLoad;
          internal UInt32 dwTotalPhys;
          internal UInt32 dwAvailPhys;
          internal UInt32 dwTotalPageFile;
          internal UInt32 dwAvailPageFile;
          internal UInt32 dwTotalVirtual;
          internal UInt32 dwAvailVirtual;
      }
      

      MemoryStatusExE.cs

      public struct MemoryStatusExE
      {
          /// <summary>
          /// 結(jié)構(gòu)的大小,以字節(jié)為單位,必須在調(diào)用 GlobalMemoryStatusEx 之前設(shè)置此成員,可以用 Init 方法提前處理
          /// </summary>
          /// <remarks>應(yīng)當(dāng)使用本對(duì)象提供的 Init ,而不是使用構(gòu)造函數(shù)!</remarks>
          internal UInt32 dwLength;
      
          /// <summary>
          /// 一個(gè)介于 0 和 100 之間的數(shù)字,用于指定正在使用的物理內(nèi)存的大致百分比(0 表示沒(méi)有內(nèi)存使用,100 表示內(nèi)存已滿(mǎn))。
          /// </summary>
          internal UInt32 dwMemoryLoad;
      
          /// <summary>
          /// 實(shí)際物理內(nèi)存量,以字節(jié)為單位
          /// </summary>
          internal UInt64 ullTotalPhys;
      
          /// <summary>
          /// 當(dāng)前可用的物理內(nèi)存量,以字節(jié)為單位。這是可以立即重用而無(wú)需先將其內(nèi)容寫(xiě)入磁盤(pán)的物理內(nèi)存量。它是備用列表、空閑列表和零列表的大小之和
          /// </summary>
          internal UInt64 ullAvailPhys;
      
          /// <summary>
          /// 系統(tǒng)或當(dāng)前進(jìn)程的當(dāng)前已提交內(nèi)存限制,以字節(jié)為單位,以較小者為準(zhǔn)。要獲得系統(tǒng)范圍的承諾內(nèi)存限制,請(qǐng)調(diào)用GetPerformanceInfo
          /// </summary>
          internal UInt64 ullTotalPageFile;
      
          /// <summary>
          /// 當(dāng)前進(jìn)程可以提交的最大內(nèi)存量,以字節(jié)為單位。該值等于或小于系統(tǒng)范圍的可用提交值。要計(jì)算整個(gè)系統(tǒng)的可承諾值,調(diào)用GetPerformanceInfo核減價(jià)值CommitTotal從價(jià)值CommitLimit
          /// </summary>
      
          internal UInt64 ullAvailPageFile;
      
          /// <summary>
          /// 調(diào)用進(jìn)程的虛擬地址空間的用戶(hù)模式部分的大小,以字節(jié)為單位。該值取決于進(jìn)程類(lèi)型、處理器類(lèi)型和操作系統(tǒng)的配置。例如,對(duì)于 x86 處理器上的大多數(shù) 32 位進(jìn)程,此值約為 2 GB,對(duì)于在啟用4 GB 調(diào)整的系統(tǒng)上運(yùn)行的具有大地址感知能力的 32 位進(jìn)程約為 3 GB 。
          /// </summary>
      
          internal UInt64 ullTotalVirtual;
      
          /// <summary>
          /// 當(dāng)前在調(diào)用進(jìn)程的虛擬地址空間的用戶(hù)模式部分中未保留和未提交的內(nèi)存量,以字節(jié)為單位
          /// </summary>
          internal UInt64 ullAvailVirtual;
      
      
          /// <summary>
          /// 預(yù)訂的。該值始終為 0
          /// </summary>
          internal UInt64 ullAvailExtendedVirtual;
      
          internal void Refresh()
          {
              dwLength = checked((UInt32)Marshal.SizeOf(typeof(MemoryStatusExE)));
          }
      }
      

      定義引用庫(kù)函數(shù)的入口:

      public static partial class Native
      {
      
          /// <summary>
          /// 檢索有關(guān)系統(tǒng)當(dāng)前使用物理和虛擬內(nèi)存的信息
          /// </summary>
          /// <param name="lpBuffer"></param>
          /// <returns></returns>
          [LibraryImport("Kernel32.dll", SetLastError = true)]
          [return: MarshalAs(UnmanagedType.Bool)]
          internal static partial Boolean GlobalMemoryStatusEx(ref MemoryStatusExE lpBuffer);
      }
      

      然后調(diào)用 Kernel32.dll 中的函數(shù):

      public class Program
      {
          static void Main()
          {
              var result = GetValue();
              Console.WriteLine($"當(dāng)前實(shí)際可用內(nèi)存量:{result.ullAvailPhys / 1000 / 1000}MB");
              Console.ReadKey();
          }
          
          /// <exception cref="Win32Exception"></exception>
          public static MemoryStatusExE GetValue()
          {
              var memoryStatusEx = new MemoryStatusExE();
              // 重新初始化結(jié)構(gòu)的大小
              memoryStatusEx.Refresh();
              // 刷新值
              if (!Native.GlobalMemoryStatusEx(ref memoryStatusEx)) throw new Win32Exception("無(wú)法獲得內(nèi)存信息");
              return memoryStatusEx;
          }
      }
      

      使用 AOT 發(fā)布項(xiàng)目,執(zhí)行 CsharpAot.exe 文件。

      image-20221109202709566

      減少體積

      在前面兩個(gè)例子中可以看到 CsharpAot.exe 文件大約在 3MB 左右,但是這個(gè)文件還是太大了,那么我們?nèi)绾芜M(jìn)一步減少 AOT 文件的大小呢?

      讀者可以從這里了解如何裁剪程序:https://learn.microsoft.com/zh-cn/dotnet/core/deploying/trimming/trim-self-contained

      需要注意的是,裁剪是沒(méi)有那么簡(jiǎn)單的,里面配置繁多,有一些選項(xiàng)不能同時(shí)使用,每個(gè)選項(xiàng)又能帶來(lái)什么樣的效果,這些選項(xiàng)可能會(huì)讓開(kāi)發(fā)者用得很迷茫。

      經(jīng)過(guò)筆者的大量測(cè)試,筆者選用了以下一些配置,能夠達(dá)到很好的裁剪效果,供讀者測(cè)試。

      首先,引入一個(gè)庫(kù):

      	<ItemGroup>
      		<PackageReference Include="Microsoft.DotNet.ILCompiler" Version="7.0.0-*" />
      	</ItemGroup>
      

      接著,在項(xiàng)目文件中加入以下選項(xiàng):

      		<!--AOT 相關(guān)-->
      		<PublishAot>true</PublishAot>	
      		<TrimMode>full</TrimMode>
      		<RunAOTCompilation>True</RunAOTCompilation>
      		<PublishTrimmed>true</PublishTrimmed>
      		<TrimmerRemoveSymbols>true</TrimmerRemoveSymbols>
      		<PublishReadyToRunEmitSymbols>false</PublishReadyToRunEmitSymbols>
      		<DebuggerSupport>false</DebuggerSupport>
      		<EnableUnsafeUTF7Encoding>true</EnableUnsafeUTF7Encoding>
      		<InvariantGlobalization>true</InvariantGlobalization>
      		<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
      		<MetadataUpdaterSupport>true</MetadataUpdaterSupport>
      		<UseSystemResourceKeys>true</UseSystemResourceKeys>
      		<IlcDisableReflection >true</IlcDisableReflection>
      

      最后,發(fā)布項(xiàng)目。

      吃驚!生成的可執(zhí)行文件只有 1MB 了,而且還可以正常執(zhí)行。

      image-20221109203013246

      筆者注:雖然現(xiàn)在看起來(lái) AOT 的文件很小了,但是如果使用到 HttpClientSystem.Text.Json 等庫(kù),哪怕只用到了一兩個(gè)函數(shù),最終包含這些庫(kù)以及這些庫(kù)使用到的依賴(lài),生成的 AOT 文件會(huì)大得驚人。

      所以,如果項(xiàng)目中使用到其他 nuget 包的時(shí)候,別想著生成的 AOT 能小多少!

      C# 導(dǎo)出函數(shù)

      這一步可以從時(shí)總的博客中學(xué)習(xí)更多:http://www.rzrgm.cn/InCerry/p/CSharp-Dll-Export.html

      PS:時(shí)總真的太強(qiáng)了。

      image-20221109235629370

      在 C 語(yǔ)言中,導(dǎo)出一個(gè)函數(shù)的格式可以這樣:

      // MyCFuncs.h
      #ifdef __cplusplus
      extern "C" {  // only need to export C interface if
                    // used by C++ source code
      #endif
      
      __declspec( dllimport ) void MyCFunc();
      __declspec( dllimport ) void AnotherCFunc();
      
      #ifdef __cplusplus
      }
      #endif
      

      當(dāng)代碼編譯之后,我們就可以通過(guò)引用生成的庫(kù)文件,調(diào)用 MyCFuncAnotherCFunc 兩個(gè)方法。

      如果不導(dǎo)出的話,別的程序是無(wú)法調(diào)用庫(kù)文件里面的函數(shù)。

      因?yàn)?.NET 7 的 AOT 做了很多改進(jìn),因此,.NET 程序也可以導(dǎo)出函數(shù)了。

      新建一個(gè)項(xiàng)目,名字就叫 CsharpExport 吧,我們接下來(lái)就在這里項(xiàng)目中編寫(xiě)我們的動(dòng)態(tài)鏈接庫(kù)。

      添加一個(gè) CsharpExport.cs 文件,內(nèi)容如下:

      using System.Runtime.InteropServices;
      
      namespace CsharpExport
      {
          public class Export
          {
              [UnmanagedCallersOnly(EntryPoint = "Add")]
              public static int Add(int a, int b)
              {
                  return a + b;
              }
          }
      }
      

      然后在 .csproj 文件中,加上 PublishAot 選項(xiàng)。

      image-20221109203907544

      然后通過(guò)以下命令發(fā)布項(xiàng)目,生成鏈接庫(kù):

       dotnet publish -p:NativeLib=Shared -r win-x64 -c Release
      

      image-20221109204002557

      看起來(lái)還是比較大,為了繼續(xù)裁剪體積,我們可以在 CsharpExport.csproj 中加入以下配置,以便生成更小的可執(zhí)行文件。

      		<!--AOT 相關(guān)-->
      		<PublishAot>true</PublishAot>
      		<TrimMode>full</TrimMode>
      		<RunAOTCompilation>True</RunAOTCompilation>
      		<PublishTrimmed>true</PublishTrimmed>
      		<TrimmerRemoveSymbols>true</TrimmerRemoveSymbols>
      		<PublishReadyToRunEmitSymbols>false</PublishReadyToRunEmitSymbols>
      		<DebuggerSupport>false</DebuggerSupport>
      		<EnableUnsafeUTF7Encoding>true</EnableUnsafeUTF7Encoding>
      		<InvariantGlobalization>true</InvariantGlobalization>
      		<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
      		<MetadataUpdaterSupport>true</MetadataUpdaterSupport>
      		<UseSystemResourceKeys>true</UseSystemResourceKeys>
      		<IlcDisableReflection >true</IlcDisableReflection>
      

      image-20221109204055118

      C# 調(diào)用 C# 生成的 AOT

      在本小節(jié)中,將使用 CsharpAot 項(xiàng)目調(diào)用 CsharpExport 生成的動(dòng)態(tài)鏈接庫(kù)。

      CsharpExport.dll 復(fù)制到 CsharpAot 項(xiàng)目中,并配置 始終復(fù)制

      image-20221109204210638

      CsharpAotNative 中加上:

          [LibraryImport("CsharpExport.dll", SetLastError = true)]
          [return: MarshalAs(UnmanagedType.I4)]
          internal static partial Int32 Add(Int32 a, Int32 b);
      

      image-20221109204443706

      然后在代碼中使用:

          static void Main()
          {
              var result = Native.Add(1, 2);
              Console.WriteLine($"1 + 2 = {result}");
              Console.ReadKey();
          }
      

      在 Visual Studio 里啟動(dòng) Debug 調(diào)試:

      image-20221109205726963

      可以看到,是正常運(yùn)行的。

      接著,將 CsharpAot 項(xiàng)目發(fā)布為 AOT 后,再次執(zhí)行:

      image-20221109204645302

      可以看到,.NET AOT 調(diào)用 .NET AOT 的代碼是沒(méi)有問(wèn)題的。

      Golang 部分

      Go 生成 Windows 動(dòng)態(tài)鏈接庫(kù),需要安裝 GCC,通過(guò) GCC 編譯代碼生成對(duì)應(yīng)平臺(tái)的文件。

      安裝 GCC

      需要安裝 GCC 10.3,如果 GCC 版本太新,會(huì)導(dǎo)致編譯 Go 代碼失敗。

      打開(kāi) tdm-gcc 官網(wǎng),通過(guò)此工具安裝 GCC,官網(wǎng)地址:

      https://jmeubank.github.io/tdm-gcc/download/

      image-20221109183853737

      下載后,根據(jù)提示安裝。

      image-20221109183510422

      然后添加環(huán)境變量:

      D:\TDM-GCC-64\bin
      

      image-20221109183553817

      運(yùn)行 gcc -v,檢查是否安裝成功,以及版本是否正確。

      image-20221109183708204

      Golang 導(dǎo)出函數(shù)

      本節(jié)的知識(shí)點(diǎn)是 cgo,讀者可以從這里了解更多:

      https://www.programmerall.com/article/11511112290/

      新建一個(gè) Go 項(xiàng)目:

      image-20221109190013070

      新建一個(gè) main.go 文件,文件內(nèi)容如下:

      package main
      
      import (
      	"fmt"
      )
      import "C"
      
      //export Start
      func Start(arg string) {
      	fmt.Println(arg)
      }
      
      // 沒(méi)用處
      func main() {
      }
      

      在 Golang 中,要導(dǎo)出此文件中的函數(shù),需要加上 import "C",并且 import "C" 需要使用獨(dú)立一行放置。

      //export {函數(shù)名稱(chēng)} 表示要導(dǎo)出的函數(shù),注意,//export 之間 沒(méi)有空格。

      main.go 編譯為動(dòng)態(tài)鏈接庫(kù):

      go build -ldflags "-s -w" -o main.dll -buildmode=c-shared main.go
      

      image-20221109230719499

      不得不說(shuō),Go 編譯出的文件,確實(shí)比 .NET AOT 小一些。

      前面,筆者演示了 .NET AOT 調(diào)用 .NET AOT ,那么, Go 調(diào)用 Go 是否可以呢?

      答案是:不可以。

      因?yàn)?Go 編譯出來(lái)的 動(dòng)態(tài)鏈接庫(kù)本身帶有 runtime,Go 調(diào)用 main.dll 時(shí) ,會(huì)出現(xiàn)異常。

      具有情況可以通過(guò) Go 官方倉(cāng)庫(kù)的 Issue 了解:https://github.com/golang/go/issues/22192

      這個(gè)時(shí)候,.NET 加 1 分。

      雖然 Go 不能調(diào)用 Go 的,但是 Go 可以調(diào)用 .NET 的。在文章后面會(huì)介紹。

      雖然說(shuō) Go 不能調(diào)用自己,這里還是繼續(xù)補(bǔ)全代碼,進(jìn)一步演示一下。

      Go 通過(guò)動(dòng)態(tài)鏈接庫(kù)調(diào)用函數(shù)的示例:

      func main() {
      	maindll := syscall.NewLazyDLL("main.dll")
      	start := maindll.NewProc("Start")
      
      	var v string = "測(cè)試代碼"
      	var ptr uintptr = uintptr(unsafe.Pointer(&v))
      	start.Call(ptr)
      }
      

      代碼執(zhí)行后會(huì)報(bào)錯(cuò):

      image-20221109204808474

      .NET C# 和 Golang 互調(diào)

      C# 調(diào)用 Golang

      main.dll 文件復(fù)制放到 CsharpAot 項(xiàng)目中,設(shè)置 始終復(fù)制

      image-20221109204850583

      然后在 Native 中添加以下代碼:

          [LibraryImport("main.dll", SetLastError = true)]
          internal static partial void Start(IntPtr arg);
      

      image-20221109205246781

      調(diào)用 main.dll 中的函數(shù):

          static void Main()
          {
              string arg = "讓 Go 跑起來(lái)";
              // 將申請(qǐng)非托管內(nèi)存string轉(zhuǎn)換為指針
              IntPtr concatPointer = Marshal.StringToHGlobalAnsi(arg);
              Native.Start(concatPointer);
              Console.ReadKey();
          }
      

      在 .NET 中 string 是引用類(lèi)型,而在 Go 語(yǔ)言中 string 是值類(lèi)型,這個(gè)代碼執(zhí)行后,會(huì)出現(xiàn)什么結(jié)果呢?

      image-20221109205306709

      執(zhí)行結(jié)果是輸出一個(gè)長(zhǎng)數(shù)字。

      筆者不太了解 Golang 內(nèi)部的原理,不確定這個(gè)數(shù)字是不是 .NET string 傳遞了指針地址,然后 Go 把指針地址當(dāng)字符串打印出來(lái)了。

      因?yàn)樵?C、Go、.NET 等語(yǔ)言中,關(guān)于 char、string 的內(nèi)部處理方式不一樣,因此這里的傳遞方式導(dǎo)致了跟我們的預(yù)期結(jié)果不一樣。

      接著,我們將 main.go 文件的 Start 函數(shù)改成:

      //export Start
      func Start(a,b int) int{
      	return a+b
      }
      

      然后執(zhí)行命令重新生成動(dòng)態(tài)鏈接庫(kù):

      go build -ldflags "-s -w" -o main.dll -buildmode=c-shared main.go
      

      main.dll 文件 復(fù)制到 CsharpAot 項(xiàng)目中,將 Start 函數(shù)引用改成:

          [LibraryImport("main.dll", SetLastError = true)]
          [return: MarshalAs(UnmanagedType.I4)]
          internal static partial Int32 Start(int a, int b);
      

      image-20221109205838696

      執(zhí)行代碼調(diào)用 Start 函數(shù):

          static void Main()
          {
              var result = Native.Start(1, 2);
              Console.WriteLine($"1 + 2 = {result}");
              Console.ReadKey();
          }
      

      image-20221109205746457

      Golang 調(diào)用 C#

      CsharpExport.dll 文件復(fù)制放到 Go 項(xiàng)目中。

      main 的代碼改成:

      func main() {
      	maindll := syscall.NewLazyDLL("CsharpExport.dll")
      	start := maindll.NewProc("Add")
      
      	var a uintptr = uintptr(1)
      	var b uintptr = uintptr(2)
      	result, _, _ := start.Call(a, b)
      
      	fmt.Println(result)
      }
      
      

      image-20221109212938467

      將參數(shù)改成 19,再次執(zhí)行:

      image-20221109212956228

      其他

      在本文中,筆者演示了 .NET AOT,雖然簡(jiǎn)單的示例看起來(lái)是正常的,體積也足夠小,但是如果加入了實(shí)際業(yè)務(wù)中需要的代碼,最終生成的 AOT 文件也是很大的。

      例如,項(xiàng)目中使用 HttpClient 這個(gè)庫(kù),會(huì)發(fā)現(xiàn)里面加入了大量的依賴(lài)文件,導(dǎo)致生成的 AOT 文件很大。

      在 .NET 的庫(kù)中,很多時(shí)候設(shè)計(jì)了大量的重載,同一個(gè)代碼有好幾個(gè)變種方式,以及函數(shù)的調(diào)用鏈太長(zhǎng),這樣會(huì)讓生成的 AOT 文件變得比較臃腫。

      目前來(lái)說(shuō), ASP.NET Core 還不支持 AOT,這也是一個(gè)問(wèn)題。

      在 C# 部分,演示了如何使用 C# 調(diào)用系統(tǒng)接口,這里讀者可以了解一下 pinvokehttp://pinvoke.net/

      這個(gè)庫(kù)封裝好了系統(tǒng)接口,開(kāi)發(fā)者不需要自己擼一遍,通過(guò)這個(gè)庫(kù)可以很輕松地調(diào)用系統(tǒng)接口,例如筆者最近在寫(xiě) MAUI 項(xiàng)目,通過(guò) Win32 API 控制桌面窗口,里面就使用到 pinvoke 簡(jiǎn)化了大量代碼。

      本文是筆者熬夜寫(xiě)的,比較趕,限于水平,文中可能會(huì)有錯(cuò)誤的地方,望大佬不吝指教。

      posted @ 2022-11-10 08:39  癡者工良  閱讀(16607)  評(píng)論(50)    收藏  舉報(bào)
      主站蜘蛛池模板: 亚洲男女羞羞无遮挡久久丫 | 亚洲国产成人精品女久久| 黄又色又污又爽又高潮| 久久精品蜜芽亚洲国产av| 亚洲国产午夜福利精品| 综合久久婷婷综合久久| 69天堂人成无码免费视频| 国语精品一区二区三区| 免费观看全黄做爰大片| 国内精品综合九九久久精品| 九九九国产| 久久一区二区中文字幕| 天堂av资源在线免费| 亚洲精品97久久中文字幕无码| 通许县| 九九热在线视频只有精品| 亚洲熟伦熟女新五十熟妇| 99在线小视频| 亚洲首页一区任你躁xxxxx| 激情久久av一区二区三区| 日本视频一两二两三区| 亚洲高潮喷水无码AV电影| 亚洲老妇女亚洲老熟女久| 天堂网国产| 怡红院一区二区三区在线| 国产精品午夜福利小视频| 91亚洲免费视频| 国产性色的免费视频网站| 久热这里只有精品视频3| 福利视频在线播放| 精品一区二区三区不卡| 少妇av一区二区三区无码| 国产成AV人片久青草影院| 国产成AV人片久青草影院| 国产老熟女视频一区二区| 三年片在线观看免费观看高清动漫| 国精偷拍一区二区三区| 国产极品精品自在线不卡| 亚洲AV无码久久久久网站蜜桃| 国产尤物精品自在拍视频首页| 黄色特级片一区二区三区|