PTR_PrintText,使用GDI打印输出文本

本文介绍了一个使用GDI进行文本打印的示例程序。通过创建打印机设备上下文,并设置打印参数,该程序能够将指定文本输出到打印机上。此外,还展示了如何处理文本中的换行符并调整字体。


// 使用GDI打印输出文本
int WINAPI PTR_PrintText(LPCSTR szDocName, LPBYTE pBuf, int iLen)
{
 AFX_MANAGE_STATE(AfxGetStaticModuleState());

 CDC printdc;
 CPrintDialog dlg(FALSE, PD_NOPAGENUMS|PD_NOSELECTION);
 
 dlg.GetDefaults();
 {
  CDC dc;
  DOCINFO di;

  dc.Attach(dlg.GetPrinterDC());//把打印设备环境附加到DC对象
  ::ZeroMemory (&di, sizeof (DOCINFO));
  di.cbSize = sizeof(DOCINFO);
  di.lpszDocName = szDocName;
  di.lpszOutput = NULL;
  di.lpszDatatype = NULL;
  di.fwType = 0;

  dc.StartDoc(&di); //通知打印机驱动程序执行一新的打印任务
  dc.StartPage();//通知打印机驱动程序打印新页
  dc.SetWindowExt(1440,1440);
  dc.SetViewportExt( dc.GetDeviceCaps(LOGPIXELSX),//返回设备的.以毫米为单位的物理显示宽度
         dc.GetDeviceCaps(LOGPIXELSX));//返回设备的.以毫米为单位的物理显示高度 )
  //X方向向右增加,Y方向向上增加
  CRect rectPrint(0, 0,
   dc.GetDeviceCaps(HORZRES),//返回设备的.以毫米为单位的物理显示宽度
   dc.GetDeviceCaps(VERTRES));//返回设备的.以毫米为单位的物理显示高度  
  dc.DPtoLP(&rectPrint);//设备物理单位转化为逻辑单位
  dc.SetWindowOrg(0, 0);//设置原点

  CFont font;
  VERIFY(font.CreatePointFont(120,
   "宋体",//"Arial",
   &dc));//为DC创建字体
  CFont* def_font = dc.SelectObject(&font);//保存现在的字体
  dc.SetTextAlign(TA_TOP|TA_LEFT);
  
  int index;
  CSize size;
  int x = 1;
  int y = 10;//3800;//注意原点位置和坐标增加的方向
  int y0 = y;
  size = dc.GetTextExtent("00", 2);//计算使用当前字体输出时文本所占大小、宽 度
 
  // #CR##LF#
  #define CRLF "#CR##LF#"
  CString s = "";
  CString szpBuf((char *)pBuf, iLen); //要打印的字符串(注意有长度的限制)

//  TfxWriteToFile("c://prr_log.txt", (LPCSTR)szpBuf);
  
  szpBuf += CRLF;
  szpBuf += "/n";
  
  //0a0d
  char achSep[4] = {0x0a, 0x0d};
  CString strSep = CString(achSep);

  while((index = szpBuf.Find(strSep)) != -1)
  {
   s += szpBuf.Left(index);
   s += CRLF;
   szpBuf = szpBuf.Mid(index+strlen(strSep));
  }
  s +=szpBuf;

  CString ss;

  while((index = s.Find(CRLF)) != -1)
  {
   ss = s.Left(index);
   if(ss.Find("/r") != -1)//输入的字符串有回车符
    ss = s.Left(index-1);
   int t = 0;
   if(((t = ss.Find("#CPI")) != -1)||((t = ss.Find("#cpi")) != -1))//输入的字符串有回车符
   {
    CString s1 = ss.Left(t);
    CString s2 = ss.Mid(t+6);
    ss = s1 + s2;
   }
   s = s.Mid(index+strlen(CRLF));//取剩余的字符串 // AfxMessageBox("A"+ss+"A");
   /*
   if((index = s.Find("#CR##LF##CR##LF##CR##LF##CR##LF##CR##LF#")) != -1)
   {
    //s = s.Mid(index+strlen("#CR##LF##CR##LF##CR##LF##CR##LF#"));//取剩余的字符串
    string szBitmapName = "C://SSPC//receipt.bmp";
    CDC memdc;
    BITMAP bm;
    memdc.CreateCompatibleDC( NULL );
    HBITMAP pbitmap = (HBITMAP)LoadImage(NULL, szBitmapName.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION|LR_DEFAULTSIZE);
    memdc.SelectObject(pbitmap); 
    GetObject(pbitmap, sizeof(bm), &bm);
    dc.StretchBlt(0, 0, bm.bmWidth, bm.bmHeight, &memdc, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
    memdc.DeleteDC();
    DeleteObject(pbitmap);

   }
   */
   int offset = 0;
   if(ss.Find("Seq No") != -1)
    ss = "";
   else if((ss.Find("CWD")!=-1)||(ss.Find("PIN")!=-1))
   {
    offset = 5*size.cx;
    ss = ss.Mid(4);
   }
   else if(ss.Find("TFR")!=-1)
   {
//    CString strICBC = "转入帐号";
//    dc.TextOut(300, y0+size.cy*18, strICBC);//打印到缓冲区
    offset = 5*size.cx;
    ss = ss.Mid(4);
   }
    
   dc.TextOut(x+offset, y, ss);//打印到缓冲区

   string ls_text="";
   ls_text = "x:"+ TfxIntToStr(x+offset) + ", y:" + TfxIntToStr(y) + ">>>";
   ls_text += (LPCSTR)ss;

  // TfxWriteToFile("c://prr_prn_log.txt", ls_text);
    
   y += size.cy;
  }

  dc.SelectObject(def_font); //恢复原来的字体
  font.DeleteObject();
  dc.EndPage(); //通知打印机驱动程序页结束
  dc.EndDoc();//通知打印机驱动程序打印完毕
  DeleteDC(dc.Detach());
 }
 

 return 0; 

}

