mirror of
https://github.com/modernw/App-Installer-For-Windows-8.x-Reset.git
synced 2026-04-11 17:57:19 +10:00
Update Shell and Fix Bugs
This commit is contained in:
@@ -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); }
|
||||
};
|
||||
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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 ();
|
||||
{
|
||||
// 设置当前目录为程序所在目录
|
||||
|
||||
@@ -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')" />
|
||||
|
||||
@@ -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
124
settings/syncutil.h
Normal 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
|
||||
Reference in New Issue
Block a user