Update Shell and Fix Bugs

This commit is contained in:
Bruce
2025-12-07 15:18:40 +08:00
parent 98c0f91b8c
commit bb6b2b7521
51 changed files with 46468 additions and 12601 deletions

View File

@@ -2,6 +2,16 @@
#include <Windows.h>
#include "mpstr.h"
#include "nstring.h"
#include "filepath.h"
#include <combaseapi.h>
#include <rapidjson\document.h>
#include <rapidjson\writer.h>
#include <rapidjson\stringbuffer.h>
#include <codecvt>
#include <locale>
#include <ShlObj.h>
#include "mpstr.h"
#include "strcode.h"
using namespace System;
using namespace System::Runtime::InteropServices;
@@ -84,4 +94,320 @@ public ref class _I_String
}
return Format (pih, newargs);
}
};
};
String ^StringArrayToJson (array <String ^> ^strs)
{
using namespace rapidjson;
Document doc;
doc.SetArray ();
Document::AllocatorType &allocator = doc.GetAllocator ();
for each (String ^s in strs)
{
std::wstring ws = MPStringToStdW (s); // String^ → std::wstring
std::string utf8 = WStringToString (ws, CP_UTF8); // 简易宽转 UTF-8如需更严谨可用 WideCharToMultiByte
doc.PushBack (Value (utf8.c_str (), allocator), allocator);
}
StringBuffer buffer;
Writer <StringBuffer> writer (buffer);
doc.Accept (writer);
std::string json = buffer.GetString ();
std::wstring wjson = StringToWString (json, CP_UTF8);
return CStringToMPString (wjson);
}
std::wstring StringArrayToJson (const std::vector<std::wstring>& arr)
{
using namespace rapidjson;
Document doc;
doc.SetArray ();
auto &allocator = doc.GetAllocator ();
for (const auto &ws : arr)
{
std::string utf8 = WStringToUtf8 (ws);
doc.PushBack (Value (utf8.c_str (), allocator), allocator);
}
StringBuffer buffer;
Writer <StringBuffer> writer (buffer);
doc.Accept (writer);
return Utf8ToWString (buffer.GetString ());
}
[ComVisible (true)]
public ref class _I_Path
{
public:
property String ^Current
{
String ^get () { return CStringToMPString (GetCurrentDirectoryW ()); }
void set (String ^dir) { SetCurrentDirectoryW (MPStringToStdW (dir).c_str ()); }
}
property String ^Program { String ^get () { return CStringToMPString (GetCurrentProgramPathW ()); } }
property String ^Root { String ^get () { return CStringToMPString (GetFileDirectoryW (GetCurrentProgramPathW ())); }}
String ^Combine (String ^l, String ^r) { return CStringToMPString (CombinePath (MPStringToStdW (l), MPStringToStdW (r))); }
String ^GetName (String ^path)
{
std::wstring cpath = MPStringToStdW (path);
LPWSTR lp = PathFindFileNameW (cpath.c_str ());
return lp ? CStringToMPString (lp) : String::Empty;
}
String ^GetDirectory (String ^path) { return CStringToMPString (GetFileDirectoryW (MPStringToStdW (path))); }
String ^GetDir (String ^path) { return GetDirectory (path); }
bool Exist (String ^path) { return IsPathExists (MPStringToStdW (path)); }
bool FileExist (String ^filepath) { return IsFileExists (MPStringToStdW (filepath)); }
bool DirectoryExist (String ^dirpath) { return IsDirectoryExists (MPStringToStdW (dirpath)); }
bool DirExist (String ^dirpath) { return DirectoryExist (dirpath); }
String ^GetEnvironmentString (String ^str) { return CStringToMPString (ProcessEnvVars (MPStringToStdW (str))); }
bool ValidName (String ^filename) { return IsValidWindowsName (MPStringToStdW (filename)); }
// 过滤器用"\"分隔每个类型
String ^EnumFilesToJson (String ^dir, String ^filter, bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
::EnumFiles (MPStringToStdW (dir), MPStringToStdW (filter), res, withpath, sort, includesub);
return CStringToMPString (StringArrayToJson (res));
}
String ^EnumDirsToJson (String ^dir, bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
EnumDirectory (MPStringToStdW (dir), res, withpath, sort, includesub);
return CStringToMPString (StringArrayToJson (res));
}
String ^EnumSubDirsToJson (String ^dir, bool withpath)
{
std::vector <std::wstring> res = EnumSubdirectories (MPStringToStdW (dir), withpath);
return CStringToMPString (StringArrayToJson (res));
}
array <String ^> ^EnumFiles (String ^dir, String ^filter, bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
::EnumFiles (MPStringToStdW (dir), MPStringToStdW (filter), res, withpath, sort, includesub);
auto retarr = gcnew array <String ^> (res.size ());
for (size_t i = 0; i < res.size (); i ++)
{
retarr [i] = CStringToMPString (res [i]);
}
return retarr;
}
array <String ^> ^EnumDirs (String ^dir, bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
EnumDirectory (MPStringToStdW (dir), res, withpath, sort, includesub);
auto retarr = gcnew array <String ^> (res.size ());
for (size_t i = 0; i < res.size (); i ++)
{
retarr [i] = CStringToMPString (res [i]);
}
return retarr;
}
array <String ^> ^EnumSubDirs (String ^dir, bool withpath)
{
std::vector <std::wstring> res = EnumSubdirectories (MPStringToStdW (dir), withpath);
auto retarr = gcnew array <String ^> (res.size ());
for (size_t i = 0; i < res.size (); i ++)
{
retarr [i] = CStringToMPString (res [i]);
}
return retarr;
}
String ^CommonPrefix (String ^path1, String ^path2) { return CStringToMPString (PathCommonPrefix (MPStringToStdW (path1), MPStringToStdW (path2))); }
String ^EnsureDirSlash (String ^dir) { return CStringToMPString (EnsureTrailingSlash (MPStringToStdW (dir))); }
String ^Normalize (String ^path) { return CStringToMPString (NormalizePath (MPStringToStdW (path))); }
String ^FullPathName (String ^path) { return CStringToMPString (GetFullPathName (MPStringToStdW (path))); }
String ^FullPath (String ^path) { return FullPathName (path); }
String ^Expand (String ^path) { return CStringToMPString (ProcessEnvVars (MPStringToStdW (path))); }
String ^GetFolder (int csidl)
{
WCHAR buf [1024] = {0};
HRESULT hr = SHGetFolderPathW (NULL, csidl, NULL, 0, buf);
if (SUCCEEDED (hr)) return CStringToMPString (buf);
else return String::Empty;
}
String ^KnownFolder (String ^guidString)
{
if (String::IsNullOrWhiteSpace (guidString)) return String::Empty;
std::wstring wguid = MPStringToStdW (guidString);
KNOWNFOLDERID kfid;
HRESULT hr = CLSIDFromString (wguid.c_str (), &kfid);
if (FAILED (hr)) return String::Empty;
PWSTR path = nullptr;
hr = SHGetKnownFolderPath (kfid, 0, NULL, &path);
if (FAILED (hr) || path == nullptr) return L"";
std::wstring result (path ? path : L"");
if (path) CoTaskMemFree (path);
return CStringToMPString (result);
}
bool PEquals (String ^l, String ^r) { return PathEquals (MPStringToStdW (l), MPStringToStdW (r)); }
};
[ComVisible (true)]
public ref class _I_Entry
{
protected:
String ^path;
public:
_I_Entry (String ^path): path (path) {}
_I_Entry (): path (String::Empty) {}
property String ^Path { String ^get () { return path; } void set (String ^file) { path = file; } }
property String ^Name
{
String ^get ()
{
std::wstring file = MPStringToStdW (path);
LPWSTR lpstr = PathFindFileNameW (file.c_str ());
return lpstr ? CStringToMPString (lpstr) : String::Empty;
}
}
property String ^Directory { String ^get () { return CStringToMPString (GetFileDirectoryW (MPStringToStdW (path))); }}
property String ^Root { String ^get () { return Directory; }}
property bool Exist { virtual bool get () { return IsPathExists (MPStringToStdW (path)); }}
property String ^Uri
{
String ^get ()
{
using namespace System;
try
{
auto uri = gcnew System::Uri (System::IO::Path::GetFullPath (path));
auto uriText = uri->AbsoluteUri;
return uriText;
}
catch (...) { return String::Empty; }
}
}
property String ^FullPath { String ^get () { return System::IO::Path::GetFullPath (path); }}
};
[ComVisible (true)]
public ref class _I_File: public _I_Entry
{
protected:
System::Text::Encoding ^lastEncoding;
public:
_I_File (String ^filepath): _I_Entry (filepath) {}
_I_File (): _I_Entry (String::Empty) {}
String ^Get ()
{
using namespace System::IO;
if (String::IsNullOrEmpty (path)) return String::Empty;
FileStream ^fs = nullptr;
StreamReader ^sr = nullptr;
try
{
fs = gcnew FileStream (
path,
FileMode::OpenOrCreate,
FileAccess::ReadWrite,
FileShare::ReadWrite
);
sr = gcnew StreamReader (fs, Encoding::UTF8, true);
String ^text = sr->ReadToEnd ();
auto lastEncoding = sr->CurrentEncoding;
return text;
}
finally
{
if (sr) delete sr;
if (fs) delete fs;
}
}
void Set (String ^content)
{
using namespace System::IO;
if (String::IsNullOrEmpty (path)) return;
Encoding ^enc = lastEncoding ? lastEncoding : Encoding::UTF8;
FileStream ^fs = nullptr;
StreamWriter ^sw = nullptr;
try
{
fs = gcnew FileStream (
path,
FileMode::Create,
FileAccess::ReadWrite,
FileShare::ReadWrite
);
sw = gcnew StreamWriter (fs, enc);
sw->Write (content);
sw->Flush ();
}
finally
{
if (sw) delete sw;
if (fs) delete fs;
}
}
property String ^Content
{
String ^get () { return Get (); }
void set (String ^value) { Set (value); }
}
property bool Exist { bool get () override { return IsFileExists (MPStringToStdW (path)); }}
property String ^FilePath { String ^get () { return this->Path; } void set (String ^value) { this->Path = value; }}
};
[ComVisible (true)]
public ref class _I_Directory: public _I_Entry
{
public:
_I_Directory (String ^dirpath): _I_Entry (dirpath) {}
_I_Directory (_I_Entry ^file): _I_Entry (file->Directory) {}
_I_Directory (): _I_Entry (String::Empty) {}
property String ^DirectoryPath { String ^get () { return this->Path; } void set (String ^value) { this->Path = value; } }
property String ^DirPath { String ^get () { return this->DirectoryPath; } void set (String ^value) { this->DirectoryPath = value; } }
property bool Exist { bool get () override { return IsDirectoryExists (MPStringToStdW (path)); }}
String ^EnumFilesToJson (String ^filter, bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
::EnumFiles (MPStringToStdW (DirPath), MPStringToStdW (filter), res, withpath, sort, includesub);
return CStringToMPString (StringArrayToJson (res));
}
String ^EnumDirsToJson (bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
EnumDirectory (MPStringToStdW (DirPath), res, withpath, sort, includesub);
return CStringToMPString (StringArrayToJson (res));
}
String ^EnumSubDirsToJson (bool withpath)
{
std::vector <std::wstring> res = EnumSubdirectories (MPStringToStdW (DirPath), withpath);
return CStringToMPString (StringArrayToJson (res));
}
array <String ^> ^EnumFiles (String ^filter, bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
::EnumFiles (MPStringToStdW (DirPath), MPStringToStdW (filter), res, withpath, sort, includesub);
auto retarr = gcnew array <String ^> (res.size ());
for (size_t i = 0; i < res.size (); i ++)
{
retarr [i] = CStringToMPString (res [i]);
}
return retarr;
}
array <String ^> ^EnumDirs (bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
EnumDirectory (MPStringToStdW (DirPath), res, withpath, sort, includesub);
auto retarr = gcnew array <String ^> (res.size ());
for (size_t i = 0; i < res.size (); i ++)
{
retarr [i] = CStringToMPString (res [i]);
}
return retarr;
}
array <String ^> ^EnumSubDirs (bool withpath)
{
std::vector <std::wstring> res = EnumSubdirectories (MPStringToStdW (DirPath), withpath);
auto retarr = gcnew array <String ^> (res.size ());
for (size_t i = 0; i < res.size (); i ++)
{
retarr [i] = CStringToMPString (res [i]);
}
return retarr;
}
};
[ComVisible (true)]
public ref class _I_Storage
{
protected:
_I_Path ^path = gcnew _I_Path ();
public:
property _I_Path ^Path { _I_Path ^get () { return path; }}
_I_File ^GetFile (String ^path) { return gcnew _I_File (path); }
_I_Directory ^GetDirectory (String ^path) { return gcnew _I_Directory (path); }
_I_Directory ^GetDir (String ^path) { return GetDirectory (path); }
};