#pragma once #include <vector> #include <opencv2/opencv.hpp> #include <tesseract/baseapi.h> #include <leptonica/allheaders.h> #include <thread> #include <atomic> #include <mutex> #include <string> #include <algorithm> #include "afxwin.h" #include <shlwapi.h> // 包含 StrCmpLogicalW 函数 #include <atlimage.h> // 添加CImage头文件 #include <tesseract/baseapi.h> #include <leptonica/allheaders.h> #include <iostream> #include <Windows.h> // 用于获取屏幕分辨率 using namespace cv; using namespace std; #pragma comment(lib, "Shlwapi.lib") // 链接 StrCmpLogicalW 所需的库 //#pragma comment(lib, "atlimage.lib") // 链接CImage库 #define WM_UPDATE_DISPLAY_IMAGE (WM_USER + 2) // B2saomiaotuxiangchuangkou 对话框 class B2saomiaotuxiangchuangkou : public CDialogEx { DECLARE_DYNAMIC(B2saomiaotuxiangchuangkou) public: B2saomiaotuxiangchuangkou(CWnd* pParent = NULL); // 标准构造函数 virtual ~B2saomiaotuxiangchuangkou(); enum { WM_LOAD_FIRST_IMAGE = WM_USER + 1 }; // 自定义控件布局调整函数(类似 SetControlPosition) void SetControlPosition(CWnd* pCtrl, int x, int y, int width, int height); void AdjustLayoutWhenResized(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_B2saomiaotuxiangchuangkou }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); virtual BOOL OnInitDialog(); afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg void OnBnClickedButtonSelectFolder(); afx_msg void OnBnClickedButtonStart(); afx_msg void OnBnClickedButtonPause(); afx_msg void OnBnClickedButtonStop(); afx_msg void OnBnClickedButtonReset(); afx_msg void OnCbnSelchangeComboSaveOption(); afx_msg void OnTimer(UINT_PTR nIDEvent); afx_msg void OnLvnItemchangedListImages(NMHDR* pNMHDR, LRESULT* pResult); afx_msg LRESULT OnLoadFirstImageMessage(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnUpdateDisplayImage(WPARAM wParam, LPARAM lParam); afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); afx_msg BOOL OnEraseBkgnd(CDC* pDC); DECLARE_MESSAGE_MAP() private: void ProcessImages(); void ProcessImage(const CString& filePath); bool LoadImageToControl(const CString& filePath, UINT controlID); HBITMAP LoadImageWithOpenCV(const CString& filePath); HBITMAP LoadImageWithGDIPlus(const CString& filePath); HBITMAP LoadImageWithCImage(const CString& filePath); // 新增CImage加载函数 HBITMAP MatToBitmap(cv::Mat& mat, UINT controlID); // 新增函数 void UpdateProgressBars(); void Cleanup(); void CreateBackup(); void RestoreBackup(); void SaveImage(cv::Mat& img, const CString& filePath); /*void SimpleBinarization(cv::Mat& input, cv::Mat& output);*/ void DisplayFirstImage(); CString GetFileNameFromPath(const CString& filePath); void LoadFilesIntoListControl(); void UpdateDisplayImage(const CString& filePath); private: CEdit m_editFolderPath; CListCtrl m_listImages; CComboBox m_saveOption; CButton m_btnStart; CButton m_btnPause; CButton m_btnStop; CButton m_btnReset; CButton m_btnSelectFolder; CProgressCtrl m_totalProgress; CProgressCtrl m_fileProgress; CStatic m_picOriginal; CStatic m_picProcessed; CEdit m_editSavePath; std::vector<CString> m_imageFiles; CString m_folderPath; CString m_savePath; CString m_backupPath; std::atomic<bool> m_processing{ false }; std::atomic<bool> m_paused{ false }; std::atomic<bool> m_stopped{ false }; std::atomic<int> m_currentIndex{ 0 }; std::thread m_processingThread; std::mutex m_mutex; std::mutex m_imageMutex; std::mutex m_bitmapMutex; bool m_firstImageLoaded{ false }; bool m_firstLoadCompleted{ false }; bool m_imageLoaded{ false }; HBITMAP m_currentBitmap{ NULL }; enum ImageLoadMode { MODE_OPENCV, MODE_GDIPlus, MODE_CIMAGE }; // 新增CIMAGE模式 ImageLoadMode m_loadMode{ MODE_CIMAGE }; // 默认使用CImage加载 CRect m_originalRect; CRect m_processedRect; private: // 新增OCR相关函数 cv::Mat rotateImage(const cv::Mat& src, double angle); cv::Mat resizeToFitScreen(const cv::Mat& img); void PerformOCRAndRotation(const CString& imagePath, cv::Mat& inputImage, cv::Mat& outputImage); // 新增Tesseract实例 tesseract::TessBaseAPI m_tess; bool m_tessInitialized{ false }; };// B2saomiaotuxiangchuangkou.cpp : 实现文件 // #include "stdafx.h" #include "Imageprocessing.h" #include "B2saomiaotuxiangchuangkou.h" #include "afxdialogex.h" #include <string> #include <chrono> #include <Gdiplus.h> #include <shlwapi.h> #pragma comment(lib, "Gdiplus.lib") #pragma comment(lib, "shlwapi.lib") #pragma comment(lib, "Shell32.lib") // GDI+初始化全局变量 Gdiplus::GdiplusStartupInput gdiplusInput; ULONG_PTR gdiplusToken; IMPLEMENT_DYNAMIC(B2saomiaotuxiangchuangkou, CDialogEx) B2saomiaotuxiangchuangkou::B2saomiaotuxiangchuangkou(CWnd* pParent /*=NULL*/) : CDialogEx(IDD_B2saomiaotuxiangchuangkou, pParent) { } B2saomiaotuxiangchuangkou::~B2saomiaotuxiangchuangkou() { Cleanup(); Gdiplus::GdiplusShutdown(gdiplusToken); // 释放GDI+资源 // 释放Tesseract资源 if (m_tessInitialized) { m_tess.End(); } } void B2saomiaotuxiangchuangkou::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_EDIT_FOLDER_PATH, m_editFolderPath); DDX_Control(pDX, IDC_LIST_IMAGES, m_listImages); DDX_Control(pDX, IDC_COMBO_SAVE_OPTION, m_saveOption); DDX_Control(pDX, IDC_BUTTON_START, m_btnStart); DDX_Control(pDX, IDC_BUTTON_PAUSE, m_btnPause); DDX_Control(pDX, IDC_BUTTON_STOP, m_btnStop); DDX_Control(pDX, IDC_BUTTON_RESET, m_btnReset); DDX_Control(pDX, IDC_BUTTON_SELECT_FOLDER, m_btnSelectFolder); DDX_Control(pDX, IDC_PROGRESS_TOTAL, m_totalProgress); DDX_Control(pDX, IDC_PROGRESS_FILE, m_fileProgress); DDX_Control(pDX, IDC_STATIC_ORIGINAL_IMAGE, m_picOriginal); DDX_Control(pDX, IDC_STATIC_PROCESSED_IMAGE, m_picProcessed); DDX_Control(pDX, IDC_EDIT2, m_editSavePath); } BEGIN_MESSAGE_MAP(B2saomiaotuxiangchuangkou, CDialogEx) ON_WM_SIZE() ON_BN_CLICKED(IDC_BUTTON_SELECT_FOLDER, &B2saomiaotuxiangchuangkou::OnBnClickedButtonSelectFolder) ON_BN_CLICKED(IDC_BUTTON_START, &B2saomiaotuxiangchuangkou::OnBnClickedButtonStart) ON_BN_CLICKED(IDC_BUTTON_PAUSE, &B2saomiaotuxiangchuangkou::OnBnClickedButtonPause) ON_BN_CLICKED(IDC_BUTTON_STOP, &B2saomiaotuxiangchuangkou::OnBnClickedButtonStop) ON_BN_CLICKED(IDC_BUTTON_RESET, &B2saomiaotuxiangchuangkou::OnBnClickedButtonReset) ON_CBN_SELCHANGE(IDC_COMBO_SAVE_OPTION, &B2saomiaotuxiangchuangkou::OnCbnSelchangeComboSaveOption) ON_WM_TIMER() ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_IMAGES, &B2saomiaotuxiangchuangkou::OnLvnItemchangedListImages) ON_MESSAGE(WM_LOAD_FIRST_IMAGE, &B2saomiaotuxiangchuangkou::OnLoadFirstImageMessage) ON_MESSAGE(WM_UPDATE_DISPLAY_IMAGE, &B2saomiaotuxiangchuangkou::OnUpdateDisplayImage) ON_WM_MOUSEWHEEL() ON_WM_ERASEBKGND() END_MESSAGE_MAP() BOOL B2saomiaotuxiangchuangkou::OnInitDialog() { CDialogEx::OnInitDialog(); ////////////////////////////////////////////////////////////////////////////////////////////// // 初始化Tesseract OCR if (m_tess.Init(nullptr, "chi_sim")) { AfxMessageBox(_T("无法初始化Tesseract OCR引擎!")); m_tessInitialized = false; } else { m_tessInitialized = true; } ////////////////////////////////////////////////////////////////////////////////////////////// m_listImages.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); m_listImages.InsertColumn(0, _T("文件名"), LVCFMT_LEFT, 200); m_listImages.InsertColumn(1, _T("大小"), LVCFMT_RIGHT, 100); m_listImages.InsertColumn(2, _T("类型"), LVCFMT_LEFT, 100); m_saveOption.AddString(_T("覆盖原图")); m_saveOption.AddString(_T("新路径")); m_saveOption.SetCurSel(0); m_totalProgress.SetRange(0, 100); m_fileProgress.SetRange(0, 100); m_totalProgress.SetPos(0); m_fileProgress.SetPos(0); m_btnPause.EnableWindow(FALSE); m_btnStop.EnableWindow(FALSE); m_btnReset.EnableWindow(FALSE); m_picOriginal.ModifyStyle(0, SS_BITMAP); m_picProcessed.ModifyStyle(0, SS_BITMAP); m_picOriginal.GetWindowRect(&m_originalRect); ScreenToClient(&m_originalRect); m_picProcessed.GetWindowRect(&m_processedRect); ScreenToClient(&m_processedRect); // 初始化GDI+ Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusInput, NULL); if (!m_folderPath.IsEmpty()) { PostMessage(WM_LOAD_FIRST_IMAGE, 0, 0); } return TRUE; } void B2saomiaotuxiangchuangkou::SetControlPosition(CWnd* pCtrl, int x, int y, int width, int height) { if (pCtrl) { CRect rect(x, y, x + width, y + height); pCtrl->MoveWindow(rect); } } void B2saomiaotuxiangchuangkou::AdjustLayoutWhenResized() { CRect clientRect; GetClientRect(&clientRect); int cx = clientRect.Width(); int cy = clientRect.Height(); // 获取屏幕尺寸(可选,若需要基于屏幕比例布局) int sW = GetSystemMetrics(SM_CXSCREEN); int sH = GetSystemMetrics(SM_CYSCREEN); int pc_W1 = (cx - 530) / 3;//图像显示控件宽 int pc_H1;//图像显示控件高 int pc_W2 = (cy - 530) / 3 * 2;//图像显示控件宽 int pc_H2;//图像显示控件高 // 示例:调整子界面中控件的位置(类似你提供的代码) SetControlPosition(GetDlgItem(IDC_STATIC1), 0, 5, 80, 30); /**/ SetControlPosition(GetDlgItem(IDC_STATIC2), 0, 50, 80, 30); /**/SetControlPosition(GetDlgItem(IDC_EDIT_FOLDER_PATH), 85, 43, 280, 30); /**/SetControlPosition(GetDlgItem(IDC_BUTTON_SELECT_FOLDER), 366, 44, 70, 30); /**/SetControlPosition(GetDlgItem(IDC_BUTTON_START), 440, 43, 70, 30);/**/SetControlPosition(GetDlgItem(IDC_STATIC_ORIGINAL_IMAGE), 530, 5, pc_W1 - 1, 490);/**/SetControlPosition(GetDlgItem(IDC_STATIC_PROCESSED_IMAGE), 530 + pc_W1 + 2, 5, 2 * pc_W1 - 5, cy - 10); SetControlPosition(GetDlgItem(IDC_STATIC3), 0, 90, 80, 30); /**/SetControlPosition(GetDlgItem(IDC_EDIT2), 85, 83, 280, 30); /**/SetControlPosition(GetDlgItem(IDC_COMBO_SAVE_OPTION), 366, 89, 70, 30); /**/SetControlPosition(GetDlgItem(IDC_BUTTON_PAUSE), 440, 84, 70, 30); SetControlPosition(GetDlgItem(IDC_STATIC4), 0, 155, 80, 30); /**/ /**/SetControlPosition(GetDlgItem(IDC_BUTTON_STOP), 440, 124, 70, 30); SetControlPosition(GetDlgItem(IDC_STATIC6), 0, 200, 80, 30); /**/SetControlPosition(GetDlgItem(IDC_PROGRESS_TOTAL), 85, 197, 280, 20); /**/SetControlPosition(GetDlgItem(IDC_BUTTON_RESET), 440, 164, 70, 30); SetControlPosition(GetDlgItem(IDC_STATIC7), 0, 240, 80, 30); /**/SetControlPosition(GetDlgItem(IDC_PROGRESS_FILE), 85, 237, 180, 20); SetControlPosition(GetDlgItem(IDC_STATIC8), 0, 305, 80, 30); /**/ /*SetControlPosition(GetDlgItem(IDC_STATIC8), 0, 305, 80, 30); /**/ /*SetControlPosition(GetDlgItem(IDC_STATIC8), 0, 305, 80, 30);*/ /**/ SetControlPosition(GetDlgItem(IDC_LIST_IMAGES), 0, 500, 530 + pc_W1 - 1, cy - 500 - 5); /**/ } void B2saomiaotuxiangchuangkou::OnSize(UINT nType, int cx, int cy) { CDialogEx::OnSize(nType, cx, cy); if (nType != SIZE_MINIMIZED) { AdjustLayoutWhenResized(); } } void B2saomiaotuxiangchuangkou::LoadFilesIntoListControl() { m_listImages.DeleteAllItems(); m_imageFiles.clear(); m_imageLoaded = false; m_firstLoadCompleted = false; std::vector<CString> files; CString searchPath = m_folderPath + _T("\\*.*"); WIN32_FIND_DATA findFileData; HANDLE hFind = FindFirstFile(searchPath, &findFileData); if (hFind != INVALID_HANDLE_VALUE) { do { if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { CString fileName = findFileData.cFileName; CString ext = fileName.Right(4).MakeLower(); // 扩展支持的图像格式 if (ext == _T(".jpg") || ext == _T(".jpeg") || ext == _T(".png") || ext == _T(".bmp") || ext == _T(".tif") || ext == _T(".tiff") || ext == _T(".webp") || ext == _T(".jp2")) { files.push_back(fileName); } } } while (FindNextFile(hFind, &findFileData)); FindClose(hFind); } std::sort(files.begin(), files.end(), [](const CString& a, const CString& b) { return StrCmpLogicalW(a, b) < 0; }); for (size_t i = 0; i < files.size(); ++i) { CString fileName = files[i]; CString filePath = m_folderPath + _T("\\") + fileName; m_imageFiles.push_back(filePath); int nIndex = m_listImages.InsertItem(i, fileName); WIN32_FILE_ATTRIBUTE_DATA fad; ULONGLONG fileSize = 0; if (GetFileAttributesEx(filePath, GetFileExInfoStandard, &fad)) { fileSize = ((ULONGLONG)fad.nFileSizeHigh << 32) | fad.nFileSizeLow; } CString sizeStr; if (fileSize < 1024) sizeStr.Format(_T("%d B"), fileSize); else if (fileSize < 1024 * 1024) sizeStr.Format(_T("%.2f KB"), fileSize / 1024.0); else sizeStr.Format(_T("%.2f MB"), fileSize / (1024.0 * 1024.0)); m_listImages.SetItemText(nIndex, 1, sizeStr); CString typeStr = fileName.Right(4).Mid(1).MakeUpper(); m_listImages.SetItemText(nIndex, 2, typeStr); } } void B2saomiaotuxiangchuangkou::OnBnClickedButtonSelectFolder() { CFolderPickerDialog dlg; if (dlg.DoModal() == IDOK) { m_folderPath = dlg.GetFolderPath(); m_editFolderPath.SetWindowText(m_folderPath); LoadFilesIntoListControl(); OnCbnSelchangeComboSaveOption(); m_btnStart.EnableWindow(!m_imageFiles.empty()); if (!m_imageFiles.empty()) { m_listImages.SetItemState(0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); m_listImages.EnsureVisible(0, FALSE); m_listImages.UpdateWindow(); PostMessage(WM_LOAD_FIRST_IMAGE, 0, 0); } } } LRESULT B2saomiaotuxiangchuangkou::OnLoadFirstImageMessage(WPARAM wParam, LPARAM lParam) { DisplayFirstImage(); return 0; } void B2saomiaotuxiangchuangkou::DisplayFirstImage() { if (m_imageFiles.empty()) return; CString filePath = m_imageFiles[0]; if (!LoadImageToControl(filePath, IDC_STATIC_ORIGINAL_IMAGE)) { return; } m_firstImageLoaded = true; m_firstLoadCompleted = true; m_imageLoaded = true; CWnd* pProcessed = GetDlgItem(IDC_STATIC_PROCESSED_IMAGE); if (pProcessed) { auto pStatic = reinterpret_cast<CStatic*>(pProcessed); HBITMAP hOldBmp = pStatic->GetBitmap(); if (hOldBmp) { DeleteObject(hOldBmp); pStatic->SetBitmap(NULL); } } } bool B2saomiaotuxiangchuangkou::LoadImageToControl(const CString& filePath, UINT controlID) { std::lock_guard<std::mutex> lock(m_imageMutex); DWORD fileAttr = GetFileAttributes(filePath); if (fileAttr == INVALID_FILE_ATTRIBUTES) { return false; } HBITMAP hBitmap = NULL; if (m_loadMode == MODE_CIMAGE) { hBitmap = LoadImageWithCImage(filePath); if (!hBitmap) { // CImage加载失败时,切换到OpenCV并重试 m_loadMode = MODE_OPENCV; hBitmap = LoadImageWithOpenCV(filePath); if (!hBitmap) { // OpenCV加载失败时,切换到GDI+ m_loadMode = MODE_GDIPlus; hBitmap = LoadImageWithGDIPlus(filePath); } } } else if (m_loadMode == MODE_OPENCV) { hBitmap = LoadImageWithOpenCV(filePath); if (!hBitmap) { m_loadMode = MODE_GDIPlus; hBitmap = LoadImageWithGDIPlus(filePath); if (!hBitmap) { m_loadMode = MODE_CIMAGE; hBitmap = LoadImageWithCImage(filePath); } } } else { hBitmap = LoadImageWithGDIPlus(filePath); if (!hBitmap) { m_loadMode = MODE_OPENCV; hBitmap = LoadImageWithOpenCV(filePath); if (!hBitmap) { m_loadMode = MODE_CIMAGE; hBitmap = LoadImageWithCImage(filePath); } } } if (!hBitmap) { return false; } CWnd* pWnd = GetDlgItem(controlID); if (pWnd) { CStatic* pStatic = dynamic_cast<CStatic*>(pWnd); if (pStatic) { HBITMAP oldBmp = pStatic->GetBitmap(); if (oldBmp) DeleteObject(oldBmp); pStatic->SetBitmap(hBitmap); pStatic->Invalidate(); return true; } } DeleteObject(hBitmap); return false; } // 使用CImage加载图像并保持比例 HBITMAP B2saomiaotuxiangchuangkou::LoadImageWithCImage(const CString& filePath) { CImage image; if (image.Load(filePath) != S_OK) { return NULL; } CRect rect; m_picOriginal.GetClientRect(&rect); int ctrlWidth = rect.Width(); int ctrlHeight = rect.Height(); int srcWidth = image.GetWidth(); int srcHeight = image.GetHeight(); // 计算等比例缩放尺寸,确保不超出控件范围 double ratioX = static_cast<double>(ctrlWidth) / srcWidth; double ratioY = static_cast<double>(ctrlHeight) / srcHeight; double ratio = std::min(ratioX, ratioY); // 取较小比例保持原始比例 int drawWidth = static_cast<int>(srcWidth * ratio); int drawHeight = static_cast<int>(srcHeight * ratio); int x = (ctrlWidth - drawWidth) / 2; int y = (ctrlHeight - drawHeight) / 2; // 创建兼容位图 HDC hdcScreen = ::GetDC(NULL); HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, ctrlWidth, ctrlHeight); HDC hdcMem = CreateCompatibleDC(hdcScreen); HBITMAP hOldBmp = (HBITMAP)SelectObject(hdcMem, hBitmap); // 填充背景为白色 HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255)); RECT rc = { 0, 0, ctrlWidth, ctrlHeight }; FillRect(hdcMem, &rc, hBrush); DeleteObject(hBrush); // 使用GDI+高质量插值缩放图像 HDC hdcImage = image.GetDC(); // 设置高质量拉伸模式 SetStretchBltMode(hdcMem, HALFTONE); // 执行高质量缩放 StretchBlt( hdcMem, x, y, drawWidth, drawHeight, hdcImage, 0, 0, srcWidth, srcHeight, SRCCOPY ); // 释放CImage的设备上下文 image.ReleaseDC(); // 恢复设备上下文并释放资源 SelectObject(hdcMem, hOldBmp); DeleteDC(hdcMem); ::ReleaseDC(NULL, hdcScreen); return hBitmap; } HBITMAP B2saomiaotuxiangchuangkou::LoadImageWithOpenCV(const CString& filePath) { cv::Mat img; #ifdef _UNICODE int len = WideCharToMultiByte(CP_UTF8, 0, filePath, -1, NULL, 0, NULL, NULL); std::vector<char> utf8Buf(len); WideCharToMultiByte(CP_UTF8, 0, filePath, -1, utf8Buf.data(), len, NULL, NULL); img = cv::imread(utf8Buf.data(), cv::IMREAD_COLOR); #else img = cv::imread(CW2A(filePath), cv::IMREAD_COLOR); #endif if (img.empty()) { return NULL; } cv::cvtColor(img, img, cv::COLOR_BGR2RGB); CRect rect; m_picOriginal.GetClientRect(&rect); int dstWidth = rect.Width(); int dstHeight = rect.Height(); double ratio = std::min(static_cast<double>(dstWidth) / img.cols, static_cast<double>(dstHeight) / img.rows); int drawWidth = static_cast<int>(img.cols * ratio); int drawHeight = static_cast<int>(img.rows * ratio); int x = (dstWidth - drawWidth) / 2; int y = (dstHeight - drawHeight) / 2; HDC hdcScreen = ::GetDC(NULL); HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, dstWidth, dstHeight); HDC hdcMem = CreateCompatibleDC(hdcScreen); HBITMAP hOldBmp = (HBITMAP)SelectObject(hdcMem, hBitmap); HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255)); RECT rc = { 0, 0, dstWidth, dstHeight }; FillRect(hdcMem, &rc, hBrush); DeleteObject(hBrush); cv::Mat resized; // 根据缩放比例选择插值算法(放大用双三次,缩小用区域插值) int interpolation = (ratio >= 1.0) ? cv::INTER_CUBIC : cv::INTER_AREA; cv::resize(img, resized, cv::Size(drawWidth, drawHeight), 0, 0, interpolation); BITMAPINFOHEADER bmiHeader = { 0 }; bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmiHeader.biWidth = resized.cols; bmiHeader.biHeight = -resized.rows; bmiHeader.biPlanes = 1; bmiHeader.biBitCount = 24; bmiHeader.biCompression = BI_RGB; SetStretchBltMode(hdcMem, HALFTONE); StretchDIBits( hdcMem, x, y, drawWidth, drawHeight, 0, 0, resized.cols, resized.rows, resized.data, (BITMAPINFO*)&bmiHeader, DIB_RGB_COLORS, SRCCOPY ); SelectObject(hdcMem, hOldBmp); DeleteDC(hdcMem); ::ReleaseDC(NULL, hdcScreen); return hBitmap; } HBITMAP B2saomiaotuxiangchuangkou::LoadImageWithGDIPlus(const CString& filePath) { Gdiplus::Image image(filePath); if (image.GetLastStatus() != Gdiplus::Ok) { return NULL; } CRect rect; m_picOriginal.GetClientRect(&rect); int ctrlWidth = rect.Width(); int ctrlHeight = rect.Height(); int srcWidth = image.GetWidth(); int srcHeight = image.GetHeight(); double scale = std::min(static_cast<double>(ctrlWidth) / srcWidth, static_cast<double>(ctrlHeight) / srcHeight); int drawWidth = static_cast<int>(srcWidth * scale); int drawHeight = static_cast<int>(srcHeight * scale); int x = (ctrlWidth - drawWidth) / 2; int y = (ctrlHeight - drawHeight) / 2; Gdiplus::Bitmap bitmap(ctrlWidth, ctrlHeight, PixelFormat32bppARGB); Gdiplus::Graphics graphics(&bitmap); // 设置最高质量渲染参数 graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality); graphics.SetPixelOffsetMode(Gdiplus::PixelOffsetModeHighQuality); graphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic); graphics.Clear(Gdiplus::Color(255, 255, 255)); graphics.DrawImage(&image, x, y, drawWidth, drawHeight); HBITMAP hBitmap = NULL; bitmap.GetHBITMAP(Gdiplus::Color::White, &hBitmap); return hBitmap; } // 新增函数:将OpenCV Mat转换为HBITMAP并适配控件 HBITMAP B2saomiaotuxiangchuangkou::MatToBitmap(cv::Mat& mat, UINT controlID) { if (mat.empty()) return NULL; // 确保是3通道RGB图像 if (mat.channels() == 1) { cv::cvtColor(mat, mat, cv::COLOR_GRAY2BGR); } else if (mat.channels() == 4) { cv::cvtColor(mat, mat, cv::COLOR_BGRA2BGR); } CRect rect; CWnd* pWnd = GetDlgItem(controlID); if (!pWnd) return NULL; pWnd->GetClientRect(&rect); int dstWidth = rect.Width(); int dstHeight = rect.Height(); double ratio = std::min(static_cast<double>(dstWidth) / mat.cols, static_cast<double>(dstHeight) / mat.rows); int drawWidth = static_cast<int>(mat.cols * ratio); int drawHeight = static_cast<int>(mat.rows * ratio); int x = (dstWidth - drawWidth) / 2; int y = (dstHeight - drawHeight) / 2; cv::Mat resized; // 根据缩放比例选择插值算法 int interpolation = (ratio >= 1.0) ? cv::INTER_CUBIC : cv::INTER_AREA; cv::resize(mat, resized, cv::Size(drawWidth, drawHeight), ratio, ratio, interpolation); HDC hdcScreen = ::GetDC(NULL); HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, dstWidth, dstHeight); HDC hdcMem = CreateCompatibleDC(hdcScreen); HBITMAP hOldBmp = (HBITMAP)SelectObject(hdcMem, hBitmap); HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255)); RECT rc = { 0, 0, dstWidth, dstHeight }; FillRect(hdcMem, &rc, hBrush); DeleteObject(hBrush); BITMAPINFOHEADER bmiHeader = { 0 }; bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmiHeader.biWidth = resized.cols; bmiHeader.biHeight = -resized.rows; bmiHeader.biPlanes = 1; bmiHeader.biBitCount = 24; bmiHeader.biCompression = BI_RGB; SetStretchBltMode(hdcMem, HALFTONE); StretchDIBits( hdcMem, x, y, drawWidth, drawHeight, 0, 0, resized.cols, resized.rows, resized.data, (BITMAPINFO*)&bmiHeader, DIB_RGB_COLORS, SRCCOPY ); SelectObject(hdcMem, hOldBmp); DeleteDC(hdcMem); ::ReleaseDC(NULL, hdcScreen); return hBitmap; } BOOL B2saomiaotuxiangchuangkou::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) { return TRUE; } BOOL B2saomiaotuxiangchuangkou::OnEraseBkgnd(CDC* pDC) { CRect rect; GetClientRect(&rect); CBrush brush(RGB(255, 255, 255)); pDC->FillRect(&rect, &brush); return TRUE; } void B2saomiaotuxiangchuangkou::OnCbnSelchangeComboSaveOption() { int option = m_saveOption.GetCurSel(); if (option == 0) { m_savePath = m_folderPath; m_editSavePath.SetWindowText(m_savePath); } else if (option == 1) { if (m_savePath.IsEmpty() || m_savePath == m_folderPath) { CFolderPickerDialog dlg; if (dlg.DoModal() == IDOK) { m_savePath = dlg.GetFolderPath(); m_editSavePath.SetWindowText(m_savePath); } else { m_saveOption.SetCurSel(0); m_savePath = m_folderPath; m_editSavePath.SetWindowText(m_savePath); } } } } void B2saomiaotuxiangchuangkou::OnBnClickedButtonStart() { if (m_folderPath.IsEmpty()) { AfxMessageBox(_T("请先选择文件夹!")); return; } if (m_imageFiles.empty()) { AfxMessageBox(_T("文件夹中没有图像文件!")); return; } if (m_saveOption.GetCurSel() == 1 && m_savePath.IsEmpty()) { AfxMessageBox(_T("请先选择保存路径!")); return; } if (m_saveOption.GetCurSel() == 0) { CreateBackup(); } m_processing = true; m_paused = false; m_stopped = false; m_currentIndex = 0; m_btnStart.EnableWindow(FALSE); m_btnPause.EnableWindow(TRUE); m_btnStop.EnableWindow(TRUE); m_btnReset.EnableWindow(FALSE); m_totalProgress.SetPos(0); m_fileProgress.SetPos(0); if (m_processingThread.joinable()) { m_processingThread.join(); } m_processingThread = std::thread(&B2saomiaotuxiangchuangkou::ProcessImages, this); SetTimer(1, 100, NULL); } void B2saomiaotuxiangchuangkou::OnBnClickedButtonPause() { m_paused = !m_paused; m_btnPause.SetWindowText(m_paused ? _T("继续") : _T("暂停")); } void B2saomiaotuxiangchuangkou::OnBnClickedButtonStop() { m_stopped = true; m_processing = false; m_btnStart.EnableWindow(TRUE); m_btnPause.EnableWindow(FALSE); m_btnStop.EnableWindow(FALSE); m_btnReset.EnableWindow(TRUE); if (m_processingThread.joinable()) { m_processingThread.join(); } KillTimer(1); } void B2saomiaotuxiangchuangkou::OnBnClickedButtonReset() { OnBnClickedButtonStop(); if (m_saveOption.GetCurSel() == 0 && !m_backupPath.IsEmpty()) { RestoreBackup(); } m_currentIndex = 0; m_totalProgress.SetPos(0); m_fileProgress.SetPos(0); m_backupPath.Empty(); m_btnReset.EnableWindow(FALSE); if (!m_imageFiles.empty()) { m_listImages.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED); PostMessage(WM_LOAD_FIRST_IMAGE, 0, 0); } } void B2saomiaotuxiangchuangkou::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == 1) { UpdateProgressBars(); } CDialogEx::OnTimer(nIDEvent); } void B2saomiaotuxiangchuangkou::OnLvnItemchangedListImages(NMHDR* pNMHDR, LRESULT* pResult) { LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR); int nItem = -1; if (pNMLV) { if ((pNMLV->uChanged & LVIF_STATE) && (pNMLV->uNewState & LVIS_SELECTED)) { nItem = pNMLV->iItem; } } else { nItem = 0; } if (!m_firstLoadCompleted && nItem == 0) { if (pResult) *pResult = 0; return; } // 当用户手动选择时,更新原图并清除处理后的图像 if (nItem >= 0 && nItem < (int)m_imageFiles.size() && !m_processing) { CString filePath = m_imageFiles[nItem]; LoadImageToControl(filePath, IDC_STATIC_ORIGINAL_IMAGE); // 清除处理后的图像 CWnd* pProcessed = GetDlgItem(IDC_STATIC_PROCESSED_IMAGE); if (pProcessed) { auto pStatic = reinterpret_cast<CStatic*>(pProcessed); HBITMAP hOldBmp = pStatic->SetBitmap(NULL); if (hOldBmp) DeleteObject(hOldBmp); } } if (pResult) { *pResult = 0; } } void B2saomiaotuxiangchuangkou::ProcessImages() { for (m_currentIndex = 0; m_currentIndex < (int)m_imageFiles.size(); ++m_currentIndex) { if (m_stopped) break; while (m_paused && !m_stopped) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } if (m_stopped) break; ProcessImage(m_imageFiles[m_currentIndex]); { std::lock_guard<std::mutex> lock(m_mutex); int totalProgress = static_cast<int>((static_cast<double>(m_currentIndex + 1) / m_imageFiles.size()) * 100); m_totalProgress.SetPos(totalProgress); m_fileProgress.SetPos(100); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } m_processing = false; m_stopped = true; PostMessage(WM_COMMAND, MAKEWPARAM(IDC_BUTTON_STOP, BN_CLICKED), (LPARAM)m_btnStop.m_hWnd); AfxMessageBox(_T("处理完成!")); } void B2saomiaotuxiangchuangkou::ProcessImage(const CString& filePath) { CT2CA filePathConverted(filePath); std::string filePathStr(filePathConverted); cv::Mat image = cv::imread(filePathStr); if (image.empty()) return; // 更新显示原图 UpdateDisplayImage(filePath); cv::Mat processed; // ====== 调用OCR方向校正函数 ====== // 创建临时副本用于OCR处理 cv::Mat imageCopy = image.clone(); PerformOCRAndRotation(filePath, imageCopy, processed); // ====== END OCR调用 ====== // 直接显示处理后的图像,不使用临时文件 HBITMAP hProcessedBmp = MatToBitmap(processed, IDC_STATIC_PROCESSED_IMAGE); if (hProcessedBmp) { CWnd* pProcessed = GetDlgItem(IDC_STATIC_PROCESSED_IMAGE); if (pProcessed) { auto pStatic = reinterpret_cast<CStatic*>(pProcessed); HBITMAP hOldBmp = pStatic->SetBitmap(NULL); if (hOldBmp) DeleteObject(hOldBmp); pStatic->SetBitmap(hProcessedBmp); pStatic->Invalidate(); } } // 保存处理后的图像 if (m_saveOption.GetCurSel() == 0) { SaveImage(processed, filePath); } else { CString fileName = GetFileNameFromPath(filePath); CString newPath = m_savePath + _T("\\") + fileName; SaveImage(processed, newPath); } // 更新文件处理进度 for (int progress = 0; progress <= 100; progress += 10) { if (m_stopped) return; while (m_paused && !m_stopped) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } if (m_stopped) return; { std::lock_guard<std::mutex> lock(m_mutex); m_fileProgress.SetPos(progress); } std::this_thread::sleep_for(std::chrono::milliseconds(50)); } } void B2saomiaotuxiangchuangkou::UpdateDisplayImage(const CString& filePath) { PostMessage(WM_UPDATE_DISPLAY_IMAGE, (WPARAM)(LPCTSTR)filePath, 0); } LRESULT B2saomiaotuxiangchuangkou::OnUpdateDisplayImage(WPARAM wParam, LPARAM lParam) { CString filePath = (LPCTSTR)wParam; if (filePath.IsEmpty()) return 0; // 只在处理过程中更新原图显示 if (m_processing && !m_paused && !m_stopped) { LoadImageToControl(filePath, IDC_STATIC_ORIGINAL_IMAGE); } return 0; } Mat B2saomiaotuxiangchuangkou::rotateImage(const Mat& src, double angle) { if (src.empty()) return Mat(); Point2f center(static_cast<float>(src.cols / 2.0), static_cast<float>(src.rows / 2.0)); Mat rotMat = getRotationMatrix2D(center, angle, 1.0); Rect2f bbox = RotatedRect(center, Size2f(src.size()), angle).boundingRect2f(); rotMat.at<double>(0, 2) += bbox.width / 2.0 - center.x; rotMat.at<double>(1, 2) += bbox.height / 2.0 - center.y; Mat dst; warpAffine(src, dst, rotMat, Size(static_cast<int>(bbox.width), static_cast<int>(bbox.height)), INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0)); return dst; } // 调整图像大小以适应屏幕 Mat B2saomiaotuxiangchuangkou::resizeToFitScreen(const Mat& img) { if (img.empty()) return Mat(); // 获取屏幕分辨率 int screenWidth = GetSystemMetrics(SM_CXSCREEN); int screenHeight = GetSystemMetrics(SM_CYSCREEN); // 设置最大显示尺寸(留出边缘空间) int maxDisplayWidth = static_cast<int>(screenWidth * 0.8); int maxDisplayHeight = static_cast<int>(screenHeight * 0.8); // 计算缩放比例 double scale = min(static_cast<double>(maxDisplayWidth) / img.cols, static_cast<double>(maxDisplayHeight) / img.rows); // 如果图像已经小于最大尺寸,则不缩放 if (scale >= 1.0) return img.clone(); // 计算新尺寸 int newWidth = static_cast<int>(img.cols * scale); int newHeight = static_cast<int>(img.rows * scale); // 缩放图像 Mat resized; resize(img, resized, Size(newWidth, newHeight), 0, 0, INTER_AREA); return resized; } void B2saomiaotuxiangchuangkou::PerformOCRAndRotation(const CString& imagePath, cv::Mat& inputImage, cv::Mat& outputImage) { // 检查OCR引擎是否初始化 if (!m_tessInitialized) { outputImage = inputImage.clone(); // 返回原始图像 return; } try { // 使用传入的图像 cv::Mat image = inputImage; // 设置图像为灰度图 (Tesseract需要) cv::Mat gray; cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY); // 设置Tesseract图像 m_tess.SetImage(gray.data, gray.cols, gray.rows, 1, gray.step); // 检测方向和脚本 int orient_deg; float orient_conf; const char* script_name; float script_conf; m_tess.DetectOrientationScript(&orient_deg, &orient_conf, &script_name, &script_conf); // 根据检测到的方向角度旋转图像(逆时针旋转) if (orient_deg != 0) { outputImage = rotateImage(image, orient_deg); } else { outputImage = image.clone(); } } catch (...) { // 处理任何异常,返回原始图像 outputImage = inputImage.clone(); } // 重置Tesseract图像 m_tess.Clear(); } void B2saomiaotuxiangchuangkou::SaveImage(cv::Mat& img, const CString& filePath) { if (img.empty()) return; CT2CA filePathConverted(filePath); std::string filePathStr(filePathConverted); std::vector<int> compression_params; CString ext = filePath.Right(4).MakeLower(); if (ext == _T(".jpg") || ext == _T(".jpeg")) { compression_params.push_back(cv::IMWRITE_JPEG_QUALITY); compression_params.push_back(95); } else if (ext == _T(".png")) { compression_params.push_back(cv::IMWRITE_PNG_COMPRESSION); compression_params.push_back(9); } cv::imwrite(filePathStr, img, compression_params); } void B2saomiaotuxiangchuangkou::UpdateProgressBars() { if (m_imageFiles.empty()) return; int totalProgress = static_cast<int>( (static_cast<double>(m_currentIndex) / m_imageFiles.size()) * 100); // 平滑过渡 int currentTotal = m_totalProgress.GetPos(); if (abs(totalProgress - currentTotal) > 5) { m_totalProgress.SetPos(totalProgress); } else if (totalProgress > currentTotal) { m_totalProgress.SetPos(currentTotal + 1); } } void B2saomiaotuxiangchuangkou::CreateBackup() { if (m_folderPath.IsEmpty()) return; CTime time = CTime::GetCurrentTime(); CString timeStr = time.Format(_T("%Y%m%d%H%M%S")); m_backupPath = m_folderPath + _T("\\backup_") + timeStr; if (!CreateDirectory(m_backupPath, NULL)) { return; } for (const auto& file : m_imageFiles) { CString fileName = GetFileNameFromPath(file); CString destPath = m_backupPath + _T("\\") + fileName; CopyFile(file, destPath, FALSE); } } void B2saomiaotuxiangchuangkou::RestoreBackup() { if (m_backupPath.IsEmpty()) return; for (const auto& file : m_imageFiles) { CString fileName = GetFileNameFromPath(file); CString sourcePath = m_backupPath + _T("\\") + fileName; CopyFile(sourcePath, file, FALSE); } RemoveDirectory(m_backupPath); m_backupPath.Empty(); } void B2saomiaotuxiangchuangkou::Cleanup() { m_stopped = true; m_processing = false; if (m_processingThread.joinable()) { m_processingThread.join(); } KillTimer(1); std::lock_guard<std::mutex> lock(m_bitmapMutex); if (m_currentBitmap) { DeleteObject(m_currentBitmap); m_currentBitmap = NULL; } // 释放控件中的位图 HBITMAP hBmp = m_picOriginal.GetBitmap(); if (hBmp) DeleteObject(hBmp); hBmp = m_picProcessed.GetBitmap(); if (hBmp) DeleteObject(hBmp); } CString B2saomiaotuxiangchuangkou::GetFileNameFromPath(const CString& filePath) { int pos = filePath.ReverseFind('\\'); if (pos == -1) pos = filePath.ReverseFind('/'); if (pos != -1) { return filePath.Mid(pos + 1); } return filePath; }中 ,在void B2saomiaotuxiangchuangkou::PerformOCRAndRotation(const CString& imagePath, cv::Mat& inputImage, cv::Mat& outputImage) { // 检查OCR引擎是否初始化 if (!m_tessInitialized) { outputImage = inputImage.clone(); // 返回原始图像 return; } try { // 使用传入的图像 cv::Mat image = inputImage; // 设置图像为灰度图 (Tesseract需要) cv::Mat gray; cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY); // 设置Tesseract图像 m_tess.SetImage(gray.data, gray.cols, gray.rows, 1, gray.step); // 检测方向和脚本 int orient_deg; float orient_conf; const char* script_name; float script_conf; m_tess.DetectOrientationScript(&orient_deg, &orient_conf, &script_name, &script_conf); // 根据检测到的方向角度旋转图像(逆时针旋转) if (orient_deg != 0) { outputImage = rotateImage(image, orient_deg); } else { outputImage = image.clone(); } } catch (...) { // 处理任何异常,返回原始图像 outputImage = inputImage.clone(); } // 重置Tesseract图像 m_tess.Clear(); }函数体中输出的outputImage添加一个判断函数,继续对该输出继续处理,判断函数根据#include <opencv2/opencv.hpp> #include <vector> #include <iostream> #include <numeric> #include <cmath> #include <future> #include <algorithm> using namespace cv; using namespace std; // 计算水平投影的方差 double calculateProjectionVariance(const Mat& binary) { vector<int> projection(binary.rows, 0); // 使用指针遍历加速 for (int y = 0; y < binary.rows; y++) { const uchar* row = binary.ptr<uchar>(y); for (int x = 0; x < binary.cols; x++) { projection[y] += row[x] / 255; // 归一化到0-1 } } // 使用STL算法计算均值和方差 double sum = accumulate(projection.begin(), projection.end(), 0.0); double mean = sum / projection.size(); double variance = 0.0; for_each(projection.begin(), projection.end(), [&](int val) { variance += pow(val - mean, 2); }); variance /= projection.size(); return variance; } // 检测文本倾斜角度 - 优化版本 double detectSkewAngle(const Mat& image) { // 转为灰度图并二值化 Mat gray, binary; cvtColor(image, gray, COLOR_BGR2GRAY); threshold(gray, binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU); // 初始角度搜索范围和步长 double angleStart = -15.0; double angleEnd = 15.0; double angleStep = 2.0; // 粗粒度搜索 double bestAngle = 0.0; // 两次迭代搜索:先粗后精 for (int iter = 0; iter < 2; iter++) { vector<double> angles; for (double angle = angleStart; angle <= angleEnd; angle += angleStep) { angles.push_back(angle); } double maxVariance = 0.0; double currentBestAngle = 0.0; // 多线程并行计算不同角度的投影方差 vector<future<pair<double, double>>> futures; for (double angle : angles) { futures.push_back(async(launch::async, [binary, angle]() { // 获取图像尺寸 int height = binary.rows; int width = binary.cols; // 计算旋转矩阵 Point2f center(width / 2.0, height / 2.0); Mat rot = getRotationMatrix2D(center, angle, 1.0); // 计算旋转后的图像尺寸 Rect bbox = RotatedRect(center, Size2f(width, height), angle).boundingRect(); // 调整旋转矩阵的平移部分 rot.at<double>(0, 2) += bbox.width / 2.0 - center.x; rot.at<double>(1, 2) += bbox.height / 2.0 - center.y; // 旋转图像 (使用更快的插值方法) Mat rotated; warpAffine(binary, rotated, rot, bbox.size(), INTER_LINEAR, BORDER_REPLICATE); // 计算投影方差 double variance = calculateProjectionVariance(rotated); return make_pair(variance, angle); })); } // 收集结果 for (auto& future : futures) { auto result = future.get(); if (result.first > maxVariance) { maxVariance = result.first; currentBestAngle = result.second; } } // 更新下一次迭代的搜索范围 bestAngle = currentBestAngle; angleStart = bestAngle - angleStep; angleEnd = bestAngle + angleStep; angleStep /= 4.0; // 精搜索步长变为原来的1/4 } return bestAngle; } // 校正倾斜图像 - 优化版本 Mat correctSkew(const Mat& image, double angle) { // 获取图像尺寸 int height = image.rows; int width = image.cols; // 计算旋转矩阵 Point2f center(width / 2.0, height / 2.0); Mat rot = getRotationMatrix2D(center, angle, 1.0); // 计算旋转后的图像尺寸 Rect bbox = RotatedRect(center, Size2f(width, height), angle).boundingRect(); // 调整旋转矩阵的平移部分 rot.at<double>(0, 2) += bbox.width / 2.0 - center.x; rot.at<double>(1, 2) += bbox.height / 2.0 - center.y; // 使用更快的插值方法 Mat corrected; warpAffine(image, corrected, rot, bbox.size(), INTER_LINEAR, BORDER_REPLICATE); return corrected; } int main() { // 读取图像 Mat image = imread("E:\\123\\14.jpg"); if (image.empty()) { cerr << "无法读取图像!" << endl; return -1; } // 检测倾斜角度 double skewAngle = detectSkewAngle(image); cout << "检测到倾斜角度: " << skewAngle << "°" << endl; // 校正倾斜 Mat correctedImage = correctSkew(image, skewAngle); // 显示结果 namedWindow("原始图像", WINDOW_NORMAL); namedWindow("校正后图像", WINDOW_NORMAL); imshow("原始图像", image); imshow("校正后图像", correctedImage); // 保存结果 imwrite("corrected_text.jpg", correctedImage); cout << "校正后的图像已保存为 corrected_text.jpg" << endl; waitKey(0); return 0; }和#include <opencv2/opencv.hpp> #include <iostream> #include <vector> #include <cmath> #include <algorithm> #include <limits> using namespace cv; using namespace std; // 计算两条直线的夹角差(0-90度) double calculateAngleDiff(double angle1, double angle2) { double diff = fabs(angle1 - angle2); if (diff > 90) diff = 180 - diff; return diff; } // 计算直线角度(0-180度) double getLineAngle(const Vec4i& line) { Point pt1(line[0], line[1]); Point pt2(line[2], line[3]); double dx = pt2.x - pt1.x; double dy = pt2.y - pt1.y; double angle = atan2(dy, dx) * 180 / CV_PI; if (angle < 0) angle += 180; return angle; } // 调整图像大小以适应屏幕显示 Mat resizeToFitScreen(const Mat& image, int maxWidth = 1200, int maxHeight = 800) { double scale = 1.0; if (image.cols > maxWidth || image.rows > maxHeight) { double scaleX = static_cast<double>(maxWidth) / image.cols; double scaleY = static_cast<double>(maxHeight) / image.rows; scale = min(scaleX, scaleY); } if (scale < 1.0) { Mat resized; resize(image, resized, Size(), scale, scale); return resized; } return image.clone(); } // 检测四边形轮廓 vector<vector<Point>> detectQuadrilaterals(Mat& edges) { vector<vector<Point>> contours; vector<Vec4i> hierarchy; findContours(edges, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE); vector<vector<Point>> quads; for (size_t i = 0; i < contours.size(); i++) { // 忽略小轮廓 if (contourArea(contours[i]) < 1000) continue; vector<Point> approx; // 多边形逼近 double epsilon = 0.02 * arcLength(contours[i], true); approxPolyDP(contours[i], approx, epsilon, true); // 如果是四边形 if (approx.size() == 4) { // 检查是否是凸四边形 if (isContourConvex(approx)) { quads.push_back(approx); } } } return quads; } // 计算四边形的最小外接矩形角度 double getQuadrilateralAngle(const vector<Point>& quad) { RotatedRect rect = minAreaRect(quad); return rect.angle; } // 检查两个四边形是否嵌套(一个在另一个内部) bool isNestedQuad(const vector<Point>& outer, const vector<Point>& inner) { for (const Point& pt : inner) { if (pointPolygonTest(outer, pt, false) < 0) { return false; } } return true; } int main() { // 硬编码图片路径 - 修改为您需要的实际路径 string imagePath = "C:/path/to/your/image.jpg"; // Windows路径 // string imagePath = "/home/user/images/test.jpg"; // Linux路径 // 1. 读取图片 Mat src = imread(imagePath); if (src.empty()) { cerr << "Error: Could not open or find the image at: " << imagePath << endl; cerr << "Please check the path and try again." << endl; return -1; } // 2. 转换为灰度图并进行边缘检测 Mat gray, edges; cvtColor(src, gray, COLOR_BGR2GRAY); GaussianBlur(gray, gray, Size(5, 5), 0); Canny(gray, edges, 50, 150); // 3. 霍夫变换检测直线 vector<Vec4i> lines; HoughLinesP(edges, lines, 1, CV_PI / 180, 50, 50, 10); // 4. 过滤短直线 double diag = sqrt(src.rows * src.rows + src.cols * src.cols); double minLength = diag / 20; vector<Vec4i> validLines; vector<double> angles; for (const auto& line : lines) { Point pt1(line[0], line[1]); Point pt2(line[2], line[3]); double length = norm(pt1 - pt2); if (length > minLength) { validLines.push_back(line); angles.push_back(getLineAngle(line)); } } // 5. 检测四边形轮廓 vector<vector<Point>> quads = detectQuadrilaterals(edges); bool hasNestedQuads = false; double nestedQuadAngle = 0; // 寻找嵌套的四边形(外框和内框) for (size_t i = 0; i < quads.size(); i++) { for (size_t j = 0; j < quads.size(); j++) { if (i == j) continue; // 检查是否嵌套 if (isNestedQuad(quads[i], quads[j])) { hasNestedQuads = true; nestedQuadAngle = getQuadrilateralAngle(quads[j]); // 使用内框的角度 break; } } if (hasNestedQuads) break; } // 6. 处理检测结果 Mat dst = src.clone(); string resultText; double rotationAngle = 0; bool foundFrame = false; const double angleTolerance = 5.0; // 如果有嵌套四边形(粗边框) if (hasNestedQuads) { foundFrame = true; rotationAngle = nestedQuadAngle; resultText = "Nested frame detected. Rotation angle: " + to_string(rotationAngle) + " degrees"; } // 否则检测水平和垂直线 else if (validLines.size() >= 2) { // 分组存储水平和垂直方向的直线 vector<Vec4i> horizontalLines, verticalLines; for (size_t i = 0; i < validLines.size(); i++) { if (angles[i] < angleTolerance || angles[i] > 180 - angleTolerance || (angles[i] > 90 - angleTolerance && angles[i] < 90 + angleTolerance)) { if (fabs(angles[i] - 90) < angleTolerance) { verticalLines.push_back(validLines[i]); } else { horizontalLines.push_back(validLines[i]); } } } // 检查是否构成线框 if (horizontalLines.size() >= 2 && verticalLines.size() >= 2) { foundFrame = true; // 计算水平线平均角度 double avgAngle = 0; int count = 0; for (double angle : angles) { if (angle < angleTolerance || angle > 180 - angleTolerance) { avgAngle += (angle > 90) ? angle - 180 : angle; count++; } } if (count > 0) { avgAngle /= count; rotationAngle = avgAngle; } resultText = "Frame detected. Rotation angle: " + to_string(rotationAngle) + " degrees"; } else { // 检查是否存在平行/共线直线 bool foundParallel = false; for (size_t i = 0; i < angles.size(); i++) { for (size_t j = i + 1; j < angles.size(); j++) { double angleDiff = calculateAngleDiff(angles[i], angles[j]); if (angleDiff < angleTolerance) { foundParallel = true; break; } } if (foundParallel) break; } if (foundParallel) { resultText = "Parallel lines detected but no frame found"; } else { resultText = "No frame or parallel lines detected"; } } } else { resultText = "No sufficient lines detected"; } cout << resultText << endl; // 7. 如果有线框,进行校正 if (foundFrame) { // 旋转图像校正 Point2f center(src.cols / 2.0f, src.rows / 2.0f); Mat rotMat = getRotationMatrix2D(center, rotationAngle, 1.0); warpAffine(src, dst, rotMat, src.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar(255, 255, 255)); } // 8. 保存结果 size_t dotPos = imagePath.find_last_of("."); string baseName = (dotPos == string::npos) ? imagePath : imagePath.substr(0, dotPos); string ext = (dotPos == string::npos) ? ".jpg" : imagePath.substr(dotPos); string origPath = baseName + "_original" + ext; string correctedPath = baseName + "_corrected" + ext; imwrite(origPath, src); if (foundFrame) { imwrite(correctedPath, dst); cout << "Saved original image: " << origPath << endl; cout << "Saved corrected image: " << correctedPath << endl; } else { cout << "Saved original image: " << origPath << endl; } // 9. 显示结果(调整大小以适应屏幕) Mat displaySrc = resizeToFitScreen(src); Mat displayDst = resizeToFitScreen(dst); // 计算字体大小比例 double fontScaleSrc = min(0.7, 600.0 / displaySrc.cols); double fontScaleDst = min(0.7, 600.0 / displayDst.cols); // 添加文本到显示图像 putText(displaySrc, resultText, Point(20, 40), FONT_HERSHEY_SIMPLEX, fontScaleSrc, Scalar(0, 0, 255), 2); if (foundFrame) { putText(displayDst, "Corrected Image", Point(20, 40), FONT_HERSHEY_SIMPLEX, fontScaleDst, Scalar(0, 0, 255), 2); } // 显示原始图像 namedWindow("Original Image", WINDOW_NORMAL); imshow("Original Image", displaySrc); // 显示校正图像(如果有) if (foundFrame) { namedWindow("Corrected Image", WINDOW_NORMAL); imshow("Corrected Image", displayDst); } waitKey(0); return 0; }这两个函数添加,判断条件符合哪一个函数就执行哪一个函数的功能(前提是将后两个函数的功能合理的嵌合到第一个代码中)
07-05
import os import random import tkinter as tk from tkinter import filedialog, messagebox, ttk import shutil import tempfile import hashlib import time import pefile import zlib import sys import platform import psutil from Crypto.Cipher import AES # 仅保留但不用于代码段加密 from Crypto.Util.Padding import pad, unpad # 仅保留但不用于代码段加密 class ExeProtectorApp: def __init__(self, root): self.root = root self.root.title("EXE文件保护工具 v4.2") self.root.geometry("750x680") self.root.resizable(True, True) # 设置中文字体 self.style = ttk.Style() self.style.configure("TLabel", font=("SimHei", 10)) self.style.configure("TButton", font=("SimHei", 10)) self.style.configure("TProgressbar", thickness=20) # 创建主框架 self.main_frame = ttk.Frame(root, padding="20") self.main_frame.pack(fill=tk.BOTH, expand=True) # 文件选择部分 ttk.Label(self.main_frame, text="选择EXE文件:").grid(row=0, column=0, sticky=tk.W, pady=5) self.file_path_var = tk.StringVar() ttk.Entry(self.main_frame, textvariable=self.file_path_var, width=50).grid(row=0, column=1, padx=5, pady=5) ttk.Button(self.main_frame, text="浏览...", command=self.browse_file).grid(row=0, column=2, padx=5, pady=5) # 输出目录选择 ttk.Label(self.main_frame, text="输出目录:").grid(row=1, column=0, sticky=tk.W, pady=5) self.output_dir_var = tk.StringVar() ttk.Entry(self.main_frame, textvariable=self.output_dir_var, width=50).grid(row=1, column=1, padx=5, pady=5) ttk.Button(self.main_frame, text="浏览...", command=self.browse_output_dir).grid(row=1, column=2, padx=5, pady=5) # 选项设置 options_frame = ttk.LabelFrame(self.main_frame, text="选项", padding="10") options_frame.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10) # 随机字节增加量 ttk.Label(options_frame, text="随机字节增加范围 (KB):").grid(row=0, column=0, sticky=tk.W, pady=5) self.min_size_var = tk.IntVar(value=100) ttk.Entry(options_frame, textvariable=self.min_size_var, width=10).grid(row=0, column=1, padx=5, pady=5) ttk.Label(options_frame, text="至").grid(row=0, column=2, padx=5, pady=5) self.max_size_var = tk.IntVar(value=1000) ttk.Entry(options_frame, textvariable=self.max_size_var, width=10).grid(row=0, column=3, padx=5, pady=5) # 随机性强度 ttk.Label(options_frame, text="随机性强度:").grid(row=0, column=4, sticky=tk.W, pady=5) self.random_strength = tk.StringVar(value="medium") strength_options = ttk.Combobox(options_frame, textvariable=self.random_strength, state="readonly", width=12) strength_options['values'] = ("低", "中", "高") strength_options.grid(row=0, column=5, padx=5, pady=5) # 程序类型模拟 ttk.Label(options_frame, text="模拟程序类型:").grid(row=1, column=0, sticky=tk.W, pady=5) self.app_type = tk.StringVar(value="generic") app_types = ttk.Combobox(options_frame, textvariable=self.app_type, state="readonly", width=15) app_types['values'] = ("通用程序", "游戏程序", "办公软件", "系统工具", "开发工具") app_types.grid(row=1, column=1, padx=5, pady=5) # 处理方法 self.process_method = tk.StringVar(value="safe") ttk.Radiobutton(options_frame, text="安全模式", variable=self.process_method, value="safe").grid(row=1, column=2, sticky=tk.W, pady=5) ttk.Radiobutton(options_frame, text="增强模式", variable=self.process_method, value="enhanced").grid(row=1, column=3, sticky=tk.W, pady=5) ttk.Radiobutton(options_frame, text="标准保护", variable=self.process_method, value="standard").grid(row=1, column=4, sticky=tk.W, pady=5) ttk.Radiobutton(options_frame, text="高级保护", variable=self.process_method, value="advanced").grid(row=1, column=5, sticky=tk.W, pady=5) # 高级选项 advanced_frame = ttk.LabelFrame(self.main_frame, text="保护选项", padding="10") advanced_frame.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10) self.obfuscate_resources = tk.BooleanVar(value=True) ttk.Checkbutton(advanced_frame, text="混淆资源文件", variable=self.obfuscate_resources).grid(row=0, column=0, sticky=tk.W, pady=5) self.encrypt_sections = tk.BooleanVar(value=True) ttk.Checkbutton(advanced_frame, text="轻度代码变换", variable=self.encrypt_sections).grid(row=0, column=1, sticky=tk.W, pady=5) self.add_dummy_sections = tk.BooleanVar(value=True) ttk.Checkbutton(advanced_frame, text="添加随机数据块", variable=self.add_dummy_sections).grid(row=1, column=0, sticky=tk.W, pady=5) self.randomize_imports = tk.BooleanVar(value=True) ttk.Checkbutton(advanced_frame, text="随机化导入表顺序", variable=self.randomize_imports).grid(row=1, column=1, sticky=tk.W, pady=5) # 终极选项 ultra_frame = ttk.LabelFrame(self.main_frame, text="高级优化", padding="10") ultra_frame.grid(row=4, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10) self.anti_vm = tk.BooleanVar(value=False) ttk.Checkbutton(ultra_frame, text="兼容虚拟机环境", variable=self.anti_vm).grid(row=0, column=0, sticky=tk.W, pady=5) self.anti_debug = tk.BooleanVar(value=False) ttk.Checkbutton(ultra_frame, text="调试模式兼容", variable=self.anti_debug).grid(row=0, column=1, sticky=tk.W, pady=5) self.random_pe_layout = tk.BooleanVar(value=True) ttk.Checkbutton(ultra_frame, text="随机PE结构布局", variable=self.random_pe_layout).grid(row=1, column=0, sticky=tk.W, pady=5) self.variable_section_count = tk.BooleanVar(value=True) ttk.Checkbutton(ultra_frame, text="随机区段数量", variable=self.variable_section_count).grid(row=1, column=1, sticky=tk.W, pady=5) # 处理按钮 ttk.Button(self.main_frame, text="保护文件", command=self.process_file).grid(row=5, column=0, columnspan=3, pady=20) # 状态和进度条 self.status_var = tk.StringVar(value="就绪") ttk.Label(self.main_frame, textvariable=self.status_var).grid(row=6, column=0, columnspan=2, sticky=tk.W, pady=5) self.progress_var = tk.DoubleVar(value=0) self.progress_bar = ttk.Progressbar(self.main_frame, variable=self.progress_var, length=100) self.progress_bar.grid(row=6, column=2, sticky=(tk.W, tk.E), pady=5) # 默认输出目录 self.output_dir_var.set(os.path.join(os.getcwd(), "protected_exes")) # 绑定窗口关闭事件 self.root.protocol("WM_DELETE_WINDOW", self.on_closing) # 初始化随机种子 self.initialize_random_seed() # 初始化随机种子,使用多种来源确保高随机性 def initialize_random_seed(self): # 使用多种系统信息和随机源作为种子材料,增强随机性 seed_material = ( time.time_ns().to_bytes(8, 'big') + os.getpid().to_bytes(4, 'big') + os.urandom(32) + # 增加随机字节数量 str(psutil.virtual_memory().available).encode() + str(psutil.cpu_percent(interval=0.1)).encode() + platform.node().encode() + str(random.getstate()).encode() ) # 使用SHA-512获取更复杂的哈希值作为种子 seed = int.from_bytes(hashlib.sha512(seed_material).digest(), 'big') random.seed(seed) # 额外增加随机状态初始化 random.getstate() # 浏览文件 def browse_file(self): file_path = filedialog.askopenfilename( filetypes=[("可执行文件", "*.exe"), ("所有文件", "*.*")] ) if file_path: self.file_path_var.set(file_path) # 浏览输出目录 def browse_output_dir(self): dir_path = filedialog.askdirectory() if dir_path: self.output_dir_var.set(dir_path) # 处理文件 def process_file(self): exe_path = self.file_path_var.get() output_dir = self.output_dir_var.get() if not exe_path: messagebox.showerror("错误", "请选择一个EXE文件") return if not os.path.exists(exe_path): messagebox.showerror("错误", "选择的文件不存在") return if not output_dir: messagebox.showerror("错误", "请选择输出目录") return if not os.path.exists(output_dir): try: os.makedirs(output_dir) except: messagebox.showerror("错误", "无法创建输出目录") return # 获取文件名和扩展名 file_name, file_ext = os.path.splitext(os.path.basename(exe_path)) # 添加随机字符串到输出文件名,确保每次不同 random_suffix = hashlib.sha256(str(time.time_ns()).encode() + os.urandom(16)).hexdigest()[:12] output_path = os.path.join(output_dir, f"{file_name}_protected_{random_suffix}{file_ext}") try: # 更新状态 self.status_var.set("正在处理文件...") self.progress_var.set(0) self.root.update() # 计算随机增加的字节大小 min_size = self.min_size_var.get() max_size = self.max_size_var.get() if min_size < 0 or max_size < 0 or min_size > max_size: messagebox.showerror("错误", "请设置有效的字节增加范围") return # 根据随机性强度调整随机范围 strength_factor = 1.0 if self.random_strength.get() == "高": strength_factor = 1.5 elif self.random_strength.get() == "低": strength_factor = 0.5 adjusted_min = int(min_size * strength_factor) adjusted_max = int(max_size * strength_factor) random_size_kb = random.randint(adjusted_min, adjusted_max) random_size_bytes = random_size_kb * 1024 # 复制原始文件 shutil.copy2(exe_path, output_path) # 计算原始文件哈希值 original_hash = self.calculate_file_hash(exe_path) # 更新进度 self.progress_var.set(5) self.root.update() # 根据选择的模式处理文件 if self.process_method.get() == "safe": self.safe_modify_exe_file(output_path, random_size_bytes) elif self.process_method.get() == "enhanced": self.enhanced_modify_exe_file(output_path, random_size_bytes) elif self.process_method.get() == "standard": self.standard_protection(output_path, random_size_bytes) else: self.advanced_protection(output_path, random_size_bytes) # 后续哈希计算、进度更新等 modified_hash = self.calculate_file_hash(output_path) self.progress_var.set(95) self.root.update() if self.verify_exe_file(output_path): self.status_var.set("文件处理完成") self.progress_var.set(100) messagebox.showinfo( "成功", f"文件保护成功!\n" f"原始文件大小: {os.path.getsize(exe_path) // 1024} KB\n" f"处理后文件大小: {os.path.getsize(output_path) // 1024} KB\n" f"增加了: {random_size_kb} KB\n\n" f"原始文件哈希 (MD5): {original_hash}\n" f"处理后文件哈希 (MD5): {modified_hash}\n\n" f"文件已保存至: {output_path}" ) else: self.status_var.set("文件验证失败") self.progress_var.set(100) messagebox.showwarning("警告", "处理后的文件可能需要在特定环境运行") except Exception as e: self.status_var.set("处理过程中出错") messagebox.showerror("错误", f"处理文件时出错: {str(e)}") finally: self.progress_var.set(0) # 每次处理后重新初始化随机种子,确保下一次处理的随机性不同 self.initialize_random_seed() # 计算文件哈希 def calculate_file_hash(self, file_path): hash_md5 = hashlib.md5() with open(file_path, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): hash_md5.update(chunk) return hash_md5.hexdigest() # 安全模式:仅添加正常数据 def safe_modify_exe_file(self, file_path, additional_bytes): with open(file_path, 'ab') as f: # 根据选择的应用类型生成对应的数据 app_type = self.app_type.get() data = self.generate_application_specific_data(additional_bytes, app_type) f.write(data) # 增强模式:优化PE结构 def enhanced_modify_exe_file(self, file_path, additional_bytes): try: pe = pefile.PE(file_path) # 更新时间戳,使用更大的随机偏移 pe.FILE_HEADER.TimeDateStamp = int(time.time()) + random.randint(-86400, 86400) # 随机偏移1天内 # 随机化更多非关键的PE头字段 if self.random_pe_layout.get(): pe.FILE_HEADER.PointerToSymbolTable = random.getrandbits(32) pe.FILE_HEADER.NumberOfSymbols = random.randint(0, 2000) # 添加更多随机化字段 pe.OPTIONAL_HEADER.MajorLinkerVersion = random.randint(1, 25) pe.OPTIONAL_HEADER.MinorLinkerVersion = random.randint(0, 99) pe.OPTIONAL_HEADER.MajorImageVersion = random.randint(1, 20) pe.OPTIONAL_HEADER.MinorImageVersion = random.randint(0, 99) # 添加正常附加数据 self.safe_modify_exe_file(file_path, additional_bytes) pe.write(file_path) pe.close() except Exception as e: print(f"增强模式执行: {e}") self.safe_modify_exe_file(file_path, additional_bytes) # 标准保护:添加合理区段 def standard_protection(self, file_path, additional_bytes): try: pe = pefile.PE(file_path) # 随机决定添加的区段数量(1-4个),增加变化性 section_count = 1 if self.variable_section_count.get(): section_count = random.randint(1, 4) # 添加多个随机区段 for _ in range(section_count): # 创建新区段 new_section = pefile.SectionStructure(pe.__IMAGE_SECTION_HEADER_format__) # 生成随机但合理的区段名 new_section.Name = self.generate_sane_section_name() # 区段大小随机(1-16KB),范围更大 section_size = random.randint(0x1000, 0x4000) new_section.Misc_VirtualSize = section_size # 地址对齐,添加更大的随机偏移 base_virtual_address = (pe.sections[-1].VirtualAddress + pe.sections[-1].Misc_VirtualSize + 0x1000 - 1) & ~0xFFF new_section.VirtualAddress = base_virtual_address + random.randint(0, 0x2000) base_raw_data = (pe.sections[-1].PointerToRawData + pe.sections[-1].SizeOfRawData + 0x1000 - 1) & ~0xFFF new_section.PointerToRawData = base_raw_data + random.randint(0, 0x2000) new_section.SizeOfRawData = section_size # 随机选择合理的区段属性,增加更多可能性 section_flags = [ 0xC0000040, 0x40000040, 0x20000040, 0x80000040, 0x00000040, 0xE0000040, 0x00000080, 0x40000080 ] new_section.Characteristics = random.choice(section_flags) # 生成与程序类型匹配的区段数据 app_type = self.app_type.get() new_data = self.generate_application_specific_data(section_size, app_type) pe.set_bytes_at_offset(new_section.PointerToRawData, new_data) # 添加新区段到PE结构 pe.sections.append(new_section) pe.FILE_HEADER.NumberOfSections += 1 pe.OPTIONAL_HEADER.SizeOfImage = (new_section.VirtualAddress + new_section.Misc_VirtualSize + 0x1000 - 1) & ~0xFFF # 轻度代码变换 if self.encrypt_sections.get(): self.apply_mild_code_transformations(pe) # 随机化导入表顺序(如果启用) if self.randomize_imports.get() and hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'): # 多次随机打乱以增加随机性 for _ in range(random.randint(1, 3)): random.shuffle(pe.DIRECTORY_ENTRY_IMPORT) # 添加文件末尾数据 self.safe_modify_exe_file(file_path, additional_bytes) # 更新时间戳,添加随机偏移 pe.FILE_HEADER.TimeDateStamp = int(time.time()) + random.randint(-86400, 86400) # 随机偏移1天内 pe.write(file_path) pe.close() except Exception as e: print(f"标准保护执行: {e}") self.enhanced_modify_exe_file(file_path, additional_bytes) # 高级保护:进一步增加随机性 def advanced_protection(self, file_path, additional_bytes): try: pe = pefile.PE(file_path) # 随机决定添加的区段数量(2-5个),增加更多变化 section_count = 2 if self.variable_section_count.get(): section_count = random.randint(2, 5) # 添加多个随机区段 for _ in range(section_count): new_section = pefile.SectionStructure(pe.__IMAGE_SECTION_HEADER_format__) new_section.Name = self.generate_sane_section_name() # 区段大小变化更大(1-32KB) section_size = random.randint(0x1000, 0x8000) new_section.Misc_VirtualSize = section_size # 地址对齐,添加更大的随机偏移 base_virtual_address = (pe.sections[-1].VirtualAddress + pe.sections[-1].Misc_VirtualSize + 0x1000 - 1) & ~0xFFF new_section.VirtualAddress = base_virtual_address + random.randint(0, 0x4000) base_raw_data = (pe.sections[-1].PointerToRawData + pe.sections[-1].SizeOfRawData + 0x1000 - 1) & ~0xFFF new_section.PointerToRawData = base_raw_data + random.randint(0, 0x4000) new_section.SizeOfRawData = section_size # 随机选择合理的区段属性,增加更多选项 section_flags = [ 0xC0000040, 0x40000040, 0x20000040, 0x80000040, 0x00000040, 0xE0000040, 0x00000080, 0x40000080, 0x80000080, 0x20000080, 0x00000100 ] new_section.Characteristics = random.choice(section_flags) # 生成特定类型的应用数据 app_type = self.app_type.get() new_data = self.generate_application_specific_data(section_size, app_type) pe.set_bytes_at_offset(new_section.PointerToRawData, new_data) pe.sections.append(new_section) pe.FILE_HEADER.NumberOfSections += 1 pe.OPTIONAL_HEADER.SizeOfImage = (new_section.VirtualAddress + new_section.Misc_VirtualSize + 0x1000 - 1) & ~0xFFF # 轻度代码变换 if self.encrypt_sections.get(): self.apply_mild_code_transformations(pe) # 混淆资源(如果启用) if self.obfuscate_resources.get() and hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'): self.obfuscate_pe_resources(pe) # 随机化导入表顺序,增加随机性 if self.randomize_imports.get() and hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'): # 多次随机打乱以确保随机性 for _ in range(random.randint(2, 5)): random.shuffle(pe.DIRECTORY_ENTRY_IMPORT) # 添加随机数据块,使用随机大小 if self.add_dummy_sections.get(): dummy_size = random.randint(additional_bytes // 3, additional_bytes * 2 // 3) self.safe_modify_exe_file(file_path, dummy_size) additional_bytes -= dummy_size # 添加文件末尾数据 self.safe_modify_exe_file(file_path, additional_bytes) # 随机化更多PE头字段 if self.random_pe_layout.get(): pe.FILE_HEADER.PointerToSymbolTable = random.getrandbits(32) pe.FILE_HEADER.NumberOfSymbols = random.randint(0, 5000) pe.OPTIONAL_HEADER.MajorImageVersion = random.randint(1, 20) pe.OPTIONAL_HEADER.MinorImageVersion = random.randint(0, 99) pe.OPTIONAL_HEADER.MajorSubsystemVersion = random.randint(4, 10) pe.OPTIONAL_HEADER.MinorSubsystemVersion = random.randint(0, 99) pe.OPTIONAL_HEADER.MajorOperatingSystemVersion = random.randint(5, 10) pe.OPTIONAL_HEADER.MinorOperatingSystemVersion = random.randint(0, 99) # 添加更多可随机化的字段 pe.OPTIONAL_HEADER.LoaderFlags = random.getrandbits(32) & 0x00000003 # 仅保留合法值 pe.OPTIONAL_HEADER.NumberOfRvaAndSizes = 16 # 标准值,但可以偶尔修改 if random.random() < 0.3: # 30%概率修改这个值 pe.OPTIONAL_HEADER.SizeOfHeaders = (pe.OPTIONAL_HEADER.SizeOfHeaders + random.randint(0x100, 0x800)) & ~0xFF # 保持对齐 # 更新时间戳,使用更大的随机偏移 pe.FILE_HEADER.TimeDateStamp = int(time.time()) + random.randint(-604800, 604800) # 随机偏移1周内 pe.write(file_path) pe.close() except Exception as e: print(f"高级保护执行: {e}") self.standard_protection(file_path, additional_bytes) # 生成模拟特定类型程序的数据,增强随机性和多样性 def generate_application_specific_data(self, size, app_type): """根据程序类型生成不同特征的数据,确保每次生成都不同""" data = bytearray() # 根据选择的应用类型生成对应的数据模板,增加更多模板项 type_templates = { "通用程序": [ b"C:\\Program Files\\Common Files\\\x00", b"HKLM\\Software\\Microsoft\\Windows\\\x00", b"ERROR_ACCESS_DENIED\x00", b"SUCCESS\x00", b"CONFIG_FILE\x00", b"LOG_FILE\x00", b"USER_SETTINGS\x00", b"APPLICATION_DATA\x00", b"SYSTEM32\x00", b"KERNEL32.DLL\x00", b"ADVAPI32.DLL\x00", (0x00000001).to_bytes(4, 'little'), (0x00000100).to_bytes(4, 'little'), (0x00010000).to_bytes(4, 'little'), (0x00100000).to_bytes(4, 'little'), ], "游戏程序": [ b"C:\\Program Files\\Game\\Data\\\x00", b"C:\\Users\\Public\\Documents\\GameSaves\\\x00", b"TEXTURE_", b"MODEL_", b"SOUND_", b"LEVEL_", b"SCORE_", b"PLAYER_", b"ENEMY_", b"WEAPON_", b"QUEST_", b"ACHIEVEMENT_", b"INVENTORY_", b"CHARACTER_", b"MAP_", b"DIFFICULTY_", (0x000F4240).to_bytes(4, 'little'), # 1000000 (0x000003E8).to_bytes(4, 'little'), # 1000 (0x00000064).to_bytes(4, 'little'), # 100 (0x0000000A).to_bytes(4, 'little'), # 10 ], "办公软件": [ b"C:\\Users\\%USERNAME%\\Documents\\\x00", b"File Format: DOCX\x00", b"File Format: XLSX\x00", b"File Format: PPTX\x00", b"Page ", b"Sheet ", b"Table ", b"Font ", b"Style ", b"Paragraph ", b"Header", b"Footer", b"Section", b"Template", b"Macro", b"Add-in", b"Spell Check", b"Grammar Check", b"Word Count", b"Character Count", (0x0000000A).to_bytes(4, 'little'), # 10 (0x00000014).to_bytes(4, 'little'), # 20 (0x00000064).to_bytes(4, 'little'), # 100 ], "系统工具": [ b"C:\\Windows\\System32\\\x00", b"C:\\Windows\\SysWOW64\\\x00", b"HKLM\\SYSTEM\\CurrentControlSet\\\x00", b"Driver ", b"Service ", b"Device ", b"Registry ", b"Process ", b"Thread ", b"Memory ", b"Disk ", b"Network ", b"Adapter ", b"Protocol ", b"Firewall ", b"Security ", b"Policy ", b"Account ", (0x00000001).to_bytes(4, 'little'), (0x00000000).to_bytes(4, 'little'), (0xFFFFFFFF).to_bytes(4, 'little'), (0x00000002).to_bytes(4, 'little'), ], "开发工具": [ b"C:\\Program Files\\Developer\\SDK\\\x00", b"C:\\Users\\%USERNAME%\\Source\\\x00", b"Compiler ", b"Linker ", b"Debugger ", b"Library ", b"Include ", b"Namespace ", b"Class ", b"Function ", b"Variable ", b"Pointer ", b"Array ", b"Struct ", b"Enum ", b"Union ", b"Template ", b"Exception ", b"Thread ", b"Mutex ", (0x00000000).to_bytes(4, 'little'), (0x00000001).to_bytes(4, 'little'), (0x00000002).to_bytes(4, 'little'), (0x00000003).to_bytes(4, 'little'), ] } # 获取对应类型的模板 templates = type_templates.get(app_type, type_templates["通用程序"]) # 根据随机性强度调整模板使用方式 template_usage = 0.7 # 70%使用模板,30%使用随机数据 if self.random_strength.get() == "高": template_usage = 0.5 # 50%使用模板,50%使用随机数据 elif self.random_strength.get() == "低": template_usage = 0.9 # 90%使用模板,10%使用随机数据 # 填充数据直到达到目标大小,使用更复杂的模式 while len(data) < size: # 随机选择使用模板还是生成随机数据 if random.random() < template_usage: # 随机选择一个模板并添加 item = random.choice(templates) data.extend(item) # 偶尔添加随机长度的空白或分隔符 if random.random() < 0.4: separator_length = random.randint(1, 16) if random.random() < 0.5: data.extend(b'\x00' * separator_length) else: data.extend(b' ' * separator_length) else: # 生成更复杂的随机数据 random_len = random.randint(1, 128) if random.random() < 0.3: # 生成随机ASCII文本 random_text = bytes(random.choice(b'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.') for _ in range(random_len)) data.extend(random_text) elif random.random() < 0.6: # 生成随机二进制数据 data.extend(os.urandom(random_len)) else: # 生成随机数值数据 for _ in range(random_len // 4 + 1): num = random.getrandbits(32) data.extend(num.to_bytes(4, 'little')) return data[:size] # 生成更多样化的合理区段名 def generate_sane_section_name(self): # 扩展区段名基础列表 base_names = [ b'.data', b'.rdata', b'.text', b'.rsrc', b'.reloc', b'.bss', b'.edata', b'.idata', b'.pdata', b'.tls', b'.data1', b'.rdata2', b'.text1', b'.rsrc1', b'.data_', b'.rdata_', b'.text_', b'.rsrc_', b'.init', b'.fini', b'.ctors', b'.dtors', b'.gnu', b'.note', b'.eh_frame', b'.debug', b'.xdata', b'.pdata', b'.data2', b'.text2', b'.code', b'.const', b'.dynamic', b'.hash', b'.plt', b'.got', b'.shstrtab', b'.symtab', b'.strtab', b'.comment', b'.note.ABI-tag' ] # 随机选择基础名称并可能添加随机后缀 name = random.choice(base_names) if random.random() < 0.8: # 提高添加后缀的概率 # 添加更多样化的随机后缀 suffix_type = random.randint(0, 2) if suffix_type == 0: # 数字后缀 suffix = str(random.randint(10, 999)).encode() elif suffix_type == 1: # 字母后缀 suffix_length = random.randint(1, 3) suffix = bytes(random.choice('abcdefghijklmnopqrstuvwxyz') for _ in range(suffix_length)) else: # 混合后缀 suffix = (str(random.randint(1, 9)) + random.choice('abcdef') + str(random.randint(10, 99))).encode() # 确保总长度不超过8字节 name = name[:8-len(suffix)] + suffix return name.ljust(8, b'\x00')[:8] # 确保正好8字节 # 轻度代码变换,增加更多变换类型 def apply_mild_code_transformations(self, pe): text_section = None for section in pe.sections: if b'.text' in section.Name: text_section = section break if text_section: data = pe.get_data(text_section.VirtualAddress, text_section.SizeOfRawData) if not isinstance(data, bytes): data = bytes(data) data_list = list(data) # 根据随机性强度调整变换程度 transform_count = len(data_list) // 200 if self.random_strength.get() == "高": transform_count = len(data_list) // 100 elif self.random_strength.get() == "低": transform_count = len(data_list) // 400 # 限制最大变换次数,但增加上限 transform_count = min(200, transform_count) # 随机选择位置进行更丰富的轻微变换 for _ in range(transform_count): i = random.randint(0, len(data_list) - 1) # 增加更多变换类型 transform_type = random.choice(range(8)) if transform_type == 0: # 加1 data_list[i] = (data_list[i] + 1) % 256 elif transform_type == 1: # 减1 data_list[i] = (data_list[i] - 1) % 256 elif transform_type == 2: # 与0xFF异或 data_list[i] ^= 0xFF elif transform_type == 3: # 左移一位 data_list[i] = (data_list[i] << 1) % 256 elif transform_type == 4: # 右移一位 data_list[i] = (data_list[i] >> 1) % 256 elif transform_type == 5: # 加一个小随机数 data_list[i] = (data_list[i] + random.randint(1, 5)) % 256 elif transform_type == 6: # 减一个小随机数 data_list[i] = (data_list[i] - random.randint(1, 5)) % 256 else: # 与一个随机数异或 data_list[i] ^= random.randint(1, 255) pe.set_bytes_at_offset(text_section.PointerToRawData, bytes(data_list)) # 增强资源混淆 def obfuscate_pe_resources(self, pe): try: # 遍历所有资源条目 for resource_type in pe.DIRECTORY_ENTRY_RESOURCE.entries: if hasattr(resource_type, 'directory'): for resource_id in resource_type.directory.entries: if hasattr(resource_id, 'directory'): for resource_lang in resource_id.directory.entries: data_rva = resource_lang.data.struct.OffsetToData size = resource_lang.data.struct.Size # 读取资源数据 resource_data = list(pe.get_data(data_rva, size)) # 根据随机性强度调整混淆程度 step_size = 200 if self.random_strength.get() == "高": step_size = 100 elif self.random_strength.get() == "低": step_size = 400 # 增加更多变换类型 for i in range(0, len(resource_data), random.randint(step_size-50, step_size+50)): if i < len(resource_data): # 随机选择一种变换 transform_type = random.randint(0, 4) if transform_type == 0: resource_data[i] = (resource_data[i] + random.randint(1, 5)) % 256 elif transform_type == 1: resource_data[i] = (resource_data[i] - random.randint(1, 5)) % 256 elif transform_type == 2: resource_data[i] ^= random.randint(1, 255) elif transform_type == 3: resource_data[i] = (resource_data[i] << random.randint(1, 3)) % 256 else: resource_data[i] = (resource_data[i] >> random.randint(1, 3)) % 256 # 对部分资源进行块级混淆 if random.random() < 0.3 and size > 1024: block_size = random.randint(64, 256) for b in range(0, size, block_size): if random.random() < 0.2: # 20%的块会被翻转 start = b end = min(b + block_size, size) resource_data[start:end] = resource_data[start:end][::-1] # 写回修改后的资源数据 pe.set_bytes_at_offset(data_rva, bytes(resource_data)) except Exception as e: print(f"资源混淆错误: {e}") # 计算PE校验和 def calculate_pe_checksum(self, pe): try: with open(pe.name, 'rb') as f: data = f.read() checksum = 0 for i in range(0, len(data), 2): if i + 1 < len(data): w = (data[i+1] << 8) | data[i] checksum += w else: checksum += data[i] checksum = (checksum >> 16) + (checksum & 0xffff) checksum += (checksum >> 16) return 0 - checksum except: return 0x00000000 # 验证EXE文件 def verify_exe_file(self, file_path): try: pe = pefile.PE(file_path) pe.close() return True except: return False # 关闭窗口 def on_closing(self): if messagebox.askokcancel("退出", "确定要退出程序吗?"): self.root.destroy() if __name__ == "__main__": root = tk.Tk() app = ExeProtectorApp(root) root.mainloop() 用户希望每次输出的程序减小TEXT代码段熵值 相对上次输出的程序静态特征都有很大差异 绕过qvm静态分析查杀 2025-07-10 09:57:50 恶意软件(HEUR/QVM10.2.95BA.Malware.Gen)MD5:f4722f1fd2c14e65558207bd3415ed0a已删除此文件,如果您发现误删,可从隔离区恢复此文件。 2025-07-10 10:07:34 恶意软件(HEUR/QVM10.2.95BA.Malware.Gen)MD5:3f519280c806a531c2fd494f392add06已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_2587963335de.exe 2025-07-10 10:05:41 恶意软件(HEUR/QVM10.2.95BA.Malware.Gen)MD5:ad5bf7bf68759eaa5f25587d0e977320已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_4df5eb7b2b48.exe 2025-07-10 10:04:55 恶意软件(HEUR/QVM10.2.95BA.Malware.Gen)MD5:ce816bbac00149bda721dec01d623773已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_5795dc64119b.exe 2025-07-10 10:03:42 恶意软件(HEUR/QVM10.2.95BA.Malware.Gen)MD5:224798374219b4fc6046b1da7f4eb632已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_17f73c7eda70.exe 2025-07-10 10:03:16 恶意软件(HEUR/QVM10.2.95BA.Malware.Gen)MD5:cb3c1534897e38110aeeea2aa71c9d5a已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_eb5a679318ab.exe 2025-07-10 09:57:50 恶意软件(HEUR/QVM10.2.95BA.Malware.Gen)MD5:f4722f1fd2c14e65558207bd3415ed0a已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_7e5282a2592431df.exe 2025-07-10 09:56:51 恶意软件(HEUR/QVM10.2.95BA.Malware.Gen)MD5:63d542dffd607fda49b4ca8880069471已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_0269d064dca0e33d.exe 2025-07-10 09:56:23 恶意软件(HEUR/QVM10.2.95BA.Malware.Gen)MD5:c7edea82a1abefe84502a88980b86356已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_98f344f33553ad04.exe 2025-07-10 09:55:20 恶意软件(HEUR/QVM10.2.95BA.Malware.Gen)MD5:8bb531f637cdae52b17ecab88b28b88d已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_32027bfd1919.exe 2025-07-10 09:54:37 恶意软件(HEUR/QVM10.2.95BA.Malware.Gen)MD5:f03cac215d14019e60e97a0a9dff564a已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_dc0e4952f25d.exe 2025-07-10 09:54:16 恶意软件(HEUR/QVM10.2.95BA.Malware.Gen)MD5:79f41c9ca52f656e0d14d7f99a7c0361已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_d0d314c24b51.exe 2025-07-10 09:36:22 恶意软件(HEUR/QVM10.2.958D.Malware.Gen)MD5:7a8102febd74e861b0f798e130e3bbdf已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_3766493edd57.exe 2025-07-10 09:35:48 恶意软件(HEUR/QVM10.2.958D.Malware.Gen)MD5:ceaa9c2e0ae64a9242eae202c127970a已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_e6c9d5d7be4e.exe 2025-07-10 09:34:47 恶意软件(HEUR/QVM10.2.958D.Malware.Gen)MD5:7cfe0a2c5d8fbce0d86a01613bef166e已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_e3d311d2cf8e.exe 2025-07-10 09:31:31 恶意软件(HEUR/QVM10.2.958D.Malware.Gen)MD5:416cfe49e997da0d85efc5eac7cd33f3已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server.exe 2025-07-10 09:18:33 恶意软件(HEUR/QVM10.2.958D.Malware.Gen)MD5:1dae68d1345fe77c6427f8f36ef257a3已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_100a4418.exe 2025-07-10 09:17:27 恶意软件(HEUR/QVM10.2.958D.Malware.Gen)MD5:22be3cbbdaf6adc964323e6cd7b1830e已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_a423e3f7.exe 2025-07-10 09:16:34 恶意软件(HEUR/QVM10.2.958D.Malware.Gen)MD5:19811a97d4f619ccc30fbd9355710c94已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_42c8f493.exe 2025-07-10 09:15:28 恶意软件(HEUR/QVM10.2.9551.Malware.Gen)MD5:603aa37cafcc1a97ba7d3b6944e7c44f已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_1870a4d5.exe 2025-07-10 09:15:28 恶意软件(HEUR/QVM10.2.9551.Malware.Gen)MD5:f4f3aaa34d45f6d392ab02d469529fa2已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_6ef68774.exe 2025-07-10 09:02:21 恶意软件(HEUR/QVM10.2.9551.Malware.Gen)MD5:d963a3b990e387d512c1505cc1defc2d已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\appdata\local\temp\vmware-odaycaogen'\vmwarednd\4e3bc625\server_protected_b162fbecda18.exe 2025-07-10 09:02:20 恶意软件(HEUR/QVM10.2.9551.Malware.Gen)MD5:d963a3b990e387d512c1505cc1defc2d已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_b162fbecda18.exe 2025-07-10 09:01:55 恶意软件(HEUR/QVM10.2.9551.Malware.Gen)MD5:6613a15d293baa581027037940f38123已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_0c7cdb86447c.exe 2025-07-10 09:01:13 恶意软件(HEUR/QVM10.2.9551.Malware.Gen)MD5:6e784a5d6e4c5cd3e102341571186c02已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_91770c617ba9.exe 2025-07-10 09:00:26 恶意软件(HEUR/QVM10.2.9551.Malware.Gen)MD5:ebaba98d0a9815b21c637a4f2cb2fea8已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_c5080514e48f.exe 2025-07-10 08:59:26 恶意软件(HEUR/QVM10.2.9551.Malware.Gen)MD5:3e2c4fc7477c93fe7446866dfa0c0d2a已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_917a4f029497.exe 2025-07-10 08:59:00 恶意软件(HEUR/QVM10.2.9551.Malware.Gen)MD5:fce5281cf86dca7226f2dde2764ceee1已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_e3ea24fe8204.exe 2025-07-09 20:41:17 恶意软件(HEUR/QVM10.2.9279.Malware.Gen)MD5:28391ff043ea6b95c0a22c42b827fd13已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_d94e61705c0d.exe 2025-07-09 20:39:48 恶意软件(HEUR/QVM10.2.9279.Malware.Gen)MD5:0dbbcc8071f854fa75fe485c055e9cb0已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_df81323ec213.exe 2025-07-09 20:39:17 恶意软件(HEUR/QVM10.2.9279.Malware.Gen)MD5:1d3e05e3d94613d048aff46974c9eae5已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_08d18b05d328.exe 2025-07-09 20:38:43 恶意软件(HEUR/QVM10.2.9279.Malware.Gen)MD5:cd26422e884d3110bc75080d27c1c069已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_e06bbef612d8.exe 2025-07-09 20:38:08 恶意软件(HEUR/QVM10.2.9279.Malware.Gen)MD5:8b12ffcdf948eca4636292ee8399b390已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_61f1aadc6f43.exe 2025-07-09 20:37:25 恶意软件(HEUR/QVM10.2.9279.Malware.Gen)MD5:7eb6481e075f5eeb5465e6395b55db92已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\users\odaycaogen'\desktop\server_protected_789d8774794b.exe 2025-07-09 18:54:48 恶意软件(HEUR/QVM10.2.9231.Malware.Gen)MD5:5c7d6aec8656f4850e914408c8779c35已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\program files\360\360safe\softmgr\whitelist\myapp_36171265\454336_protected_9d9289dd.exe 2025-07-09 18:54:43 恶意软件(HEUR/QVM10.2.9231.Malware.Gen)MD5:5c7d6aec8656f4850e914408c8779c35已删除此文件,如果您发现误删,可从隔离区恢复此文件。 c:\program files\360\360safe\softmgr\whitelist\myapp_36170329\454336_protected_9d9289dd.exe 2025-07-09 18:48:32 恶意软件(HEUR/QVM10.2.9231.Malware.Gen)MD5:8b18925bca5cf9c236bae2ed5f39e67c已删除此文件,如果您发现误删,可从隔离区恢复此文件。
07-11
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值