周立功的CAN /FD实现
This commit is contained in:
@@ -2,8 +2,10 @@ using CapMachine.Wpf.CanDrive;
|
||||
using CapMachine.Wpf.Services;
|
||||
using Prism.Mvvm;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -21,6 +23,9 @@ namespace CapMachine.Wpf.CanDrive.ZlgCan
|
||||
private readonly object _sync = new object();
|
||||
private readonly ILogService _log;
|
||||
|
||||
private static IntPtr _preloadedZlgcanHandle = IntPtr.Zero;
|
||||
private static string? _preloadedZlgcanError;
|
||||
|
||||
private IntPtr _deviceHandle = IntPtr.Zero;
|
||||
private readonly IntPtr[] _canChannelHandles = new IntPtr[2];
|
||||
private IntPtr _linChannelHandle = IntPtr.Zero;
|
||||
@@ -189,16 +194,189 @@ namespace CapMachine.Wpf.CanDrive.ZlgCan
|
||||
|
||||
EnsureNativeDllExists("zlgcan.dll");
|
||||
|
||||
_deviceHandle = ZLGCAN.ZCAN_OpenDevice(ZLGCAN.ZCAN_USBCANFD_200U, deviceIndex, 0);
|
||||
var deviceType = ZLGCAN.ZCAN_USBCANFD_200U;
|
||||
|
||||
try
|
||||
{
|
||||
_deviceHandle = ZLGCAN.ZCAN_OpenDevice(deviceType, deviceIndex, 0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var baseDir = AppContext.BaseDirectory;
|
||||
var dllFullPath = Path.Combine(baseDir, "zlgcan.dll");
|
||||
var msg = $"ZCAN_OpenDevice 调用异常。deviceType={deviceType}, deviceIndex={deviceIndex}, " +
|
||||
$"Is64BitProcess={Environment.Is64BitProcess}, ProcessArch={RuntimeInformation.ProcessArchitecture}, " +
|
||||
$"DllPath={dllFullPath}, BaseDir={baseDir}。异常:{ex.Message}";
|
||||
_log.Error(msg);
|
||||
throw new InvalidOperationException(msg, ex);
|
||||
}
|
||||
|
||||
if (_deviceHandle == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("ZCAN_OpenDevice 失败,请确认驱动/设备连接/程序位数与 DLL 匹配。");
|
||||
var lastError = Marshal.GetLastWin32Error();
|
||||
var lastErrorMsg = string.Empty;
|
||||
try
|
||||
{
|
||||
lastErrorMsg = new Win32Exception(lastError).Message;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
var baseDir = AppContext.BaseDirectory;
|
||||
var dllFullPath = Path.Combine(baseDir, "zlgcan.dll");
|
||||
var loadedModulePath = GetLoadedModulePath("zlgcan.dll") ?? string.Empty;
|
||||
var depDiag = string.Join(" | ", new[]
|
||||
{
|
||||
BuildDllDiag(baseDir, "zlgcan.dll"),
|
||||
BuildDllDiag(baseDir, "USB2XXX.dll"),
|
||||
BuildDllDiag(baseDir, "libusb-1.0.dll"),
|
||||
BuildDllDiag(baseDir, "zdbc.dll"),
|
||||
BuildDllDiag(baseDir, "kerneldlls\\USBCANFD.dll"),
|
||||
BuildDllDiag(baseDir, "kerneldlls\\CANDevCore.dll"),
|
||||
BuildDllDiag(baseDir, "kerneldlls\\CANDevice.dll"),
|
||||
BuildDllDiag(baseDir, "kerneldlls\\CANFDCOM.dll"),
|
||||
BuildDllDiag(baseDir, "kerneldlls\\CANFDNET.dll"),
|
||||
BuildDllDiag(baseDir, "kerneldlls\\ZPSCANFD.dll"),
|
||||
BuildDllDiag(baseDir, "kerneldlls\\USBCANFD800U.dll"),
|
||||
BuildDllDiag(baseDir, "kerneldlls\\USBCAN.dll"),
|
||||
BuildDllDiag(baseDir, "kerneldlls\\usbcan.dll"),
|
||||
}.Where(s => !string.IsNullOrWhiteSpace(s)));
|
||||
string dllVer = string.Empty;
|
||||
try
|
||||
{
|
||||
if (File.Exists(dllFullPath))
|
||||
{
|
||||
var vi = FileVersionInfo.GetVersionInfo(dllFullPath);
|
||||
dllVer = vi?.FileVersion ?? string.Empty;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
var msg = $"ZCAN_OpenDevice 失败。deviceType={deviceType}, deviceIndex={deviceIndex}。" +
|
||||
$"Win32LastError={lastError}(0x{lastError:X}){(string.IsNullOrWhiteSpace(lastErrorMsg) ? string.Empty : $"({lastErrorMsg})")}。" +
|
||||
$"Is64BitProcess={Environment.Is64BitProcess}, ProcessArch={RuntimeInformation.ProcessArchitecture}。" +
|
||||
$"DllPath={dllFullPath}, DllVersion={dllVer}, LoadedModulePath={loadedModulePath}, BaseDir={baseDir}。" +
|
||||
$"PreloadError={_preloadedZlgcanError ?? string.Empty}。" +
|
||||
$"DirDllDiag={depDiag}。" +
|
||||
"请确认:1) 已安装ZLG驱动;2) 设备已连接且未被其它程序占用;3) 程序位数与 zlgcan.dll 匹配;4) 设备类型/索引正确。";
|
||||
|
||||
_log.Error(msg);
|
||||
throw new InvalidOperationException(msg);
|
||||
}
|
||||
|
||||
OpenState = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 预加载指定路径的原生 DLL,尽量避免被 PATH 中的同名 DLL 干扰。
|
||||
/// </summary>
|
||||
/// <param name="dllFullPath">DLL 完整路径。</param>
|
||||
private static void TryPreloadNativeDll(string dllFullPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_preloadedZlgcanHandle != IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_preloadedZlgcanError))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_preloadedZlgcanHandle = NativeLibrary.Load(dllFullPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_preloadedZlgcanError = $"{ex.GetType().Name}: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造输出目录下某个 DLL 的诊断信息(架构/大小/版本)。
|
||||
/// </summary>
|
||||
/// <param name="baseDir">输出目录。</param>
|
||||
/// <param name="fileName">DLL 文件名。</param>
|
||||
/// <returns>诊断信息字符串;不存在返回 "xxx:not_found";异常返回 null。</returns>
|
||||
private static string? BuildDllDiag(string baseDir, string fileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(baseDir) || string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var full = Path.Combine(baseDir, fileName);
|
||||
if (!File.Exists(full))
|
||||
{
|
||||
return $"{fileName}:not_found";
|
||||
}
|
||||
|
||||
var arch = TryGetPeArchitecture(full) ?? string.Empty;
|
||||
long size = 0;
|
||||
try
|
||||
{
|
||||
size = new FileInfo(full).Length;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
var ver = string.Empty;
|
||||
try
|
||||
{
|
||||
var vi = FileVersionInfo.GetVersionInfo(full);
|
||||
ver = vi?.FileVersion ?? string.Empty;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return $"{fileName}:arch={arch},size={size},ver={ver}";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前进程中已加载模块的完整路径。
|
||||
/// </summary>
|
||||
/// <param name="moduleFileName">模块文件名(如 zlgcan.dll)。</param>
|
||||
/// <returns>完整路径;获取失败返回 null。</returns>
|
||||
private static string? GetLoadedModulePath(string moduleFileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(moduleFileName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using var p = Process.GetCurrentProcess();
|
||||
foreach (ProcessModule m in p.Modules)
|
||||
{
|
||||
if (string.Equals(m.ModuleName, moduleFileName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return m.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开设备并初始化 CANFD 通道。
|
||||
/// </summary>
|
||||
@@ -1286,6 +1464,78 @@ namespace CapMachine.Wpf.CanDrive.ZlgCan
|
||||
{
|
||||
throw new FileNotFoundException($"未找到 {dllName},请将其复制到程序输出目录:{baseDir}", full);
|
||||
}
|
||||
|
||||
TryPreloadNativeDll(full);
|
||||
if (_preloadedZlgcanHandle == IntPtr.Zero && !string.IsNullOrWhiteSpace(_preloadedZlgcanError))
|
||||
{
|
||||
throw new InvalidOperationException($"预加载 {dllName} 失败:{_preloadedZlgcanError}。DLL 路径:{full}");
|
||||
}
|
||||
|
||||
var dllArch = TryGetPeArchitecture(full);
|
||||
if (!string.IsNullOrWhiteSpace(dllArch))
|
||||
{
|
||||
var procArch = Environment.Is64BitProcess ? "x64" : "x86";
|
||||
if (Environment.Is64BitProcess && !string.Equals(dllArch, "x64", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException($"{dllName} 位数不匹配:当前进程为 {procArch},但 DLL 架构为 {dllArch}。请替换为 x64 版本 DLL,或将程序改为 x86 运行。DLL 路径:{full}");
|
||||
}
|
||||
if (!Environment.Is64BitProcess && !string.Equals(dllArch, "x86", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException($"{dllName} 位数不匹配:当前进程为 {procArch},但 DLL 架构为 {dllArch}。请替换为 x86 版本 DLL,或将程序改为 x64 运行。DLL 路径:{full}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试读取 PE 头判断 DLL 架构。
|
||||
/// </summary>
|
||||
/// <param name="dllFullPath">DLL 完整路径。</param>
|
||||
/// <returns>返回 "x86"/"x64"/"arm64"/"unknown(...)";读取失败返回 null。</returns>
|
||||
private static string? TryGetPeArchitecture(string dllFullPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var fs = new FileStream(dllFullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using var br = new BinaryReader(fs);
|
||||
|
||||
if (fs.Length < 0x40)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var mz = br.ReadUInt16();
|
||||
if (mz != 0x5A4D)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
fs.Position = 0x3C;
|
||||
var peOffset = br.ReadInt32();
|
||||
if (peOffset <= 0 || peOffset > fs.Length - 6)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
fs.Position = peOffset;
|
||||
var peSig = br.ReadUInt32();
|
||||
if (peSig != 0x00004550)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var machine = br.ReadUInt16();
|
||||
return machine switch
|
||||
{
|
||||
0x014c => "x86",
|
||||
0x8664 => "x64",
|
||||
0xAA64 => "arm64",
|
||||
_ => $"unknown(0x{machine:X})"
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, CanDbcModel> BuildDbcModelIndex(IEnumerable<CanDbcModel> models)
|
||||
|
||||
Reference in New Issue
Block a user