UIDiag.cpp 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  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. // ★ 全库唯一的日志开关:需要关闭诊断时改这里(返回 false)。
  18. inline bool Enabled()
  19. {
  20. return true;
  21. }
  22. // 极简 TCHAR* → UTF-8 char* 转换(仅用于日志路径;非线程安全,够用即可)
  23. static const char* Narrow(LPCTSTR s)
  24. {
  25. static char s_buf[MAX_PATH * 3] = { 0 };
  26. #ifdef _UNICODE
  27. s_buf[0] = '\0';
  28. if (s != NULL)
  29. ::WideCharToMultiByte(CP_UTF8, 0, s, -1, s_buf, (int)sizeof(s_buf) - 1, NULL, NULL);
  30. return s_buf;
  31. #else
  32. return s;
  33. #endif
  34. }
  35. void Log(LPCTSTR fmt, ...)
  36. {
  37. if (!Enabled()) return;
  38. TCHAR path[MAX_PATH] = { 0 };
  39. ::GetModuleFileName(NULL, path, MAX_PATH);
  40. TCHAR* p = _tcsrchr(path, _T('\\'));
  41. if (p != NULL) *(p + 1) = _T('\0');
  42. _tcscat_s(path, MAX_PATH, _T("touch_trace.log"));
  43. TCHAR msg[1024] = { 0 };
  44. va_list ap;
  45. va_start(ap, fmt);
  46. _vsntprintf_s(msg, _countof(msg), _TRUNCATE, fmt, ap);
  47. va_end(ap);
  48. char utf8[2048] = { 0 };
  49. #ifdef _UNICODE
  50. if (::WideCharToMultiByte(CP_UTF8, 0, msg, -1, utf8, (int)sizeof(utf8) - 1, NULL, NULL) <= 0)
  51. return;
  52. #else
  53. _tcscpy_s(utf8, _countof(utf8), msg);
  54. #endif
  55. FILE* f = NULL;
  56. // 保护:单个日志超过 32MB 就不再追加,避免在收银机上长期运行把日志写到失控。
  57. // (全量 INPUT-TRACE 很啰嗦,阈值从 4MB 放宽到 32MB,否则一轮诊断还没跑完就静默停写了。)
  58. // 注意:GetFileAttributesEx 是 TCHAR 宏(Unicode 工程下展开为 ...ExW),
  59. // 必须传 TCHAR 的 path,不能传 Narrow(path) 得到的 UTF-8 char*(否则 C2664)。
  60. WIN32_FILE_ATTRIBUTE_DATA fad = { 0 };
  61. if (::GetFileAttributesEx(path, GetFileExInfoStandard, &fad)
  62. && fad.nFileSizeHigh == 0 && fad.nFileSizeLow >= (32u * 1024u * 1024u))
  63. return;
  64. // fopen_s 只吃 char*(窄字符路径),这里才需要 UTF-8 的 Narrow(path)
  65. if (fopen_s(&f, Narrow(path), "a") == 0 && f != NULL)
  66. {
  67. fprintf(f, "[%8u][T%5u] %s\n", (unsigned)::GetTickCount(), (unsigned)::GetCurrentThreadId(), utf8);
  68. // 立刻冲盘:程序被强杀 / 挂死 / 断电时,日志尾部(往往正是关键现场)不能丢。
  69. fflush(f);
  70. fclose(f);
  71. }
  72. }
  73. void LogButtonActivate(LPCTSTR name, bool captureState, unsigned buttonState, bool pushed)
  74. {
  75. // 位域含义(取自 UIManager.h 的 #define,勿凭记忆写):
  76. // UISTATE_DISABLED=0x04 / UISTATE_HOT=0x08
  77. // UISTATE_PUSHED=0x10 / UISTATE_CAPTURED=0x40
  78. Log(_T("BUTTON-ACTIVATE name=%s mgrCaptured=%d state=0x%04X pushed=%d hot=%d captured=%d disabled=%d"),
  79. name != NULL ? name : _T("null"),
  80. (int)(captureState ? 1 : 0),
  81. buttonState,
  82. (int)(pushed ? 1 : 0),
  83. (int)((buttonState & 0x0008u) ? 1 : 0),
  84. (int)((buttonState & 0x0040u) ? 1 : 0),
  85. (int)((buttonState & 0x0004u) ? 1 : 0));
  86. }
  87. }