五月微风 發表於 2019-10-24 11:23:00

C# 结合 Golang 开发

<h3 id="1-实现方式与语法形式">1. 实现方式与语法形式</h3>
<p>基本方式:将 Go 程序编译成 DLL 供 C# 调用。</p>
<h4 id="11-go代码">1.1 Go代码</h4>
<p>注意:代码中 export 的注释是定义的入口描述不能省略</p>
<pre><code>package main

import "C"
import "fmt"

func main() {
        fmt.Println(Test())
}

var _count = 0

//Test :
//export Test
func Test() int {
        _count++
        return _count
}
</code></pre>
<p>在 LiteIDE 中将编译配置的 <code>BUILDARGS</code> 自定义值为 <code>--buildmode=c-shared -o Test.dll</code>,从而形成以下编译语句。</p>
<pre><code>go build --buildmode=c-shared -o Test.dll
</code></pre>
<h4 id="12-c-代码">1.2 C# 代码</h4>
<pre><code>
extern static int Test();
</code></pre>
<h3 id="2-windows-下编译依赖的环境">2. Windows 下编译依赖的环境</h3>
<p>生成 DLL 依赖于 gcc,没有 gcc 环境时,会报以下错误:</p>
<blockquote>
<p>"gcc": executable file not found in %PATH%</p>
</blockquote>
<p>GCC下载:Windows 64位版本&nbsp; || Windows 32位版本,也可以从从云盘下载。<br>
下载之后,解压后确保 gcc 命令在搜索路径(Path)中。<br>
更多信息可参考:https://www.cnblogs.com/ghj1976/p/3540257.html</p>
<h3 id="3-操作系统-64-位与-32-的编译">3. 操作系统 64 位与 32 的编译</h3>
<p>在 LiteIDE 中,可以通过配置 <code>win32.env</code> 和 <code>win64.env</code> 来指定不同的 gcc 环境路径达到生成指定系统的 DLL 文件。</p>
<h3 id="4-c-中操作系统-64-位与-32-的适配">4. c# 中操作系统 64 位与 32 的适配</h3>
<p>在 c# 中判断操作系统是否 64 位,可以使用以下语句。</p>
<pre><code>bool is64 = Environment.Is64BitOperatingSystem;
</code></pre>
<p>为了在不同的操作系统下,加载不同的 DLL,采取以下步骤来进行组织。<br>
(1)将 32 位的版本命名为 Test32.dll,将 64 位的版本命名为 Test64.dll<br>
(2)定义 ITest 接口,将 DLL 将要调用的方法定义为接口方法<br>
(3)分别为ITest接口实现 Test32 与 Test64 类,在类中加载相应的 DLL<br>
(4)通过判断操作系统类型,实例化一个 ITest 的具体实现类实例来使用<br>
<strong>具体接口与类实现代码如下:</strong></p>
<pre><code>public interface ITest
{
    int Test();
}

public class Test32 : ITest
{
    class TestDLL
    {
      const string DLL_NAME = "Test32.dll";

      
      public extern static int Test();
    }

    public int Test()
    {
      return TestDLL.Test();
    }
}

public class Test64 : ITest
{
    class TestDLL
    {
      const string DLL_NAME = "Test64.dll";

      
      public extern static int Test();
    }

    public int Test()
    {
      return TestDLL.Test();
    }
}
</code></pre>
<p><strong>实例化与调用:</strong></p>
<pre><code>ITest test = Environment.Is64BitOperatingSystem ? (ITest)new Test64() : (ITest)new Test32();
int result = test.Test();
</code></pre>
<h3 id="5-其它一些问题">5. 其它一些问题</h3>
<h4 id="51-字符串转换">5.1 字符串转换</h4>
<ul>
<li>传入字符串,C#: byte[] -&gt; GO: *C.char</li>
<li>接收字符串,GO: string -&gt; C#: GoString struct<br>
GO 定义示例</li>
</ul>
<pre><code>//Hello :
//export Hello
func Hello(name *C.char) string {
        return fmt.Sprintf("hello %s", C.GoString(name))
}
</code></pre>
<p>C# GoString struct 定义</p>
<pre><code>public struct GoString
{      
    public IntPtr p;
    public int n;
    public GoString(IntPtr n1, int n2)
    {
      p = n1; n = n2;
    }
}
</code></pre>
<p>C# DllImport 声明</p>
<pre><code>
public extern static GoString Hello(byte[] name);
</code></pre>
<p>C# GoString struct 转 String</p>
<pre><code>public string GoStringToCSharpString(GoString goString)
{
    byte[] bytes = new byte;
    for (int i = 0; i &lt; goString.n; i++)
    {
      bytes = Marshal.ReadByte(goString.p, i);
    }
    string result = Encoding.UTF8.GetString(bytes);
    return result;
}
</code></pre>
<p>C# 调用示例</p>
<pre><code>GoString goResult = test.Hello(Encoding.UTF8.GetBytes("张三"));
Debug.WriteLine(GoStringToCSharpString(goResult));
</code></pre>
<h4 id="52-调试">5.2 调试</h4>
<p>CallingConvention<br>
在声明中加入 <code>CallingConvention = CallingConvention.Cdecl</code> 避免未知异常。</p>
<pre><code>
</code></pre>
<p>程序崩溃甚至异常提示都没有,可在加载 DLL 之前:</p>
<pre><code>Environment.SetEnvironmentVariable("GODEBUG", "cgocheck=0");
</code></pre>
<h3 id="6-相关参考">6. 相关参考</h3>
<ul>
<li>GO 生成 DLL,C# 调用的一个完整小示例:<br>
https://github.com/Baozisoftware/go-dll</li>
<li>字符串处理相关的一个问答<br>
https://stackoverflow.com/questions/48208098/using-generated-golang-dll-to-return-string-or-c-char</li>
</ul>


</div>
<div id="MySignature" role="contentinfo">
    关注“时间维度”公众号,来一场时空交织!<br><br>
来源:https://www.cnblogs.com/timeddd/p/11731160.html
頁: [1]
查看完整版本: C# 结合 Golang 开发