1
2 // MusicPlayer2.cpp : 定义应用程序的类行为。
3 //
4
5 #include "stdafx.h"
6 #include "MusicPlayer2.h"
7 #include "WinVersionHelper.h"
8 #include "MusicPlayerDlg.h"
9 #include "MessageDlg.h"
10 #include "crashtool.h"
11 #include <Gdiplus.h>
12 #include "UpdateHelper.h"
13 #include "MusicPlayerCmdHelper.h"
14 #include "SongDataManager.h"
15 #include "UiMediaLibItemMgr.h"
16
17 #ifdef _DEBUG
18 #define new DEBUG_NEW
19 #endif
20
21
22 // CMusicPlayerApp
23
BEGIN_MESSAGE_MAP(CMusicPlayerApp,CWinApp)24 BEGIN_MESSAGE_MAP(CMusicPlayerApp, CWinApp)
25 //ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
26 ON_COMMAND(ID_HELP, &CMusicPlayerApp::OnHelp)
27 ON_COMMAND(ID_HELP_UPDATE_LOG, &CMusicPlayerApp::OnHelpUpdateLog)
28 ON_COMMAND(ID_HELP_CUSTOM_UI, &CMusicPlayerApp::OnHelpCustomUi)
29 ON_COMMAND(ID_HELP_FAQ, &CMusicPlayerApp::OnHelpFaq)
30 END_MESSAGE_MAP()
31
32
33 // CMusicPlayerApp 构造
34
35 CMusicPlayerApp::CMusicPlayerApp()
36 {
37 // 支持重新启动管理器
38 //m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART; //不需要支持重新启动管理器
39
40 // TODO: 在此处添加构造代码,
41 // 将所有重要的初始化放置在 InitInstance 中
42
43 CRASHREPORT::StartCrashReport();
44
45 //获取当前DPI
46 HDC hDC = ::GetDC(NULL);
47 m_dpi = GetDeviceCaps(hDC, LOGPIXELSY);
48 ::ReleaseDC(NULL, hDC);
49 if (m_dpi == 0)
50 {
51 WriteLog(L"Get system DPI failed!");
52 m_dpi = 96;
53 }
54 }
55
56
57 // 唯一的一个 CMusicPlayerApp 对象
58
59 CMusicPlayerApp theApp;
60
61
62 // CMusicPlayerApp 初始化
63
InitInstance()64 BOOL CMusicPlayerApp::InitInstance()
65 {
66 //替换掉对话框程序的默认类名
67 WNDCLASS wc;
68 ::GetClassInfo(AfxGetInstanceHandle(), _T("#32770"), &wc); //MFC默认的所有对话框的类名为#32770
69 wc.lpszClassName = _T("MusicPlayer_l3gwYT"); //将对话框的类名修改为新类名
70 AfxRegisterClass(&wc);
71
72 //初始化路径
73 wchar_t path[MAX_PATH];
74 GetModuleFileNameW(NULL, path, MAX_PATH);
75 m_module_path_reg = path;
76 if (m_module_path_reg.find(L' ') != std::wstring::npos)
77 {
78 //如果路径中有空格,则在程序路径前后添加双引号
79 m_module_path_reg = L'\"' + m_module_path_reg;
80 m_module_path_reg += L'\"';
81 }
82 m_module_dir = CCommon::GetExePath();
83 m_appdata_dir = CCommon::GetAppDataConfigDir();
84
85 LoadGlobalConfig();
86
87 #ifdef _DEBUG
88 m_local_dir = L".\\";
89 #else
90 m_local_dir = m_module_dir;
91 #endif // _DEBUG
92 if (m_general_setting_data.portable_mode)
93 m_config_dir = m_module_dir;
94 else
95 m_config_dir = m_appdata_dir;
96
97 m_config_path = m_config_dir + L"config.ini";
98 m_song_data_path = m_config_dir + L"song_data.dat";
99 m_recent_list_dat_path = m_config_dir + L"recent_list.dat";
100 // 以下三项不再使用,但路径留作旧版兼容,新程序不再创建这三个文件
101 m_recent_path_dat_path = m_config_dir + L"recent_path.dat";
102 m_recent_playlist_data_path = m_config_dir + L"playlist\\recent_playlist.dat";
103 m_recent_medialib_playlist_path = m_config_dir + L"recent_medialib_item.dat";
104
105 m_desktop_path = CCommon::GetDesktopPath();
106 m_lastfm_path = m_config_dir + L"lastfm.dat";
107 m_ui_data_path = m_config_dir + L"user_ui.dat";
108 //m_temp_path = CCommon::GetTemplatePath() + L"MusicPlayer2\\";
109 m_playlist_dir = m_config_dir + L"playlist\\";
110 CCommon::CreateDir(m_playlist_dir);
111
112 wstring cmd_line{ m_lpCmdLine };
113 //当程序被Windows重新启动时,直接退出程序
114 if (cmd_line.find(L"RestartByRestartManager") != wstring::npos)
115 {
116 LoadConfig(); // m_str_table.Init之前需要先加载语言配置
117 // Init略花时间,不应当在互斥量成功创建之前执行,这里是特例
118 m_str_table.Init(m_local_dir + L"language\\", m_general_setting_data.language_);
119 CCommon::SetThreadLanguageList(m_str_table.GetLanguageTag());
120 // 将命令行参数写入日志文件
121 wstring info = theApp.m_str_table.LoadTextFormat(L"LOG_RESTART_EXIT", { cmd_line });
122 WriteLog(info);
123 return FALSE;
124 }
125
126 bool cmd_control = CCommon::GetCmdLineCommand(cmd_line, m_cmd); //命令行参数是否包含参数命令
127 if (cmd_control) //如果从命令行参数解析到了命令,则将命令行参数清除
128 cmd_line.clear();
129
130 LPCTSTR str_mutex{};
131 #ifdef _DEBUG
132 str_mutex = _T("EY7n2uGon722");
133 #else
134 str_mutex = _T("bXS1E7joK0Kh");
135 #endif
136 //检查是否已有实例正在运行
137 HANDLE hMutex = ::CreateMutex(NULL, TRUE, str_mutex); //使用一个随机的字符串创建一个互斥量
138 if (hMutex != NULL)
139 {
140 if (GetLastError() == ERROR_ALREADY_EXISTS) //互斥量创建失败,说明已经有一个程序的实例正在运行
141 {
142 //AfxMessageBox(_T("已经有一个程序正在运行。"));
143 bool add_files{ false };
144 HWND handle = FindWindow(_T("MusicPlayer_l3gwYT"), NULL); //根据类名查找已运行实例窗口的句柄
145 if (handle == NULL)
146 {
147 //如果没有找到窗口句柄,则可能程序窗口还未创建,先延时一段时间再查找。
148 //这种情况只可能是在同时打开多个音频文件时启动了多个进程,这些进程几乎是同时启动的,
149 //此时虽然检测到已有另一个程序实例在运行,但是窗口还未创建,
150 //此时将命令行参数的文件添加到现有实例的播放列表中(通过将add_files设置为true来实现)
151 Sleep(500);
152 handle = FindWindow(_T("MusicPlayer_l3gwYT"), NULL);
153 add_files = true;
154 }
155 if (handle != NULL)
156 {
157 HWND minidlg_handle = FindWindow(_T("MiniDlg_ByH87M"), NULL);
158 if (!cmd_control || m_cmd == ControlCmd::MINI_MODE)
159 {
160 if (minidlg_handle == NULL) //没有找到“迷你模式”窗口,则激活主窗口
161 {
162 ShowWindow(handle, SW_SHOWNORMAL); //激活并显示窗口
163 SetForegroundWindow(handle); //将窗口设置为焦点
164 ::SendMessage(handle, WM_MAIN_WINDOW_ACTIVATED, 0, 0);
165 }
166 else //找到了“迷你模式”窗口,则激活“迷你模式”窗口
167 {
168 ShowWindow(minidlg_handle, SW_SHOWNORMAL);
169 SetForegroundWindow(minidlg_handle);
170 }
171 }
172
173 if (cmd_control)
174 {
175 if (m_cmd & ControlCmd::PLAY_PAUSE)
176 ::SendMessage(handle, WM_COMMAND, ID_PLAY_PAUSE, 0);
177 if (m_cmd & ControlCmd::_PREVIOUS)
178 ::SendMessage(handle, WM_COMMAND, ID_PREVIOUS, 0);
179 if (m_cmd & ControlCmd::_NEXT)
180 ::SendMessage(handle, WM_COMMAND, ID_NEXT, 0);
181 if (m_cmd & ControlCmd::STOP)
182 ::SendMessage(handle, WM_COMMAND, ID_STOP, 0);
183 if (m_cmd & ControlCmd::FF)
184 ::SendMessage(handle, WM_COMMAND, ID_FF, 0);
185 if (m_cmd & ControlCmd::REW)
186 ::SendMessage(handle, WM_COMMAND, ID_REW, 0);
187 if (m_cmd & ControlCmd::VOLUME_UP)
188 ::SendMessage(handle, WM_COMMAND, ID_VOLUME_UP, 0);
189 if (m_cmd & ControlCmd::VOLUME_DOWM)
190 ::SendMessage(handle, WM_COMMAND, ID_VOLUME_DOWN, 0);
191 if (m_cmd & ControlCmd::MINI_MODE)
192 ::PostMessage(handle, WM_COMMAND, ID_MINI_MODE, 0); //这里应该用PostMessage,因为响应ID_MINI_MODE的函数中有DoModel函数,只有当模态对话框关闭后函数才会返回
193 }
194
195 if (!cmd_line.empty()) //如果通过命令行传递了打开的文件名,且已有一个进程在运行,则将打开文件的命令和命令行参数传递给该进程
196 {
197 //通过WM_COPYDATA消息向已有进程传递消息
198 COPYDATASTRUCT copy_data;
199 copy_data.dwData = add_files ? COPY_DATA_ADD_FILE : COPY_DATA_OPEN_FILE;
200 copy_data.cbData = cmd_line.size() * sizeof(wchar_t);
201 copy_data.lpData = (const PVOID)cmd_line.c_str();
202 ::SendMessage(handle, WM_COPYDATA, 0, (LPARAM)©_data);
203 }
204 }
205 else //仍然找不到窗口句柄,说明程序还没有退出
206 {
207 LoadConfig();
208 m_str_table.Init(m_local_dir + L"language\\", m_general_setting_data.language_);
209 CCommon::SetThreadLanguageList(m_str_table.GetLanguageTag());
210 const wstring& info = theApp.m_str_table.LoadText(L"MSG_APP_RUNNING_INFO");
211 AfxMessageBox(info.c_str(), MB_ICONINFORMATION | MB_OK);
212 }
213 return FALSE; //退出当前程序
214 }
215 }
216
217 LoadConfig();
218 // 获取互斥量后StrTable应尽早初始化以免某些LoadText后以静态变量保存字符串引用的地方加载到空字符串<error>
219 m_str_table.Init(m_local_dir + L"language\\", m_general_setting_data.language_);
220
221 LoadSongData();
222 LoadLastFMData();
223
224 // 获取默认线程语言
225 CCommon::GetThreadLanguageList(m_def_lang_list);
226 //初始化界面语言
227 CCommon::SetThreadLanguageList(m_str_table.GetLanguageTag());
228
229 //检查bass.dll的版本是否和API的版本匹配
230 WORD dll_version{ HIWORD(BASS_GetVersion()) };
231 //WORD dll_version{ 0x203 };
232 if (dll_version != BASSVERSION)
233 {
234 wstring dll_version_str{ std::to_wstring(static_cast<int>(HIBYTE(dll_version))) + L'.' + std::to_wstring(static_cast<int>(LOBYTE(dll_version))) };
235 wstring bass_version_str{ std::to_wstring(static_cast<int>(HIBYTE(BASSVERSION))) + L'.' + std::to_wstring(static_cast<int>(LOBYTE(BASSVERSION))) };
236 wstring info = theApp.m_str_table.LoadTextFormat(L"MSG_BASS_VERSION_WARNING", { dll_version_str, bass_version_str });
237 if (AfxMessageBox(info.c_str(), MB_ICONWARNING | MB_OKCANCEL) == IDCANCEL)
238 return FALSE;
239 }
240
241 //启动时检查更新
242 #ifndef _DEBUG //DEBUG下不在启动时检查更新
243 if (m_general_setting_data.check_update_when_start)
244 {
245 AfxBeginThread(CheckUpdateThreadFunc, NULL);
246 }
247 #endif // !_DEBUG
248
249 //启动后台线程将歌曲数据分类
250 //StartClassifySongData();
251
252 // 如果一个运行在 Windows XP 上的应用程序清单指定要
253 // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
254 //则需要 InitCommonControlsEx()。 否则,将无法创建窗口。
255 INITCOMMONCONTROLSEX InitCtrls;
256 InitCtrls.dwSize = sizeof(InitCtrls);
257 // 将它设置为包括所有要在应用程序中使用的
258 // 公共控件类。
259 InitCtrls.dwICC = ICC_WIN95_CLASSES;
260 InitCommonControlsEx(&InitCtrls);
261
262 CWinApp::InitInstance();
263
264
265 AfxEnableControlContainer();
266
267 // 创建 shell 管理器,以防对话框包含
268 // 任何 shell 树视图控件或 shell 列表视图控件。
269 CShellManager* pShellManager = new CShellManager;
270
271 // 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题
272 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
273
274 // 标准初始化
275 // 如果未使用这些功能并希望减小
276 // 最终可执行文件的大小,则应移除下列
277 // 不需要的特定初始化例程
278 // 更改用于存储设置的注册表项
279 // TODO: 应适当修改该字符串,
280 // 例如修改为公司或组织名
281 //SetRegistryKey(_T("Apps By ZhongYang"));
282
283 //设置一个全局钩子以截获多媒体按键消息
284 if (m_hot_key_setting_data.global_multimedia_key_enable && !CWinVersionHelper::IsWindows81OrLater())
285 m_multimedia_key_hook = SetWindowsHookEx(WH_KEYBOARD_LL, CMusicPlayerApp::MultiMediaKeyHookProc, m_hInstance, 0);
286
287 // 初始化ITaskbarList3
288 if (CWinVersionHelper::IsWindows7OrLater())
289 EnableTaskbarInteraction(TRUE);
290
291 Gdiplus::GdiplusStartupInput gdiplusStartupInput;
292 GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
293
294 m_hScintillaModule = LoadLibrary(_T("SciLexer.dll"));
295 m_accelerator_res.Init();
296 m_chinese_pingyin_res.Init();
297
298 CMusicPlayerDlg dlg(cmd_line);
299 //CMusicPlayerDlg dlg(L"\"D:\\音乐\\纯音乐\\班得瑞\\05. Chariots Of Fire 火战车.mp3\"");
300 m_pMainWnd = &dlg;
301 INT_PTR nResponse = dlg.DoModal();
302 if (nResponse == IDOK)
303 {
304 // TODO: 在此放置处理何时用
305 // “确定”来关闭对话框的代码
306 }
307 else if (nResponse == IDCANCEL)
308 {
309 // TODO: 在此放置处理何时用
310 // “取消”来关闭对话框的代码
311 }
312 else if (nResponse == -1)
313 {
314 TRACE(traceAppMsg, 0, L"警告: 对话框创建失败,应用程序将意外终止。\n");
315 TRACE(traceAppMsg, 0, L"警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");
316 }
317
318 SaveLastFMData();
319 SaveGlobalConfig();
320
321 //如果媒体库正在更新,通知线程退出
322 m_media_update_para.thread_exit = true;
323 if (theApp.m_media_lib_updating)
324 WaitForSingleObject(m_media_lib_update_thread->m_hThread, 1000); //等待线程退出
325
326 SaveSongData();
327
328 // 删除上面创建的 shell 管理器。
329 if (pShellManager != NULL)
330 {
331 delete pShellManager;
332 }
333
334
335 //if (m_multimedia_key_hook != NULL)
336 //{
337 // UnhookWindowsHookEx(m_multimedia_key_hook);
338 // m_multimedia_key_hook = NULL;
339 //}
340
341 //如果音频音量处于淡出状态,则等待淡出结束再退出程序
342 for (int i{}; i < 100 && CPlayer::GetInstance().GetPlayerCore()->IsVolumeFadingOut(); i++)
343 {
344 Sleep(10);
345 }
346
347 // 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
348 // 而不是启动应用程序的消息泵。
349 return FALSE;
350 }
351
352
353
OnHelp()354 void CMusicPlayerApp::OnHelp()
355 {
356 // TODO: 在此添加命令处理程序代码
357 CMessageDlg helpDlg(L"HelpDlg"); // 此字符串对应CBaseDialog::GetDialogName,用于保证对话框不会重复打开
358 helpDlg.SetWindowTitle(theApp.m_str_table.LoadText(L"TITLE_HELP_DLG"));
359 helpDlg.SetInfoText(theApp.m_str_table.LoadText(L"TXT_HELP_DLG_WELCOM_TO_USE"));
360 helpDlg.SetLinkInfo(theApp.m_str_table.LoadText(L"TXT_HELP_DLG_SHOW_ONLINE_HELP"), L"https://github.com/zhongyang219/MusicPlayer2/wiki");
361 wstring info = theApp.m_str_table.LoadText(L"TXT_HELP_DLG_HELP_INFO");
362 info += L"\r\n\r\n" + GetSystemInfoString();
363 helpDlg.SetMessageText(info);
364 helpDlg.DoModal();
365 }
366
SaveSongData()367 void CMusicPlayerApp::SaveSongData()
368 {
369 CSongDataManager::GetInstance().SaveSongData(m_song_data_path);
370 }
371
CheckUpdate(bool message)372 void CMusicPlayerApp::CheckUpdate(bool message)
373 {
374 if (m_checking_update) //如果还在检查更新,则直接返回
375 return;
376 CFlagLocker update_locker(m_checking_update);
377 CWaitCursor wait_cursor;
378
379 wstring version; //程序版本
380 wstring link; //下载链接
381 CUpdateHelper update_helper;
382 update_helper.SetUpdateSource(static_cast<CUpdateHelper::UpdateSource>(m_general_setting_data.update_source));
383 if (!update_helper.CheckForUpdate())
384 {
385 if (message)
386 {
387 const wstring& info = theApp.m_str_table.LoadText(L"MSG_UPDATE_CHECK_FAILED");
388 AfxMessageBox(info.c_str(), MB_OK | MB_ICONWARNING);
389 }
390 return;
391 }
392
393 version = update_helper.GetVersion();
394 #ifdef _M_X64
395 link = update_helper.GetLink64();
396 #else
397 link = update_helper.GetLink();
398 #endif
399
400 if (version.empty() || link.empty())
401 {
402 if (message)
403 {
404 wstring info = theApp.m_str_table.LoadTextFormat(L"MSG_UPDATE_CHECK_ERROR", { L"row_data = " + std::to_wstring(update_helper.IsRowData()) });
405 theApp.m_pMainWnd->MessageBox(info.c_str(), NULL, MB_OK | MB_ICONWARNING);
406 }
407 return;
408 }
409 if (version > APP_VERSION) //如果服务器上的版本大于本地版本
410 {
411 //根据语言设置选择对应语言版本的更新内容
412 bool is_zh_cn = theApp.m_str_table.IsSimplifiedChinese();
413 wstring contents{ is_zh_cn ? update_helper.GetContentsZhCn() : update_helper.GetContentsEn() };
414
415 wstring info;
416 if (contents.empty())
417 info = theApp.m_str_table.LoadTextFormat(L"MSG_UPDATE_AVLIABLE", { version });
418 else
419 info = theApp.m_str_table.LoadTextFormat(L"MSG_UPDATE_AVLIABLE_2", { version, contents });
420
421 if (theApp.m_pMainWnd->MessageBox(info.c_str(), NULL, MB_YESNO | MB_ICONQUESTION) == IDYES)
422 {
423 ShellExecute(NULL, _T("open"), link.c_str(), NULL, NULL, SW_SHOW); //转到下载链接
424 }
425 }
426 else
427 {
428 if (message)
429 {
430 const wstring& info = theApp.m_str_table.LoadText(L"MSG_UPDATE_ALREADY");
431 theApp.m_pMainWnd->MessageBox(info.c_str(), NULL, MB_OK | MB_ICONINFORMATION);
432 }
433 }
434 }
435
CheckUpdateInThread(bool message)436 void CMusicPlayerApp::CheckUpdateInThread(bool message)
437 {
438 AfxBeginThread(CheckUpdateThreadFunc, (LPVOID)message);
439 }
440
LoadGlobalConfig()441 void CMusicPlayerApp::LoadGlobalConfig()
442 {
443 bool portable_mode_default{ false };
444 wstring global_cfg_path{ m_module_dir + L"global_cfg.ini" };
445 if (!CCommon::FileExist(global_cfg_path.c_str())) //如果global_cfg.ini不存在,则根据AppData/Roaming/MusicPlayer2目录下是否存在config.ini来判断配置文件的保存位置
446 {
447 portable_mode_default = !CCommon::FileExist((m_appdata_dir + L"config.ini").c_str());
448 }
449
450 CIniHelper ini{ global_cfg_path };
451 m_general_setting_data.portable_mode = ini.GetBool(L"config", L"portable_mode", portable_mode_default);
452
453 //执行一次保存操作,以检查当前目录是否有写入权限
454 m_module_dir_writable = ini.Save();
455
456 if (m_module_dir.find(CCommon::GetTemplatePath()) != wstring::npos) //如果当前路径是在Temp目录下,则强制将数据保存到Appdata
457 {
458 m_module_dir_writable = false;
459 }
460
461 if (!m_module_dir_writable) //如果当前目录没有写入权限,则设置配置保存到AppData目录
462 {
463 m_general_setting_data.portable_mode = false;
464 }
465 }
466
SaveGlobalConfig()467 void CMusicPlayerApp::SaveGlobalConfig()
468 {
469 CIniHelper ini{ m_module_dir + L"global_cfg.ini" };
470 ini.WriteBool(L"config", L"portable_mode", m_general_setting_data.portable_mode);
471
472 //检查是否保存成功
473 if (!ini.Save())
474 {
475 //if (m_cannot_save_global_config_warning)
476 //{
477 // CString info;
478 // info.LoadString(IDS_CONNOT_SAVE_CONFIG_WARNING);
479 // info.Replace(_T("<%file_path%>"), m_module_dir.c_str());
480 // AfxMessageBox(info, MB_ICONWARNING);
481 //}
482 //m_cannot_save_global_config_warning = false;
483 //return;
484 }
485 }
486
CheckUpdateThreadFunc(LPVOID lpParam)487 UINT CMusicPlayerApp::CheckUpdateThreadFunc(LPVOID lpParam)
488 {
489 CCommon::SetThreadLanguageList(theApp.m_str_table.GetLanguageTag());
490 theApp.CheckUpdate(lpParam); //检查更新
491 return 0;
492 }
493
SaveConfig()494 void CMusicPlayerApp::SaveConfig()
495 {
496 CIniHelper ini(m_config_path);
497 ini.WriteString(L"app", L"version", APP_VERSION);
498 ini.WriteBool(L"general", L"check_update_when_start", m_general_setting_data.check_update_when_start);
499 // ini.WriteInt(_T("general"), _T("language"), static_cast<int>(m_general_setting_data.language));
500 ini.WriteString(L"general", L"language_", m_general_setting_data.language_);
501 if (!CWinVersionHelper::IsWindows81OrLater())
502 ini.WriteBool(L"hot_key", L"global_multimedia_key_enable", m_hot_key_setting_data.global_multimedia_key_enable);
503 ini.Save();
504 }
505
LoadConfig()506 void CMusicPlayerApp::LoadConfig()
507 {
508 CIniHelper ini(m_config_path);
509 wstring config_version = ini.GetString(L"app", L"version", L"");
510 m_general_setting_data.check_update_when_start = ini.GetBool(L"general", L"check_update_when_start", true);
511 // m_general_setting_data.language = static_cast<Language>(ini.GetInt(L"general", L"language", 0));
512 m_general_setting_data.language_ = ini.GetString(L"general", L"language_", L""); // 留空表示“跟随系统”
513 if (!CWinVersionHelper::IsWindows81OrLater())
514 m_hot_key_setting_data.global_multimedia_key_enable = ini.GetBool(L"hot_key", L"global_multimedia_key_enable", false);
515 }
516
LoadImgResource()517 void CMusicPlayerApp::LoadImgResource()
518 {
519 //加载图片资源
520 m_image_set.default_cover_img = CCommon::GetPngImageResource(IDB_DEFAULT_ALBUM_COVER);
521 m_image_set.default_cover_not_played_img = CCommon::GetPngImageResource(IDB_DEFAULT_ALBUM_COVER_NOT_PLAYED);
522
523 m_image_set.default_cover_img_data = CCommon::GetPngImageResourceData(IDB_DEFAULT_ALBUM_COVER);
524 m_image_set.default_cover_not_played_img_data = CCommon::GetPngImageResourceData(IDB_DEFAULT_ALBUM_COVER_NOT_PLAYED);
525 }
526
DPI(int pixel)527 int CMusicPlayerApp::DPI(int pixel)
528 {
529 return (m_dpi * (pixel) / 96);
530 }
531
DPI(double pixel)532 int CMusicPlayerApp::DPI(double pixel)
533 {
534 return static_cast<int>(m_dpi * (pixel) / 96);
535 }
536
DPIRound(double pixel,double round)537 int CMusicPlayerApp::DPIRound(double pixel, double round)
538 {
539 double rtn;
540 rtn = static_cast<double>(m_dpi) * pixel / 96;
541 rtn += round;
542 return static_cast<int>(rtn);
543 }
544
GetDPIFromWindow(CWnd * pWnd)545 void CMusicPlayerApp::GetDPIFromWindow(CWnd* pWnd)
546 {
547 CWindowDC dc(pWnd);
548 HDC hDC = dc.GetSafeHdc();
549 m_dpi = GetDeviceCaps(hDC, LOGPIXELSY);
550 }
551
GetSystemInfoString()552 wstring CMusicPlayerApp::GetSystemInfoString()
553 {
554 std::wstringstream wss;
555 wss << L"System Info:\r\n"
556 << L"Windows Version: " << CWinVersionHelper::GetMajorVersion() << L'.' << CWinVersionHelper::GetMinorVersion()
557 << L" build " << CWinVersionHelper::GetBuildNumber() << L"\r\n"
558 << L"DPI: " << GetDPI() << L"\r\n";
559
560 wss << L"System Language List:\r\n";
561 for (const wstring& str : m_def_lang_list)
562 wss << str << L"; ";
563
564 return std::move(wss).str();
565 }
566
SetAutoRun(bool auto_run)567 void CMusicPlayerApp::SetAutoRun(bool auto_run)
568 {
569 CRegKey key;
570 //打开注册表项
571 if (key.Open(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Run")) != ERROR_SUCCESS)
572 {
573 return;
574 }
575 if (auto_run) //写入注册表项
576 {
577 if (key.SetStringValue(APP_NAME, m_module_path_reg.c_str()) != ERROR_SUCCESS)
578 {
579 return;
580 }
581 }
582 else //删除注册表项
583 {
584 //删除前先检查注册表项是否存在,如果不存在,则直接返回
585 wchar_t buff[256];
586 ULONG size{ 256 };
587 if (key.QueryStringValue(APP_NAME, buff, &size) != ERROR_SUCCESS)
588 return;
589 if (key.DeleteValue(APP_NAME) != ERROR_SUCCESS)
590 {
591 return;
592 }
593 }
594 }
595
GetAutoRun()596 bool CMusicPlayerApp::GetAutoRun()
597 {
598 CRegKey key;
599 if (key.Open(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Run")) != ERROR_SUCCESS)
600 {
601 //打开注册表“Software\\Microsoft\\Windows\\CurrentVersion\\Run”失败,则返回false
602 return false;
603 }
604 wchar_t buff[256];
605 ULONG size{ 256 };
606 if (key.QueryStringValue(APP_NAME, buff, &size) == ERROR_SUCCESS) //如果找到了APP_NAME键
607 {
608 return (m_module_path_reg == buff); //如果APP_NAME的值是当前程序的路径,就返回true,否则返回false
609 }
610 else
611 {
612 return false; //没有找到APP_NAME键,返回false
613 }
614 }
615
WriteLog(const wstring & log_str,int log_type)616 void CMusicPlayerApp::WriteLog(const wstring& log_str, int log_type)
617 {
618 static std::mutex log_mutex;
619 std::lock_guard<std::mutex> lock(log_mutex);
620 if (((log_type & NonCategorizedSettingData::LT_ERROR) != 0) && ((m_nc_setting_data.debug_log & NonCategorizedSettingData::LT_ERROR) != 0))
621 CCommon::WriteLog((m_config_dir + L"error.log").c_str(), log_str);
622 #ifdef _DEBUG
623 if (((log_type & NonCategorizedSettingData::LT_NORMAL) != 0) && ((m_nc_setting_data.debug_log & NonCategorizedSettingData::LT_NORMAL) != 0))
624 CCommon::WriteLog((m_config_dir + L"debug.log").c_str(), log_str);
625 #endif
626 }
627
StartUpdateMediaLib(bool force)628 void CMusicPlayerApp::StartUpdateMediaLib(bool force)
629 {
630 if (!m_media_lib_updating)
631 {
632 m_media_lib_updating = true;
633 m_media_update_para.num_added = 0;
634 m_media_update_para.force = force;
635 m_media_lib_update_thread = AfxBeginThread([](LPVOID lpParam)->UINT
636 {
637 if (theApp.m_media_lib_setting_data.remove_file_not_exist_when_update)
638 {
639 CMusicPlayerCmdHelper::CleanUpRecentFolders(); // 虽然仍有线程安全问题不过这行在“启动时更新媒体库”时立刻进行冲突的可能性比较小
640 CMusicPlayerCmdHelper::CleanUpSongData();
641 }
642 CMusicPlayerCmdHelper::UpdateMediaLib();
643 theApp.m_media_lib_updating = false;
644 //更新UI中我喜欢的音乐、所有曲目和媒体库项目列表
645 CUiMyFavouriteItemMgr::Instance().UpdateMyFavourite();
646 CUiFolderExploreMgr::Instance().UpdateFolders();
647 CUiAllTracksMgr::Instance().UpdateAllTracks();
648 CUiMediaLibItemMgr::Instance().Init();
649 return 0;
650 }, nullptr);
651 }
652 }
653
AutoSelectNotifyIcon()654 void CMusicPlayerApp::AutoSelectNotifyIcon()
655 {
656 if (CWinVersionHelper::IsWindows10OrLater())
657 {
658 bool light_mode = CWinVersionHelper::IsWindows10LightTheme();
659 if (light_mode) //浅色模式下,如果图标是白色,则改成黑色
660 {
661 if (m_app_setting_data.notify_icon_selected == 1)
662 m_app_setting_data.notify_icon_selected = 2;
663 }
664 else //深色模式下,如果图标是黑色,则改成白色
665 {
666 if (m_app_setting_data.notify_icon_selected == 2)
667 m_app_setting_data.notify_icon_selected = 1;
668 }
669 }
670 }
671
GetNotifyIncon(int index)672 HICON CMusicPlayerApp::GetNotifyIncon(int index)
673 {
674 static HICON hIcon_color = theApp.m_icon_mgr.GetHICON(IconMgr::IconType::IT_App, IconMgr::IconStyle::IS_Color, IconMgr::IconSize::IS_DPI_16);
675 static HICON hIcon_white = theApp.m_icon_mgr.GetHICON(IconMgr::IconType::IT_App_Monochrome, IconMgr::IconStyle::IS_OutlinedLight, IconMgr::IconSize::IS_DPI_16);
676 static HICON hIcon_black = theApp.m_icon_mgr.GetHICON(IconMgr::IconType::IT_App_Monochrome, IconMgr::IconStyle::IS_OutlinedDark, IconMgr::IconSize::IS_DPI_16);
677
678 if (index == 1)
679 return hIcon_white;
680 else if (index == 2)
681 return hIcon_black;
682 else
683 return hIcon_color;
684 }
685
IsScintillaLoaded() const686 bool CMusicPlayerApp::IsScintillaLoaded() const
687 {
688 return m_hScintillaModule != NULL;
689 }
690
691 //void CMusicPlayerApp::StartClassifySongData()
692 //{
693 // AfxBeginThread(ClassifySongDataThreadFunc, NULL);
694 //}
695
LoadSongData()696 void CMusicPlayerApp::LoadSongData()
697 {
698 CSongDataManager::GetInstance().LoadSongData(m_song_data_path);
699 }
700
MultiMediaKeyHookProc(int nCode,WPARAM wParam,LPARAM lParam)701 LRESULT CMusicPlayerApp::MultiMediaKeyHookProc(int nCode, WPARAM wParam, LPARAM lParam)
702 {
703 // if (wParam == HSHELL_APPCOMMAND)
704 //{
705 // int a = 0;
706 //}
707
708 //截获全局的多媒体按键消息
709 if (wParam == WM_KEYUP && !CPlayer::GetInstance().m_controls.IsActive())
710 {
711 KBDLLHOOKSTRUCT* pKBHook = (KBDLLHOOKSTRUCT*)lParam;
712 switch (pKBHook->vkCode)
713 {
714 case VK_MEDIA_PLAY_PAUSE:
715 SendMessage(AfxGetMainWnd()->GetSafeHwnd(), WM_COMMAND, ID_PLAY_PAUSE, 0);
716 return TRUE;
717 case VK_MEDIA_PREV_TRACK:
718 SendMessage(AfxGetMainWnd()->GetSafeHwnd(), WM_COMMAND, ID_PREVIOUS, 0);
719 return TRUE;
720 case VK_MEDIA_NEXT_TRACK:
721 SendMessage(AfxGetMainWnd()->GetSafeHwnd(), WM_COMMAND, ID_NEXT, 0);
722 return TRUE;
723 case VK_MEDIA_STOP:
724 SendMessage(AfxGetMainWnd()->GetSafeHwnd(), WM_COMMAND, ID_STOP, 0);
725 return TRUE;
726 default:
727 break;
728 }
729 }
730
731 CallNextHookEx(theApp.m_multimedia_key_hook, nCode, wParam, lParam);
732
733 return LRESULT();
734 }
735
736
737
ExitInstance()738 int CMusicPlayerApp::ExitInstance()
739 {
740 // TODO: 在此添加专用代码和/或调用基类
741 // 卸载GDI+
742 Gdiplus::GdiplusShutdown(m_gdiplusToken);
743
744 const auto& unknown_key = m_str_table.GetUnKnownKey();
745 if (!unknown_key.empty())
746 {
747 wstring log_str = m_str_table.LoadText(L"LOG_STRING_TABLE_UNKNOWN_KEY");
748 for (const auto& item : unknown_key)
749 log_str += item + L';';
750 WriteLog(log_str);
751 }
752 const auto& error_para_key = m_str_table.GetErrorParaKey();
753 if (!error_para_key.empty())
754 {
755 wstring log_str = m_str_table.LoadText(L"LOG_STRING_TABLE_PARA_ERROR");
756 for (const auto& item : error_para_key)
757 log_str += item + L';';
758 WriteLog(log_str);
759 }
760 // 释放ITaskbarList3
761 ReleaseTaskBarRefs();
762
763 return CWinApp::ExitInstance();
764 }
765
766
OnHelpUpdateLog()767 void CMusicPlayerApp::OnHelpUpdateLog()
768 {
769 // TODO: 在此添加命令处理程序代码
770 bool is_zh_cn = theApp.m_str_table.IsSimplifiedChinese();
771 if (is_zh_cn)
772 ShellExecute(NULL, _T("open"), _T("https://github.com/zhongyang219/MusicPlayer2/blob/master/Documents/update_log.md"), NULL, NULL, SW_SHOW);
773 else
774 ShellExecute(NULL, _T("open"), _T("https://github.com/zhongyang219/MusicPlayer2/blob/master/Documents/update_log_en-us.md"), NULL, NULL, SW_SHOW);
775
776 }
777
778
OnHelpCustomUi()779 void CMusicPlayerApp::OnHelpCustomUi()
780 {
781 ShellExecute(NULL, _T("open"), _T("https://github.com/zhongyang219/MusicPlayer2/wiki/%E7%94%A8%E6%88%B7%E8%87%AA%E5%AE%9A%E4%B9%89%E7%95%8C%E9%9D%A2"), NULL, NULL, SW_SHOW);
782 }
783
784
OnHelpFaq()785 void CMusicPlayerApp::OnHelpFaq()
786 {
787 bool is_zh_cn = theApp.m_str_table.IsSimplifiedChinese();
788 if (is_zh_cn)
789 ShellExecute(NULL, _T("open"), _T("https://github.com/zhongyang219/MusicPlayer2/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98"), NULL, NULL, SW_SHOW);
790 else
791 ShellExecute(NULL, _T("open"), _T("https://github.com/zhongyang219/MusicPlayer2/wiki/FAQ"), NULL, NULL, SW_SHOW);
792 }
793
LoadLastFMData()794 void CMusicPlayerApp::LoadLastFMData() {
795 m_lastfm.LoadData(m_lastfm_path);
796 }
797
SaveLastFMData()798 void CMusicPlayerApp::SaveLastFMData() {
799 m_lastfm.SaveData(m_lastfm_path);
800 }
801
UpdateLastFMNowPlaying()802 void CMusicPlayerApp::UpdateLastFMNowPlaying() {
803 AfxBeginThread(UpdateLastFMNowPlayingFunProc, (LPVOID)NULL);
804 }
805
UpdateLastFMNowPlayingFunProc(LPVOID lpParam)806 UINT CMusicPlayerApp::UpdateLastFMNowPlayingFunProc(LPVOID lpParam) {
807 theApp.m_lastfm.UpdateNowPlaying();
808 return 0;
809 }
810
UpdateLastFMFavourite(bool favourite)811 void CMusicPlayerApp::UpdateLastFMFavourite(bool favourite) {
812 AfxBeginThread(UpdateLastFMFavouriteFunProc, (LPVOID)favourite);
813 }
814
UpdateLastFMFavouriteFunProc(LPVOID lpParam)815 UINT CMusicPlayerApp::UpdateLastFMFavouriteFunProc(LPVOID lpParam) {
816 auto favourite = (bool)lpParam;
817 if (favourite) {
818 theApp.m_lastfm.Love();
819 }
820 else {
821 theApp.m_lastfm.Unlove();
822 }
823 return 0;
824 }
825
LastFMScrobble()826 void CMusicPlayerApp::LastFMScrobble() {
827 AfxBeginThread(LastFMScrobbleFunProc, (LPVOID)NULL);
828 }
829
LastFMScrobbleFunProc(LPVOID lpParam)830 UINT CMusicPlayerApp::LastFMScrobbleFunProc(LPVOID lpParam) {
831 theApp.m_lastfm.Scrobble();
832 return 0;
833 }
834
UpdateUiMeidaLibItems()835 void CMusicPlayerApp::UpdateUiMeidaLibItems()
836 {
837 AfxBeginThread([](LPVOID lpParam)->UINT {
838 //如果没有设置“启动时更新媒体库”,才在这里更新Ui中所有曲目和媒体库项目列表,否则在StartUpdateMediaLib中更新
839 if (!theApp.m_media_lib_setting_data.update_media_lib_when_start_up)
840 {
841 CUiMyFavouriteItemMgr::Instance().UpdateMyFavourite();
842 CUiFolderExploreMgr::Instance().UpdateFolders();
843 CUiAllTracksMgr::Instance().UpdateAllTracks();
844 CUiMediaLibItemMgr::Instance().Init();
845 }
846 return 0;
847 }, (LPVOID)NULL);
848 }
849