You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#创建未转义十六进制字节字符串解决C++ API路径编码问题

C#调用C++ API时非英文字符路径的编码问题

问题背景

在C#代码库中调用C++ API加载文件时,遇到路径包含西里尔文、日文、äö等非英文字符的场景会报错。相关函数信息如下:

  • C++函数签名:
    GetModelFromFileCpp(ModelRefType * model, const char * file_path)
    
  • C#封装代码:
    [DllImport("CPPLibrary.dll", EntryPoint="GetModelFromFileCpp", CallingConvention=CallingConvention.Cdecl)]
    public static extern ResultType GetModelFromFileCs(out IntPtr model, string file_path)
    

根据C++库文档,该函数要求传入UTF-8编码的文件路径,但直接传递含特殊字符的路径字符串时,会出现序列化错误,无法读取文件。

测试与现象

编写测试代码验证字符串编码:

string path = @"C:\Users\UserDirWithäöChar\App Data\Local\Temp";
byte[] b = Encoding.UTF8.GetBytes(path);
string r = String.Empty;
foreach(byte bite in b)
{
   r += (@"\x" + String.Format("{0:X2}", bite));
}
var result = r.ToCharArray();
Console.WriteLine(result);

输出的十六进制转义字符串为:

\x43\x3A\x5C\x55\x73\x65\x72\x73\x5C\x55\x73\x65\x72\x44\x69\x72\x57\x69\x74\x68\xC3\xA4\xC3\xB6\x43\x68\x61\x72\x5C\x41\x70\x70\x20\x44\x61\x74\x61\x5C\x4C\x6F\x63\x61\x6C\x5C\x54\x65\x6D\x70

发现两个关键现象:

  1. 将上述输出直接粘贴到Visual Studio即时窗口调用封装函数,C++库能返回正确结果;
  2. 把该值存入字符串变量传递时,字符串会被自动转义(变为\\x43\\x3A...),导致调用失效,且两种场景下的字符串长度相差4倍。

当前实现代码

public static Model CreateModelFromFile(string path)
{
   byte[] bytes = Encoding.UTF8.GetBytes(path);

   string encodedPath = String.Empty;

   foreach(byte b in bytes)
   {
       encodedPath += (@"\x" + String.Format("{0:X2}", b));
   }

   IntPtr model_ref;

   if (ModelAPI.CreateFromFileWithStatus(out model_ref, 
       encodedPath))
      {
         return new Model(model_ref);
      }

   return null;
}

问题解答

两种字符串的核心区别

  • 即时窗口粘贴的是编译期转义后的字符串:\xXX是C#的字符转义序列,编译器会直接将其解析为对应的单个字节(比如\x43对应字符'C'),最终传递给C++的是符合要求的UTF-8编码字节流。
  • 代码中拼接的是字面意义的字符串内容:每个\xXX会被处理成4个独立字符(\、x、两位十六进制数),字符串中的\会被自动转义为\\,传递给C++的是这些字符的ASCII编码,完全不是预期的UTF-8路径字节。

能否将未转义的十六进制字节字符串存入变量?

不能直接通过C#字符串实现——因为C#字符串本质是UTF-16编码的Unicode字符序列,无法直接存储“未转义的十六进制字节”。正确的做法是让P/Invoke自动处理编码转换,而非手动拼接转义字符串。

正确解决方案

修改C#的DllImport声明,指定CharSet = CharSet.Utf8,让CLR自动将C#字符串转换为UTF-8编码的字节数组传递给C++:

[DllImport("CPPLibrary.dll", EntryPoint="GetModelFromFileCpp", CallingConvention=CallingConvention.Cdecl, CharSet = CharSet.Utf8)]
public static extern ResultType GetModelFromFileCs(out IntPtr model, string file_path);

之后直接传递原始路径字符串即可,无需手动编码:

public static Model CreateModelFromFile(string path)
{
    IntPtr model_ref;
    if (ModelAPI.GetModelFromFileCs(out model_ref, path))
    {
        return new Model(model_ref);
    }
    return null;
}

原理说明

  • 默认情况下,DllImport的CharSet为CharSet.Ansi,会将C#字符串转换为系统默认的ANSI编码,这在非英文环境下会丢失特殊字符;
  • 指定CharSet.Utf8后,CLR会自动把UTF-16编码的C#字符串转成UTF-8字节数组,并添加null终止符,完全匹配C++函数对const char*参数的UTF-8路径要求。

内容的提问来源于stack exchange,提问作者Alaaddin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 04:54:35