View File

@@ -1,20 +1,19 @@
#pragma once
#include <Windows.h>
#include <WinInet.h>
#include <winhttp.h>
#include <string>
#include "strcode.h"
#include "mpstr.h"
#include <chrono>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include "syncutil.h"
// Generated by ChatGTP
using namespace System;
using namespace System::Threading;
using namespace System::Reflection;
#include <Windows.h>
#include <string>
std::wstring GetLastErrorString ()
{
@@ -50,6 +49,8 @@ std::wstring GetLastErrorString ()
return msg;
}
CriticalSection g_download_cs;
public ref class DownloadHelper
{
public:
@@ -72,49 +73,163 @@ public ref class DownloadHelper
th->Start ();
}
private:
HINTERNET hSession = nullptr,
hConnect = nullptr,
hRequest = nullptr;
void CancelHttpHandle (HINTERNET hInternet)
{
if (hInternet) WinHttpCloseHandle (hInternet);
hInternet = nullptr;
}
std::wstring FormatSpeed (long long speed)
{
if (speed < 0) return L"--/s";
const wchar_t* units [] = {L"B/s", L"KB/s", L"MB/s", L"GB/s", L"TB/s"};
double s = (double)speed;
int idx = 0;
while (s >= 1024.0 && idx < 4) {
s /= 1024.0;
if (s / 1024.0 < 1) break;
idx++;
}
wchar_t buf [64];
swprintf (buf, 64, L"%.2f %s", s, units [idx]);
return buf;
}
public:
~DownloadHelper ()
{
if (hSession) CancelHttpHandle (hSession);
if (hConnect) CancelHttpHandle (hConnect);
if (hRequest) CancelHttpHandle (hRequest);
}
private:
void Worker ()
{
CreateScopedLock (g_download_cs);
std::wstring url = MPStringToStdW (m_url);
std::wstring outPath = MPStringToStdW (m_savePath);
HINTERNET hInternet = InternetOpenW (L"MyDownloader",
INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
URL_COMPONENTS urlComp = {0};
urlComp.dwStructSize = sizeof (urlComp);
if (!hInternet)
wchar_t host [256];
wchar_t path [2048];
urlComp.lpszHostName = host;
urlComp.dwHostNameLength = _countof (host);
urlComp.lpszUrlPath = path;
urlComp.dwUrlPathLength = _countof (path);
if (!WinHttpCrackUrl (url.c_str (), 0, 0, &urlComp))
{
ReportError (outPath, L"InternetOpenW Failed: " + GetLastErrorString ());
ReportError (outPath, L"WinHttpCrackUrl failed: " + GetLastErrorString ());
return;
}
HINTERNET hFile = InternetOpenUrlW (
hInternet,
url.c_str (),
NULL,
0,
INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE,
0
BOOL isHttps = (urlComp.nScheme == INTERNET_SCHEME_HTTPS);
hSession = WinHttpOpen (
L"MyDownloader",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0);
if (!hSession)
{
ReportError (outPath, L"WinHttpOpen failed: " + GetLastErrorString ());
return;
}
DWORD protocols =
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 |
WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 |
WINHTTP_FLAG_SECURE_PROTOCOL_SSL2 |
WINHTTP_FLAG_SECURE_PROTOCOL_ALL;
WinHttpSetOption (
hSession,
WINHTTP_OPTION_SECURE_PROTOCOLS,
&protocols,
sizeof (protocols)
);
if (!hFile)
hConnect = WinHttpConnect (
hSession,
urlComp.lpszHostName,
urlComp.nPort,
0);
if (!hConnect)
{
InternetCloseHandle (hInternet);
ReportError (outPath, L"InternetOpenUrlW Failed: " + GetLastErrorString ());
CancelHttpHandle (hSession); hSession = nullptr;
ReportError (outPath, L"WinHttpConnect failed: " + GetLastErrorString ());
return;
}
DWORD fileSize = 0;
DWORD len = sizeof (fileSize);
HttpQueryInfoW (hFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
&fileSize, &len, NULL);
hRequest = WinHttpOpenRequest (
hConnect,
L"GET",
urlComp.lpszUrlPath,
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
isHttps ? WINHTTP_FLAG_SECURE : 0);
HANDLE hOut = CreateFileW (outPath.c_str (), GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (!hRequest)
{
CancelHttpHandle (hConnect); hConnect = nullptr;
CancelHttpHandle (hSession); hSession = nullptr;
ReportError (outPath, L"WinHttpOpenRequest failed: " + GetLastErrorString ());
return;
}
if (!WinHttpSendRequest (hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
{
CancelHttpHandle (hRequest);
CancelHttpHandle (hConnect);
CancelHttpHandle (hSession);
ReportError (outPath, L"WinHttpSendRequest failed: " + GetLastErrorString ());
return;
}
if (!WinHttpReceiveResponse (hRequest, NULL))
{
CancelHttpHandle (hRequest); hRequest = nullptr;
CancelHttpHandle (hConnect); hConnect = nullptr;
CancelHttpHandle (hSession); hSession = nullptr;
ReportError (outPath, L"WinHttpReceiveResponse failed: " + GetLastErrorString ());
return;
}
// ---- 获取 Content-Length ----
DWORD dwSize = sizeof (DWORD);
DWORD fileSize = 0;
WinHttpQueryHeaders (
hRequest,
WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER,
NULL,
&fileSize,
&dwSize,
NULL);
HANDLE hOut = CreateFileW (outPath.c_str (), GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hOut == INVALID_HANDLE_VALUE)
{
InternetCloseHandle (hFile);
InternetCloseHandle (hInternet);
CancelHttpHandle (hRequest); hRequest = nullptr;
CancelHttpHandle (hConnect); hConnect = nullptr;
CancelHttpHandle (hSession); hSession = nullptr;
ReportError (outPath, L"Cannot create output file: " + GetLastErrorString ());
return;
}
@@ -125,23 +240,38 @@ public ref class DownloadHelper
long long received = 0;
auto t0 = std::chrono::high_resolution_clock::now ();
auto lastCheck = t0;
unsigned long long lastBytes = 0;
while (InternetReadFile (hFile, buffer, sizeof (buffer), &bytesRead) && bytesRead > 0)
while (WinHttpReadData (hRequest, buffer, sizeof (buffer), &bytesRead) && bytesRead > 0)
{
WriteFile (hOut, buffer, bytesRead, &bytesWritten, NULL);
received += bytesRead;
// 计算速度
auto t1 = std::chrono::high_resolution_clock::now ();
double sec = std::chrono::duration<double> (t1 - t0).count ();
long long speed = (long long)(received / (sec > 0 ? sec : 1));
auto now = std::chrono::high_resolution_clock::now ();
double intervalSec = std::chrono::duration<double> (now - lastCheck).count ();
long long speed = -1; // -1 表示“保持上次速度”
// 每 0.5 秒刷新一次速度(可调)
if (intervalSec >= 0.5)
{
unsigned long long bytesInInterval = received - lastBytes;
if (intervalSec > 0)
speed = (long long)(bytesInInterval / intervalSec); // B/s
lastCheck = now;
lastBytes = received;
}
ReportProgress (received, fileSize, speed);
}
CloseHandle (hOut);
InternetCloseHandle (hFile);
InternetCloseHandle (hInternet);
CancelHttpHandle (hRequest); hRequest = nullptr;
CancelHttpHandle (hConnect); hConnect = nullptr;
CancelHttpHandle (hSession); hSession = nullptr;
ReportComplete (outPath, received);
}
@@ -158,7 +288,12 @@ public ref class DownloadHelper
w.StartObject ();
w.Key ("received"); w.Uint64 (received);
w.Key ("total"); w.Uint64 (total);
w.Key ("speed"); w.Uint64 (speed);
std::wstring speedText = FormatSpeed (speed);
std::string speedUtf8 = WStringToString (speedText, CP_UTF8);
w.Key ("speed");
w.String (speedUtf8.c_str ());
w.Key ("progress"); w.Double (received / (double)total * 100);
w.EndObject ();

View File

@@ -869,310 +869,3 @@ bool PathEquals (const std::wstring &path1, const std::wstring &path2)
PathCanonicalizeW (buf2.data (), path2.c_str ());
return IsNormalizeStringEquals (buf1.data (), buf2.data ());
}
#ifdef __cplusplus_cli
#include <rapidjson\document.h>
#include <rapidjson\writer.h>
#include <rapidjson\stringbuffer.h>
#include <codecvt>
#include <locale>
#include "mpstr.h"
#include "strcode.h"
using namespace System;
using namespace System::Runtime::InteropServices;
String ^StringArrayToJson (array <String ^> ^strs)
{
using namespace rapidjson;
Document doc;
doc.SetArray ();
Document::AllocatorType &allocator = doc.GetAllocator ();
for each (String ^s in strs)
{
std::wstring ws = MPStringToStdW (s); // String^ → std::wstring
std::string utf8 = WStringToString (ws, CP_UTF8); // 简易宽转 UTF-8如需更严谨可用 WideCharToMultiByte
doc.PushBack (Value (utf8.c_str (), allocator), allocator);
}
StringBuffer buffer;
Writer <StringBuffer> writer (buffer);
doc.Accept (writer);
std::string json = buffer.GetString ();
std::wstring wjson = StringToWString (json, CP_UTF8);
return CStringToMPString (wjson);
}
std::wstring StringArrayToJson (const std::vector<std::wstring>& arr)
{
using namespace rapidjson;
Document doc;
doc.SetArray ();
auto &allocator = doc.GetAllocator ();
for (const auto &ws : arr)
{
std::string utf8 = WStringToUtf8 (ws);
doc.PushBack (Value (utf8.c_str (), allocator), allocator);
}
StringBuffer buffer;
Writer <StringBuffer> writer (buffer);
doc.Accept (writer);
return Utf8ToWString (buffer.GetString ());
}
[ComVisible (true)]
public ref class _I_Path
{
public:
property String ^Current
{
String ^get () { return CStringToMPString (GetCurrentDirectoryW ()); }
void set (String ^dir) { SetCurrentDirectoryW (MPStringToStdW (dir).c_str ()); }
}
property String ^Program { String ^get () { return CStringToMPString (GetCurrentProgramPathW ()); } }
property String ^Root { String ^get () {
std::wstring program = GetCurrentProgramPathW ();
std::wstring path = GetFileDirectoryW (program);
return CStringToMPString (GetFileDirectoryW (GetCurrentProgramPathW ()));
}}
String ^Combine (String ^l, String ^r) { return CStringToMPString (CombinePath (MPStringToStdW (l), MPStringToStdW (r))); }
String ^GetName (String ^path)
{
std::wstring cpath = MPStringToStdW (path);
LPWSTR lp = PathFindFileNameW (cpath.c_str ());
return lp ? CStringToMPString (lp) : String::Empty;
}
String ^GetDirectory (String ^path) { return CStringToMPString (GetFileDirectoryW (MPStringToStdW (path))); }
String ^GetDir (String ^path) { return GetDirectory (path); }
bool Exist (String ^path) { return IsPathExists (MPStringToStdW (path)); }
bool FileExist (String ^filepath) { return IsFileExists (MPStringToStdW (filepath)); }
bool DirectoryExist (String ^dirpath) { return IsDirectoryExists (MPStringToStdW (dirpath)); }
bool DirExist (String ^dirpath) { return DirectoryExist (dirpath); }
String ^GetEnvironmentString (String ^str) { return CStringToMPString (ProcessEnvVars (MPStringToStdW (str))); }
bool ValidName (String ^filename) { return IsValidWindowsName (MPStringToStdW (filename)); }
// 过滤器用"\"分隔每个类型
String ^EnumFilesToJson (String ^dir, String ^filter, bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
::EnumFiles (MPStringToStdW (dir), MPStringToStdW (filter), res, withpath, sort, includesub);
return CStringToMPString (StringArrayToJson (res));
}
String ^EnumDirsToJson (String ^dir, bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
EnumDirectory (MPStringToStdW (dir), res, withpath, sort, includesub);
return CStringToMPString (StringArrayToJson (res));
}
String ^EnumSubDirsToJson (String ^dir, bool withpath)
{
std::vector <std::wstring> res = EnumSubdirectories (MPStringToStdW (dir), withpath);
return CStringToMPString (StringArrayToJson (res));
}
array <String ^> ^EnumFiles (String ^dir, String ^filter, bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
::EnumFiles (MPStringToStdW (dir), MPStringToStdW (filter), res, withpath, sort, includesub);
auto retarr = gcnew array <String ^> (res.size ());
for (size_t i = 0; i < res.size (); i ++)
{
retarr [i] = CStringToMPString (res [i]);
}
return retarr;
}
array <String ^> ^EnumDirs (String ^dir, bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
EnumDirectory (MPStringToStdW (dir), res, withpath, sort, includesub);
auto retarr = gcnew array <String ^> (res.size ());
for (size_t i = 0; i < res.size (); i ++)
{
retarr [i] = CStringToMPString (res [i]);
}
return retarr;
}
array <String ^> ^EnumSubDirs (String ^dir, bool withpath)
{
std::vector <std::wstring> res = EnumSubdirectories (MPStringToStdW (dir), withpath);
auto retarr = gcnew array <String ^> (res.size ());
for (size_t i = 0; i < res.size (); i ++)
{
retarr [i] = CStringToMPString (res [i]);
}
return retarr;
}
String ^CommonPrefix (String ^path1, String ^path2) { return CStringToMPString (PathCommonPrefix (MPStringToStdW (path1), MPStringToStdW (path2))); }
String ^EnsureDirSlash (String ^dir) { return CStringToMPString (EnsureTrailingSlash (MPStringToStdW (dir))); }
String ^Normalize (String ^path) { return CStringToMPString (NormalizePath (MPStringToStdW (path))); }
String ^FullPathName (String ^path) { return CStringToMPString (GetFullPathName (MPStringToStdW (path))); }
bool PEquals (String ^l, String ^r) { return PathEquals (MPStringToStdW (l), MPStringToStdW (r)); }
};
[ComVisible (true)]
public ref class _I_Entry
{
protected:
String ^path;
public:
_I_Entry (String ^path): path (path) {}
_I_Entry (): path (String::Empty) {}
property String ^Path { String ^get () { return path; } void set (String ^file) { path = file; } }
property String ^Name
{
String ^get ()
{
std::wstring file = MPStringToStdW (path);
LPWSTR lpstr = PathFindFileNameW (file.c_str ());
return lpstr ? CStringToMPString (lpstr) : String::Empty;
}
}
property String ^Directory { String ^get () { return CStringToMPString (GetFileDirectoryW (MPStringToStdW (path))); }}
property String ^Root { String ^get () { return Directory; }}
property bool Exist { virtual bool get () { return IsPathExists (MPStringToStdW (path)); }}
property String ^Uri
{
String ^get ()
{
using namespace System;
try
{
auto uri = gcnew System::Uri (System::IO::Path::GetFullPath (path));
auto uriText = uri->AbsoluteUri;
return uriText;
}
catch (...) { return String::Empty; }
}
}
property String ^FullPath { String ^get () { return System::IO::Path::GetFullPath (path); }}
};
[ComVisible (true)]
public ref class _I_File: public _I_Entry
{
protected:
System::Text::Encoding ^lastEncoding;
public:
_I_File (String ^filepath): _I_Entry (filepath) {}
_I_File (): _I_Entry (String::Empty) {}
String ^Get ()
{
using namespace System::IO;
if (String::IsNullOrEmpty (path)) return String::Empty;
FileStream ^fs = nullptr;
StreamReader ^sr = nullptr;
try
{
fs = gcnew FileStream (
path,
FileMode::OpenOrCreate,
FileAccess::ReadWrite,
FileShare::ReadWrite
);
sr = gcnew StreamReader (fs, Encoding::UTF8, true);
String ^text = sr->ReadToEnd ();
auto lastEncoding = sr->CurrentEncoding;
return text;
}
finally
{
if (sr) delete sr;
if (fs) delete fs;
}
}
void Set (String ^content)
{
using namespace System::IO;
if (String::IsNullOrEmpty (path)) return;
Encoding ^enc = lastEncoding ? lastEncoding : Encoding::UTF8;
FileStream ^fs = nullptr;
StreamWriter ^sw = nullptr;
try
{
fs = gcnew FileStream (
path,
FileMode::Create,
FileAccess::ReadWrite,
FileShare::ReadWrite
);
sw = gcnew StreamWriter (fs, enc);
sw->Write (content);
sw->Flush ();
}
finally
{
if (sw) delete sw;
if (fs) delete fs;
}
}
property String ^Content
{
String ^get () { return Get (); }
void set (String ^value) { Set (value); }
}
property bool Exist { bool get () override { return IsFileExists (MPStringToStdW (path)); }}
property String ^FilePath { String ^get () { return this->Path; } void set (String ^value) { this->Path = value; }}
};
[ComVisible (true)]
public ref class _I_Directory: public _I_Entry
{
public:
_I_Directory (String ^dirpath): _I_Entry (dirpath) {}
_I_Directory (_I_Entry ^file): _I_Entry (file->Directory) {}
_I_Directory (): _I_Entry (String::Empty) {}
property String ^DirectoryPath { String ^get () { return this->Path; } void set (String ^value) { this->Path = value; } }
property String ^DirPath { String ^get () { return this->DirectoryPath; } void set (String ^value) { this->DirectoryPath = value; } }
property bool Exist { bool get () override { return IsDirectoryExists (MPStringToStdW (path)); }}
String ^EnumFilesToJson (String ^filter, bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
::EnumFiles (MPStringToStdW (DirPath), MPStringToStdW (filter), res, withpath, sort, includesub);
return CStringToMPString (StringArrayToJson (res));
}
String ^EnumDirsToJson (bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
EnumDirectory (MPStringToStdW (DirPath), res, withpath, sort, includesub);
return CStringToMPString (StringArrayToJson (res));
}
String ^EnumSubDirsToJson (bool withpath)
{
std::vector <std::wstring> res = EnumSubdirectories (MPStringToStdW (DirPath), withpath);
return CStringToMPString (StringArrayToJson (res));
}
array <String ^> ^EnumFiles (String ^filter, bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
::EnumFiles (MPStringToStdW (DirPath), MPStringToStdW (filter), res, withpath, sort, includesub);
auto retarr = gcnew array <String ^> (res.size ());
for (size_t i = 0; i < res.size (); i ++)
{
retarr [i] = CStringToMPString (res [i]);
}
return retarr;
}
array <String ^> ^EnumDirs (bool withpath, bool sort, bool includesub)
{
std::vector <std::wstring> res;
EnumDirectory (MPStringToStdW (DirPath), res, withpath, sort, includesub);
auto retarr = gcnew array <String ^> (res.size ());
for (size_t i = 0; i < res.size (); i ++)
{
retarr [i] = CStringToMPString (res [i]);
}
return retarr;
}
array <String ^> ^EnumSubDirs (bool withpath)
{
std::vector <std::wstring> res = EnumSubdirectories (MPStringToStdW (DirPath), withpath);
auto retarr = gcnew array <String ^> (res.size ());
for (size_t i = 0; i < res.size (); i ++)
{
retarr [i] = CStringToMPString (res [i]);
}
return retarr;
}
};
[ComVisible (true)]
public ref class _I_Storage
{
protected:
_I_Path ^path = gcnew _I_Path ();
public:
property _I_Path ^Path { _I_Path ^get () { return path; }}
_I_File ^GetFile (String ^path) { return gcnew _I_File (path); }
_I_Directory ^GetDirectory (String ^path) { return gcnew _I_Directory (path); }
_I_Directory ^GetDir (String ^path) { return GetDirectory (path); }
};
#endif

View File

@@ -2,6 +2,10 @@
#include <set>
#include <msclr/marshal_cppstd.h>
#include <ShObjIdl.h>
#include <ShlObj.h> // KNOWNFOLDERID, SHGetKnownFolderPath
#include <commdlg.h> // OPENFILENAME
#include <comdef.h> // _com_error
#include <winhttp.h> // WinHTTP
#include <MsHTML.h>
#include <ExDisp.h>
#include <atlbase.h>
@@ -9,10 +13,12 @@
#include <comdef.h>
#include <vcclr.h>
#include <map>
#include <commdlg.h>
#include <rapidjson\document.h>
#include <rapidjson\writer.h>
#include <rapidjson\stringbuffer.h>
#include "download.h"
#include <tlhelp32.h>
#include <Psapi.h>
#include "module.h"
#include "themeinfo.h"
#include "mpstr.h"
@@ -20,6 +26,7 @@
#include "vemani.h"
#include "ieshell.h"
#include "localeex.h"
#include "download.h"
#include "bridge.h"
#include "rctools.h"
#include "nstring.h"
@@ -45,6 +52,7 @@ struct iconhandle
LPCWSTR g_lpAppId = L"WindowsModern.PracticalToolsProject!Settings";
LPCWSTR g_idInVe = L"Settings";
LPCWSTR g_wndclass = L"Win32_WebUI_WindowsModern";
iconhandle g_hIconMain (LoadRCIcon (IDI_ICON_MAIN));
initfile g_initfile (CombinePath (GetProgramRootDirectoryW (), L"config.ini"));
vemanifest g_vemani (
@@ -61,6 +69,73 @@ ref class MainHtmlWnd;
msclr::gcroot <MainHtmlWnd ^> g_mainwnd;
std::wstring g_lastfile;
inline std::wstring ToStdWString (const std::wstring &str) { return str; }
std::string GetSuitableLanguageValue (const std::map <std::nstring, std::string> &map, const std::nstring &localename)
{
for (auto &it : map) if (it.first == localename) return it.second;
for (auto &it : map) if (LocaleNameCompare (pugi::as_wide (it.first), pugi::as_wide (localename))) return it.second;
for (auto &it : map) if (IsNormalizeStringEquals (GetLocaleRestrictedCodeA (it.first), GetLocaleRestrictedCodeA (localename))) return it.second;
for (auto &it : map) if (LocaleNameCompare (pugi::as_wide (GetLocaleRestrictedCodeA (it.first)), pugi::as_wide (GetLocaleRestrictedCodeA (localename)))) return it.second;
return "";
}
std::string GetSuitableLanguageValue (const std::map <std::nstring, std::string> &map)
{
if (map.empty ()) return "";
std::string ret = GetSuitableLanguageValue (map, pugi::as_utf8 (GetComputerLocaleCodeW ()));
if (ret.empty ()) ret = GetSuitableLanguageValue (map, "en-US");
if (ret.empty ()) ret = map.begin ()->second;
return ret;
}
struct xmlstrres
{
pugi::xml_document doc;
bool isvalid = false;
void destroy ()
{
if (isvalid) doc.reset ();
isvalid = false;
}
bool create (const std::wstring &filepath)
{
destroy ();
auto res = doc.load_file (filepath.c_str ());
return isvalid = res;
}
xmlstrres (const std::wstring &filepath) { create (filepath); }
~xmlstrres () { destroy (); }
std::string get (const std::string &id) const
{
auto root = doc.first_child ();
auto nodes = root.children ();
for (auto &it : nodes)
{
if (IsNormalizeStringEquals (std::string (it.attribute ("id").as_string ()), id))
{
auto strings = it.children ();
std::map <std::nstring, std::string> lang_value;
for (auto &sub : strings)
{
std::nstring lang = sub.attribute ("name").as_string ();
if (!lang.empty ()) lang_value [lang] = sub.text ().get ();
}
return GetSuitableLanguageValue (lang_value);
}
}
return "";
}
std::wstring get (const std::wstring &id) const { return pugi::as_wide (get (pugi::as_utf8 (id))); }
std::wstring operator [] (const std::wstring &id) const { return get (id); }
std::wstring operator [] (const std::wstring &id) { return get (id); }
std::string operator [] (const std::string &id) const { return get (id); }
std::string operator [] (const std::string &id) { return get (id); }
};
xmlstrres g_winjspri (CombinePath (GetProgramRootDirectoryW (), L"locale\\resources.xml"));
struct
{
bool jump = false;
std::wstring section = L"";
std::wstring item = L"";
std::wstring arg = L"";
};
size_t ExploreFile (HWND hParent, std::vector <std::wstring> &results, LPWSTR lpFilter = L"Windows Store App Package (*.appx; *.appxbundle)\0*.appx;*.appxbundle", DWORD dwFlags = OFN_EXPLORER | OFN_ALLOWMULTISELECT | OFN_PATHMUSTEXIST, const std::wstring &swWndTitle = std::wstring (L"Please select the file(-s): "), const std::wstring &swInitDir = GetFileDirectoryW (g_lastfile))
{
@@ -407,6 +482,104 @@ public ref class _I_IEFrame_Base
return "{}";
}
};
int ExecuteProgram (
const std::wstring &cmdline,
const std::wstring &file,
int wndshowmode,
bool wait,
const std::wstring &execdir = L"")
{
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory (&si, sizeof (si));
ZeroMemory (&pi, sizeof (pi));
si.cb = sizeof (si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = static_cast <WORD> (wndshowmode);
std::vector <WCHAR> buf (cmdline.capacity () + 1);
wcscpy (buf.data (), cmdline.c_str ());
LPCWSTR workdir = IsNormalizeStringEmpty (execdir) ? NULL : execdir.c_str ();
BOOL ok = CreateProcessW (
IsNormalizeStringEmpty (file) ? NULL : file.c_str (), // 应用程序路径
IsNormalizeStringEmpty (cmdline) ? NULL : buf.data (), // 命令行必须可写
NULL, // 进程安全属性
NULL, // 线程安全属性
FALSE, // 不继承句柄
0, // 创建标志
NULL, // 使用父进程环境变量
workdir, // 工作目录
&si,
&pi
);
if (!ok) return static_cast <int> (GetLastError ());
if (wait) WaitForSingleObject (pi.hProcess, INFINITE);
CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);
return 0;
}
bool KillProcessByFilePath (
const std::wstring &filepath,
bool multiple = false,
bool isonlyname = false
)
{
if (filepath.empty ()) return false;
std::wstring targetPath = filepath;
std::wstring targetName;
if (isonlyname)
{
size_t pos = filepath.find_last_of (L"\\/");
if (pos != std::wstring::npos) targetName = filepath.substr (pos + 1);
else targetName = filepath; // 直接是文件名
}
HANDLE hSnap = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
if (hSnap == INVALID_HANDLE_VALUE) return false;
PROCESSENTRY32W pe;
pe.dwSize = sizeof (pe);
bool killed = false;
if (Process32FirstW (hSnap, &pe))
{
do
{
bool match = false;
if (isonlyname)
{
if (PathEquals (pe.szExeFile, targetName.c_str ())) match = true;
}
else
{
// 比较完整路径,需要 QueryFullProcessImageNameW
HANDLE hProc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE, FALSE, pe.th32ProcessID);
if (hProc)
{
wchar_t exePath [MAX_PATH] = {0};
DWORD sz = MAX_PATH;
if (QueryFullProcessImageNameW (hProc, 0, exePath, &sz))
{
if (_wcsicmp (exePath, targetPath.c_str ()) == 0)
match = true;
}
CloseHandle (hProc);
}
}
if (match)
{
HANDLE hProc = OpenProcess (PROCESS_TERMINATE, FALSE, pe.th32ProcessID);
if (hProc)
{
TerminateProcess (hProc, 1);
CloseHandle (hProc);
killed = true;
}
if (!multiple) break;
}
} while (Process32NextW (hSnap, &pe));
}
CloseHandle (hSnap);
return killed;
}
[ComVisible (true)]
public ref class SplashForm: public System::Windows::Forms::Form
@@ -679,11 +852,85 @@ public ref class MainHtmlWnd: public System::Windows::Forms::Form, public IScrip
}
property _I_UI2 ^UI { _I_UI2 ^get () { return ui2; } }
};
[ComVisible (true)]
ref class _I_Process
{
public:
using String = System::String;
ref class ProcessWorker
{
_I_Process ^parent;
public:
ProcessWorker (_I_Process ^parent): parent (parent) {}
ProcessWorker (): parent (gcnew _I_Process ()) {}
String ^cmdline = String::Empty;
String ^filepath = String::Empty;
int wndtype = SW_NORMAL;
String ^runpath = String::Empty;
Object ^callback = nullptr;
void Work ()
{
int ret = parent->Run (cmdline, filepath, wndtype, true, runpath);
if (callback)
{
try
{
callback->GetType ()->InvokeMember (
"call",
BindingFlags::InvokeMethod,
nullptr,
callback,
gcnew array<Object^>{ 1, ret }
);
}
catch (...) {}
}
}
};
public:
int Run (String ^cmdline, String ^filepath, int wndtype, bool wait, String ^runpath)
{
return ExecuteProgram (
MPStringToStdW (cmdline),
MPStringToStdW (filepath),
wndtype,
wait,
MPStringToStdW (runpath)
);
}
void RunAsync (String ^cmdline, String ^filepath, int wndtype, String ^runpath, Object ^callback)
{
auto worker = gcnew ProcessWorker (this);
worker->cmdline = cmdline;
worker->filepath = filepath;
worker->wndtype = wndtype;
worker->runpath = runpath;
worker->callback = callback;
Thread^ th = gcnew Thread (gcnew ThreadStart (worker, &ProcessWorker::Work));
th->IsBackground = true;
th->Start ();
}
bool Kill (String ^filename, bool allproc, bool onlyname) { return KillProcessByFilePath (MPStringToStdW (filename), allproc, onlyname); }
};
[ComVisible (true)]
ref class _I_ResourcePri
{
public:
using String = System::String;
String ^GetString (String ^uri)
{
auto ret = g_winjspri.get (MPStringToStdW (uri));
auto retstr = CStringToMPString (ret);
return retstr;
}
};
private:
_I_IEFrame ^ieframe;
_I_System3 ^sys;
_I_VisualElements2 ^ve;
_I_Download ^download;
_I_Process ^proc;
_I_ResourcePri ^winjs_res;
public:
IBridge (MainHtmlWnd ^wnd): wndinst (wnd), _I_Bridge_Base2 (wnd)
{
@@ -692,17 +939,30 @@ public ref class MainHtmlWnd: public System::Windows::Forms::Form, public IScrip
storage = gcnew _I_Storage ();
ve = gcnew _I_VisualElements2 ();
download = gcnew _I_Download ();
proc = gcnew _I_Process ();
winjs_res = gcnew _I_ResourcePri ();
}
property _I_IEFrame ^IEFrame { _I_IEFrame ^get () { return ieframe; }}
property _I_System3 ^System { _I_System3 ^get () { return sys; }}
property _I_VisualElements2 ^VisualElements { _I_VisualElements2 ^get () { return ve; } }
property _I_Download ^Download { _I_Download ^get () { return download; }}
property _I_Process ^Process { _I_Process ^get () { return proc; }}
property _I_ResourcePri ^WinJsStringRes { _I_ResourcePri ^get () { return winjs_res; }}
void CloseWindow ()
{
if (wndinst && wndinst->IsHandleCreated) wndinst->Close ();
}
};
protected:
property WebBrowser ^WebUI { WebBrowser ^get () { return this->webui; } }
property SplashForm ^SplashScreen { SplashForm ^get () { return this->splash; } }
property int DPIPercent { int get () { return GetDPI (); }}
property double DPI { double get () { return DPIPercent * 0.01; }}
virtual void OnHandleCreated (EventArgs ^e) override
{
::SetClassLongPtrW ((HWND)this->Handle.ToPointer (), GCLP_HBRBACKGROUND, (LONG_PTR)g_wndclass);
Form::OnHandleCreated (e);
}
void InitSize ()
{
unsigned ww = 0, wh = 0;
@@ -1009,10 +1269,71 @@ HRESULT SetCurrentAppUserModelID (PCWSTR appID)
catch (...) { return E_FAIL; }
return E_FAIL;
}
void SetProgramSingleInstance (
const std::wstring &mutexName,
std::function <void ()> repeatCallback = nullptr,
bool focusMain = true)
{
HANDLE hMutex = CreateMutexW (NULL, TRUE, mutexName.c_str ());
if (hMutex == NULL) return;
destruct _mutexFree ([&] () { CloseHandle (hMutex); });
if (GetLastError () != ERROR_ALREADY_EXISTS) return;
if (repeatCallback) repeatCallback ();
wchar_t pathBuf [MAX_PATH] = {0};
GetModuleFileNameW (NULL, pathBuf, MAX_PATH);
std::wstring exeName = pathBuf;
exeName = exeName.substr (exeName.find_last_of (L"\\/") + 1);
DWORD existingPid = 0;
HANDLE snap = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
if (snap != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32W pe = {sizeof (pe)};
if (Process32FirstW (snap, &pe))
{
do {
if (_wcsicmp (pe.szExeFile, exeName.c_str ()) == 0 &&
pe.th32ProcessID != GetCurrentProcessId ())
{
existingPid = pe.th32ProcessID;
break;
}
} while (Process32NextW (snap, &pe));
}
CloseHandle (snap);
}
HWND targetHwnd = NULL;
if (existingPid)
{
EnumWindows ([] (HWND hwnd, LPARAM lParam) -> BOOL {
DWORD pid = 0;
GetWindowThreadProcessId (hwnd, &pid);
if (pid == (DWORD)lParam && IsWindowVisible (hwnd))
{
*((HWND *)(&lParam)) = hwnd;
return FALSE;
}
return TRUE;
}, (LPARAM)&targetHwnd);
}
if (focusMain && targetHwnd)
{
if (IsIconic (targetHwnd)) ShowWindow (targetHwnd, SW_RESTORE);
DWORD thisThread = GetCurrentThreadId ();
DWORD wndThread = GetWindowThreadProcessId (targetHwnd, NULL);
AttachThreadInput (thisThread, wndThread, TRUE);
SetActiveWindow (targetHwnd);
SetForegroundWindow (targetHwnd);
AttachThreadInput (thisThread, wndThread, FALSE);
BringWindowToTop (targetHwnd);
}
ExitProcess (0);
}
[STAThread]
int APIENTRY wWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
SetCurrentProcessExplicitAppUserModelID (g_lpAppId);
SetProgramSingleInstance (g_lpAppId);
SetProcessDPIAware ();
{
// 设置当前目录为程序所在目录

View File

@@ -23,6 +23,7 @@
<Keyword>Win32Proj</Keyword>
<RootNamespace>settings</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
@@ -97,7 +98,8 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>shlwapi.lib;version.lib;dwmapi.lib;wininet.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shlwapi.lib;version.lib;dwmapi.lib;winhttp.lib;Psapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -130,7 +132,8 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>shlwapi.lib;version.lib;dwmapi.lib;wininet.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>shlwapi.lib;version.lib;dwmapi.lib;winhttp.lib;Psapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -155,21 +158,7 @@
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<Reference Include="System">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.dll</HintPath>
</Reference>
<Reference Include="System.Drawing">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Drawing.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Windows.Forms.dll</HintPath>
</Reference>
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\appinstaller\bridge.h" />
<ClInclude Include="..\appinstaller\dynarr.h" />
<ClInclude Include="..\appinstaller\filepath.h" />
<ClInclude Include="..\appinstaller\ieshell.h" />
<ClInclude Include="..\appinstaller\initfile.h" />
<ClInclude Include="..\appinstaller\localeex.h" />
@@ -184,9 +173,12 @@
<ClInclude Include="..\appinstaller\typestrans.h" />
<ClInclude Include="..\appinstaller\vemani.h" />
<ClInclude Include="..\appinstaller\version.h" />
<ClInclude Include="bridge.h" />
<ClInclude Include="download.h" />
<ClInclude Include="filepath.h" />
<ClInclude Include="resmap.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="syncutil.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="settings.rc" />
@@ -199,6 +191,22 @@
<Image Include="res\icons\main.ico" />
<Image Include="res\icons\white.ico" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Drawing">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Drawing.dll</HintPath>
</Reference>
<Reference Include="System.Net">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Net.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Net.Http.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.XML">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.XML.dll</HintPath>
</Reference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\packages\rapidjson.1.0.2\build\native\rapidjson.targets" Condition="Exists('..\packages\rapidjson.1.0.2\build\native\rapidjson.targets')" />

View File

@@ -20,15 +20,9 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\appinstaller\bridge.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="..\appinstaller\dynarr.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="..\appinstaller\filepath.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="..\appinstaller\ieshell.h">
<Filter>头文件</Filter>
</ClInclude>
@@ -80,6 +74,15 @@
<ClInclude Include="download.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="bridge.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="filepath.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="syncutil.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="settings.rc">

124
settings/syncutil.h Normal file
View File

@@ -0,0 +1,124 @@
#pragma once
#pragma once
#include <Windows.h>
class CriticalSection
{
private:
CRITICAL_SECTION m_csection;
public:
CriticalSection (DWORD spinCount = 4000) { InitializeCriticalSectionAndSpinCount (&m_csection, spinCount); }
CriticalSection (const CriticalSection &) = delete;
CriticalSection &operator = (const CriticalSection &) = delete;
~CriticalSection () { DeleteCriticalSection (&m_csection); }
void Lock () { EnterCriticalSection (&m_csection); }
void Unlock () { LeaveCriticalSection (&m_csection); }
bool TryLock () { return TryEnterCriticalSection (&m_csection) != 0; }
class ScopedLock
{
public:
explicit ScopedLock (CriticalSection &cs): m_cs (cs) { m_cs.Lock (); }
~ScopedLock () { m_cs.Unlock (); }
ScopedLock (const ScopedLock &) = delete;
ScopedLock &operator = (const ScopedLock &) = delete;
private:
CriticalSection& m_cs;
};
};
#define CreateScopedLock(_obj_cs_) CriticalSection::ScopedLock _obj_cs_##sl (_obj_cs_)
#ifdef __cplusplus_cli
ref struct TaskStructEvent
{
typedef array <Object ^> args;
typedef void (*eventfunc) (... args ^args);
eventfunc post;
args ^postargs;
public:
TaskStructEvent (
eventfunc prefunc,
args ^preargs,
eventfunc postfunc,
args ^postargs
): post (postfunc), postargs (postargs)
{
if (prefunc == nullptr) {
#pragma message("警告:预处理函数指针为空,可能跳过初始化操作")
}
if (prefunc)
{
if (preargs)
{
#pragma region 参数验证示例
/*
实际项目中应添加具体类型检查,例如:
ValidateArgsType<Button^>(preargs);
*/
#pragma endregion
prefunc (preargs);
}
else prefunc (gcnew args {});
}
}
~TaskStructEvent ()
{
if (post == nullptr)
{
#pragma message("警告:后处理函数指针为空,资源可能无法正确释放")
return;
}
try
{
if (postargs) { post (postargs); }
else { post (gcnew args {}); }
}
catch (Exception ^e)
{
#pragma message("注意:后处理中的异常需手动处理")
}
}
};
#define CreateStructEvent(_varname_taskname_, _func_construct_, _args_construct_, _func_destruct_, _args_destruct_) \
TaskStructEvent _varname_taskname_ ( \
_func_construct_, \
_args_construct_, \
_func_destruct_, \
_args_destruct_ \
)
#endif
#ifdef __cplusplus
#include <functional>
#include <utility>
template <typename PreCallback, typename PostCallback> class ScopedEvent
{
public:
ScopedEvent (PreCallback &&pre, PostCallback &&post)
: m_post (std::forward <PostCallback> (post))
{
static_assert (
std::is_constructible <std::function <void ()>, PreCallback>::value,
"预处理回调必须可转换为 void () 类型"
);
if (pre) { pre (); }
}
~ScopedEvent () noexcept
{
if (m_post) { m_post (); }
}
ScopedEvent (const ScopedEvent &) = delete;
ScopedEvent &operator = (const ScopedEvent &) = delete;
ScopedEvent (ScopedEvent &&) = default;
ScopedEvent &operator =(ScopedEvent &&) = default;
private:
PostCallback m_post;
};
template <typename PreFunc, typename PostFunc> auto make_scoped_event (PreFunc &&pre, PostFunc &&post)
{
return ScopedEvent <PreFunc, PostFunc> (
std::forward <PreFunc> (pre),
std::forward <PostFunc> (post)
);
}
#endif