using HalconDotNet;
|
using Microsoft.Win32;
|
using LB_SmartVision;
|
using SharpCompress.Common;
|
using System.Collections.Concurrent;
|
using System.Configuration;
|
using System.Diagnostics;
|
using System.Drawing.Imaging;
|
using System.Runtime.InteropServices;
|
using System.Security;
|
using System.Security.Cryptography;
|
using System.Text;
|
using System.Text.RegularExpressions;
|
using System.Threading.Tasks;
|
|
namespace LB_SmartVision.Tool
|
{
|
public sealed class Tool : IDisposable
|
{
|
// 静态构造函数启动消费者任务
|
static Tool()
|
{
|
// 启动消费者任务
|
_logConsumerTask = Task.Factory.StartNew(
|
() => ProcessLogQueue(_logCts.Token),
|
_logCts.Token,
|
TaskCreationOptions.LongRunning,
|
TaskScheduler.Default);
|
|
// FIFO 行为,超出容量时阻塞
|
_RealImageQueue = new BlockingCollection<ImageSaveTask>(capacity);
|
_RealImageCts = new CancellationTokenSource();
|
StartRealImageConsumer();
|
}
|
|
public void Dispose()
|
{
|
try
|
{
|
// 停止接受新日志
|
_logQueue.CompleteAdding();
|
// 取消正在进行的操作
|
_logCts.Cancel();
|
// 等待消费者完成
|
if (!_logConsumerTask.Wait(TimeSpan.FromSeconds(5)))
|
{
|
Debug.WriteLine("日志消费者线程关闭超时");
|
}
|
|
_RealImageCts.Cancel();
|
_RealImageQueue.CompleteAdding();
|
while (_RealImageQueue.TryTake(out var task))
|
{
|
if (task.Image is Bitmap bitmap)
|
{
|
bitmap.Dispose();
|
}
|
else if (task.Image is HObject hobject)
|
{
|
hobject.Dispose();
|
}
|
}
|
_RealImageCts.Dispose();
|
_RealImageQueue.Dispose();
|
_RealImageConsumerTask?.Dispose();
|
}
|
catch { }
|
finally
|
{
|
_logCts.Dispose();
|
_logQueue.Dispose();
|
}
|
}
|
|
#region 日志功能
|
|
|
public static string _logFilePath = Path.Combine(GlobalVar.strPathLog, $"{GlobalVar.strStartTime}.log");
|
public static string _logFilePathBackup = Path.Combine(GlobalVar.strPathLog, $"{GlobalVar.strStartTime}(副本).log");
|
|
private static readonly BlockingCollection<string> _logQueue = new BlockingCollection<string>(1024);
|
private static readonly CancellationTokenSource _logCts = new CancellationTokenSource();
|
private static Task _logConsumerTask;
|
private static readonly object _fileLock = new object();
|
|
// 添加日志到队列
|
public static bool AddLog(string message)
|
{
|
if (!_logQueue.IsAddingCompleted)
|
{
|
try
|
{
|
_logQueue.Add(message, _logCts.Token);
|
return true;
|
}
|
catch (InvalidOperationException)
|
{
|
// 队列已关闭
|
return false;
|
}
|
}
|
return false;
|
}
|
|
// 消费者任务处理队列
|
private static void ProcessLogQueue(CancellationToken ct)
|
{
|
try
|
{
|
foreach (var logEntry in _logQueue.GetConsumingEnumerable(ct))
|
{
|
lock (_fileLock)
|
{
|
try
|
{
|
File.AppendAllText(_logFilePath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {logEntry}{Environment.NewLine}");
|
}
|
catch
|
{
|
try
|
{
|
// 日志写入失败二次尝试
|
File.AppendAllText(_logFilePath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {logEntry}{Environment.NewLine}");
|
}
|
catch
|
{
|
try
|
{
|
// 日志写入失败处理(可添加到备用队列或控制台输出)
|
File.AppendAllText(_logFilePathBackup, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {logEntry}{Environment.NewLine}");
|
}
|
catch (Exception ex)
|
{
|
Debug.WriteLine($"日志写入失败: {ex.Message}");
|
}
|
}
|
}
|
}
|
|
}
|
}
|
catch (OperationCanceledException)
|
{
|
// 正常取消
|
}
|
}
|
|
// 尝试写入文件的辅助方法
|
private static bool TryWriteToFile(string path, byte[] data)
|
{
|
try
|
{
|
using (var stream = new FileStream(
|
path,
|
FileMode.Append,
|
FileAccess.Write,
|
FileShare.ReadWrite,
|
bufferSize: 4096,
|
useAsync: true))
|
{
|
stream.Write(data, 0, data.Length);
|
return true;
|
}
|
}
|
catch
|
{
|
return false;
|
}
|
}
|
#endregion
|
|
#region 存原图功能
|
/// <summary>
|
/// 超出容量时阻塞
|
/// </summary>
|
private static readonly BlockingCollection<ImageSaveTask> _RealImageQueue;
|
private static readonly CancellationTokenSource _RealImageCts;
|
private static Task _RealImageConsumerTask;
|
private static int _restartCount;
|
private const int MaxRestarts = 5;
|
private static int capacity = 10; // 队列容量
|
|
// 获取指定图像格式的编码器信息
|
private static ImageCodecInfo GetEncoder(ImageFormat format)
|
{
|
// 获取所有已安装的图像编码器
|
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
|
|
// 查找匹配指定格式的编码器
|
return codecs.FirstOrDefault(codec => codec.FormatID == format.Guid);
|
}
|
|
public static long _imageQuality = 100L;
|
static ImageCodecInfo jpegEncoder = null;
|
static EncoderParameters encoderParams = null;
|
static char[] invalidChars = Path.GetInvalidFileNameChars();
|
|
// 图片保存任务结构
|
public struct ImageSaveTask
|
{
|
public object Image { get; set; }
|
public string FullPath { get; set; }
|
|
public string ImageType { get; set; }
|
|
public long Quality { get; set; }
|
|
public ImageSaveTask(object image, string fullPath, string imageType = "jpg", long quality = 50L)
|
{
|
Image = image;
|
FullPath = fullPath;
|
ImageType = imageType.ToLower();
|
Quality = quality;
|
}
|
}
|
|
private static void StartRealImageConsumer()
|
{
|
_RealImageConsumerTask = Task.Run(async () =>
|
{
|
try
|
{
|
await RealImageProcessQueue();
|
}
|
catch when (_RealImageCts.IsCancellationRequested)
|
{
|
// 正常关闭
|
}
|
catch (Exception ex)
|
{
|
if (++_restartCount <= MaxRestarts)
|
{
|
StartRealImageConsumer(); // 自动重启
|
}
|
AddLog($"保存图片StartRealImageConsumer出现异常:{ex.Message}");
|
}
|
}, _RealImageCts.Token);
|
}
|
|
private static async Task RealImageProcessQueue()
|
{
|
foreach (var task in _RealImageQueue.GetConsumingEnumerable(_RealImageCts.Token))
|
{
|
try
|
{
|
await SaveImageAsync(task);
|
}
|
catch (Exception ex)
|
{
|
AddLog($"保存图片RealImageProcessQueue出现异常:{ex.Message}");
|
}
|
finally
|
{
|
if (task.Image != null)
|
{
|
if (task.Image is Bitmap bitmap)
|
{
|
bitmap.Dispose();
|
}
|
if (task.Image is HObject hobject)
|
{
|
hobject.Dispose();
|
}
|
}
|
}
|
}
|
}
|
|
public static void AddRealImage(object image, string filePath, string fileName, string imageType = "jpg", long quality = 50L)
|
{
|
if (_RealImageCts.IsCancellationRequested)
|
throw new InvalidOperationException("Processor is shutting down");
|
|
if (IsSave(filePath))
|
{
|
string fullPath = Path.Combine(filePath, $"{fileName}.{imageType.ToLower()}");
|
|
// FIFO 行为,超出容量时阻塞
|
//// 缓存图片大于容量说明磁盘存储不过来了,取出所有元素直到队列为空
|
//if (_RealImageQueue.Count >= capacity + 1)
|
//{
|
// AddLog("缓存图片超过10张,清空已经缓存的旧图片");
|
// while (_RealImageQueue.TryTake(out _)) { }
|
//}
|
|
if (image is HObject hObject)
|
{
|
_RealImageQueue.Add(new ImageSaveTask
|
{
|
Image = hObject.Clone(),
|
FullPath = fullPath,
|
ImageType = imageType.ToLower(),
|
Quality = quality
|
}, _RealImageCts.Token);
|
}
|
else if (image is Bitmap bitmap)
|
{
|
_RealImageQueue.Add(new ImageSaveTask
|
{
|
Image = bitmap.Clone(),
|
FullPath = fullPath,
|
ImageType = imageType.ToLower(),
|
Quality = quality
|
}, _RealImageCts.Token);
|
}
|
}
|
}
|
|
// 程序启动时初始化编码器缓存
|
private static readonly Dictionary<Guid, ImageCodecInfo> _encoderCache =
|
ImageCodecInfo.GetImageEncoders()
|
.ToDictionary(codec => codec.FormatID);
|
|
// 通过ImageFormat获取编码器
|
private static ImageCodecInfo GetEncoderByFormat(ImageFormat format)
|
{
|
_encoderCache.TryGetValue(format.Guid, out var encoder);
|
return encoder;
|
}
|
|
// 通过字符串类型获取编码器
|
private static ImageCodecInfo GetEncoderByType(string imageType)
|
{
|
var format = GetImageFormat(imageType);
|
return GetEncoderByFormat(format);
|
}
|
|
// 获取ImageFormat的辅助方法
|
private static ImageFormat GetImageFormat(string imageType)
|
{
|
switch (imageType.ToLowerInvariant())
|
{
|
case "jpg":
|
case "jpeg": return ImageFormat.Jpeg;
|
case "png": return ImageFormat.Png;
|
case "bmp": return ImageFormat.Bmp;
|
case "gif": return ImageFormat.Gif;
|
case "tif":
|
case "tiff": return ImageFormat.Tiff;
|
default: throw new NotSupportedException($"不支持的图片格式: {imageType}");
|
}
|
}
|
|
private static async Task SaveImageAsync(ImageSaveTask task)
|
{
|
try
|
{
|
// 参数校验
|
if (task.Image == null) throw new ArgumentNullException(nameof(task.Image));
|
if (string.IsNullOrWhiteSpace(task.FullPath))
|
throw new ArgumentException("Invalid file path");
|
|
var dir = Path.GetDirectoryName(task.FullPath);
|
if (!Directory.Exists(dir))
|
{
|
Directory.CreateDirectory(dir);
|
}
|
|
if (task.Image is HObject hObject && hObject.IsInitialized())
|
{
|
try
|
{
|
//Halcon存图需要用斜杠
|
task.FullPath.Replace("\\", "/");
|
HOperatorSet.WriteImage(hObject, task.ImageType, 0, task.FullPath);
|
}
|
catch { AddLog("保存图片时HOperatorSet.WriteImage发生异常"); }
|
}
|
else if (task.Image is Bitmap bitmap)
|
{
|
// 获取对应格式的编码器
|
ImageCodecInfo encoder = GetEncoderByType(task.ImageType);
|
|
// 配置编码参数
|
var encoderParams = new EncoderParameters(1);
|
encoderParams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, task.Quality);
|
|
// 使用内存流中转避免文件锁冲突
|
using (var memoryStream = new MemoryStream())
|
{
|
// 同步编码转异步操作(线程池优化)
|
await Task.Run(() =>
|
{
|
bitmap.Save(memoryStream, encoder, encoderParams);
|
memoryStream.Position = 0; // 重置流位置
|
}).ConfigureAwait(false);
|
|
// 原子性写入(临时文件+替换)
|
var tempPath = $"{task.FullPath}.{Guid.NewGuid()}.tmp";
|
|
// 异步文件写入(带缓冲区优化)
|
using (var fileStream = new FileStream(
|
tempPath,
|
FileMode.Create,
|
FileAccess.Write,
|
FileShare.None,
|
bufferSize: 81920, // 80KB缓冲区
|
useAsync: true))
|
{
|
await memoryStream.CopyToAsync(fileStream)
|
.ConfigureAwait(false);
|
}
|
|
// 原子替换文件(跨平台兼容)
|
File.Move(tempPath, task.FullPath);
|
}
|
}
|
}
|
catch (Exception ex) when (IsRetryableException(ex))
|
{
|
AddLog($"保存图片IsRetryableException出现异常:{ex.Message}");
|
}
|
catch (Exception ex)
|
{
|
AddLog($"保存图片SaveImageAsync出现异常:{ex.Message}");
|
}
|
}
|
|
// 判断是否可重试的异常
|
private static bool IsRetryableException(Exception ex)
|
{
|
return ex is IOException
|
or UnauthorizedAccessException
|
or TimeoutException;
|
}
|
#endregion
|
|
#region 磁盘回收功能
|
public static double MinSpace = 10;
|
/// <summary>
|
/// 获取磁盘剩余空间
|
/// </summary>
|
/// <param name="strPath"></param>
|
/// <returns></returns>
|
public static double GetHardDiskFreeSpace(string strPath)
|
{
|
//磁盘名称
|
string strHardDiskName = strPath.Substring(0, 1);
|
|
//获取C盘的剩余空间
|
DriveInfo cDrive = new DriveInfo(strHardDiskName);
|
// 检查驱动器是否是有效的
|
if (cDrive.IsReady)
|
{
|
// 获取剩余空间
|
long freeSpace = cDrive.AvailableFreeSpace;
|
|
// 将字节数转换为GB单位
|
return (double)freeSpace / (1024 * 1024 * 1024);
|
}
|
else
|
{
|
return 0;
|
}
|
}
|
|
/// <summary>
|
/// 判断是否允许保存
|
/// </summary>
|
/// <param name="fullPath">保存文件的完整路径</param>
|
/// <returns></returns>
|
public static bool IsSave(string fullPath)
|
{
|
// 获取上一级目录(父目录)
|
string parentDirectory = Path.GetDirectoryName(fullPath);
|
if (GetHardDiskFreeSpace(parentDirectory) >= MinSpace)
|
return true;
|
else
|
{
|
if (DeletOutTimeFiles(parentDirectory))
|
{
|
// 删除超期文件后再次判断容量能否存储
|
if (GetHardDiskFreeSpace(parentDirectory) >= MinSpace)
|
return true;
|
|
return false;
|
}
|
else
|
return false;
|
}
|
}
|
|
/// <summary>
|
///删除超出时间的文件夹路径
|
/// </summary>
|
/// <param name="directoryPath"></param>
|
/// <returns></returns>
|
public static bool DeletOutTimeFiles(string directoryPath, int days = 30)
|
{
|
try
|
{
|
// 保存时间不超过30天
|
DateTime NowTime = DateTime.Now; // 当前日期作为截止日期
|
DirectoryInfo directoryInfo = new DirectoryInfo(directoryPath);
|
DirectoryInfo[] directories = directoryInfo.GetDirectories();
|
|
for (int i = 0; i < directories.Length; i++)
|
{
|
if (directories[i].CreationTime.AddDays(days) < NowTime)
|
{
|
try
|
{
|
Directory.Delete(directories[i].FullName, recursive: true);
|
Debug.WriteLine($"Deleted {directories[i].FullName}");
|
}
|
catch (Exception ex)
|
{
|
// 可以选择在这里记录日志继续处理下一个文件
|
Debug.WriteLine($"Error deleting {directories[i].FullName}: {ex.Message}");
|
}
|
}
|
}
|
|
AddLog($"磁盘空间不足,已删除路径[{directoryPath}]超过{days}天的文件,累计{directories.Length}个!");
|
return true;
|
}
|
catch { return false; }
|
}
|
|
#endregion
|
|
public static bool ContainsChinese(string text)
|
{
|
if (string.IsNullOrEmpty(text))
|
{
|
return false;
|
}
|
|
return Regex.IsMatch(text, @"[\u4e00-\u9fa5]");
|
}
|
|
/// <summary>
|
/// 保存CSV到本地
|
/// </summary>
|
/// <param name="fileDirectoryAndName"></param>
|
/// <param name="obj"></param>
|
public static void WriteDataToFile(string fileDirectoryAndName, object[] obj)
|
{
|
try
|
{
|
string text = "";
|
// 写入CSV文件
|
using (var streamWriter = new StreamWriter(fileDirectoryAndName, true, Encoding.UTF8))
|
{
|
for (int i = 0; i < obj.Length; i++)
|
{
|
text += (obj[i].ToString().Replace(",", ","));
|
if (i < obj.Length - 1)
|
{
|
text += ",";
|
}
|
}
|
streamWriter.Write(text);
|
streamWriter.WriteLine(); // 写入换行符
|
}
|
}
|
catch { }
|
}
|
|
/// <summary>
|
/// 保存数据到本地
|
/// </summary>
|
/// <param name="strCCDName"></param>
|
/// <param name="timeCode"></param>
|
/// <param name="productCode"></param>
|
/// <param name="result"></param>
|
/// <param name="resultData"></param>
|
public static void SaveData(string filePath, List<string> DataTitle, Dictionary<string, object> resultData)
|
{
|
try
|
{
|
if (resultData.Count <= 0 || resultData == null)
|
{
|
return;
|
}
|
|
var dir = Path.GetDirectoryName(filePath);
|
if (!Directory.Exists(dir))
|
{
|
Directory.CreateDirectory(dir);
|
}
|
|
if (!File.Exists(filePath))
|
{
|
WriteDataToFile(filePath, DataTitle.ToArray());
|
}
|
|
List<string> SaveData = new List<string>();
|
|
foreach (var item in DataTitle)
|
{
|
if (resultData.ContainsKey(item))
|
{
|
double dData = 0;
|
if (double.TryParse(resultData[item].ToString(), out dData))
|
{
|
SaveData.Add(dData.ToString("F2"));
|
}
|
else
|
{
|
SaveData.Add((resultData[item]?.ToString()).Replace(",", ",").ToString());
|
}
|
}
|
else
|
SaveData.Add("NULL");
|
}
|
try
|
{
|
WriteDataToFile(filePath, SaveData.ToArray());
|
}
|
catch (Exception ex)
|
{
|
//文件被占用保存到副本中
|
try
|
{
|
//再次尝试写入
|
WriteDataToFile(filePath, SaveData.ToArray());
|
}
|
catch
|
{
|
//写入失败写到副本中
|
filePath = AddCopySuffix(filePath, "(副本)");
|
if (!File.Exists(filePath))
|
{
|
WriteDataToFile(filePath, DataTitle.ToArray());
|
}
|
|
WriteDataToFile(filePath, SaveData.ToArray());
|
}
|
}
|
}
|
catch (Exception ex)
|
{
|
AddLog("生产数据保存失败,原因是:" + ex.ToString());
|
}
|
}
|
|
public static string AddCopySuffix(string filePath, string suffix = "(副本)")
|
{
|
if (string.IsNullOrEmpty(filePath))
|
{
|
return filePath;
|
}
|
|
string directory = Path.GetDirectoryName(filePath);
|
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath);
|
string extension = Path.GetExtension(filePath);
|
|
return Path.Combine(directory, $"{fileNameWithoutExtension}({suffix}){extension}");
|
}
|
|
/// <summary>
|
/// 加密文件
|
/// </summary>
|
/// <param name="inputFilePath"></param>
|
/// <param name="outputFilePath"></param>
|
/// <param name="key"></param>
|
/// <param name="iv"></param>
|
public static void EncryptFile(string inputFilePath, string outputFilePath, string key = "", string iv = "")
|
{
|
using (Aes aes = Aes.Create())
|
{
|
if (key != "")
|
{
|
aes.Key = Convert.FromBase64String(key);
|
aes.IV = Convert.FromBase64String(iv);
|
}
|
else
|
{
|
aes.Key = new byte[32];
|
aes.IV = new byte[16];
|
}
|
|
using (FileStream outputFileStream = new FileStream(outputFilePath, FileMode.Create))
|
using (ICryptoTransform encryptor = aes.CreateEncryptor())
|
using (CryptoStream cryptoStream = new CryptoStream(outputFileStream, encryptor, CryptoStreamMode.Write))
|
using (FileStream inputFileStream = new FileStream(inputFilePath, FileMode.Open))
|
{
|
inputFileStream.CopyTo(cryptoStream);
|
}
|
}
|
}
|
|
/// <summary>
|
/// 解密文件
|
/// </summary>
|
/// <param name="inputFilePath"></param>
|
/// <param name="outputFilePath"></param>
|
/// <param name="key"></param>
|
/// <param name="iv"></param>
|
public static void DecryptFile(string inputFilePath, string outputFilePath, string key = "", string iv = "")
|
{
|
using (Aes aes = Aes.Create())
|
{
|
if (key != "")
|
{
|
aes.Key = Convert.FromBase64String(key);
|
aes.IV = Convert.FromBase64String(iv);
|
}
|
else
|
{
|
aes.Key = new byte[32];
|
aes.IV = new byte[16];
|
}
|
|
using (FileStream inputFileStream = new FileStream(inputFilePath, FileMode.Open))
|
using (ICryptoTransform decryptor = aes.CreateDecryptor())
|
using (CryptoStream cryptoStream = new CryptoStream(inputFileStream, decryptor, CryptoStreamMode.Read))
|
using (FileStream outputFileStream = new FileStream(outputFilePath, FileMode.Create))
|
{
|
cryptoStream.CopyTo(outputFileStream);
|
}
|
}
|
}
|
|
#region 加密狗
|
private const string DOG_AES_SALT = "MYAPP_KEY_SALT";
|
private const string DOG_AES_KEY = "MYAPP_AES_KEY";
|
private const string DOG_AES_IV = "MYAPP_AES_IV";
|
private const string REGISTRY_PATH = @"SOFTWARE\MyApp\SoftDog";
|
|
private static bool SetRegistryValue(RegistryKey baseKey, string valueName, string value)
|
{
|
try
|
{
|
using (RegistryKey key = baseKey.CreateSubKey(REGISTRY_PATH, RegistryKeyPermissionCheck.ReadWriteSubTree))
|
{
|
if (key != null)
|
{
|
key.SetValue(valueName, value, RegistryValueKind.String);
|
return true;
|
}
|
}
|
}
|
catch (SecurityException)
|
{
|
// 权限不足,正常情况
|
}
|
catch (Exception ex)
|
{
|
Debug.WriteLine($"设置注册表值失败: {ex.Message}");
|
}
|
|
return false;
|
}
|
|
private static string GetRegistryValue(string valueName)
|
{
|
// 先尝试HKLM
|
string value = GetRegistryValue(Registry.LocalMachine, valueName);
|
if (!string.IsNullOrEmpty(value))
|
{
|
return value;
|
}
|
|
// 再尝试HKCU
|
return GetRegistryValue(Registry.CurrentUser, valueName);
|
}
|
|
private static string GetRegistryValue(RegistryKey baseKey, string valueName)
|
{
|
try
|
{
|
using (RegistryKey key = baseKey.OpenSubKey(REGISTRY_PATH, false))
|
{
|
return key?.GetValue(valueName) as string;
|
}
|
}
|
catch
|
{
|
return null;
|
}
|
}
|
|
/// <summary>
|
/// 获取AES加密参数
|
/// </summary>
|
public static (byte[] Key, byte[] IV) GetAesParameters()
|
{
|
try
|
{
|
string keyBase64 = GetRegistryValue(DOG_AES_KEY);
|
string ivBase64 = GetRegistryValue(DOG_AES_IV);
|
|
// 如果系统级没有,尝试用户级
|
|
if (!string.IsNullOrEmpty(keyBase64) && !string.IsNullOrEmpty(ivBase64))
|
{
|
byte[] key = Convert.FromBase64String(keyBase64);
|
byte[] iv = Convert.FromBase64String(ivBase64);
|
|
return (key, iv);
|
}
|
|
return (null, null);
|
}
|
catch (Exception ex)
|
{
|
Debug.WriteLine($"获取AES参数失败: {ex.Message}");
|
return (null, null);
|
}
|
}
|
|
/// <summary>
|
/// 验证加密狗
|
/// </summary>
|
public static bool ValidateAesDog()
|
{
|
var (key, iv) = GetAppBoundKey("VisionUltra");
|
|
if (key == null || iv == null)
|
return false;
|
|
// 验证密钥和IV的格式和长度
|
return key.Length == 32 && iv.Length == 16; // AES-256: 32字节密钥, 16字节IV
|
}
|
|
/// <summary>
|
/// 生成与应用绑定的AES加密狗
|
/// </summary>
|
public static bool GenerateAppBoundDog(string appIdentifier)
|
{
|
try
|
{
|
using (Aes aes = Aes.Create())
|
{
|
aes.GenerateKey();
|
aes.GenerateIV();
|
|
// 使用应用标识符作为盐值
|
byte[] salt = Encoding.UTF8.GetBytes(appIdentifier);
|
|
// 加密密钥本身(可选的安全增强)
|
byte[] encryptedKey = EncryptKeyWithSalt(aes.Key, salt, aes.IV);
|
|
// 存储加密后的密钥和盐值
|
string encryptedKeyBase64 = Convert.ToBase64String(encryptedKey);
|
string saltBase64 = Convert.ToBase64String(salt);
|
string ivBase64 = Convert.ToBase64String(aes.IV);
|
|
bool success = SetRegistryValue(Registry.CurrentUser, DOG_AES_KEY, encryptedKeyBase64);
|
success &= SetRegistryValue(Registry.CurrentUser, DOG_AES_SALT, saltBase64);
|
success &= SetRegistryValue(Registry.CurrentUser, DOG_AES_IV, ivBase64);
|
|
return success;
|
}
|
}
|
catch (Exception ex)
|
{
|
Debug.WriteLine($"生成应用绑定加密狗失败: {ex.Message}");
|
return false;
|
}
|
}
|
|
/// <summary>
|
/// 验证并获取应用绑定密钥
|
/// </summary>
|
public static (byte[] Key, byte[] IV) GetAppBoundKey(string expectedAppIdentifier)
|
{
|
try
|
{
|
string encryptedKeyBase64 = GetRegistryValue(DOG_AES_KEY);
|
string saltBase64 = GetRegistryValue(DOG_AES_SALT);
|
string ivBase64 = GetRegistryValue(DOG_AES_IV);
|
|
if (string.IsNullOrEmpty(encryptedKeyBase64) || string.IsNullOrEmpty(saltBase64) || string.IsNullOrEmpty(ivBase64))
|
{
|
return (null, null);
|
}
|
|
byte[] encryptedKey = Convert.FromBase64String(encryptedKeyBase64);
|
byte[] salt = Convert.FromBase64String(saltBase64);
|
byte[] iv = Convert.FromBase64String(ivBase64);
|
|
// 验证应用标识符
|
string storedAppIdentifier = Encoding.UTF8.GetString(salt);
|
if (storedAppIdentifier != expectedAppIdentifier)
|
{
|
return (null, null);
|
}
|
|
// 解密密钥
|
byte[] key = DecryptKeyWithSalt(encryptedKey, salt, iv);
|
|
return (key, iv);
|
}
|
catch (Exception ex)
|
{
|
Debug.WriteLine($"获取应用绑定密钥失败: {ex.Message}");
|
return (null, null);
|
}
|
}
|
|
private static byte[] EncryptKeyWithSalt(byte[] key, byte[] salt, byte[] iv)
|
{
|
using (var deriveBytes = new Rfc2898DeriveBytes(key, salt, 10000, HashAlgorithmName.SHA256))
|
{
|
byte[] derivedKey = deriveBytes.GetBytes(32); // AES-256
|
// 这里可以添加额外的加密逻辑
|
return derivedKey;
|
}
|
}
|
|
private static byte[] DecryptKeyWithSalt(byte[] encryptedKey, byte[] salt, byte[] iv)
|
{
|
// 反向解密过程
|
using (var deriveBytes = new Rfc2898DeriveBytes(encryptedKey, salt, 10000, HashAlgorithmName.SHA256))
|
{
|
return deriveBytes.GetBytes(32);
|
}
|
}
|
|
/// <summary>
|
/// 使用加密狗中的密钥加密数据
|
/// </summary>
|
public static string EncryptWithDog(string plainText)
|
{
|
var (key, iv) = GetAesParameters();
|
|
if (key == null || iv == null)
|
throw new InvalidOperationException("未找到有效的加密狗");
|
|
using (Aes aes = Aes.Create())
|
{
|
aes.Key = key;
|
aes.IV = iv;
|
|
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
|
|
using (var ms = new MemoryStream())
|
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
|
{
|
using (var sw = new StreamWriter(cs))
|
{
|
sw.Write(plainText);
|
}
|
return Convert.ToBase64String(ms.ToArray());
|
}
|
}
|
}
|
|
/// <summary>
|
/// 使用加密狗中的密钥解密数据
|
/// </summary>
|
public static string DecryptWithDog(string cipherText)
|
{
|
var (key, iv) = GetAesParameters();
|
|
if (key == null || iv == null)
|
throw new InvalidOperationException("未找到有效的加密狗");
|
|
using (Aes aes = Aes.Create())
|
{
|
aes.Key = key;
|
aes.IV = iv;
|
|
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
|
byte[] cipherBytes = Convert.FromBase64String(cipherText);
|
|
using (var ms = new MemoryStream(cipherBytes))
|
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
|
using (var sr = new StreamReader(cs))
|
{
|
return sr.ReadToEnd();
|
}
|
}
|
}
|
|
/// <summary>
|
/// 注册加密狗
|
/// </summary>
|
public static bool RegistrationDog()
|
{
|
Debug.WriteLine("正在生成AES加密狗...");
|
|
if (GenerateAppBoundDog("VisionUltra"))
|
{
|
Debug.WriteLine("AES加密狗生成成功!");
|
|
// 建议用户备份这些值
|
Debug.WriteLine("\n请妥善保存以上密钥信息!");
|
return true;
|
}
|
else
|
{
|
Debug.WriteLine("加密狗生成失败,请以管理员身份运行");
|
return false;
|
}
|
}
|
|
/// <summary>
|
/// 检查软加密狗
|
/// </summary>
|
/// <returns></returns>
|
public static bool CheckDog()
|
{
|
if (!ValidateAesDog())
|
{
|
Debug.WriteLine("未找到有效的AES加密狗");
|
|
return false;
|
}
|
|
Debug.WriteLine("AES加密狗验证成功");
|
return true;
|
}
|
|
|
|
#endregion
|
|
/// <summary>
|
/// 复制文件夹及其内容
|
/// </summary>
|
/// <param name="sourceDir"></param>
|
/// <param name="destDir"></param>
|
/// <returns></returns>
|
public static bool CopyDirectory(string sourceDir, string destDir)
|
{
|
try
|
{
|
// 检查目标文件夹是否存在,如果不存在,则创建它
|
if (!Directory.Exists(destDir))
|
{
|
Directory.CreateDirectory(destDir);
|
}
|
|
// 复制所有文件到目标文件夹
|
foreach (string file in Directory.GetFiles(sourceDir))
|
{
|
string destFile = Path.Combine(destDir, Path.GetFileName(file));
|
File.Copy(file, destFile, true); // 第三个参数为 true,表示如果文件已存在,则覆盖它
|
}
|
|
// 递归复制所有子文件夹
|
foreach (string subDir in Directory.GetDirectories(sourceDir))
|
{
|
string destSubDir = Path.Combine(destDir, Path.GetFileName(subDir));
|
CopyDirectory(subDir, destSubDir); // 递归调用复制子文件夹
|
}
|
return true;
|
}
|
catch { return false; }
|
}
|
|
/// <summary>
|
/// 重命名文件夹
|
/// </summary>
|
/// <param name="oldDir"></param>
|
/// <param name="newDir"></param>
|
/// <returns></returns>
|
public static bool RenameDirectory(string oldDir, string newDir)
|
{
|
try
|
{
|
// 重命名文件夹
|
if (Directory.Exists(oldDir))
|
{
|
Directory.Move(oldDir, newDir);
|
return true;
|
}
|
Debug.WriteLine($"文件不存在: {oldDir}");
|
return false;
|
}
|
catch { return false; }
|
}
|
|
/// <summary>
|
/// 重命名文件
|
/// </summary>
|
/// <param name="oldFile"></param>
|
/// <param name="newFile"></param>
|
/// <returns></returns>
|
public static bool RenameFile(string oldFile, string newFile)
|
{
|
try
|
{
|
if (!File.Exists(newFile))
|
{
|
// 重命名文件
|
File.Move(oldFile, newFile);
|
Debug.WriteLine($"文件已重命名: {oldFile} -> {newFile}");
|
return true;
|
}
|
|
Debug.WriteLine($"文件不存在: {oldFile}");
|
return false;
|
}
|
catch { return false; }
|
}
|
|
#region 读写到exe.Config文件
|
public static void WriteConfig(string node, string value)
|
{
|
System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
|
if (config.AppSettings.Settings[node] == null)
|
{
|
config.AppSettings.Settings.Add(node, value);
|
}
|
else
|
{
|
config.AppSettings.Settings[node].Value = value;
|
}
|
config.Save(ConfigurationSaveMode.Modified);
|
ConfigurationManager.RefreshSection("appSettings");//重新加载新的配置文件
|
}
|
public static bool ReadStringConfig(string node, out string result)
|
{
|
result = "";
|
try
|
{
|
if (ConfigurationManager.AppSettings != null && ConfigurationManager.AppSettings.Count > 0 && ConfigurationManager.AppSettings.AllKeys.Contains(node))
|
{
|
result = ConfigurationManager.AppSettings[node].ToString();
|
if (result == null)
|
{
|
WriteConfig(node, "");
|
result = "";
|
return false;
|
}
|
else
|
{
|
return true;
|
}
|
}
|
else
|
{
|
return false;
|
}
|
}
|
catch
|
{
|
WriteConfig(node, "");
|
result = "";
|
return false;
|
}
|
}
|
|
public static bool ReadBoolenConfig(string node, out bool result)
|
{
|
result = false;
|
try
|
{
|
result = Convert.ToBoolean(ConfigurationManager.AppSettings[node]);
|
if (result == null)
|
{
|
WriteConfig(node, (false).ToString());
|
result = false;
|
return false;
|
|
}
|
else
|
{
|
return true;
|
}
|
}
|
catch
|
{
|
WriteConfig(node, (false).ToString());
|
result = false;
|
return false;
|
}
|
}
|
#endregion
|
}
|
}
|