周立功的CAN /FD实现

This commit is contained in:
2026-02-06 12:34:34 +08:00
parent 2e8ad1cffa
commit 74338fdb3a
13 changed files with 4260 additions and 310 deletions

View File

@@ -79,6 +79,33 @@ namespace CapMachine.Wpf.CanDrive.ZlgCan
throw new FileNotFoundException("DBC 文件不存在。", dbcPath);
}
var fileSize = 0L;
try
{
fileSize = new FileInfo(dbcPath).Length;
}
catch
{
}
var fullPath = dbcPath;
try
{
fullPath = Path.GetFullPath(dbcPath);
}
catch
{
}
var shortPath = TryGetShortPath(fullPath);
var candidatePaths = new List<string>();
if (!string.IsNullOrWhiteSpace(fullPath)) candidatePaths.Add(fullPath);
if (!string.IsNullOrWhiteSpace(shortPath)
&& !string.Equals(shortPath, fullPath, StringComparison.OrdinalIgnoreCase))
{
candidatePaths.Add(shortPath);
}
lock (_sync)
{
if (_dbcHandle == 0 || _dbcHandle == ZDBC.INVALID_DBC_HANDLE)
@@ -91,26 +118,42 @@ namespace CapMachine.Wpf.CanDrive.ZlgCan
}
}
var fileInfo = new ZDBC.FileInfo
var loadOk = false;
foreach (var path in candidatePaths)
{
strFilePath = BuildFixedPathBytes(dbcPath, ZDBC._MAX_FILE_PATH_ + 1),
type = protocolType,
merge = (byte)(merge ? 1 : 0)
};
IntPtr pFile = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.FileInfo)));
try
{
Marshal.StructureToPtr(fileInfo, pFile, false);
var ok = ZDBC.ZDBC_LoadFile(_dbcHandle, pFile);
if (!ok)
var fileInfo = new ZDBC.FileInfo
{
throw new InvalidOperationException("ZDBC_LoadFile 失败,请检查 DBC 文件格式。");
strFilePath = BuildFixedPathBytes(path, ZDBC._MAX_FILE_PATH_ + 1),
type = protocolType,
merge = (byte)(merge ? 1 : 0)
};
IntPtr pFile = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.FileInfo)));
try
{
Marshal.StructureToPtr(fileInfo, pFile, false);
loadOk = ZDBC.ZDBC_LoadFile(_dbcHandle, pFile);
if (loadOk)
{
break;
}
}
finally
{
Marshal.FreeHGlobal(pFile);
}
}
finally
if (!loadOk)
{
Marshal.FreeHGlobal(pFile);
// fallback直接按内容加载绕开路径编码问题同时尝试多种编码以提升兼容性。
loadOk = TryLoadContentFallback_NoLock(fullPath, merge);
}
if (!loadOk)
{
var msg = $"ZDBC_LoadFile 失败。DbcPath={fullPath}ShortPath={shortPath ?? string.Empty}Size={fileSize}。请检查 DBC 文件格式,或将 DBC 放到纯英文路径后重试。";
throw new InvalidOperationException(msg);
}
RefreshMessageCache_NoLock();
@@ -542,13 +585,128 @@ namespace CapMachine.Wpf.CanDrive.ZlgCan
private static byte[] BuildFixedPathBytes(string path, int fixedLen)
{
var bytes = Encoding.ASCII.GetBytes(path);
var bytes = Encoding.Default.GetBytes(path);
var dst = new byte[fixedLen];
Array.Clear(dst, 0, dst.Length);
Array.Copy(bytes, 0, dst, 0, Math.Min(bytes.Length, fixedLen - 1));
return dst;
}
/// <summary>
/// 当 ZDBC_LoadFile 失败时的兜底加载策略:读取文件内容并调用 ZDBC_LoadContent。
/// </summary>
/// <param name="fullPath">DBC 完整路径。</param>
/// <param name="merge">是否合并。</param>
/// <returns>加载成功返回 true。</returns>
private bool TryLoadContentFallback_NoLock(string fullPath, bool merge)
{
// zdbc.dll 入口为 ANSI 字符串,因此这里优先使用 ASCII将非 ASCII 字符替换为 ?
// 避免中文注释/特殊字符导致原生库解析失败;同时需要兼容 UTF-16 BOM否则用 ASCII 读会出现大量 \0 造成内容截断。
var encodings = new List<Encoding>();
try
{
var bytes = File.ReadAllBytes(fullPath);
if (bytes.Length >= 2)
{
// UTF-16 LE BOM: FF FE
if (bytes[0] == 0xFF && bytes[1] == 0xFE)
{
encodings.Add(Encoding.Unicode);
}
// UTF-16 BE BOM: FE FF
else if (bytes[0] == 0xFE && bytes[1] == 0xFF)
{
encodings.Add(Encoding.BigEndianUnicode);
}
}
if (bytes.Length >= 3)
{
// UTF-8 BOM: EF BB BF
if (bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF)
{
encodings.Add(Encoding.UTF8);
}
}
}
catch
{
}
// 常规兜底编码序列
encodings.Add(new UTF8Encoding(false, true));
encodings.Add(Encoding.UTF8);
encodings.Add(Encoding.Default);
encodings.Add(Encoding.ASCII);
foreach (var enc in encodings.Distinct())
{
IntPtr pContent = IntPtr.Zero;
try
{
var content = File.ReadAllText(fullPath, enc);
pContent = Marshal.StringToHGlobalAnsi(content);
var ok = ZDBC.ZDBC_LoadContent(_dbcHandle, pContent, merge ? 1u : 0u);
if (ok)
{
return true;
}
}
catch
{
}
finally
{
if (pContent != IntPtr.Zero)
{
Marshal.FreeHGlobal(pContent);
}
}
}
return false;
}
/// <summary>
/// 获取 Windows 8.3 短路径。
/// </summary>
/// <param name="lpszLongPath">长路径。</param>
/// <param name="lpszShortPath">短路径缓冲。</param>
/// <param name="cchBuffer">缓冲区大小。</param>
/// <returns>返回写入的字符数量0 表示失败。</returns>
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern uint GetShortPathName(string lpszLongPath, StringBuilder lpszShortPath, uint cchBuffer);
/// <summary>
/// 尝试将长路径转换为短路径8.3),用于兼容部分原生库对中文/特殊字符路径处理不完整的问题。
/// </summary>
/// <param name="fullPath">长路径。</param>
/// <returns>短路径;失败返回 null。</returns>
private static string? TryGetShortPath(string fullPath)
{
try
{
if (string.IsNullOrWhiteSpace(fullPath))
{
return null;
}
var sb = new StringBuilder(1024);
var ret = GetShortPathName(fullPath, sb, (uint)sb.Capacity);
if (ret == 0)
{
return null;
}
return sb.ToString();
}
catch
{
return null;
}
}
private static string ByteArrayToAsciiString(byte[]? bytes)
{
if (bytes == null || bytes.Length == 0) return string.Empty;
@@ -556,7 +714,7 @@ namespace CapMachine.Wpf.CanDrive.ZlgCan
int len = Array.IndexOf(bytes, (byte)0);
if (len < 0) len = bytes.Length;
return Encoding.ASCII.GetString(bytes, 0, len).Trim();
return Encoding.Default.GetString(bytes, 0, len).Trim();
}
private void ThrowIfDisposed()