英文:
Unhandled Exception: System.EntryPointNotFoundException: Unable to find an entry point named 'SampleMethod' in DLL 'goDLL.dll'
问题
我正在尝试从C#调用一个Go语言的DLL,并将结果和性能与从C#调用C语言的DLL进行比较,所以我按照以下步骤进行了操作:
首先,我构建了C语言的DLL并进行了调用:
步骤1:编写C代码
// cmdll.c
// 编译命令: -LD
int __declspec(dllexport) SampleMethod(int i)
{
return i*10;
}
步骤2:编译C代码:
- 打开“Visual Studio x64 Native Tools Command Prompt”
- 运行命令:
cl -LD cmdll.c
步骤3:编写C#代码
// cm.cs
using System;
using System.Runtime.InteropServices;
public class MainClass
{
[DllImport("Cmdll.dll")]
public static extern int SampleMethod(int x); // 函数签名,必须有返回类型
static void Main()
{
Console.WriteLine("SampleMethod() 返回 {0}。", SampleMethod(5));
}
}
步骤4:编译C#文件并构建可执行文件:
- 打开“Visual Studio x64 Native Tools Command Prompt”
- 运行命令:
csc -platform:x64 cm.cs
以上步骤顺利运行。
我想用相同的方法来调用Go语言的DLL,按照以下步骤进行操作:
步骤1:编写Go代码:
//lib.go
package main
import "C"
//export SampleMethod
func SampleMethod(i int) int {
return i * 10
}
func main() {
// 需要一个main函数以便将CGO编译包作为C共享库
}
步骤2:通过以下方式编译上述代码,生成DLL文件:
go build -ldflags="-s -w" -o lib.dll -buildmode=c-shared lib.go
我使用了-ldflags="-s -w"
来减小生成文件的大小,并且不确定应该使用哪个-buildmode
,所以随机选择了c-shared
而不是c-archive
。
更新:我还尝试了go build -ldflags="-s -w" -o lib.dll -buildmode=c-archive lib.go
,结果相同。
步骤3:编写一个C代码,将从Go生成的.dll
和.h
文件组合起来生成一个等效的C DLL:
//goDll.c
#include <stdio.h>
#include "lib.h"
// 强制gcc链接Go运行时(可能有更好的解决方案)
GoInt SampleMethod(GoInt i);
void main() {
}
步骤4:将goDll.c文件编译为:
gcc -shared -pthread -o goDll.dll goDll.c lib.dll -lWinMM -lntdll -lWS2_32
步骤5:构建C#代码以调用生成的DLL,代码与上述相同,只需更改DLL文件名:
// cm.cs
using System;
using System.Runtime.InteropServices;
public class MainClass
{
[DllImport("goDll.dll")]
public static extern int SampleMethod(int x); // 函数签名,必须有返回类型
static void Main()
{
Console.WriteLine("SampleMethod() 返回 {0}。", SampleMethod(5));
}
}
步骤6:编译C#文件并构建可执行文件:
- 打开“Visual Studio x64 Native Tools Command Prompt”
- 运行命令:
csc -platform:x64 cm.cs
然后尝试运行生成的./cm.exe
文件,但出现以下错误:
Unhandled Exception: System.EntryPointNotFoundException: Unable to find an entry point named 'SampleMethod' in DLL 'goDll.dll'.
at MainClass.SampleMethod(Int32 i)
at MainClass.Main()
英文:
I'm trying to call a golang dll from c#, and comparing the results and performance against calling c dll from c#, so I did the below:
I started with building the c dll and calling it
step 1: Writting the c code
// cmdll.c
// Compile with: -LD
int __declspec(dllexport) SampleMethod(int i)
{
return i*10;
}
step 2: Compiling the c code:
- Open the
Visual Studio x64 Native Tools Command Prompt
- Run the command:
cl -LD cmdll.c
step 3: Writting the c# code
// cm.cs
using System;
using System.Runtime.InteropServices;
public class MainClass
{
[DllImport("Cmdll.dll")]
public static extern int SampleMethod(int x); // function signature, must have a return type
static void Main()
{
Console.WriteLine("SampleMethod() returns {0}.", SamplMethod(5));
}
}
step 4: Compile the c# file and build the exe as:
- Open the
Visual Studio x64 Native Tools Command Prompt
- Run the command:
csc -platform:x64 cm.cs
The things above run smoothly
I wanted to do the same using golang, and followed the below:
step 1: Write the go code:
//lib.go
package main
import "C"
//export SamplMethod
func SamplMethod(i int) int {
return i * 10
}
func main() {
// Need a main function to make CGO compile package as C shared library
}
step 2: Build the dll file, by compiling the above code as:
go build -ldflags="-s -w" -o lib.dll -buildmode=c-shared lib.go
I used the -ldflags="-s -w"
to reduce the resulting file size, and not sure what -buildmode
shall i use, so randomly selected c-shared
instead of c-archive
update: I tried also with go build -ldflags="-s -w" -o lib.dll -buildmode=c-archive lib.go
and got the same result
step 3: Write a c code that combine the .dll
and .h
file generated from the go
to generate an equivalent c dll
//goDll.c
#include <stdio.h>
#include "lib.h"
// force gcc to link in go runtime (may be a better solution than this)
GoInt SamplMethod(GoInt i);
void main() {
}
step 4: Compile the goDll.c file as:
gcc -shared -pthread -o goDll.dll goDll.c lib.dll -lWinMM -lntdll -lWS2_32
step 5: Build the c# code to call the generated dll, same code as above, but changing the dll file name:
// cm.cs
using System;
using System.Runtime.InteropServices;
public class MainClass
{
[DllImport("goDll.dll")]
public static extern int SampleMethod(int x); // function signature, must have a return type
static void Main()
{
Console.WriteLine("SampleMethod() returns {0}.", SamplMethod(5));
}
}
step 4: Compile the c# file and build the exe as:
- Open the
Visual Studio x64 Native Tools Command Prompt
- Run the command:
csc -platform:x64 cm.cs
Then tried to run the generated ./cm.exe
file, but got the below error:
Unhandled Exception: System.EntryPointNotFoundException: Unable to find an entry point named 'SampleMethod' in DLL 'goDll.dll'.
at MainClass.SampleMethod(Int32 i)
at MainClass.Main()
答案1
得分: 0
感谢@Pak Uula的评论,以下是我翻译好的内容:
步骤1:编写Go代码:
// main.go
package main
import "C"
import "fmt"
//export HelloWorld
func HelloWorld() {
fmt.Printf("hello world from GO\n")
}
func main() {}
// 编译代码:
// go build -ldflags="-s -w" -buildmode=c-shared -o libgo.dll main.go
步骤2:编写C#代码:
// main.cs
using System;
using System.Runtime.InteropServices;
public class MainClass
{
[DllImport("libgo.dll")]
public static extern void HelloWorld(); // 函数签名,必须有返回类型
static void Main()
{
HelloWorld();
}
}
// 编译代码:
// 打开:
// Visual Studio x64 Native Tools Command Prompt
// csc -platform:x64 cm.cs
步骤3:编译两个文件,首先编译go
文件。
步骤4:运行可执行文件。
更新
- Go文件:
// main.go
package main
import (
"fmt"
)
// The import "C" should come directly after the // #include ..., i.e. no empty lines allowed,
// if there are empty lines, the compliler will read the // #include as normal comment, not as C import
/*
#include <stdlib.h>
*/
import "C"
//export GetHello
func GetHello(Input *C.char) *C.char {
cStr := C.CString(fmt.Sprintf("From DLL: Hello, %s!\n", C.GoString(Input)))
// C.free(unsafe.Pointer(cStr))
return cStr
}
func main() {}
// 编译代码:
// go build -ldflags="-s -w" -buildmode=c-shared -o libgo.dll main.go
- C#文件:
// main.cs
using System;
using System.Text;
using System.Runtime.InteropServices;
public class MainClass
{
[DllImport("libgo.dll", CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetHello(byte[] data);
static string CallDll(string name) {
IntPtr output= IntPtr.Zero;
var a = GetHello(Encoding.UTF8.GetBytes(name));
return "GetHello Returns: " + Marshal.PtrToStringAnsi(a);
}
static void Main()
{
Console.WriteLine(CallDll("Ahmad"));
}
}
// 编译代码:
// 打开:
// Visual Studio x64 Native Tools Command Prompt
// csc -platform:x64 main.cs
这是一个使用字符串的示例:
英文:
Thanks for @Pak Uula comment, it worked with me as below:
step 1: Write the go code:
// main.go
package main
import "C"
import "fmt"
//export HelloWorld
func HelloWorld() {
fmt.Printf("hello world from GO\n")
}
func main() {}
// compile the code as:
// go build -ldflags="-s -w" -buildmode=c-shared -o libgo.dll main.go
step 2: Write the c# code as:
// main.cs
using System;
using System.Runtime.InteropServices;
public class MainClass
{
[DllImport("libgo.dll")]
public static extern void HelloWorld(); // function signature, must have a return type
static void Main()
{
HelloWorld();
}
}
// compile as:
// open:
// Visual Studio x64 Native Tools Command Prompt
// csc -platform:x64 cm.cs
step 3: Compile both files, start by compiling the go
file
step 4: Run the executable file:
UPDATE
- Go file:
// main.go
package main
import (
"fmt"
)
// The import "C" should come directly after the // #include ..., i.e. no empty lines allowed,
// if there are empty lines, the compliler will read the // #include as normal comment, not as C import
/*
#include <stdlib.h>
*/
import "C"
//export GetHello
func GetHello(Input *C.char) *C.char {
cStr := C.CString(fmt.Sprintf("From DLL: Hello, %s!\n", C.GoString(Input)))
// C.free(unsafe.Pointer(cStr))
return cStr
}
func main() {}
// compile the code as:
// go build -ldflags="-s -w" -buildmode=c-shared -o libgo.dll main.go
- C# file:
// main.cs
using System;
using System.Text;
using System.Runtime.InteropServices;
public class MainClass
{
[DllImport("libgo.dll", CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetHello(byte[] data);
static string CallDll(string name) {
IntPtr output= IntPtr.Zero;
var a = GetHello(Encoding.UTF8.GetBytes(name));
return "GetHello Returns: " + Marshal.PtrToStringAnsi(a);
}
static void Main()
{
Console.WriteLine(CallDll("Ahmad"));
}
}
// compile as:
// open:
// Visual Studio x64 Native Tools Command Prompt
// csc -platform:x64 main.cs
Here is an example that is working with string:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论