clq
浏览(626) +
2022-01-01 13:07:41 发表
编辑
关键字:
[2022-07-30 19:19:23 最后更新]
libgolang 项目 开一个新的正式开源项目。我相信这个项目会是本站最实用的开源项目。因为这是一个工具集,不象 xmppmini 那样太庞大,让大家望而生畏(其实也不难,希望大家参与)。 这个项目的目的很简单,就是把一些 golang 中很好用的功能导出给 c/c++ 和 delphi/lazarus 使用。 对于我来说本质上是给我的跨平台的开发框架提供更通用的功能,补充其系统下的功能缺失。比如 https ,在 windows 下要使用的话要加入 openssl 。我实在不想在各个平台下编译它。 至于为什么不直接用 golang 直接完整开发呢。因为 golang 目前(甚至在可预见的未来)还是不能在手机上用的。而我的跨平台框架手机是很重要的应用平台。 当然网友的应用场景和我肯定是不一样的,不过仍然可以从这个库中学到很多东西或者是直接开箱得到某些需要的模块/函数。 ---------------------------------------------------------------- 项目其实两三年前就存在了,当时是为了给一家公司做一个门外汉也能很快会用的 http 服务器。比如在 iis 中更换 https 证书这样的,其实很多人搞不定的(有些客户的环境受操作系统版本限制实际上装不上)。 1.golang 中导出函数的方法和坑 真的很多坑。 1.1 golang 的版本 太低了不支持导出为 dll ,太高了不支持 32 位。我在 windows 下一直使用 go1.10.8 。如果没有 32 位的要求估计应该可以使用最新的版本吧。 1.2 //https://www.qycn.com/xzx/article/10494.html //go build -buildmode=c-shared -o exportgo.dll exportgo.go //go build -buildmode=c-shared -o libgolang.dll http_dll.go //linux 下 //go build -buildmode=c-shared -o libgolang.so http_dll.go //---- //可以指定几个文件名,也可以不指定使用几个文件 //go build -buildmode=c-shared -o libgolang.so http_dll.go check_error.go //不过这其中一定要包含一个 main() 函数 //---- //要加这个才会有导出 C 语言的函数 //import "C" //---- //linux 下这样查看是否成功导出了函数 //nm -D libgolang.so //一定要加 export 在函数名前,才会导出这个函数 //export Sum func Sum(a int, b int) int { return a + b; } 1.3 golang 要求 dll 中也要有个 main 函数 1.4 //参考 https://segmentfault.com/q/1010000040761516/ //C.CString 的结果是要用 stdlib.h 中的 C.free 来释放的 // Go string to C string // The C string is allocated in the C heap using malloc. // It is the caller's responsibility to arrange for it to be // freed, such as by calling C.free (be sure to include stdlib.h // if C.free is needed). //func C.CString(string) *C.char return C.CString("golang aaa"); //这个要用 cfree 释放 另外 include stdlib.h 的方法是要有技巧的,和导出函数一样要使用注释语法,并且也一样要紧挨着下一行代码(ps.我觉得这种魔法语法会导致 golang 的难度成倍上升,其实 golang 的设计理念真的很好,可惜在实现时有些地方实在太过随意,影响了它的推广)。 //import "C" 要紧挨着 /*...*/ 注释块 //要加这个才会有导出 C 语言的函数 //#include /* #include */ import "C" 1.5 这些语法大多叫 CGO 。 1.6 在 QT 中调用非常简单。(示例为 linux ) # //---------------------------------------------------------------- # //qt 调用 so 的方法 # 奇怪,这样不行 #LIBS += ./libgolang.so # 这样是可以的 //加不加引号都可以,不过为何要全路径? #LIBS += '/home/ccc/Desktop/test1/qt_linux_tool/linux_email/libgolang.so' //LIBS += /home/ccc/Desktop/test1/qt_linux_tool/linux_email/libgolang.so //奇怪,用这个上级路径也可以 LIBS += ../libgolang.so # //---------------------------------------------------------------- 2.关联项目 2.1 64 位 cairo 库的得到方法。 http://newbt.net/ms/vdisk/show_bbs.php?id=6F077723C652558A48A79ED4CD951713&pid=160
clq
2022-07-30 19:19:23 发表
编辑
目前的代码为: package main; //import "C" 要紧挨着 /*...*/ 注释块 //要加这个才会有导出 C 语言的函数 //#include <stdlib.h> /* #include <stdlib.h> */ import "C" import ( // "fmt" // "fmt" //"io/ioutil" // "database/sql" //"fmt" // "reflect" // "strconv" //"strings" // "syscall" "unsafe" // "net/http" // "runtime" //"runtime/internal/atomic" //这个是内部包,不能这样引用 //从 Go\src\syscall\os_windows.go 来看,一个 stdcall 是开了一个线程来调用一个 dll 函数的 //不对,好象是 tstart_stdcall, newosproc 才开 // "runtime/debug" // "runtime" ) //据说是高手的代码 //https://github.com/liudch/goci/blob/master/oci.go //2019 更新,这份代码的字符串传递也是有问题的,例如 /* func OCILogon(env *OCIHandle, username, password, database string) (svcctx *OCIHandle, err error) { var s *OCIHandle = new(OCIHandle) // Service context s.t = OCI_HTYPE_SVCCTX var e *OCIHandle = new(OCIHandle) // Error handle e.t = OCI_HTYPE_ERROR r0, _, e1 := procOCILogon.Call( // sword OCILogon ( env.h, // OCIEnv *envhp, uintptr(unsafe.Pointer(&e.h)), // OCIError *errhp, uintptr(unsafe.Pointer(&s.h)), // OCISvcCtx **svchp, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(username))), // CONST OraText *username, uintptr(len(username)), // ub4 uname_len, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(password))), // CONST OraText *password, uintptr(len(password)), // ub4 passwd_len, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(database))), // CONST OraText *dbname, uintptr(len(database))) // ub4 dbname_len ); if r0 != 0 { err = error(e1) return } return s, nil } 这其中的用户名就是有可能在大压力环境下失效 */ //其实只要导出一个函数就可以了 var ( // http_func * syscall.LazyProc = nil; //syscall.NewLazyDLL("C:\\Program Files\\Oracle\\instantclient_11_2\\oci.dll") // http_func_sql * syscall.LazyProc = nil; // http_func_sql_err1 * syscall.LazyProc = nil; // free_buf * syscall.LazyProc = nil; ) func LoadHttpDlls(){ //mod_http := syscall.NewLazyDLL("http.dll"); // mod_http := syscall.NewLazyDLL("http_dll.dll") // http_func = mod_http.NewProc("http_func"); // Clear all attribute-value information in a namespace of an application context // http_func_sql = mod_http.NewProc("http_func_sql"); // http_func_sql_err1 = mod_http.NewProc("http_func_sql_err1"); // free_buf = mod_http.NewProc("free_buf"); }// //重要!!! 调用 dll 传递字符串参数因 gc 导致数据失效的示例(实际上会因此内存访问错误崩溃) func dll_errtest1(sql string) (string, error) { defer PrintError("dll_errtest1"); /* //------------------ //调试 delphi 的 dll 时意外发现 syscall.StringToUTF16Ptr 已经是弃用的了 go 1.7.3 的源码注释中说了用 UTF16PtrFromString 来代替 sqlp, err := syscall.UTF16PtrFromString(sql); if err !=nil { panic("dll 调用参数严重错误!!!");} a, err2 := syscall.UTF16FromString(sql); //来自 UTF16PtrFromString 的源码 if err2 !=nil { panic("dll 调用参数严重错误!!! err2");} sqlp = &a[0]; //------------------ p1 := uintptr(unsafe.Pointer(sqlp)); runtime.GC(); //不调用这两个应该也是很容易重现的//还是加上容易重现,并且是一大堆一起失效,而没的的话则是久不久失效一个指针 debug.FreeOSMemory(); //放到一个单独的函数时,这里调用 gc 后面会立即崩溃! 不 gc 的时候还能运行一下 r0, _, e1 := http_func_sql.Call( //utf82utf16(r.URL.Path),//, p1, //uintptr(unsafe.Pointer(sqlp)), // utf82utf16(sql),//, !!! 这个会出错,内存混乱 //uintptr(unsafe.Pointer(http_func)), // OCIEnv **envhpp, //uintptr(0), // ub4 mode, //0, // CONST dvoid *ctxp, ); //if r0 != 0 { // return "", error(e1) //}//if if e1 != nil { // return "", error(e1) }//if ////s := prttostr(r0, 4*1024*1024); //这个确实是有问题的 fmt.Println(r0); //fmt.Println(p1); //强制再引用一会,不让垃圾回收//这个变量没用 //fmt.Println(a); //强制再引用一会,不让垃圾回收//这个变量有用 ////fmt.Println(sqlp); //强制再引用一会,不让垃圾回收//这个变量有用,这个 *uint16 变量居然也可以让其所在的 []uint16 数组保持引用!!! 也就是说即使是引用了指针,其数组也是会保持的,那么会解引用的原因应该只是那个 unsafe.Pointer 后的变量 //也就是说 unsafe.Pointer 后的变量就随时可能被释放掉! //fmt.Println(a); //奇怪这里不调用的话, a 的内存在多线程中会发生变化,估计是调用时间拖动得长的话被垃圾回收了,所以一定要在 dll 调用后再使用一下其中的变量, //特别是字符串变量一定要注意,不能是只使用它的指针,要整个字节缓冲区一起//过会我写个可以一定重现的 dll 函数调用 //参考 zsyscall_windows.go 的用法,也是一直引用一个内存区的 */ //return s, nil; return "", nil; }// //测试不保留引用的情况下,看看只传递非 unsafe.Pointer() 的指针,是否能保持内存的存在//感觉 go1.7.3 就是这样传递字符串参数的 //如果成立说明对非 unsafe.Pointer 指针的引用可以保持到传递出去的函数调用返回时 func dll_errtest3(sql string) (string, error) { defer PrintError("dll_errtest3"); /* sqlp, err := syscall.UTF16PtrFromString(sql); if err !=nil { panic("dll 调用参数严重错误!!!");} //------------------ //p1 := uintptr(unsafe.Pointer(sqlp)); runtime.GC(); //不调用这两个应该也是很容易重现的//还是加上容易重现,并且是一大堆一起失效,而没的的话则是久不久失效一个指针 debug.FreeOSMemory(); //放到一个单独的函数时,这里调用 gc 后面会立即崩溃! 不 gc 的时候还能运行一下 // dll_errtest3_sub(sqlp); //fmt.Println(sqlp); //重要! 可以看到,将非 unsafe.Pointer 指针传递出去后就可以不用再人为的保持内存块引用了 */ return "", nil; }// //-------------------------------------------------- //所以综上所述,为了更好的保持 golang 中的指针指向最好是按照 go 本身调用 dll 的做法再封装一层同名函数(加下划线在前面) //参考 zsyscall_windows.go //func DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status error) { // var _p0 *uint16 // _p0, status = UTF16PtrFromString(name) // if status != nil { // return // } // return _DnsQuery(_p0, qtype, options, extra, qrs, pr) //} //这里不传递字符串参数,因此不用再来一个包装的 dll 函数了 func dll_free_buf(pbuf uintptr) { /* //返回值中,第一个就是函数的返回值,第三个是调用中的错误,但第二个一直没看到官方说明 r0, _, e1 := free_buf.Call( //utf82utf16(r.URL.Path),//, pbuf, // utf82utf16(sql),//, !!! 这个会出错,内存混乱 //uintptr(unsafe.Pointer(http_func)), // OCIEnv **envhpp, //uintptr(0), // ub4 mode, //0, // CONST dvoid *ctxp, ); //if r0 != 0 { // return "", error(e1) //}//if if e1 != nil { // return "", error(e1) }//if fmt.Println("dll_free_buf r0:", r0); //这个 dll 函数是没有返回值的,那么这里会得到什么呢 //参考 golang 自己的 DnsRecordListFree 实现,就是根本不管返回值,虽然最后的 call 调用是有返回值的 //不过这里我们不能保证自己的 dll 一定是正确的调用方法,所以还是检查返回值的好 */ }// //---------------------------------------------------------------- //https://www.qycn.com/xzx/article/10494.html //go build -buildmode=c-shared -o exportgo.dll exportgo.go //go build -buildmode=c-shared -o libgolang.dll http_dll.go //linux 下 //go build -buildmode=c-shared -o libgolang.so http_dll.go //---- //可以指定几个文件名,也可以不指定使用几个文件 //go build -buildmode=c-shared -o libgolang.so http_dll.go check_error.go //不过这其中一定要包含一个 main() 函数 //---- //要加这个才会有导出 C 语言的函数 //import "C" //---- //linux 下这样查看是否成功导出了函数 //nm -D libgolang.so //一定要加 export 在函数名前,才会导出这个函数 //export Sum func Sum(a int, b int) int { return a + b; } //export run_golang_json func run_golang_json(src_json *C.char) (*C.char) { defer PrintError("run_golang_json()"); print("Hello ", C.GoString(src_json)); //这里不能用fmt包,会报错,调了很久... //参考 https://segmentfault.com/q/1010000040761516/ //C.CString 的结果是要用 stdlib.h 中的 C.free 来释放的 // Go string to C string // The C string is allocated in the C heap using malloc. // It is the caller's responsibility to arrange for it to be // freed, such as by calling C.free (be sure to include stdlib.h // if C.free is needed). //func C.CString(string) *C.char return C.CString("golang aaa"); //这个要用 cfree 释放 }//import "C" 要紧挨着 /*...*/ 注释块 //释放 golang 自己产生的 c 风格字符串 //export free_golang_cstr func free_golang_cstr(golang_c_str *C.char) { defer PrintError("free_golang_cstr()"); print("free_golang_cstr() ok. \r\n"); //这里不能用fmt包,会报错,调了很久... //c.free(); //C.free(golang_c_str); //这个要用 cfree 释放 C.free(unsafe.Pointer(golang_c_str)) }// //---- //golang 要求 dll 中也要有个 main 函数 func main() { // Need a main function to make CGO compile package as C shared library }//
NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.