xref: /MusicPlayer2/MusicPlayer2/Common.cpp (revision ba5c72e4b2a0da2a9d0a434aa7cfdd1e3f4bc68b)
1 #include "stdafx.h"
2 #include "Common.h"
3 #include "resource.h"
4 #include "FilePathHelper.h"
5 #include <random>
6 #include <strsafe.h>
7 // #include <pathcch.h>
8 
CCommon()9 CCommon::CCommon()
10 {
11 }
12 
13 
~CCommon()14 CCommon::~CCommon()
15 {
16 }
17 
18 
19 //void CCommon::GetAllFormatFiles(wstring path, vector<wstring>& files, const vector<wstring>& format, size_t max_file)
20 //{
21 //	//文件句柄
22 //	int hFile = 0;
23 //	//文件信息(用Unicode保存使用_wfinddata_t,多字节字符集使用_finddata_t)
24 //	_wfinddata_t fileinfo;
25 //	wstring file_path;
26 //	for (auto a_format : format)
27 //	{
28 //		if ((hFile = _wfindfirst(file_path.assign(path).append(L"\\*.").append(a_format).c_str(), &fileinfo)) != -1)
29 //		{
30 //			do
31 //			{
32 //				if (files.size() >= max_file) break;
33 //				files.push_back(file_path.assign(fileinfo.name));  //将文件名保存
34 //			} while (_wfindnext(hFile, &fileinfo) == 0);
35 //		}
36 //		_findclose(hFile);
37 //	}
38 //	std::sort(files.begin(), files.end());		//对容器里的文件按名称排序
39 //}
40 
FileExist(const wstring & file,bool is_case_sensitive)41 bool CCommon::FileExist(const wstring& file, bool is_case_sensitive)
42 {
43     WIN32_FIND_DATA findFileData;
44     HANDLE hFind = FindFirstFileW(file.c_str(), &findFileData);
45     if (hFind == INVALID_HANDLE_VALUE)  // 没有找到说明不区分大小写也没有匹配文件
46         return false;
47     FindClose(hFind);
48     if (is_case_sensitive)              // 如果需要区分大小写那么重新严格比较
49         return CFilePathHelper(file).GetFileName() == findFileData.cFileName;
50     return true;
51 }
52 
FolderExist(const wstring & file)53 bool CCommon::FolderExist(const wstring& file)
54 {
55     DWORD dwAttrib = GetFileAttributes(file.c_str());
56     return INVALID_FILE_ATTRIBUTES != dwAttrib && 0 != (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
57 }
58 
IsFolder(const wstring & path)59 bool CCommon::IsFolder(const wstring& path)
60 {
61     DWORD dwAttrib = GetFileAttributes(path.c_str());
62     return (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0;
63 }
64 
CheckAndFixFile(wstring & file)65 bool CCommon::CheckAndFixFile(wstring& file)
66 {
67     WIN32_FIND_DATA findFileData;
68     HANDLE hFind = FindFirstFileW(file.c_str(), &findFileData);
69     if (hFind == INVALID_HANDLE_VALUE)
70         return false;
71     FindClose(hFind);
72     CFilePathHelper file_path(file);
73     file = file_path.GetDir() + findFileData.cFileName;  // 修正file的文件名大小写到与文件一致
74     return true;
75 }
76 
GetFileLastModified(const wstring & file_path,unsigned __int64 & modified_time)77 bool CCommon::GetFileLastModified(const wstring& file_path, unsigned __int64& modified_time)
78 {
79     // 使用GetFileAttributesEx,耗时大约为FindFirstFile的2/3
80     WIN32_FILE_ATTRIBUTE_DATA file_attributes;
81     if (GetFileAttributesEx(file_path.c_str(), GetFileExInfoStandard, &file_attributes))
82     {
83         ULARGE_INTEGER last_modified_time{};
84         last_modified_time.HighPart = file_attributes.ftLastWriteTime.dwHighDateTime;
85         last_modified_time.LowPart = file_attributes.ftLastWriteTime.dwLowDateTime;
86         modified_time = last_modified_time.QuadPart;
87         return true;
88     }
89     return false;
90 }
91 
GetFileCreateTime(const wstring & file_path,unsigned __int64 & create_time)92 bool CCommon::GetFileCreateTime(const wstring& file_path, unsigned __int64& create_time)
93 {
94     // 使用GetFileAttributesEx,耗时大约为FindFirstFile的2/3
95     WIN32_FILE_ATTRIBUTE_DATA file_attributes;
96     if (GetFileAttributesEx(file_path.c_str(), GetFileExInfoStandard, &file_attributes))
97     {
98         ULARGE_INTEGER time{};
99         time.HighPart = file_attributes.ftCreationTime.dwHighDateTime;
100         time.LowPart = file_attributes.ftCreationTime.dwLowDateTime;
101         create_time = time.QuadPart;
102         return true;
103     }
104     return false;
105 }
106 
FileTimeToTimeT(unsigned __int64 file_time)107 time_t CCommon::FileTimeToTimeT(unsigned __int64 file_time)
108 {
109     if (file_time == 0)
110         return 0;
111     // 1601年到1970年之间的时间间隔,以100纳秒为单位
112     const ULONGLONG epochDelta = 116444736000000000ULL;
113 
114     // 将 FILETIME 转换为自1970年1月1日以来的100纳秒间隔数
115     ULONGLONG ull = file_time - epochDelta;
116 
117     // 将100纳秒转换为秒
118     time_t t = ull / 10000000ULL;
119 
120     return t;
121 }
122 
IsFileHidden(const wstring & file_path)123 bool CCommon::IsFileHidden(const wstring& file_path)
124 {
125     DWORD attirbute = GetFileAttributes(file_path.c_str());
126     return (attirbute & FILE_ATTRIBUTE_HIDDEN) != 0;
127 }
128 
SetFileHidden(const wstring & file_path,bool hidden)129 void CCommon::SetFileHidden(const wstring& file_path, bool hidden)
130 {
131     DWORD attirbute = GetFileAttributes(file_path.c_str());
132     if (hidden)
133         attirbute |= FILE_ATTRIBUTE_HIDDEN;
134     else
135         attirbute &= ~FILE_ATTRIBUTE_HIDDEN;
136     SetFileAttributes(file_path.c_str(), attirbute);
137 }
138 
DeleteStringBom(string & str)139 void CCommon::DeleteStringBom(string& str)
140 {
141     //去掉utf8的BOM
142     if (str.size() >= 3 && str.substr(0, 3) == string{ '\xef', '\xbb', '\xbf' })
143         str = str.substr(3);
144     //去掉utf16的BOM
145     if (str.size() >= 2 && str.substr(0, 2) == string{ '\xff', '\xfe' })
146         str = str.substr(2);
147 }
148 
StringCsvNormalize(CString & str)149 bool CCommon::StringCsvNormalize(CString& str)
150 {
151     bool rtn{ false };
152     //如果字符串中有双引号,则将其替换为两个双引号
153     if (str.Replace(L"\"", L"\"\""))
154         rtn = true;
155 
156     //如果字符串中包含换行符、双引号和/或逗号,则将其用双引号包裹
157     const LPCTSTR spec_str = L"\r\",/";
158     if (str.FindOneOf(spec_str) >= 0)
159     {
160         str = L'\"' + str;
161         str.AppendChar(L'\"');
162         rtn = true;
163     }
164     return rtn;
165 }
166 
167 //bool CCommon::FileIsMidi(const wstring & file_name)
168 //{
169 //	wstring type;
170 //	type = file_name.substr(file_name.size() - 4, 4);			//获取扩展名
171 //	std::transform(type.begin(), type.end(), type.begin(), tolower);		//将扩展名转换成小写
172 //	return (type == L".mid");
173 //}
174 
StringCopy(char * dest,int size,string source)175 void CCommon::StringCopy(char* dest, int size, string source)
176 {
177     int source_size = source.size();
178     for (int i{}; i < size && i < source_size; i++)
179     {
180         dest[i] = source[i];
181     }
182 }
183 
184 //bool CCommon::StringCompareNoCase(const wstring & str1, const wstring & str2)
185 //{
186 //	wstring _str1{ str1 }, _str2{ str2 };
187 //	StringTransform(_str1, false);
188 //	StringTransform(_str2, false);
189 //	return (_str1 == _str2);
190 //}
191 
192 //size_t CCommon::StringFindNoCase(const wstring & str, const wstring & find_str)
193 //{
194 //	wstring _str{ str }, _find_str{ find_str };
195 //	StringTransform(_str, false);
196 //	StringTransform(_find_str, false);
197 //	return _str.find(_find_str);
198 //}
199 
IsDivideChar(wchar_t ch)200 bool CCommon::IsDivideChar(wchar_t ch)
201 {
202     if ((ch >= L'0' && ch <= L'9') || (ch >= L'a' && ch <= L'z') || (ch >= L'A' && ch <= L'Z') || ch > 255)
203         return false;
204     else
205         return true;
206 }
207 
StrIsNumber(const wstring & str)208 bool CCommon::StrIsNumber(const wstring& str)
209 {
210     if (str.empty())
211         return false;
212     for (const auto& ch : str)
213     {
214         if (ch < L'0' || ch > L'9')
215             return false;
216     }
217     return true;
218 }
219 
CharIsNumber(wchar_t ch)220 bool CCommon::CharIsNumber(wchar_t ch)
221 {
222     return (ch >= L'0' && ch <= L'9');
223 }
224 
StringToInt(const wstring & str)225 int CCommon::StringToInt(const wstring& str)
226 {
227     size_t start{ std::wstring::npos };
228     size_t end{ std::wstring::npos };
229     for (size_t i{}; i < str.size(); i++)
230     {
231         //标记第一个数字的位置
232         if (CharIsNumber(str[i]) && start == std::wstring::npos)
233             start = i;
234         //标记找到第一个数字后第一个不是数字的位置
235         if (start != std::wstring::npos && !CharIsNumber(str[i]))
236         {
237             end = i;
238             break;
239         }
240     }
241     if (start < str.size() && end > start)
242     {
243         wstring num_str = str.substr(start, end - start);
244         return _wtoi(num_str.c_str());
245     }
246     return 0;
247 }
248 
StringSplitLine(const wstring & source_str,vector<wstring> & results,bool skip_empty,bool trim)249 void CCommon::StringSplitLine(const wstring& source_str, vector<wstring>& results, bool skip_empty, bool trim)
250 {
251     results.clear();
252     if (source_str.empty())
253         return;
254 
255     auto push_back_str = [&](const wchar_t* start, const wchar_t* end)
256         {
257             wstring tmp(start, end);
258             if (trim)
259                 StringNormalize(tmp);
260             if (!skip_empty || !tmp.empty())
261                 results.push_back(std::move(tmp));
262         };
263 
264     const wchar_t* line_start_pos = source_str.data();
265     const wchar_t* cur_pos = line_start_pos;
266     const wchar_t* end_pos = line_start_pos + source_str.size();
267     while (cur_pos < end_pos)
268     {
269         if (*cur_pos == L'\r' || *cur_pos == L'\n')
270         {
271             push_back_str(line_start_pos, cur_pos);
272             ++cur_pos;                                          // 指针移动到下一个字符
273             if (*cur_pos == L'\n' && *(cur_pos - 1) == L'\r')   // 如果下一个字符是LF且位于CR后面那么跳过
274                 ++cur_pos;
275             line_start_pos = cur_pos;
276         }
277         else
278             ++cur_pos;
279     }
280     push_back_str(line_start_pos, cur_pos);
281 }
282 
283 template<class T>
_StringSplit(const T & str,wchar_t div_ch,vector<T> & results,bool skip_empty,bool trim)284 static void _StringSplit(const T& str, wchar_t div_ch, vector<T>& results, bool skip_empty, bool trim)
285 {
286     results.clear();
287     size_t split_index = -1;
288     size_t last_split_index = -1;
289     while (true)
290     {
291         split_index = str.find(div_ch, split_index + 1);
292         T split_str = str.substr(last_split_index + 1, split_index - last_split_index - 1);
293         if (trim)
294             CCommon::StringNormalize(split_str);
295         if (!split_str.empty() || !skip_empty)
296             results.push_back(split_str);
297         if (split_index == wstring::npos)
298             break;
299         last_split_index = split_index;
300     }
301 }
302 
303 template<class T>
_StringSplit(const T & str,const T & div_str,vector<T> & results,bool skip_empty,bool trim)304 void _StringSplit(const T& str, const T& div_str, vector<T>& results, bool skip_empty /*= true*/, bool trim)
305 {
306     results.clear();
307     size_t split_index = 0 - div_str.size();
308     size_t last_split_index = 0 - div_str.size();
309     while (true)
310     {
311         split_index = str.find(div_str, split_index + div_str.size());
312         T split_str = str.substr(last_split_index + div_str.size(), split_index - last_split_index - div_str.size());
313         if (trim)
314             CCommon::StringNormalize(split_str);
315         if (!split_str.empty() || !skip_empty)
316             results.push_back(split_str);
317         if (split_index == wstring::npos)
318             break;
319         last_split_index = split_index;
320     }
321 }
322 
StringSplit(const wstring & str,wchar_t div_ch,vector<wstring> & results,bool skip_empty,bool trim)323 void CCommon::StringSplit(const wstring& str, wchar_t div_ch, vector<wstring>& results, bool skip_empty, bool trim)
324 {
325     _StringSplit<std::wstring>(str, div_ch, results, skip_empty, trim);
326 }
327 
StringSplit(const wstring & str,const wstring & div_str,vector<wstring> & results,bool skip_empty,bool trim)328 void CCommon::StringSplit(const wstring& str, const wstring& div_str, vector<wstring>& results, bool skip_empty /*= true*/, bool trim)
329 {
330     _StringSplit<std::wstring>(str, div_str, results, skip_empty, trim);
331 }
332 
StringSplit(const string & str,char div_ch,vector<string> & result,bool skip_empty,bool trim)333 void CCommon::StringSplit(const string& str, char div_ch, vector<string>& result, bool skip_empty, bool trim)
334 {
335     _StringSplit<std::string>(str, div_ch, result, skip_empty, trim);
336 }
337 
StringSplit(const string & str,const string & div_str,vector<string> & results,bool skip_empty,bool trim)338 void CCommon::StringSplit(const string& str, const string& div_str, vector<string>& results, bool skip_empty, bool trim)
339 {
340     _StringSplit<std::string>(str, div_str, results, skip_empty, trim);
341 }
342 
StringSplitWithMulitChars(const wstring & str,const wstring & div_ch,vector<wstring> & results,bool skip_empty)343 void CCommon::StringSplitWithMulitChars(const wstring& str, const wstring& div_ch, vector<wstring>& results, bool skip_empty /*= true*/)
344 {
345     results.clear();
346     size_t split_index = -1;
347     size_t last_split_index = -1;
348     while (true)
349     {
350         split_index = str.find_first_of(div_ch, split_index + 1);
351         wstring split_str = str.substr(last_split_index + 1, split_index - last_split_index - 1);
352         StringNormalize(split_str);
353         if (!split_str.empty() || !skip_empty)
354             results.push_back(split_str);
355         if (split_index == wstring::npos)
356             break;
357         last_split_index = split_index;
358     }
359 }
360 
StringSplitWithSeparators(const wstring & str,const vector<wstring> & separators,vector<wstring> & results,bool skip_empty)361 void CCommon::StringSplitWithSeparators(const wstring& str, const vector<wstring>& separators, vector<wstring>& results, bool skip_empty /*= true*/)
362 {
363     results.clear();
364     size_t split_index = 0;
365     size_t last_split_index = 0;
366 
367     wstring split_str;
368     size_t i{};
369     for (; i < separators.size(); i++)
370     {
371         if (i == 0)
372             split_index = str.find(separators[i]);
373         else
374             split_index = str.find(separators[i], split_index + separators[i - 1].size());
375 
376         if (split_index == wstring::npos)
377             break;
378 
379         if (i == 0)
380             split_str = str.substr(0, split_index);
381         else
382             split_str = str.substr(last_split_index + separators[i - 1].size(), split_index - last_split_index - separators[i - 1].size());
383         if (!split_str.empty() || !skip_empty)
384             results.push_back(split_str);
385 
386         last_split_index = split_index;
387     }
388 
389     if (i - 1 >= 0 && i - 1 < separators.size())
390     {
391         size_t index = last_split_index + separators[i - 1].size();
392         if (index < str.size())
393             split_str = str.substr(last_split_index + separators[i - 1].size());
394         if (!split_str.empty() || !skip_empty)
395             results.push_back(split_str);
396     }
397 }
398 
StringMerge(const vector<wstring> & strings,wchar_t div_ch)399 wstring CCommon::StringMerge(const vector<wstring>& strings, wchar_t div_ch)
400 {
401     wstring result;
402     for (const auto& str : strings)
403     {
404         result.append(str).push_back(div_ch);
405     }
406     if (!strings.empty())
407         result.pop_back();
408     return result;
409 }
410 
MergeStringList(const vector<wstring> & values)411 wstring CCommon::MergeStringList(const vector<wstring>& values)
412 {
413     wstring str_merge;
414     int index = 0;
415     // 在每个字符串前后加上引号,再将它们用逗号连接起来
416     for (wstring str : values)
417     {
418         StringNormalize(str);
419         if (str.empty()) continue;
420         if (index > 0)
421             str_merge.push_back(L',');
422         str_merge.push_back(L'\"');
423         str_merge += str;
424         str_merge.push_back(L'\"');
425         index++;
426     }
427     return str_merge;
428 }
429 
SplitStringList(vector<wstring> & values,const wstring & str_value)430 void CCommon::SplitStringList(vector<wstring>& values, const wstring& str_value)
431 {
432     CCommon::StringSplit(str_value, L"\",\"", values);
433     if (!values.empty())
434     {
435         // 结果中第一项前面和最后一项的后面各还有一个引号,将它们删除
436         values.front() = values.front().substr(1);
437         values.back().pop_back();
438     }
439 }
440 
TranslateToSimplifiedChinese(const wstring & str)441 wstring CCommon::TranslateToSimplifiedChinese(const wstring& str)
442 {
443     wstring result;
444     size_t size{ str.size() };
445     if (size == 0) return wstring();
446     wchar_t* out_buff = new wchar_t[size + 1];
447     WORD wLanguageID = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED);
448     LCID Locale = MAKELCID(wLanguageID, SORT_CHINESE_PRCP);
449     int rtn = LCMapString(Locale, LCMAP_SIMPLIFIED_CHINESE, str.c_str(), -1, out_buff, size * sizeof(wchar_t));
450     result.assign(out_buff);
451     delete[] out_buff;
452     return result;
453 }
454 
TranslateToTranditionalChinese(const wstring & str)455 wstring CCommon::TranslateToTranditionalChinese(const wstring& str)
456 {
457     wstring result;
458     size_t size{ str.size() };
459     if (size == 0) return wstring();
460     wchar_t* out_buff = new wchar_t[size + 1];
461     WORD wLanguageID = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED);
462     LCID Locale = MAKELCID(wLanguageID, SORT_CHINESE_PRCP);
463     int rtn = LCMapString(Locale, LCMAP_TRADITIONAL_CHINESE, str.c_str(), -1, out_buff, size * sizeof(wchar_t));
464     result.assign(out_buff);
465     delete[] out_buff;
466     return result;
467 }
468 
FileNameNormalize(wstring & file_name)469 void CCommon::FileNameNormalize(wstring& file_name)
470 {
471     //查找字符串中的无效字符,并将其替换成‘_’
472     const wstring invalid_chars{ L"\\\"/:*?<>|\a\b\f\n\r\t\v" };
473     int index{ -1 };
474     while (true)
475     {
476         index = file_name.find_first_of(invalid_chars, index + 1);
477         if (index == wstring::npos)
478         {
479             return;
480         }
481         else
482         {
483             if (file_name[index] == L'<')
484                 file_name[index] = L'(';
485             else if (file_name[index] == L'>')
486                 file_name[index] = L')';
487             else
488                 file_name[index] = L'_';
489         }
490     }
491 }
492 
IsFileNameValid(const wstring & file_name)493 bool CCommon::IsFileNameValid(const wstring& file_name)
494 {
495     const wstring invalid_chars{ L"\\\"/:*?<>|\a\b\f\n\r\t\v" };
496     return (file_name.find_first_of(invalid_chars) == wstring::npos);
497 }
498 
GetFileSize(const wstring & file_name)499 size_t CCommon::GetFileSize(const wstring& file_name)
500 {
501     std::streampos l, m;
502     ifstream file(file_name, std::ios::in | std::ios::binary);
503     l = file.tellg();
504     file.seekg(0, std::ios::end);
505     m = file.tellg();
506     file.close();
507     return static_cast<size_t>(m - l);
508 }
509 
StrToUnicode(const string & str,CodeType code_type,bool auto_utf8)510 wstring CCommon::StrToUnicode(const string& str, CodeType code_type, bool auto_utf8)
511 {
512     // 当使用ANSI,UTF8,UTF8_NO_BOM,UTF16LE,UTF16BE时不检测直接转换
513     // 使用默认值CodeType::AUTO时先检测BOM,如果没有BOM则按ANSI转换
514     // 将auto_utf8置true使AUTO检测没有BOM的字串是否为UTF8序列,如果符合则按UTF8_NO_BOM转换
515     // auto_utf8有可能将过短的ANSI误认为是UTF8导致乱码
516     if (str.empty()) return wstring();
517     // code_type为AUTO时从这里开始
518     if (code_type == CodeType::AUTO)	// 先根据BOM判断编码类型
519     {
520         code_type = JudgeCodeType(str, CodeType::ANSI, auto_utf8);
521     }
522     bool result_ready = false;
523     int size;
524     wstring result;
525     // 如果编码类型此时已经可以确定并转换好不必回落到ANSI则置位result_ready
526     if (code_type == CodeType::UTF8 || code_type == CodeType::UTF8_NO_BOM)
527     {
528         string temp;
529         //如果前面有BOM,则去掉BOM
530         if (str.size() >= 3 && str[0] == -17 && str[1] == -69 && str[2] == -65)
531             temp = str.substr(3);
532         else
533             temp = str;
534         size = MultiByteToWideChar(CP_UTF8, 0, temp.c_str(), -1, NULL, 0);
535         if (size <= 0) return wstring();
536         wchar_t* str_unicode = new wchar_t[size + 1];
537         MultiByteToWideChar(CP_UTF8, 0, temp.c_str(), -1, str_unicode, size);
538         result.assign(str_unicode);
539         delete[] str_unicode;
540         result_ready = true;
541     }
542     else if (code_type == CodeType::UTF16LE)
543     {
544         string temp;
545         //如果前面有BOM,则去掉BOM
546         if (str.size() >= 2 && str[0] == -1 && str[1] == -2)
547             temp = str.substr(2);
548         else
549             temp = str;
550         if (temp.size() % 2 == 1)
551             temp.pop_back();
552         temp.push_back('\0');
553         result = (const wchar_t*)temp.c_str();
554         result_ready = true;
555     }
556     else if (code_type == CodeType::UTF16BE)
557     {
558         string temp;
559         //如果前面有BOM,则去掉BOM
560         if (str.size() >= 2 && str[0] == -2 && str[1] == -1)
561             temp = str.substr(2);
562         else
563             temp = str;
564         if (temp.size() % 2 == 1)
565             temp.pop_back();
566         temp.push_back('\0');
567         wchar_t* p = (wchar_t*)temp.c_str();
568         convertBE_LE(p, temp.size() >> 1);
569         result = p;
570         result_ready = true;
571     }
572 
573     // 如果以上均未执行那么按系统ANSI编码处理
574     if (!result_ready)
575     {
576         size = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
577         if (size <= 0) return wstring();
578         wchar_t* str_unicode = new wchar_t[size + 1];
579         MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, str_unicode, size);
580         result.assign(str_unicode);
581         delete[] str_unicode;
582     }
583     return result;
584 }
585 
UnicodeToStr(const wstring & wstr,CodeType code_type,bool * char_cannot_convert)586 string CCommon::UnicodeToStr(const wstring& wstr, CodeType code_type, bool* char_cannot_convert)
587 {
588     if (wstr.empty()) return string();
589     if (char_cannot_convert != nullptr)
590         *char_cannot_convert = false;
591     BOOL UsedDefaultChar{ FALSE };
592     string result;
593     int size{ 0 };
594     if (code_type == CodeType::ANSI)
595     {
596         size = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
597         if (size <= 0) return string();
598         char* str = new char[size + 1];
599         WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, str, size, NULL, &UsedDefaultChar);
600         result.assign(str);
601         delete[] str;
602     }
603     else if (code_type == CodeType::UTF8 || code_type == CodeType::UTF8_NO_BOM)
604     {
605         result.clear();
606         if (code_type == CodeType::UTF8)
607         {
608             result.push_back(-17);
609             result.push_back(-69);
610             result.push_back(-65);
611         }
612         size = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
613         if (size <= 0) return string();
614         char* str = new char[size + 1];
615         WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, str, size, NULL, NULL);
616         result.append(str);
617         delete[] str;
618     }
619     else if (code_type == CodeType::UTF16LE)
620     {
621         result.clear();
622         result.push_back(-1);	//在前面加上UTF16LE的BOM
623         result.push_back(-2);
624         result.append((const char*)wstr.c_str(), (const char*)wstr.c_str() + wstr.size() * 2);
625         result.push_back('\0');
626     }
627     else if (code_type == CodeType::UTF16BE)
628     {
629         result.clear();
630         result.push_back(-2);	//在前面加上UTF16BE的BOM
631         result.push_back(-1);
632         wchar_t* p = (wchar_t*)wstr.c_str();
633         convertBE_LE(p, wstr.size());
634         wstring temp{ p };
635         result.append((const char*)temp.c_str(), (const char*)temp.c_str() + temp.size() * 2);
636         result.push_back('\0');
637     }
638     if (char_cannot_convert != nullptr)
639         *char_cannot_convert = (UsedDefaultChar != FALSE);
640     return result;
641 }
642 
ASCIIToUnicode(const string & ascii)643 wstring CCommon::ASCIIToUnicode(const string& ascii)
644 {
645     wstring result;
646     for (const char& ch : ascii)
647     {
648         result.push_back(ch);
649     }
650     return result;
651 }
652 
IsUTF8Bytes(const char * data)653 bool CCommon::IsUTF8Bytes(const char* data)
654 {
655     int charByteCounter = 1;  //计算当前正分析的字符应还有的字节数
656     unsigned char curByte; //当前分析的字节.
657     bool ascii = true;
658     int length = strlen(data);
659     for (int i = 0; i < length; i++)
660     {
661         curByte = static_cast<unsigned char>(data[i]);
662         if (charByteCounter == 1)
663         {
664             if (curByte >= 0x80)
665             {
666                 ascii = false;
667                 //判断当前
668                 while (((curByte <<= 1) & 0x80) != 0)
669                 {
670                     charByteCounter++;
671                 }
672                 //标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X
673                 if (charByteCounter == 1 || charByteCounter > 6)
674                 {
675                     return false;
676                 }
677             }
678         }
679         else
680         {
681             //若是UTF-8 此时第一位必须为1
682             if ((curByte & 0xC0) != 0x80)
683             {
684                 return false;
685             }
686             charByteCounter--;
687         }
688     }
689     if (ascii) return false;		//如果全是ASCII字符,返回false
690     else return true;
691 }
692 
JudgeCodeType(const string & str,CodeType default_code,bool auto_utf8)693 CodeType CCommon::JudgeCodeType(const string& str, CodeType default_code, bool auto_utf8)
694 {
695     CodeType code_type{ default_code };
696     if (!str.empty())
697     {
698         // 如果前面有UTF8的BOM,则编码类型为UTF8
699         if (str.size() >= 3 && str[0] == -17 && str[1] == -69 && str[2] == -65)
700             code_type = CodeType::UTF8;
701         // 如果前面有UTF16LE的BOM,则编码类型为UTF16LE
702         else if (str.size() >= 2 && str[0] == -1 && str[1] == -2)
703             code_type = CodeType::UTF16LE;
704         // 如果前面有UTF16BE的BOM,则编码类型为UTF16BE
705         else if (str.size() >= 2 && str[0] == -2 && str[1] == -1)
706             code_type = CodeType::UTF16BE;
707         // AUTO时是否将符合UTF8格式的str作为UTF8_NO_BOM处理
708         else if (auto_utf8 && IsUTF8Bytes(str.c_str()))
709             code_type = CodeType::UTF8_NO_BOM;
710     }
711     return code_type;
712 }
713 
IsURL(const wstring & str)714 bool CCommon::IsURL(const wstring& str)
715 {
716     return (str.substr(0, 7) == L"http://" || str.substr(0, 8) == L"https://" || str.substr(0, 6) == L"ftp://" || str.substr(0, 6) == L"mms://");
717 }
718 
IsWindowsPath(const wstring & str)719 bool CCommon::IsWindowsPath(const wstring& str)
720 {
721     return (str.size() >= 3                                                             // windows 路径至少3个字符
722         && ((str[0] >= L'A' && str[0] <= L'Z') || (str[0] >= L'a' && str[0] <= L'z'))   // 第1个字符必须为字母
723         && str[1] == L':'                                                               // 第2个字符必须为冒号
724         && (str[2] == L'/' || str[2] == L'\\')                                          // 第3个字符必须为斜杠
725         );
726 }
727 
IsPath(const wstring & str)728 bool CCommon::IsPath(const wstring& str)
729 {
730     if (str.size() < 3)
731         return false;
732 
733     bool is_windows_path{ IsWindowsPath(str) };
734     bool is_UNC_path{ str[0] == L'\\' && str[1] == L'\\' };
735 
736     if (!is_windows_path && !is_UNC_path)
737         return false;
738 
739     const wstring invalid_chars{ L":*?\"<>|" };
740     if (str.find_first_of(invalid_chars, is_windows_path ? 2 : 0) != wstring::npos)
741         return false;
742 
743     return true;
744 }
745 
StringCharacterReplace(wstring & str,wchar_t ch,wchar_t ch_replaced)746 bool CCommon::StringCharacterReplace(wstring& str, wchar_t ch, wchar_t ch_replaced)
747 {
748     bool replaced = false;
749     for (size_t i = 0; i < str.size(); i++)
750     {
751         if (str[i] == ch)
752         {
753             str[i] = ch_replaced;
754             replaced = true;
755         }
756     }
757     return replaced;
758 }
759 
StringReplace(wstring & str,const wstring & str_old,const wstring & str_new)760 bool CCommon::StringReplace(wstring& str, const wstring& str_old, const wstring& str_new)
761 {
762     if (str.empty())
763         return false;
764     bool replaced{ false };
765     size_t pos = 0;
766     while ((pos = str.find(str_old, pos)) != std::wstring::npos)
767     {
768         str.replace(pos, str_old.length(), str_new);
769         replaced = true;
770         pos += str_new.length();    // 前进到替换后的字符串末尾
771     }
772     return replaced;
773 }
774 
DataSizeToString(size_t data_size)775 CString CCommon::DataSizeToString(size_t data_size)
776 {
777     CString size_info;
778     if (data_size < 1024)
779         size_info.Format(_T("%u B"), data_size);
780     else if (data_size < 1024 * 1024)
781         size_info.Format(_T("%.2f KB"), data_size / 1024.0f);
782     else if (data_size < 1024 * 1024 * 1024)
783         size_info.Format(_T("%.2f MB"), data_size / 1024.0f / 1024.0f);
784     else
785         size_info.Format(_T("%.2f GB"), data_size / 1024.0f / 1024.0f / 1024.0f);
786     return size_info;
787 }
788 
GetExePath()789 wstring CCommon::GetExePath()
790 {
791     wchar_t path[MAX_PATH];
792     GetModuleFileNameW(NULL, path, MAX_PATH);
793     size_t index;
794     wstring current_path{ path };
795     index = current_path.find_last_of(L'\\');
796     current_path = current_path.substr(0, index + 1);
797     return current_path;
798 }
799 
GetDesktopPath()800 wstring CCommon::GetDesktopPath()
801 {
802     LPITEMIDLIST ppidl;
803     TCHAR pszDesktopPath[MAX_PATH];
804     if (SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &ppidl) == S_OK)
805     {
806         SHGetPathFromIDList(ppidl, pszDesktopPath);
807         CoTaskMemFree(ppidl);
808     }
809     return wstring(pszDesktopPath);
810 }
811 
GetTemplatePath()812 wstring CCommon::GetTemplatePath()
813 {
814     wstring result;
815     wchar_t buff[MAX_PATH];
816     GetTempPath(MAX_PATH, buff);		//获取临时文件夹的路径
817     result = buff;
818     if (!result.empty() && result.back() != L'\\' && result.back() != L'/')		//确保路径后面有斜杠
819         result.push_back(L'\\');
820     return result;
821 }
822 
823 
GetAppDataConfigDir()824 wstring CCommon::GetAppDataConfigDir()
825 {
826     LPITEMIDLIST ppidl;
827     TCHAR pszAppDataPath[MAX_PATH];
828     if (SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &ppidl) == S_OK)
829     {
830         SHGetPathFromIDList(ppidl, pszAppDataPath);
831         CoTaskMemFree(ppidl);
832     }
833     wstring app_data_path{ pszAppDataPath };        //获取到C:/User/用户名/AppData/Roaming路径
834     CreateDirectory(app_data_path.c_str(), NULL);       //如果Roaming不存在,则创建它
835     app_data_path += L'\\';
836     app_data_path += APP_NAME;
837 #ifdef _DEBUG
838     app_data_path += L" (Debug)";
839 #endif
840     app_data_path += L'\\';
841     CreateDirectory(app_data_path.c_str(), NULL);       //如果C:/User/用户名/AppData/Roaming/MusicPlayer2不存在,则创建它
842 
843     return app_data_path;
844 }
845 
GetSpecialDir(int csidl)846 wstring CCommon::GetSpecialDir(int csidl)
847 {
848     bool rtn{};
849     LPITEMIDLIST ppidl;
850     wchar_t folder_dir[MAX_PATH];
851     if (SHGetSpecialFolderLocation(NULL, csidl, &ppidl) == S_OK)
852     {
853         rtn = SHGetPathFromIDListW(ppidl, folder_dir);
854         CoTaskMemFree(ppidl);
855     }
856     ASSERT(rtn);
857     if (rtn)
858         return wstring(folder_dir);
859     return wstring();
860 }
861 
CreateDir(const _tstring & path)862 bool CCommon::CreateDir(const _tstring& path)
863 {
864     if (!FolderExist(path))
865     {
866         if (CreateDirectory(path.c_str(), NULL))
867         {
868             return true;
869         }
870     }
871     return false;
872 }
873 
FileRename(const _tstring & file_path,const _tstring & new_file_name)874 _tstring CCommon::FileRename(const _tstring& file_path, const _tstring& new_file_name)
875 {
876     if (!FileExist(file_path))
877         return _tstring();
878 
879     _tstring dir;
880     size_t index = file_path.find_last_of(_T("/\\"));
881     if (index == _tstring::npos)
882         return _tstring();
883     dir = file_path.substr(0, index + 1);
884 
885     _tstring extension;
886     size_t index_ext = file_path.rfind(_T('.'));
887     if (index_ext < file_path.size() - 1 && index_ext > index)
888         extension = file_path.substr(index_ext);
889 
890     _tstring new_file_path = dir + new_file_name + extension;
891     try
892     {
893         CFile::Rename(file_path.c_str(), new_file_path.c_str());
894     }
895     catch (CFileException* pEx)
896     {
897         new_file_path.clear();
898         pEx->Delete();
899     }
900     return new_file_path;
901 }
902 
RelativePathToAbsolutePath(const _tstring & relative_path,const _tstring & cur_dir)903 _tstring CCommon::RelativePathToAbsolutePath(const _tstring& relative_path, const _tstring& cur_dir)
904 {
905     // relative_path如果是盘符开头的绝对路径那么返回此绝对路径
906     // 否则将relative_path视为相对路径或斜杠开头的绝对路径并与cur_dir拼接
907     _tstring result = relative_path;
908     _tstring dir = cur_dir;
909     if (!IsPath(result) && !dir.empty())
910     {
911         // https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/
912         // PathCombine拼接简化两个合法路径,当result为斜杠开头的绝对路径时将其只与dir的盘符拼接
913         // PathCchCombine 是处理了缓冲区溢出的安全版本,Windows 8开始支持,位于pathcch.h
914         TCHAR lpBuffer[MAX_PATH]{};
915         PathCombine(lpBuffer, dir.c_str(), result.c_str());
916         // PathCchCombine(lpBuffer, MAX_PATH, dir.c_str(), result.c_str());
917         result = lpBuffer;
918     }
919     return result;
920 }
921 
CopyStringToClipboard(const wstring & str)922 bool CCommon::CopyStringToClipboard(const wstring& str)
923 {
924     if (OpenClipboard(NULL))
925     {
926         HGLOBAL clipbuffer;
927         EmptyClipboard();
928         size_t size = (str.size() + 1) * 2;
929         clipbuffer = GlobalAlloc(GMEM_DDESHARE, size);
930         memcpy_s(GlobalLock(clipbuffer), size, str.c_str(), size);
931         GlobalUnlock(clipbuffer);
932         if (SetClipboardData(CF_UNICODETEXT, clipbuffer) == NULL)
933             return false;
934         CloseClipboard();
935         return true;
936     }
937     else return false;
938 }
939 
GetStringFromClipboard()940 wstring CCommon::GetStringFromClipboard()
941 {
942     if (OpenClipboard(NULL))
943     {
944         if (IsClipboardFormatAvailable(CF_TEXT))
945         {
946             HANDLE hClip;
947             wchar_t* pBuf;
948             hClip = GetClipboardData(CF_UNICODETEXT); //获取剪贴板数据
949             if (hClip == NULL)
950                 return wstring();
951             pBuf = (wchar_t*)GlobalLock(hClip);
952             if (pBuf == nullptr)
953                 return wstring();
954             CloseClipboard();
955             return wstring(pBuf);
956         }
957     }
958     return wstring();
959 }
960 
WriteLog(const wchar_t * path,const wstring & content)961 void CCommon::WriteLog(const wchar_t* path, const wstring& content)
962 {
963     SYSTEMTIME cur_time;
964     GetLocalTime(&cur_time);
965     char buff[32];
966     sprintf_s(buff, "%d/%.2d/%.2d %.2d:%.2d:%.2d.%.3d ", cur_time.wYear, cur_time.wMonth, cur_time.wDay,
967         cur_time.wHour, cur_time.wMinute, cur_time.wSecond, cur_time.wMilliseconds);
968     ofstream out_put{ path, std::ios::app };
969     out_put << buff << CCommon::UnicodeToStr(content, CodeType::UTF8_NO_BOM) << std::endl;
970     out_put.close();    // 这里需要显式关闭以避免被不同线程连续调用时丢失内容(不过还是不能承受并发,多线程并发时请自行加锁
971 }
972 
DisposeCmdLineFiles(const wstring & cmd_line,vector<wstring> & files)973 void CCommon::DisposeCmdLineFiles(const wstring& cmd_line, vector<wstring>& files)
974 {
975     // 解析命令行参数中的文件/文件夹路径放入files
976     // files 中能够接受音频文件路径/播放列表文件路径/文件夹路径随意乱序出现
977     // files 中无法被识别为“播放列表文件路径”“文件夹路径”的项目会被直接加入默认播放列表
978     // TODO: 这里可能需要添加以下功能,我没有其他windows版本的经验,不确定这里怎样改
979     //       文件/文件夹存在判断;路径通配符展开;相对路径转换绝对路径;支持不在同一文件夹下的多个文件路径
980     files.clear();
981     if (cmd_line.empty()) return;
982     wstring path;
983     //先找出字符串中的文件夹路径,从命令行参数传递过来的文件肯定都是同一个文件夹下的
984     if (cmd_line[0] == L'\"')		//如果第一个文件用双引号包含起来
985     {
986         int index1 = cmd_line.find(L'\"', 1);		//查找和第1个双引号匹配的双引号
987         int index2 = cmd_line.rfind(L'\\', index1);		//往前查找反斜杠
988         path = cmd_line.substr(1, index2);		//获取文件夹路径(包含最后一个反斜杠)
989         files.push_back(cmd_line.substr(1, index1 - 1));
990     }
991     else		//如果第一个文件没有用双引号包含起来,则说明路径中不包含空格,
992     {
993         int index1 = cmd_line.find(L' ');		//查找和第1空格
994         int index2 = cmd_line.rfind(L'\\', index1);		//往前查找反斜杠
995         path = cmd_line.substr(0, index2 + 1);		//获取文件夹路径(包含最后一个反斜杠)
996         files.push_back(cmd_line.substr(0, index1));
997     }
998     int path_size = path.size();
999     if (path_size < 2) return;
1000     int index{};
1001     while (true)
1002     {
1003         index = cmd_line.find(path, index + path_size);		//从第2个开始查找路径出现的位置
1004         if (index == string::npos) break;
1005         if (index > 0 && cmd_line[index - 1] == L'\"')		//如果路径前面一个字符是双引号
1006         {
1007             int index1 = cmd_line.find(L'\"', index);
1008             files.push_back(cmd_line.substr(index, index1 - index));
1009         }
1010         else
1011         {
1012             int index1 = cmd_line.find(L' ', index);
1013             files.push_back(cmd_line.substr(index, index1 - index));
1014         }
1015     }
1016     return;
1017     //CString out_info;
1018     //out_info += _T("命令行参数:");
1019     //out_info += cmd_line.c_str();
1020     //out_info += _T("\r\n");
1021     //out_info += _T("路径:");
1022     //out_info += path.c_str();
1023     //out_info += _T("\r\n");
1024     //CCommon::WriteLog(L".\\command.log", wstring{ out_info });
1025 }
1026 
GetCmdLineCommand(const wstring & cmd_line,int & command)1027 bool CCommon::GetCmdLineCommand(const wstring& cmd_line, int& command)
1028 {
1029     if (CCommon::StringNatchWholeWord(cmd_line, wstring(L"-play_pause")) != string::npos || CCommon::StringNatchWholeWord(cmd_line, wstring(L"-p")) != string::npos)
1030         command |= ControlCmd::PLAY_PAUSE;
1031     if (CCommon::StringNatchWholeWord(cmd_line, wstring(L"-previous")) != string::npos || CCommon::StringNatchWholeWord(cmd_line, wstring(L"-pre")) != string::npos)
1032         command |= ControlCmd::_PREVIOUS;
1033     if (CCommon::StringNatchWholeWord(cmd_line, wstring(L"-next")) != string::npos || CCommon::StringNatchWholeWord(cmd_line, wstring(L"-n")) != string::npos)
1034         command |= ControlCmd::_NEXT;
1035     if (CCommon::StringNatchWholeWord(cmd_line, wstring(L"-stop")) != string::npos || CCommon::StringNatchWholeWord(cmd_line, wstring(L"-s")) != string::npos)
1036         command |= ControlCmd::STOP;
1037     if (CCommon::StringNatchWholeWord(cmd_line, wstring(L"-ff")) != string::npos)
1038         command |= ControlCmd::FF;
1039     if (CCommon::StringNatchWholeWord(cmd_line, wstring(L"-rew")) != string::npos)
1040         command |= ControlCmd::REW;
1041     if (CCommon::StringNatchWholeWord(cmd_line, wstring(L"-vol_up")) != string::npos)
1042         command |= ControlCmd::VOLUME_UP;
1043     if (CCommon::StringNatchWholeWord(cmd_line, wstring(L"-vol_down")) != string::npos)
1044         command |= ControlCmd::VOLUME_DOWM;
1045     if (CCommon::StringNatchWholeWord(cmd_line, wstring(L"-mini")) != string::npos)
1046         command |= ControlCmd::MINI_MODE;
1047     return command != ControlCmd::NONE;
1048 }
1049 
CreateFileShortcut(LPCTSTR lpszLnkFileDir,LPCTSTR lpszFileName,LPCTSTR lpszLnkFileName,LPCTSTR lpszWorkDir,WORD wHotkey,LPCTSTR lpszDescription,int iShowCmd,LPCTSTR lpszArguments,int nIconOffset)1050 bool CCommon::CreateFileShortcut(LPCTSTR lpszLnkFileDir, LPCTSTR lpszFileName, LPCTSTR lpszLnkFileName, LPCTSTR lpszWorkDir, WORD wHotkey, LPCTSTR lpszDescription, int iShowCmd, LPCTSTR lpszArguments, int nIconOffset)
1051 {
1052     if (lpszLnkFileDir == NULL)
1053         return false;
1054 
1055     HRESULT hr;
1056     IShellLink* pLink;  //IShellLink对象指针
1057     IPersistFile* ppf; //IPersisFil对象指针
1058 
1059     //创建IShellLink对象
1060     hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&pLink);
1061     if (FAILED(hr))
1062         return false;
1063 
1064     //从IShellLink对象中获取IPersistFile接口
1065     hr = pLink->QueryInterface(IID_IPersistFile, (void**)&ppf);
1066     if (FAILED(hr))
1067     {
1068         pLink->Release();
1069         return false;
1070     }
1071 
1072     TCHAR file_path[MAX_PATH];		//当前进程exe文件路径
1073     GetModuleFileName(NULL, file_path, MAX_PATH);
1074     LPCTSTR pFilePath;				//快捷方式目标
1075 
1076     //目标
1077     if (lpszFileName == NULL)
1078         pFilePath = file_path;
1079     else
1080         pFilePath = lpszFileName;
1081     pLink->SetPath(pFilePath);
1082 
1083     //工作目录
1084     if (lpszWorkDir != NULL)
1085     {
1086         pLink->SetWorkingDirectory(lpszWorkDir);
1087     }
1088     else
1089     {
1090         //设置工作目录为快捷方式目标所在位置
1091         _tstring workDir = pFilePath;
1092         int index = workDir.find_last_of(_T("\\/"));
1093         if (index != workDir.npos)
1094             workDir = workDir.substr(0, index);
1095         pLink->SetWorkingDirectory(workDir.c_str());
1096     }
1097 
1098     //快捷键
1099     if (wHotkey != 0)
1100         pLink->SetHotkey(wHotkey);
1101 
1102     //备注
1103     if (lpszDescription != NULL)
1104         pLink->SetDescription(lpszDescription);
1105 
1106     //显示方式
1107     pLink->SetShowCmd(iShowCmd);
1108 
1109     //参数
1110     if (lpszArguments != NULL)
1111         pLink->SetArguments(lpszArguments);
1112 
1113     //图标
1114     if (nIconOffset > 0)
1115         pLink->SetIconLocation(pFilePath, nIconOffset);
1116 
1117     //快捷方式的路径 + 名称
1118     _tstring shortcutName;
1119     shortcutName = lpszLnkFileDir;
1120     if (!shortcutName.empty() && shortcutName.back() != _T('\\') && shortcutName.back() != _T('/'))
1121         shortcutName.push_back(_T('\\'));
1122 
1123     if (lpszLnkFileName != NULL) //指定了快捷方式的名称
1124     {
1125         shortcutName += lpszLnkFileName;
1126     }
1127     else
1128     {
1129         //没有指定名称,就从取指定文件的文件名作为快捷方式名称。
1130         _tstring fileName = pFilePath;
1131 
1132         int index1, index2;
1133         index1 = fileName.find_last_of(_T("\\/"));
1134         index2 = fileName.rfind(_T('.'));
1135 
1136         if (index1 == _tstring::npos || index2 == _tstring::npos || index1 >= index2)
1137         {
1138             ppf->Release();
1139             pLink->Release();
1140             return false;
1141         }
1142         fileName = fileName.substr(index1 + 1, index2 - index1 - 1);
1143         fileName += _T(".lnk");
1144         shortcutName += fileName;
1145     }
1146 
1147     //保存快捷方式到指定目录下
1148     hr = ppf->Save(shortcutName.c_str(), TRUE);
1149 
1150     ppf->Release();
1151     pLink->Release();
1152     return SUCCEEDED(hr);
1153 }
1154 
GetFiles(wstring file_name,vector<wstring> & files,std::function<bool (const wstring &)> fun_is_valid)1155 void CCommon::GetFiles(wstring file_name, vector<wstring>& files, std::function<bool(const wstring&)> fun_is_valid)
1156 {
1157     files.clear();
1158     //文件句柄
1159     intptr_t hFile = 0;
1160     //文件信息(用Unicode保存使用_wfinddata_t,多字节字符集使用_finddata_t)
1161     _wfinddata_t fileinfo;
1162     if ((hFile = _wfindfirst(file_name.c_str(), &fileinfo)) != -1)
1163     {
1164         do
1165         {
1166             if (fun_is_valid(fileinfo.name))
1167                 files.push_back(fileinfo.name);
1168         } while (_wfindnext(hFile, &fileinfo) == 0);
1169     }
1170     _findclose(hFile);
1171 }
1172 
GetImageFiles(wstring file_name,vector<wstring> & files)1173 void CCommon::GetImageFiles(wstring file_name, vector<wstring>& files)
1174 {
1175     files.clear();
1176     //文件句柄
1177     intptr_t hFile = 0;
1178     //文件信息(用Unicode保存使用_wfinddata_t,多字节字符集使用_finddata_t)
1179     _wfinddata_t fileinfo;
1180     if ((hFile = _wfindfirst(file_name.c_str(), &fileinfo)) != -1)
1181     {
1182         do
1183         {
1184             if (FileIsImage(fileinfo.name))
1185                 files.push_back(fileinfo.name);
1186         } while (_wfindnext(hFile, &fileinfo) == 0);
1187     }
1188     _findclose(hFile);
1189 }
1190 
IsFolderMatchKeyWord(wstring dir,const wstring & key_word)1191 bool CCommon::IsFolderMatchKeyWord(wstring dir, const wstring& key_word)
1192 {
1193     //文件句柄
1194     intptr_t hFile = 0;
1195     //文件信息
1196     _wfinddata_t fileinfo;
1197     if (dir.back() != '\\' && dir.back() != '/')
1198         dir.push_back('\\');
1199 
1200     wstring folder_name;
1201     size_t index = dir.rfind(L'\\', dir.size() - 2);
1202     folder_name = dir.substr(index + 1, dir.size() - index - 2);
1203 
1204     if ((hFile = _wfindfirst((dir + L"*").c_str(), &fileinfo)) != -1)
1205     {
1206         do
1207         {
1208             wstring file_name = fileinfo.name;
1209             if (file_name == L"." || file_name == L"..")
1210                 continue;
1211 
1212             if (CCommon::IsFolder(dir + file_name))        //当前是文件夹,则递归调用
1213             {
1214                 if (IsFolderMatchKeyWord(dir + file_name, key_word))
1215                 {
1216                     _findclose(hFile);
1217                     return true;
1218                 }
1219             }
1220             //else
1221             //{
1222             //    if (StringFindNoCase(file_name, key_word) != wstring::npos)
1223             //        return true;
1224             //}
1225         } while (_wfindnext(hFile, &fileinfo) == 0);
1226 
1227         if (StringFindNoCase(folder_name, key_word) != wstring::npos)
1228         {
1229             _findclose(hFile);
1230             return true;
1231         }
1232     }
1233     _findclose(hFile);
1234     return false;
1235 }
1236 
FileIsImage(const wstring & file_name)1237 bool CCommon::FileIsImage(const wstring& file_name)
1238 {
1239     size_t index;
1240     index = file_name.find_last_of(L'.');
1241     wstring type;
1242     if (index != string::npos)
1243         type = file_name.substr(index);			//获取扩展名
1244     std::transform(type.begin(), type.end(), type.begin(), tolower);		//将扩展名转换成小写
1245     return (type == L".jpg" || type == L".jpeg" || type == L".png" || type == L".gif" || type == L".bmp");
1246 }
1247 
GetRandomString(int length)1248 wstring CCommon::GetRandomString(int length)
1249 {
1250     wstring result;
1251     SYSTEMTIME current_time;
1252     GetLocalTime(&current_time);			//获取当前时间
1253     srand(current_time.wMilliseconds);		//用当前时间的毫秒数设置产生随机数的种子
1254     int char_type;		//当前要生成的字符类型 0:数字;1:小写字母;2:大写字母
1255     for (int i{}; i < length; i++)
1256     {
1257         char_type = rand() % 3;		//随机确定要生成的字符类型
1258         wchar_t current_char;
1259         switch (char_type)
1260         {
1261         case 0:
1262             current_char = L'0' + (rand() % 10);
1263             break;
1264         case 1:
1265             current_char = L'a' + (rand() % 26);
1266             break;
1267         case 2:
1268             current_char = L'A' + (rand() % 26);
1269             break;
1270         }
1271         result.push_back(current_char);
1272     }
1273     return result;
1274 }
1275 
IsFileNameNumbered(const wstring & file_name,int & number,size_t & index)1276 bool CCommon::IsFileNameNumbered(const wstring& file_name, int& number, size_t& index)
1277 {
1278     if (file_name.empty())
1279         return false;
1280     if (file_name.back() != L')')
1281         return false;
1282     size_t size{ file_name.size() };
1283     //size_t index;
1284     index = file_name.rfind(L'(');		//往前查找右括号
1285     if (index == wstring::npos || index == 0)		//左括号不能在字符串开头
1286         return false;
1287     wstring number_str{ file_name.substr(index + 1, size - index - 2) };		//获取两个括号之间的文本
1288     if (StrIsNumber(number_str))				//判断文本是否是数字
1289     {
1290         number = _wtoi(number_str.c_str());
1291         return true;
1292     }
1293     else
1294     {
1295         return false;
1296     }
1297 }
1298 
SizeZoom(CSize & size,int side)1299 void CCommon::SizeZoom(CSize& size, int side)
1300 {
1301     float width_height_ratio{ static_cast<float>(size.cx) / size.cy };	//长宽比
1302     if (width_height_ratio > 1)		//长宽比大于1:1
1303     {
1304         size.cx = side;
1305         size.cy = static_cast<int>(side / width_height_ratio);
1306     }
1307     else if (width_height_ratio < 1)
1308     {
1309         size.cy = side;
1310         size.cx = static_cast<int>(side * width_height_ratio);
1311     }
1312     else
1313     {
1314         size.cx = size.cy = side;
1315     }
1316 }
1317 
GetWindowsThemeColor()1318 COLORREF CCommon::GetWindowsThemeColor()
1319 {
1320 #ifdef COMPILE_IN_WIN_XP
1321     return RGB(0, 120, 215);
1322 #else
1323     DWORD crColorization;
1324     BOOL fOpaqueBlend;
1325     COLORREF theme_color{};
1326     HRESULT result = DwmGetColorizationColor(&crColorization, &fOpaqueBlend);
1327     if (result == S_OK)
1328     {
1329         BYTE r, g, b;
1330         r = (crColorization >> 16) % 256;
1331         g = (crColorization >> 8) % 256;
1332         b = crColorization % 256;
1333         theme_color = RGB(r, g, b);
1334     }
1335     return theme_color;
1336 #endif
1337 }
1338 
1339 
AppendMenuOp(HMENU hDst,HMENU hSrc)1340 int CCommon::AppendMenuOp(HMENU hDst, HMENU hSrc)
1341 {
1342     int iCnt = 0;
1343     ASSERT(hDst && hSrc);
1344 
1345     for (int iSrc = 0, iDst = GetMenuItemCount(hDst); iSrc < GetMenuItemCount(hSrc); iSrc++)
1346     {
1347         TCHAR szMenuStr[256] = { 0 };
1348         MENUITEMINFO mInfo = { 0 };
1349         mInfo.cbSize = sizeof(mInfo);
1350         mInfo.fMask = 0
1351             | MIIM_CHECKMARKS //Retrieves or sets the hbmpChecked and hbmpUnchecked members.
1352             | MIIM_DATA //Retrieves or sets the dwItemData member.
1353             | MIIM_ID //Retrieves or sets the wID member.
1354             | MIIM_STATE //Retrieves or sets the fState member.
1355             | MIIM_SUBMENU //Retrieves or sets the hSubMenu member.
1356             | MIIM_FTYPE //Retrieves or sets the fType and dwTypeData members.
1357             //这里如果不添加MIIM_STRING和MIIM_BITMAP,会导致有图标的菜单项文本不显示
1358             | MIIM_STRING
1359             | MIIM_BITMAP
1360             | 0;
1361         mInfo.dwTypeData = szMenuStr;
1362         mInfo.cch = _countof(szMenuStr);
1363 
1364         VERIFY(GetMenuItemInfo(hSrc, iSrc, TRUE, &mInfo));
1365 
1366         if (mInfo.hSubMenu)
1367         {
1368             HMENU hSub = CreatePopupMenu();
1369             AppendMenuOp(hSub, mInfo.hSubMenu);
1370             mInfo.hSubMenu = hSub;
1371         }
1372 
1373         InsertMenuItem(hDst, iDst++, TRUE, &mInfo);
1374         iCnt++;
1375     }
1376 
1377     return iCnt;
1378 }
1379 
IsMenuItemInMenu(CMenu * pMenu,UINT id)1380 bool CCommon::IsMenuItemInMenu(CMenu* pMenu, UINT id)
1381 {
1382     if (pMenu == nullptr)
1383         return false;
1384 
1385     int item_count = pMenu->GetMenuItemCount();
1386     for (int i = 0; i < item_count; i++)
1387     {
1388         if (pMenu->GetMenuItemID(i) == id)
1389             return true;
1390         CMenu* pSubMenu = pMenu->GetSubMenu(i);
1391         if (IsMenuItemInMenu(pSubMenu, id))
1392             return true;
1393     }
1394     return false;
1395 }
1396 
GetMenuItemPosition(CMenu * pMenu,UINT id)1397 int CCommon::GetMenuItemPosition(CMenu* pMenu, UINT id)
1398 {
1399     int pos = -1;
1400     int item_count = pMenu->GetMenuItemCount();
1401     for (int i = 0; i < item_count; i++)
1402     {
1403         if (pMenu->GetMenuItemID(i) == id)
1404         {
1405             pos = i;
1406             break;
1407         }
1408     }
1409     return pos;
1410 }
1411 
IterateMenuItem(CMenu * pMenu,std::function<void (CMenu *,UINT)> func)1412 void CCommon::IterateMenuItem(CMenu* pMenu, std::function<void(CMenu*, UINT)> func)
1413 {
1414     if (pMenu != nullptr)
1415     {
1416         int item_count = pMenu->GetMenuItemCount();
1417         for (int i = 0; i < item_count; i++)
1418         {
1419             CMenu* pSubMenu = pMenu->GetSubMenu(i);
1420             if (pSubMenu != nullptr)
1421                 IterateMenuItem(pSubMenu, func);
1422             UINT id = pMenu->GetMenuItemID(i);
1423             if (id > 0)
1424                 func(pMenu, id);
1425         }
1426     }
1427 }
1428 
StringLeftMatch(const std::wstring & str,const std::wstring & matched_str)1429 bool CCommon::StringLeftMatch(const std::wstring& str, const std::wstring& matched_str)
1430 {
1431     if (str.size() < matched_str.size())
1432         return false;
1433     return str.substr(0, matched_str.size()) == matched_str;
1434 }
1435 
GetThreadLanguageList(vector<wstring> & language_tag)1436 bool CCommon::GetThreadLanguageList(vector<wstring>& language_tag)
1437 {
1438     language_tag.clear();
1439     ULONG num{};
1440     vector<wchar_t> buffer(LOCALE_NAME_MAX_LENGTH, L'\0');
1441     ULONG buffer_size{ static_cast<ULONG>(buffer.size()) };
1442     bool rtn = GetThreadPreferredUILanguages(MUI_LANGUAGE_NAME, &num, buffer.data(), &buffer_size); // >=Windows Vista
1443     if (!rtn) return false;
1444     ASSERT(buffer_size <= LOCALE_NAME_MAX_LENGTH);
1445     size_t pos{};
1446     for (size_t i{}; i < buffer_size; ++i)
1447     {
1448         if (buffer[i] != L'\0' || pos == i)
1449             continue;
1450         wstring tmp(buffer.begin() + pos, buffer.begin() + i);
1451         language_tag.push_back(tmp);
1452         pos = i + 1;
1453     }
1454     ASSERT(language_tag.size() == num);
1455     return true;
1456 }
1457 
SetThreadLanguageList(const vector<wstring> & language_tag)1458 bool CCommon::SetThreadLanguageList(const vector<wstring>& language_tag)
1459 {
1460     vector<wchar_t> buffer;
1461     size_t buffer_size{ 1 };
1462     for (const wstring& tag : language_tag)
1463     {
1464         if (tag.empty()) continue;
1465         buffer_size += tag.size() + 1;
1466         if (buffer_size > LOCALE_NAME_MAX_LENGTH)
1467             break;
1468         buffer.insert(buffer.end(), tag.begin(), tag.end());
1469         buffer.push_back(L'\0');
1470     }
1471     if (buffer.empty()) // buffer为空时多插入一个\0确保双\0结尾(空buffer会重置之前设置的线程首选UI语言列表)
1472         buffer.push_back(L'\0');
1473     buffer.push_back(L'\0');
1474     bool rtn = SetThreadPreferredUILanguages(MUI_LANGUAGE_NAME, buffer.data(), nullptr); // >=Windows Vista
1475     return rtn;
1476 }
1477 
GetSystemDefaultUIFont()1478 wstring CCommon::GetSystemDefaultUIFont()
1479 {
1480     NONCLIENTMETRICS metrics;
1481     metrics.cbSize = sizeof(NONCLIENTMETRICS);
1482     SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &metrics, 0);
1483     wstring font_name = metrics.lfMessageFont.lfFaceName;
1484     return font_name;
1485 }
1486 
WStringCopy(wchar_t * str_dest,int dest_size,const wchar_t * str_source,int source_size)1487 void CCommon::WStringCopy(wchar_t* str_dest, int dest_size, const wchar_t* str_source, int source_size)
1488 {
1489     if (dest_size <= 0)
1490         return;
1491     if (source_size <= 0 || str_source == nullptr)
1492     {
1493         str_dest[0] = L'\0';
1494         return;
1495     }
1496     int i;
1497     for (i = 0; i < dest_size && i < source_size && str_source[i] != L'\0'; i++)
1498         str_dest[i] = str_source[i];
1499     //确保目标字符串末尾有一个\0
1500     int copy_cnt = i;
1501     if (copy_cnt < dest_size)
1502         str_dest[copy_cnt] = L'\0';
1503     else
1504         str_dest[dest_size - 1] = L'\0';
1505 }
1506 
NormalizeFont(LOGFONT & font)1507 void CCommon::NormalizeFont(LOGFONT& font)
1508 {
1509     wstring name;
1510     wstring style;
1511     name = font.lfFaceName;
1512     if (name.empty())
1513         return;
1514     if (name.back() == L' ')
1515         name.pop_back();
1516     size_t index = name.rfind(L' ');
1517     if (index == wstring::npos)
1518         return;
1519     style = name.substr(index + 1);
1520     bool style_acquired = false;
1521     if (style == L"Light")
1522     {
1523         font.lfWeight = FW_LIGHT;
1524         style_acquired = true;
1525     }
1526     else if (style == L"Semilight")
1527     {
1528         font.lfWeight = 350;
1529         style_acquired = true;
1530     }
1531     else if (style == L"ExtraLight")
1532     {
1533         font.lfWeight = FW_EXTRALIGHT;
1534         style_acquired = true;
1535     }
1536     else if (style == L"Semibold")
1537     {
1538         font.lfWeight = FW_SEMIBOLD;
1539         style_acquired = true;
1540     }
1541     else if (style == L"Bold")
1542     {
1543         font.lfWeight = FW_BOLD;
1544         style_acquired = true;
1545     }
1546     else if (style == L"ExtraBold")
1547     {
1548         font.lfWeight = FW_EXTRABOLD;
1549         style_acquired = true;
1550     }
1551     else if (style == L"Black" || style == L"Heavy")
1552     {
1553         font.lfWeight = FW_BLACK;
1554         style_acquired = true;
1555     }
1556     else if (style == L"Normal" || style == L"Regular")
1557     {
1558         font.lfWeight = FW_NORMAL;
1559         style_acquired = true;
1560     }
1561 
1562     if (style_acquired)
1563     {
1564         name = name.substr(0, index);
1565     }
1566     //wcsncpy_s(font.lfFaceName, name.c_str(), 32);
1567     WStringCopy(font.lfFaceName, 32, name.c_str(), name.size());
1568 }
1569 
GetMenuBarHeight(HWND hWnd)1570 int CCommon::GetMenuBarHeight(HWND hWnd)
1571 {
1572     MENUBARINFO menuInfo{};
1573     menuInfo.cbSize = sizeof(MENUBARINFO);
1574     int rtn = GetMenuBarInfo(hWnd, OBJID_MENU, 0, &menuInfo);
1575 
1576     int menu_bar_height = 0;
1577     if (rtn != 0)
1578         menu_bar_height = menuInfo.rcBar.bottom - menuInfo.rcBar.top;
1579 
1580     return menu_bar_height;
1581 }
1582 
DoubleRound(double dVal,int format)1583 double CCommon::DoubleRound(double dVal, int format)
1584 {
1585     auto ipow = std::pow(10, format);
1586     return std::floor(dVal * ipow + 0.5) / ipow;
1587 }
1588 
IconSizeNormalize(int size)1589 int CCommon::IconSizeNormalize(int size)
1590 {
1591     //查找标准图标大小列表中和指定大小最接近的一个,然后将其返回
1592 
1593     const vector<int> standard_size{ 16, 20, 24, 28, 32, 48, 64, 128, 256, 512 };
1594     int min_diff = MAXINT;
1595     int index = 0;
1596     for (size_t i = 0; i < standard_size.size(); i++)
1597     {
1598         int diff = std::abs(size - standard_size[i]);
1599         if (diff == min_diff && diff > 2)
1600             return size;
1601 
1602         if (diff < min_diff)
1603         {
1604             min_diff = diff;
1605             index = i;
1606         }
1607     }
1608     return standard_size[index];
1609 }
1610 
SetWindowOpacity(HWND hWnd,int opacity)1611 void CCommon::SetWindowOpacity(HWND hWnd, int opacity)
1612 {
1613     SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
1614     ::SetLayeredWindowAttributes(hWnd, 0, opacity * 255 / 100, LWA_ALPHA);  //透明度取值范围为0~255
1615 }
1616 
StringIsVersion(LPCTSTR str)1617 bool CCommon::StringIsVersion(LPCTSTR str)
1618 {
1619     CString version_str{ str };
1620     return (version_str.GetLength() == 4 || version_str.GetLength() == 5) && version_str[1] == _T('.') && CharIsNumber(version_str[0]) && CharIsNumber(version_str[2]) && CharIsNumber(version_str[3]);
1621 }
1622 
GetFileContent(const wchar_t * file_path,string & contents_buff,size_t max_size)1623 bool CCommon::GetFileContent(const wchar_t* file_path, string& contents_buff, size_t max_size)
1624 {
1625     std::ifstream file{ file_path, std::ios::binary | std::ios::in };
1626     if (file.fail())
1627         return false;
1628     //获取文件长度
1629     file.seekg(0, file.end);
1630     unsigned int length{ static_cast<unsigned int>(file.tellg()) };
1631     file.seekg(0, file.beg);
1632 
1633     char* buff = new char[length];
1634     file.read(buff, length);
1635     file.close();
1636 
1637     contents_buff.assign(buff, length);
1638     delete[] buff;
1639 
1640     return true;
1641 }
1642 
GetFileContent(const wchar_t * file_path,size_t & length)1643 const char* CCommon::GetFileContent(const wchar_t* file_path, size_t& length)
1644 {
1645     std::ifstream file{ file_path, std::ios::binary };
1646     length = 0;
1647     if (file.fail())
1648         return nullptr;
1649     //获取文件长度
1650     file.seekg(0, file.end);
1651     length = static_cast<size_t>(file.tellg());
1652     file.seekg(0, file.beg);
1653 
1654     char* buff = new char[length];
1655     file.read(buff, length);
1656     file.close();
1657 
1658     return buff;
1659 }
1660 
SaveDataToFile(const string & data,const wstring & file_path)1661 bool CCommon::SaveDataToFile(const string& data, const wstring& file_path)
1662 {
1663     ofstream out_put{ file_path, std::ios::binary };
1664     if (!out_put.is_open())
1665         return false;
1666     if (!data.empty())
1667         out_put << data;
1668     out_put.close();
1669     return true;
1670 }
1671 
DoOpenFileDlg(const wstring & filter,vector<wstring> & path_list,CWnd * pParent)1672 void CCommon::DoOpenFileDlg(const wstring& filter, vector<wstring>& path_list, CWnd* pParent)
1673 {
1674     path_list.clear();
1675     //构造打开文件对话框
1676     CFileDialog fileDlg(TRUE, NULL, NULL, OFN_ALLOWMULTISELECT, filter.c_str(), pParent);
1677     //设置保存文件名的字符缓冲的大小为128kB(如果以平均一个文件名长度为32字节计算,最多可以打开大约4096个文件)
1678     fileDlg.m_ofn.nMaxFile = 128 * 1024;
1679     LPTSTR ch = new TCHAR[fileDlg.m_ofn.nMaxFile];
1680     fileDlg.m_ofn.lpstrFile = ch;
1681     //对内存块清零
1682     ZeroMemory(fileDlg.m_ofn.lpstrFile, sizeof(TCHAR) * fileDlg.m_ofn.nMaxFile);
1683     //显示打开文件对话框
1684     if (IDOK == fileDlg.DoModal())
1685     {
1686         POSITION posFile = fileDlg.GetStartPosition();
1687         while (posFile != NULL)
1688         {
1689             path_list.push_back(fileDlg.GetNextPathName(posFile).GetString());
1690         }
1691     }
1692     delete[] ch;
1693 
1694 }
1695 
SetDialogFont(CWnd * pDlg,CFont * pFont)1696 void CCommon::SetDialogFont(CWnd* pDlg, CFont* pFont)
1697 {
1698     if (pDlg->GetSafeHwnd() != NULL)
1699     {
1700         CWnd* pWndChild;
1701         pWndChild = pDlg->GetWindow(GW_CHILD);
1702         while (pWndChild)
1703         {
1704             pWndChild->SetFont(pFont);
1705             pWndChild = pWndChild->GetWindow(GW_HWNDNEXT);
1706         }
1707     }
1708 }
1709 
FileAutoRename(wstring & file_path)1710 void CCommon::FileAutoRename(wstring& file_path)
1711 {
1712     while (CCommon::FileExist(file_path))
1713     {
1714         //判断文件名的末尾是否符合“(数字)”的形式
1715         wstring file_name;		//文件名(不含扩展名)
1716         CFilePathHelper c_file_path(file_path);
1717         file_name = c_file_path.GetFileNameWithoutExtension();
1718         wstring ext{ c_file_path.GetFileExtension() };
1719         int num;
1720         size_t index;
1721         bool is_numbered{ CCommon::IsFileNameNumbered(file_name, num, index) };		//文件名的末尾是否符合“(数字)”的形式
1722         if (!is_numbered)		//如果文件名末尾没有“(数字)”,则在末尾添加“ (1)”
1723         {
1724             file_name += L" (1)";
1725         }
1726         else		//否则,将原来的数字加1
1727         {
1728             file_name = file_name.substr(0, index);
1729             CString num_str;
1730             num_str.Format(_T("(%d)"), num + 1);
1731             file_name += num_str;
1732         }
1733         file_path = c_file_path.GetDir() + file_name + L'.' + ext;
1734     }
1735 
1736 }
1737 
StringCompareInLocalLanguage(const wstring & str1,const wstring & str2,bool no_case)1738 int CCommon::StringCompareInLocalLanguage(const wstring& str1, const wstring& str2, bool no_case)
1739 {
1740 #ifndef COMPILE_IN_WIN_XP
1741     int rtn = CompareStringEx(LOCALE_NAME_USER_DEFAULT, (no_case ? NORM_IGNORECASE : 0) | SORT_DIGITSASNUMBERS, str1.c_str(), str1.size(), str2.c_str(), str2.size(), NULL, NULL, 0);
1742 #else
1743     int rtn = CompareString(LOCALE_NAME_USER_DEFAULT, (no_case ? NORM_IGNORECASE : 0), str1.c_str(), str1.size(), str2.c_str(), str2.size());
1744 #endif
1745     if (rtn == CSTR_EQUAL)
1746         return 0;
1747     else if (rtn == CSTR_GREATER_THAN)
1748         return 1;
1749     else
1750         return -1;
1751 }
1752 
SetNumberBit(unsigned short & num,int bit,bool value)1753 void CCommon::SetNumberBit(unsigned short& num, int bit, bool value)
1754 {
1755     if (value)
1756     {
1757         num |= (1 << bit);
1758     }
1759     else
1760     {
1761         num &= ~(1 << bit);
1762     }
1763 }
1764 
GetNumberBit(unsigned short num,int bit)1765 bool CCommon::GetNumberBit(unsigned short num, int bit)
1766 {
1767     return (num & (1 << bit)) != 0;
1768 }
1769 
GetTextResourceRawData(UINT id)1770 std::string CCommon::GetTextResourceRawData(UINT id)
1771 {
1772     std::string res_data;
1773     HINSTANCE hInstance = AfxGetResourceHandle();
1774     HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(id), _T("TEXT"));
1775     if (hRsrc != NULL)
1776     {
1777         HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
1778         if (hGlobal != NULL)
1779         {
1780             const char* pResource = static_cast<const char*>(LockResource(hGlobal));
1781             if (pResource != NULL)
1782             {
1783                 // 获取资源大小
1784                 DWORD dwSize = SizeofResource(hInstance, hRsrc);
1785                 // 将资源数据存储到std::string
1786                 return std::string(pResource, dwSize);
1787             }
1788             UnlockResource(hGlobal);
1789         }
1790     }
1791     return res_data;
1792 }
1793 
GetTextResource(UINT id,CodeType code_type)1794 wstring CCommon::GetTextResource(UINT id, CodeType code_type)
1795 {
1796     string res_data = GetTextResourceRawData(id);
1797     wstring res_str = CCommon::StrToUnicode(res_data, code_type);
1798     return res_str;
1799 }
1800 
GetPngImageResource(UINT id)1801 Gdiplus::Image* CCommon::GetPngImageResource(UINT id)
1802 {
1803     Gdiplus::Image* pImage = nullptr;
1804     HINSTANCE hInst = AfxGetResourceHandle();
1805     if (HRSRC hRes = ::FindResource(hInst, MAKEINTRESOURCE(id), _T("PNG")))
1806     {
1807         DWORD imageSize = ::SizeofResource(hInst, hRes);
1808         if (HGLOBAL hResData = ::LoadResource(hInst, hRes))
1809         {
1810             LPVOID pResourceData = ::LockResource(hResData);
1811 #ifdef COMPILE_IN_WIN_XP
1812             if (HGLOBAL hBuffer = ::GlobalAlloc(GMEM_FIXED, imageSize))
1813             {
1814                 ::CopyMemory(hBuffer, pResourceData, imageSize);
1815                 IStream* pStream = nullptr;
1816                 if (SUCCEEDED(::CreateStreamOnHGlobal(hBuffer, TRUE, &pStream)))
1817                 {
1818                     pImage = Gdiplus::Image::FromStream(pStream);
1819                     pStream->Release();
1820                 }
1821                 ::GlobalFree(hBuffer);  // 释放内存句柄
1822             }
1823 #else       // 在缓冲区上创建内存流
1824             if (IStream* pStream = SHCreateMemStream(static_cast<const BYTE*>(pResourceData), imageSize))
1825             {
1826                 pImage = Gdiplus::Image::FromStream(pStream);
1827                 pStream->Release();
1828             }
1829 #endif // COMPILE_IN_WIN_XP
1830             ::FreeResource(hResData);
1831         }
1832     }
1833     return pImage;
1834 }
1835 
1836 
GetPngImageResourceData(UINT id)1837 string CCommon::GetPngImageResourceData(UINT id)
1838 {
1839     string data;
1840     HINSTANCE hInst = AfxGetResourceHandle();
1841     if (HRSRC hRes = ::FindResource(hInst, MAKEINTRESOURCE(id), _T("PNG")))
1842     {
1843         DWORD imageSize = ::SizeofResource(hInst, hRes);
1844         if (HGLOBAL hResData = ::LoadResource(hInst, hRes))
1845         {
1846             LPVOID pResourceData = ::LockResource(hResData);
1847             data = string(static_cast<const char*>(pResourceData), imageSize);
1848             ::FreeResource(hResData);
1849         }
1850     }
1851     return data;
1852 }
1853 
Random(int min,int max)1854 int CCommon::Random(int min, int max)
1855 {
1856     std::random_device rd;
1857     std::mt19937 engine(rd());
1858     std::uniform_int_distribution<int> dis(min, max - 1);
1859     int dis_{ dis(engine) };
1860     return dis_;
1861 }
1862 
GetDesktopBackgroundPath()1863 CString CCommon::GetDesktopBackgroundPath()
1864 {
1865     CString path;
1866     CRegKey key;
1867     if (key.Open(HKEY_CURRENT_USER, _T("Control Panel\\Desktop\\")) == ERROR_SUCCESS)
1868     {
1869         TCHAR buff[MAX_PATH]{};
1870         ULONG size{ MAX_PATH };
1871         key.QueryStringValue(_T("WallPaper"), buff, &size);
1872         path = buff;
1873     }
1874     return path;
1875 }
1876 
CalculateWindowMoveOffset(CRect & check_rect,vector<CRect> & screen_rects)1877 POINT CCommon::CalculateWindowMoveOffset(CRect& check_rect, vector<CRect>& screen_rects)
1878 {
1879     POINT mov{};                                                        // 所需偏移量
1880     // 确保窗口在一个监视器内并且可见,判断移动距离并向所需移动距离较小的方向移动
1881     LONG mov_xy = 0;                                                    // 记录移动距离
1882     for (auto& a : screen_rects)
1883     {
1884         LONG x = 0, y = 0;
1885         if (check_rect.left < a.left || check_rect.Width() > a.Width()) // 需要向右移动
1886             x = a.left - check_rect.left;
1887         else if (check_rect.right > a.right)                            // 需要向左移动
1888             x = a.right - check_rect.right;
1889         if (check_rect.top < a.top || check_rect.Height() > a.Height()) // 需要向下移动
1890             y = a.top - check_rect.top;
1891         else if (check_rect.bottom > a.bottom)                          // 需要向上移动
1892             y = a.bottom - check_rect.bottom;
1893         if (x == 0 && y == 0)                                           // 窗口已在一个监视器内
1894         {
1895             mov.x = 0;
1896             mov.y = 0;
1897             break;
1898         }
1899         else if (abs(x) + abs(y) < mov_xy || mov_xy == 0)
1900         {
1901             mov.x = x;
1902             mov.y = y;
1903             mov_xy = abs(x) + abs(y);
1904         }
1905     }
1906     return mov;
1907 }
1908 
GetLastCompileTime(wstring & time_str,wstring & hash_str)1909 void CCommon::GetLastCompileTime(wstring& time_str, wstring& hash_str)
1910 {
1911     wstring compile_time = GetTextResource(IDR_COMPILE_TIME, CodeType::ANSI);
1912     size_t pos = compile_time.find(L"\r\n");
1913     time_str = compile_time.substr(0, pos);
1914     if (compile_time.size() > pos + 10)                 // 如果hash存在
1915         hash_str = compile_time.substr(pos + 2, 8);     // 截取hash前8位
1916 }
1917 
GetCurTimeElapse()1918 unsigned __int64 CCommon::GetCurTimeElapse()
1919 {
1920     SYSTEMTIME sys_time;
1921     GetLocalTime(&sys_time);
1922     CTime cur_time(sys_time);
1923     unsigned __int64 c_time = cur_time.GetTime();
1924     static unsigned __int64 last_time{};
1925     // 此处用作排序时间戳,需要避免重复
1926     last_time = (last_time < c_time) ? c_time : (last_time + 1);
1927     return last_time;
1928 }
1929 
EncodeURIComponent(wstring uri)1930 wstring CCommon::EncodeURIComponent(wstring uri) {
1931     StrReplace(uri, L"%", L"%25");
1932     StrReplace(uri, L":", L"%3A");
1933     StrReplace(uri, L"/", L"%2F");
1934     StrReplace(uri, L"?", L"%3F");
1935     StrReplace(uri, L"#", L"%23");
1936     StrReplace(uri, L"[", L"%5B");
1937     StrReplace(uri, L"]", L"%5D");
1938     StrReplace(uri, L"@", L"%40");
1939     StrReplace(uri, L"$", L"%24");
1940     StrReplace(uri, L"&", L"%26");
1941     StrReplace(uri, L"+", L"%2B");
1942     StrReplace(uri, L",", L"%2C");
1943     StrReplace(uri, L";", L"%3B");
1944     StrReplace(uri, L"=", L"%3D");
1945     return uri;
1946 }
1947 
OutputDebugStringFormat(LPCTSTR str,...)1948 void CCommon::OutputDebugStringFormat(LPCTSTR str, ...)
1949 {
1950     va_list args;
1951     va_start(args, str);
1952     TCHAR buf[1024] = { 0 };
1953     StringCchVPrintf(buf, 1023, str, args);
1954     va_end(args);
1955     buf[1023] = L'\0';
1956     OutputDebugString(buf);
1957 }
1958 
StrReplace(wstring & input,wstring pattern,wstring new_content)1959 wstring CCommon::StrReplace(wstring& input, wstring pattern, wstring new_content) {
1960     auto loc = input.find(pattern, 0);
1961     auto len = pattern.length();
1962     auto len2 = new_content.length();
1963     while (loc != -1) {
1964         input.replace(loc, len, new_content);
1965         if (loc + len2 < input.length()) loc = input.find(pattern, max(0, loc + len2));
1966         else break;
1967     }
1968     return input;
1969 }
1970