UIDiag.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. // =============================================================================
  2. // 诊断日志唯一出口的实现。说明见 Utils/UIDiag.h。
  3. //
  4. // 为什么是独立 .cpp(而不是 header-only + inline):
  5. // 如果写成头文件里的 inline 函数,每个包含它的 .cpp 都会有一份 static 局部
  6. // 变量副本(s_buf / path 缓存),日志行为虽然仍正确,但每次都重新
  7. // GetModuleFileName + 打开/关闭文件,且在 DLL 边界上语义容易含混。
  8. // 放 .cpp 里只编译一份 —— 这也是"全库只有本 .cpp 真正落盘"的实现基础。
  9. // =============================================================================
  10. #include "StdAfx.h"
  11. #include "UIDiag.h"
  12. #include <stdarg.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. namespace DuiLibDiag
  16. {
  17. // ★ 全库唯一的日志开关:需要重新开启诊断时把这里改回 true 并重编。
  18. //
  19. // 2026-09-24(第 41 轮):Win10 触摸键盘输入问题(第 40 轮修:CEditUI 合成鼠标手势
  20. // 只发 DOWN 不发 UP ⇒ 原生 EDIT 停在半截手势)已真机验收通过,收尾关闭诊断输出。
  21. // 关闭后 Enabled() 是编译期常量 false,整个 Log 函数体会被当死代码消除
  22. // ⇒ 运行时零开销、零文件 IO,二进制里也不再出现 touch_trace.log / DIAG-BUILD 字样。
  23. // 埋点代码全部保留在源码里,随时改回 true 重编即可复现现场。
  24. inline bool Enabled()
  25. {
  26. return false;
  27. }
  28. // 极简 TCHAR* → UTF-8 char* 转换(仅用于日志路径;非线程安全,够用即可)
  29. static const char* Narrow(LPCTSTR s)
  30. {
  31. static char s_buf[MAX_PATH * 3] = { 0 };
  32. #ifdef _UNICODE
  33. s_buf[0] = '\0';
  34. if (s != NULL)
  35. ::WideCharToMultiByte(CP_UTF8, 0, s, -1, s_buf, (int)sizeof(s_buf) - 1, NULL, NULL);
  36. return s_buf;
  37. #else
  38. return s;
  39. #endif
  40. }
  41. void Log(LPCTSTR fmt, ...)
  42. {
  43. if (!Enabled()) return;
  44. // ★ 版本横幅 —— 整个日志里只写一次。
  45. //
  46. // 存在的唯一理由:收银机不是本机,DLL 要手工拷过去。用户说"还是不行"时,
  47. // 第一步永远是先确认"他跑的是不是这一轮编出来的 DLL"。
  48. // 只要日志第一行有这行横幅,就说明新库确实被加载了。
  49. static bool s_bBannerWritten = false;
  50. const bool bWriteBanner = !s_bBannerWritten;
  51. s_bBannerWritten = true;
  52. TCHAR path[MAX_PATH] = { 0 };
  53. ::GetModuleFileName(NULL, path, MAX_PATH);
  54. TCHAR* p = _tcsrchr(path, _T('\\'));
  55. if (p != NULL) *(p + 1) = _T('\0');
  56. _tcscat_s(path, MAX_PATH, _T("touch_trace.log"));
  57. TCHAR msg[1024] = { 0 };
  58. va_list ap;
  59. va_start(ap, fmt);
  60. _vsntprintf_s(msg, _countof(msg), _TRUNCATE, fmt, ap);
  61. va_end(ap);
  62. char utf8[2048] = { 0 };
  63. #ifdef _UNICODE
  64. if (::WideCharToMultiByte(CP_UTF8, 0, msg, -1, utf8, (int)sizeof(utf8) - 1, NULL, NULL) <= 0)
  65. return;
  66. #else
  67. _tcscpy_s(utf8, _countof(utf8), msg);
  68. #endif
  69. FILE* f = NULL;
  70. // 保护:单个日志超过 32MB 就不再追加,避免在收银机上长期运行把日志写到失控。
  71. // (全量 INPUT-TRACE 很啰嗦,阈值从 4MB 放宽到 32MB,否则一轮诊断还没跑完就静默停写了。)
  72. // 注意:GetFileAttributesEx 是 TCHAR 宏(Unicode 工程下展开为 ...ExW),
  73. // 必须传 TCHAR 的 path,不能传 Narrow(path) 得到的 UTF-8 char*(否则 C2664)。
  74. WIN32_FILE_ATTRIBUTE_DATA fad = { 0 };
  75. if (::GetFileAttributesEx(path, GetFileExInfoStandard, &fad)
  76. && fad.nFileSizeHigh == 0 && fad.nFileSizeLow >= (32u * 1024u * 1024u))
  77. return;
  78. // fopen_s 只吃 char*(窄字符路径),这里才需要 UTF-8 的 Narrow(path)
  79. if (fopen_s(&f, Narrow(path), "a") == 0 && f != NULL)
  80. {
  81. if (bWriteBanner)
  82. {
  83. fprintf(f, "======== DIAG-BUILD 2026-09-24 r43 (SetFocus(NULL) null-deref fix; diagnostics OFF by default) ========\n");
  84. }
  85. fprintf(f, "[%8u][T%5u] %s\n", (unsigned)::GetTickCount(), (unsigned)::GetCurrentThreadId(), utf8);
  86. // 立刻冲盘:程序被强杀 / 挂死 / 断电时,日志尾部(往往正是关键现场)不能丢。
  87. fflush(f);
  88. fclose(f);
  89. }
  90. }
  91. void LogButtonActivate(LPCTSTR name, bool captureState, unsigned buttonState, bool pushed)
  92. {
  93. // 位域含义(取自 UIManager.h 的 #define,勿凭记忆写):
  94. // UISTATE_DISABLED=0x04 / UISTATE_HOT=0x08
  95. // UISTATE_PUSHED=0x10 / UISTATE_CAPTURED=0x40
  96. Log(_T("BUTTON-ACTIVATE name=%s mgrCaptured=%d state=0x%04X pushed=%d hot=%d captured=%d disabled=%d"),
  97. name != NULL ? name : _T("null"),
  98. (int)(captureState ? 1 : 0),
  99. buttonState,
  100. (int)(pushed ? 1 : 0),
  101. (int)((buttonState & 0x0008u) ? 1 : 0),
  102. (int)((buttonState & 0x0040u) ? 1 : 0),
  103. (int)((buttonState & 0x0004u) ? 1 : 0));
  104. }
  105. }