Golang中Cgo编译的C-shared二进制字符串从C++返回时出现乱码问题

Golang中Cgo编译的C-shared二进制字符串从C++返回时出现乱码问题 我有一段使用cgo编译为C共享库的Go代码。这段代码随后在一个使用gcc在Ubuntu下编译的C++文件中使用。

当我在M1 Mac上编译并使用这段代码时,一切正常,函数在C++中返回的字符串看起来是正常的。然而,当我在Ubuntu上编译时(代码完全相同),函数运行没有错误,但函数返回的字符串却是这种奇怪的乱码编码。它看起来像下面这样,但每次运行的结果都不同。我不认为这是加密,因为在C代码内部的返回字符串是正常的。

image

func GetOsStore(serviceName *C.char, keyName *C.char) *C.char {
	fmt.Println("GetOsStore start")
	backendType, err := getBackendType()
	if err != nil {
		panic("Getting backend type has failed! " + err.Error())
	}
	fmt.Println("GetOsStore.BackendType", backendType)

	ring, openErr := keyring.Open(keyring.Config{
		AllowedBackends: []keyring.BackendType{backendType},
		ServiceName:     C.GoString(serviceName),
	})
	if openErr != nil {
		fmt.Println("GetOsStore open keyring error", openErr)
	} else {
		fmt.Println("GetOsStore opened keyring")
	}

	i, getErr := ring.Get((C.GoString(keyName)))
	dataStr := string(i.Data[:])
	returnStr := (*C.char)(C.CString(dataStr)) // returnStr prints without issue
	defer C.free(unsafe.Pointer(returnStr))
	if getErr != nil {
		fmt.Println("GetOsStore get keyring error", getErr)
	} else {
		fmt.Println("GetOsStore got keyring")
	}
	fmt.Println("GetOsStore end")
	return returnStr
}
Napi::String getOsStore(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();
  
  std::string serviceNameArg = info[0].As<Napi::String>().ToString();
  char *serviceName = new char[serviceNameArg.length() + 1];
  strcpy(serviceName, serviceNameArg.c_str());

  std::string keyNameArg = info[1].As<Napi::String>().ToString();
  char *keyName = new char[keyNameArg.length() + 1];
  strcpy(keyName, keyNameArg.c_str());

  Napi::String result = Napi::String::New(env, GetOsStore(serviceName, keyName));
  std::string resultStr = result.ToString();
  cout << "getOsStore from c++ string: " << resultStr; // both these cout produce the encoded text
  char *resultCStr = new char[resultStr.length() + 1];
  strcpy(resultCStr, resultStr.c_str());
  cout << "getOsStore from c string: " << resultCStr; 

  delete [] serviceName;
  delete [] keyName;
  return result;
}

更多关于Golang中Cgo编译的C-shared二进制字符串从C++返回时出现乱码问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中Cgo编译的C-shared二进制字符串从C++返回时出现乱码问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是一个典型的跨平台内存管理和字符串编码问题。问题出现在Go返回的C字符串在C++端的处理方式上。

在你的Go代码中,C.CString()创建了一个以null结尾的C字符串,这个字符串在Go的堆上分配。问题在于Ubuntu和macOS可能有不同的内存对齐或字符串处理方式。

以下是修复方案:

// 在Go端,确保正确处理字符串编码
func GetOsStore(serviceName *C.char, keyName *C.char) *C.char {
    // ... 你的现有代码 ...
    
    dataStr := string(i.Data[:])
    
    // 关键修复:显式转换为UTF-8并确保null终止
    utf8Str := C.CString(dataStr)
    
    // 不要在这里defer,因为C++端需要这个内存
    // defer C.free(unsafe.Pointer(utf8Str))
    
    return utf8Str
}

// 添加一个专门的释放函数
//export FreeCString
func FreeCString(str *C.char) {
    C.free(unsafe.Pointer(str))
}

在C++端,需要修改为:

extern "C" {
    char* GetOsStore(const char* serviceName, const char* keyName);
    void FreeCString(char* str);
}

Napi::String getOsStore(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    
    std::string serviceNameArg = info[0].As<Napi::String>().ToString();
    std::string keyNameArg = info[1].As<Napi::String>().ToString();
    
    // 直接传递std::string的c_str()
    char* result = GetOsStore(serviceNameArg.c_str(), keyNameArg.c_str());
    
    if (result == nullptr) {
        return Napi::String::New(env, "");
    }
    
    // 使用正确的编码转换
    Napi::String jsResult = Napi::String::New(env, result, strlen(result));
    
    // 释放Go分配的内存
    FreeCString(result);
    
    return jsResult;
}

或者使用更安全的字符串处理:

Napi::String getOsStore(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    
    if (info.Length() < 2) {
        Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
        return Napi::String::New(env, "");
    }
    
    if (!info[0].IsString() || !info[1].IsString()) {
        Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
        return Napi::String::New(env, "");
    }
    
    std::string serviceName = info[0].As<Napi::String>().Utf8Value();
    std::string keyName = info[1].As<Napi::String>().Utf8Value();
    
    char* cResult = GetOsStore(serviceName.c_str(), keyName.c_str());
    
    if (cResult == nullptr) {
        return Napi::String::New(env, "");
    }
    
    // 显式指定字符串长度
    std::string resultStr(cResult);
    Napi::String jsResult = Napi::String::New(env, resultStr);
    
    FreeCString(cResult);
    
    return jsResult;
}

还需要在Go端导出函数时确保正确的调用约定:

/*
#include <stdlib.h>
*/
import "C"

//export GetOsStore
func GetOsStore(serviceName *C.char, keyName *C.char) *C.char {
    // 函数实现
}

编译时确保使用相同的字符编码:

# 在Ubuntu上编译时指定UTF-8编码
CGO_CFLAGS="-finput-charset=UTF-8 -fexec-charset=UTF-8" go build -buildmode=c-shared -o libstore.so

问题通常源于:

  1. Go和C++之间的内存管理不一致
  2. 字符串编码不匹配(UTF-8 vs 其他编码)
  3. 平台相关的内存对齐差异
  4. 缺少null终止符或字符串长度计算错误

使用上述修复应该能解决Ubuntu上的乱码问题。

回到顶部