Files
App-Installer-For-Windows-8…/appinstaller/main.cpp
2025-11-20 09:17:32 +08:00

595 lines
19 KiB
C++

#include <Windows.h>
#include <set>
#include <msclr/marshal_cppstd.h>
#include <ShObjIdl.h>
#include <MsHTML.h>
#include <ExDisp.h>
#include <atlbase.h>
#include "cmdargs.h"
#include "themeinfo.h"
#include "mpstr.h"
#include "initfile.h"
#include "resource.h"
#include "vemani.h"
#include "ieshell.h"
using namespace System;
using namespace System::Runtime::InteropServices;
#ifdef _DEBUG
#define DEBUGMODE true
#else
#define DEBUGMODE false
#endif
LPCWSTR g_lpAppId = L"Microsoft.DesktopAppInstaller";
auto &g_identity = g_lpAppId;
auto &m_idenName = g_lpAppId;
struct iconhandle
{
HICON hIcon = nullptr;
iconhandle (HICON hIcon = nullptr): hIcon (hIcon) {}
~iconhandle () { try { if (hIcon) DestroyIcon (hIcon); hIcon = nullptr; } catch (...) {} }
};
iconhandle g_hIconMain (LoadRCIcon (IDI_ICON_MAIN));
initfile g_initfile (CombinePath (GetProgramRootDirectoryW (), L"config.ini"));
ref class MainHtmlWnd;
msclr::gcroot <MainHtmlWnd ^> g_mainwnd;
vemanifest g_vemani (
IsFileExists (CombinePath (GetProgramRootDirectoryW (), L"VisualElementsManifest.xml")) ?
CombinePath (GetProgramRootDirectoryW (), L"VisualElementsManifest.xml") :
CombinePath (GetProgramRootDirectoryW (), L"AppInstaller.VisualElementsManifest.xml")
);
resxmldoc g_scaleres (
IsFileExists (CombinePath (GetProgramRootDirectoryW (), L"VisualElements\\scale.xml")) ?
CombinePath (GetProgramRootDirectoryW (), L"VisualElements\\scale.xml") :
CombinePath (GetProgramRootDirectoryW (), L"VisualElementsManifest.xml")
);
HRESULT GetWebBrowser2Interface (System::Windows::Forms::WebBrowser ^fwb, IWebBrowser2 **output)
{
if (fwb == nullptr || output == nullptr) return E_INVALIDARG;
*output = nullptr;
Object ^activeX = fwb->ActiveXInstance;
if (activeX == nullptr) return E_FAIL;
IntPtr pUnk = Marshal::GetIUnknownForObject (activeX);
if (pUnk == IntPtr::Zero) return E_FAIL;
HRESULT hr = ((IUnknown *)pUnk.ToPointer ())->QueryInterface (IID_IWebBrowser2, (void **)output);
Marshal::Release (pUnk);
return hr;
}
public ref class SplashForm: public System::Windows::Forms::Form
{
public:
using PictureBox = System::Windows::Forms::PictureBox;
using Timer = System::Windows::Forms::Timer;
private:
PictureBox ^picbox;
Timer ^timer;
double opastep = 0.05;
void InitForm ()
{
this->DoubleBuffered = true;
InitializeComponent ();
this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::None;
this->StartPosition = System::Windows::Forms::FormStartPosition::Manual;
this->ShowInTaskbar = false;
this->AllowTransparency = true;
this->Opacity = 1.0;
}
void InitializeComponent ()
{
double dDpi = GetDPI () * 0.01;
this->picbox = gcnew System::Windows::Forms::PictureBox ();
this->picbox->Size = System::Drawing::Size (620 * dDpi, 300 * dDpi);
this->picbox->BackColor = System::Drawing::Color::Transparent;
picbox->Anchor = System::Windows::Forms::AnchorStyles::None;
picbox->SizeMode = System::Windows::Forms::PictureBoxSizeMode::Zoom;
}
void OnFadeTimer (Object ^sender, EventArgs ^e)
{
auto fadeTimer = timer;
auto opacityStep = opastep;
if (this->Opacity > 0)
{
this->Opacity -= opacityStep;
}
else
{
fadeTimer->Stop ();
this->Close ();
}
}
void OnLoad (Object ^sender, EventArgs ^e)
{
this->ChangePosAndSize ();
this->Visible = true;
}
void OnResize (Object ^sender, EventArgs ^e)
{
if (IsHandleCreated && picbox->IsHandleCreated)
{
Drawing::Size sz = this->ClientSize;
this->picbox->Location = Drawing::Point (
(sz.Width - picbox->Width) * 0.5,
(sz.Height - picbox->Height) * 0.5
);
}
}
void OnResizeOwner (Object ^sender, EventArgs ^e) { this->ChangePosAndSize (); }
void OnLocationChangedOwner (Object ^sender, EventArgs ^e) { this->ChangePosAndSize (); }
protected:
virtual void OnHandleCreated (EventArgs^ e) override
{
Form::OnHandleCreated (e);
if (Environment::OSVersion->Version->Major >= 6)
{
INT mr = 0;
MARGINS margins = {mr, mr, mr, mr};
HRESULT hr = DwmExtendFrameIntoClientArea ((HWND)this->Handle.ToPointer (), &margins);
}
}
public:
SplashForm (System::String ^imgpath, System::Drawing::Color backcolor, System::Windows::Forms::Form ^owner)
{
if (owner != nullptr) this->Owner = owner;
InitForm ();
std::wstring filefullpath = MPStringToStdW (imgpath);
if (filefullpath.find (L'%') != filefullpath.npos) filefullpath = ProcessEnvVars (filefullpath);
filefullpath = GetFullPathName (imgpath ? MPStringToStdW (imgpath) : L"");
try
{
auto img = System::Drawing::Image::FromFile (gcnew System::String (filefullpath.c_str ()));
if (img != nullptr) picbox->Image = img;
}
catch (...) { }
if (backcolor != Drawing::Color::Transparent)
{
picbox->BackColor = backcolor;
this->BackColor = backcolor;
}
if (this->Owner != nullptr)
{
this->Owner->Resize += gcnew System::EventHandler (this, &SplashForm::OnResizeOwner);
this->Owner->LocationChanged += gcnew System::EventHandler (this, &SplashForm::OnLocationChangedOwner);
}
this->Controls->Add (picbox);
this->Resize += gcnew EventHandler (this, &SplashForm::OnResize);
timer = gcnew System::Windows::Forms::Timer ();
timer->Interval = 15;
timer->Tick += gcnew System::EventHandler (this, &SplashForm::OnFadeTimer);
this->Load += gcnew EventHandler (this, &SplashForm::OnLoad);
}
void ChangePosAndSize ()
{
if (this->Owner && this->Owner->IsHandleCreated)
{
this->Owner->Update ();
System::Drawing::Point pt = this->Owner->PointToScreen (this->Owner->ClientRectangle.Location);
this->Location = pt;
this->Size = this->Owner->ClientSize;
}
else if (this->Parent && this->Parent->IsHandleCreated)
{
this->Parent->Update ();
System::Drawing::Point pt = this->Parent->PointToScreen (this->Parent->ClientRectangle.Location);
this->Location = pt;
this->Size = this->Parent->ClientSize;
}
if (IsHandleCreated && picbox->IsHandleCreated)
{
Drawing::Size sz = this->ClientSize;
this->picbox->Location = Drawing::Point (
(sz.Width - picbox->Width) * 0.5,
(sz.Height - picbox->Height) * 0.5
);
}
}
void SetSplashImage (System::Drawing::Image ^img) { if (picbox) picbox->Image = img; }
void SetSplashImage (System::String ^imgpath) { try { SetSplashImage (System::Drawing::Image::FromFile (imgpath)); } catch (...) {} }
void SetSplashImage (const std::wstring &imgpath) { SetSplashImage (CStringToMPString (imgpath)); }
void SetSplashBackgroundColor (System::Drawing::Color color) { picbox->BackColor = color; this->BackColor = color; }
// ½¥±äÏûʧ
void FadeOut () { timer->Start (); }
// Á¢¼´Ïûʧ
void FadeAway () { this->Visible = false; this->Close (); }
~SplashForm ()
{
if (this->Owner != nullptr)
{
this->Owner->Resize -= gcnew System::EventHandler (this, &SplashForm::OnResizeOwner);
this->Owner->LocationChanged -= gcnew System::EventHandler (this, &SplashForm::OnLocationChangedOwner);
}
}
};
[ComVisible (true)]
public ref class MainHtmlWnd: public System::Windows::Forms::Form
{
public:
using WebBrowser = System::Windows::Forms::WebBrowser;
private:
WebBrowser ^webui;
SplashForm ^splash;
public:
[ComVisible (true)]
ref class IBridge
{
private:
MainHtmlWnd ^wndinst = nullptr;
public:
IBridge (MainHtmlWnd ^wnd): wndinst (wnd) {}
ref class _I_System
{
private:
MainHtmlWnd ^wndinst = nullptr;
public:
ref class _I_UI
{
private:
MainHtmlWnd ^wndinst = nullptr;
public:
_I_UI (MainHtmlWnd ^wnd): wndinst (wnd) {}
property int DPIPercent { int get () { return GetDPI (); }}
property double DPI { double get () { return DPIPercent * 0.01; }}
void showSplash () { wndinst->SplashScreen->Show (); }
void fadeAwaySplash () { wndinst->SplashScreen->FadeAway (); }
void fadeOutSplash () { wndinst->SplashScreen->FadeOut (); }
};
private:
_I_UI ^ui = gcnew _I_UI (wndinst);
public:
_I_System (MainHtmlWnd ^wnd): wndinst (wnd) {}
property _I_UI ^UI { _I_UI ^get () { return ui; } }
};
ref class _I_IEFrame
{
private:
MainHtmlWnd ^wndinst = nullptr;
public:
_I_IEFrame (MainHtmlWnd ^wnd): wndinst (wnd) {}
property int Scale
{
int get () { return wndinst->PageScale; }
void set (int value) { return wndinst->PageScale = value; }
}
property int Version { int get () { return GetInternetExplorerVersionMajor (); }}
};
ref class _I_Storage
{
private:
MainHtmlWnd ^wndinst = nullptr;
public:
_I_Storage (MainHtmlWnd ^wnd): wndinst (wnd) {}
ref class Path
{
};
};
ref class _I_String
{
public:
ref class _I_NString
{
public:
bool NEquals (String ^l, String ^r) { return IsNormalizeStringEquals (MPStringToPtrW (l), MPStringToPtrW (r)); }
bool Empty (String ^l) { return IsNormalizeStringEmpty (MPStringToStdW (l)); }
int Compare (String ^l, String ^r) { return NormalizeStringCompare (MPStringToPtrW (l), MPStringToPtrW (r)); }
int Length (String ^l) { return GetNormalizeStringLength (MPStringToStdW (l)); }
};
private:
_I_NString ^nstr = gcnew _I_NString ();
public:
property _I_NString ^NString { _I_NString ^get () { return nstr; }}
String ^Trim (String ^src)
{
std::wstring csrc = MPStringToStdW (src);
return CStringToMPString (::StringTrim (csrc));
}
String ^ToLower (String ^src) { return CStringToMPString (StringToLower (MPStringToStdW (src))); }
String ^ToUpper (String ^src) { return CStringToMPString (StringToUpper (MPStringToStdW (src))); }
};
private:
_I_System ^system = gcnew _I_System (wndinst);
_I_IEFrame ^ieframe = gcnew _I_IEFrame (wndinst);
_I_Storage ^storage = gcnew _I_Storage (wndinst);
_I_String ^str = gcnew _I_String ();
public:
property _I_System ^System { _I_System ^get () { return system; }}
property _I_IEFrame ^IEFrame { _I_IEFrame ^get () { return ieframe; }}
property _I_Storage ^Storage { _I_Storage ^get () { return storage; }}
property _I_String ^String { _I_String ^get () { return str; }}
};
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; }}
void Init ()
{
this->Visible = false;
this->webui = gcnew System::Windows::Forms::WebBrowser ();
this->SuspendLayout ();
this->webui->Dock = System::Windows::Forms::DockStyle::Fill;
this->webui->IsWebBrowserContextMenuEnabled = DEBUGMODE;
this->webui->AllowWebBrowserDrop = false;
this->Controls->Add (this->webui);
if (g_hIconMain.hIcon)
{
try { this->Icon = System::Drawing::Icon::FromHandle (IntPtr (g_hIconMain.hIcon)); }
catch (...) {}
}
unsigned ww = 0, wh = 0;
auto &ini = g_initfile;
auto setsect = ini ["Settings"];
if (setsect [L"SavePosAndSizeBeforeCancel"].read_bool ())
{
ww = setsect [L"LastWidth"].read_uint (setsect [L"DefaultWidth"].read_uint (rcInt (IDS_DEFAULTWIDTH)));
wh = setsect [L"LastHeight"].read_uint (setsect [L"DefaultHeight"].read_uint (rcInt (IDS_DEFAULTHEIGHT)));
}
else
{
ww = setsect [L"DefaultWidth"].read_uint (rcInt (IDS_DEFAULTWIDTH));
wh = setsect [L"DefaultHeight"].read_uint (rcInt (IDS_DEFAULTHEIGHT));
}
this->MinimumSize = System::Drawing::Size (
setsect [L"MinimumWidth"].read_uint (rcInt (IDS_MINWIDTH)) * DPI,
setsect [L"MinimumHeight"].read_uint (rcInt (IDS_MINHIEHGT)) * DPI
);
this->ClientSize = System::Drawing::Size (ww * DPI, wh * DPI);
this->WindowState = (System::Windows::Forms::FormWindowState)setsect [L"LastWndState"].read_int ((int)System::Windows::Forms::FormWindowState::Normal);
this->Text = rcString (IDS_WINTITLE);
this->ResumeLayout (false);
webui->ObjectForScripting = gcnew IBridge (this);
this->webui->DocumentCompleted += gcnew System::Windows::Forms::WebBrowserDocumentCompletedEventHandler (this, &MainHtmlWnd::OnDocumentCompleted);
this->webui->PreviewKeyDown += gcnew System::Windows::Forms::PreviewKeyDownEventHandler (this, &MainHtmlWnd::OnPreviewKeyDown_WebBrowser);
this->Resize += gcnew System::EventHandler (this, &MainHtmlWnd::OnResize);
this->Load += gcnew EventHandler (this, &MainHtmlWnd::OnCreate);
this->ResizeEnd += gcnew EventHandler (this, &MainHtmlWnd::OnResizeEnd);
}
void OnDocumentCompleted (Object ^sender, System::Windows::Forms::WebBrowserDocumentCompletedEventArgs ^e)
{
if (e->Url->ToString () == webui->Url->ToString ())
{
ExecScript ("Windows.UI.DPI.mode = 1");
ExecScript ("Bridge.Frame.scale = Bridge.Frame.scale * Bridge.UI.dpi");
splash->FadeOut ();
}
}
void OnCreate (System::Object ^sender, System::EventArgs ^e)
{
splash->Owner = this;
splash->ChangePosAndSize ();
splash->Show ();
splash->Update ();
webui->Navigate (CStringToMPString (CombinePath (GetProgramRootDirectoryW (), L"html\\install.html")));
}
void OnPreviewKeyDown_WebBrowser (System::Object ^sender, System::Windows::Forms::PreviewKeyDownEventArgs ^e)
{
if (e->KeyCode == System::Windows::Forms::Keys::F5 || (e->KeyCode == System::Windows::Forms::Keys::R && e->Control))
e->IsInputKey = true;
}
void OnResize (Object ^sender, EventArgs ^e)
{
}
void OnResizeEnd (Object ^sender, EventArgs ^e)
{
}
public:
MainHtmlWnd ()
{
splash = gcnew SplashForm (
gcnew String (g_vemani.splash_screen_image (L"App").c_str ()),
StringToColor (gcnew String (g_vemani.splash_screen_backgroundcolor (L"App").c_str ())),
this
);
System::Windows::Forms::Application::DoEvents ();
Init ();
}
Object ^CallScriptFunction (String ^lpFuncName, ... array <Object ^> ^alpParams)
{
try { return this->webui->Document->InvokeScript (lpFuncName, alpParams); }
catch (Exception ^e) {}
return nullptr;
}
Object ^CallScriptFunction (String ^lpScriptName)
{
try { return this->webui->Document->InvokeScript (lpScriptName); }
catch (Exception ^e) {}
return nullptr;
}
Object ^InvokeCallScriptFunction (String ^lpFuncName, ... array <Object ^> ^alpParams)
{
try
{
if (this->InvokeRequired) return (Object ^)this->Invoke (gcnew Func <String ^, array <Object ^> ^, Object ^> (this, &MainHtmlWnd::CallScriptFunction), lpFuncName, alpParams);
else return CallScriptFunction (lpFuncName, alpParams);
}
catch (Exception ^e) {}
return nullptr;
}
Object ^InvokeCallScriptFunction (String ^lpScriptName)
{
try
{
if (this->InvokeRequired) return (Object ^)this->Invoke (gcnew Func <String ^, Object ^> (this, &MainHtmlWnd::CallScriptFunction), lpScriptName);
else return CallScriptFunction (lpScriptName);
}
catch (Exception ^e) {}
return nullptr;
}
Object ^ExecScript (... array <Object ^> ^alpScript) { return InvokeCallScriptFunction ("eval", alpScript); }
property int PageScale
{
int get ()
{
CComPtr <IWebBrowser2> web2;
HRESULT hr = GetWebBrowser2Interface (webui, &web2);
if (FAILED (hr)) return 0;
VARIANT v;
VariantInit (&v);
hr = web2->ExecWB (OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DODEFAULT, nullptr, &v);
if (FAILED (hr) || v.vt != VT_I4) return 0;
int val = v.lVal;
VariantClear (&v);
return val;
}
void set (int value)
{
CComPtr <IWebBrowser2> web2;
HRESULT hr = GetWebBrowser2Interface (webui, &web2);
if (FAILED (hr)) return;
VARIANT v;
VariantInit (&v);
v.vt = VT_I4;
v.lVal = value;
web2->ExecWB (OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, &v, nullptr);
}
}
};
using MainWnd = MainHtmlWnd;
std::vector <std::wstring> LoadFileListW (const std::wstring &filePath)
{
std::vector <std::wstring> result;
HANDLE hFile = CreateFileW (
filePath.c_str (),
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr);
if (hFile == INVALID_HANDLE_VALUE) return result;
LARGE_INTEGER fileSize {};
if (!GetFileSizeEx (hFile, &fileSize) || fileSize.QuadPart == 0)
{
CloseHandle (hFile);
return result;
}
DWORD size = static_cast <DWORD> (fileSize.QuadPart);
std::vector <WCHAR> buf;
std::wstring buffer;
buffer.resize (size / sizeof (wchar_t) + 2 + 2);
DWORD readBytes = 0;
ReadFile (hFile, buf.data (), size, &readBytes, nullptr);
buffer += buf.data ();
CloseHandle (hFile);
buffer [readBytes / sizeof (wchar_t)] = L'\0';
size_t start = 0;
while (true)
{
size_t pos = buffer.find (L'\n', start);
std::wstring line;
if (pos == std::wstring::npos)
{
line = buffer.substr (start);
}
else
{
line = buffer.substr (start, pos - start);
start = pos + 1;
}
if (!line.empty () && line.back () == L'\r') line.pop_back ();
if (!line.empty ()) result.push_back (line);
if (pos == std::wstring::npos) break;
}
return result;
}
enum class CMDPARAM: DWORD
{
NONE = 0b000,
SILENT = 0b001,
VERYSILENT = 0b011,
MULTIPLE = 0b100
};
DWORD CmdMapsToFlags (std::map <cmdkey, cmdvalue> cmdpairs, std::set <std::wnstring> &files, std::set <std::wnstring> &uris)
{
DWORD dwret = 0;
for (auto &it : cmdpairs)
{
switch (it.first.type)
{
case paramtype::file: {
if (IsFileExists (it.first.key)) files.insert (it.first.key);
} break;
case paramtype::uri: {
uris.insert (it.first.key);
} break;
default:
case paramtype::string: {
auto &key = it.first;
auto &value = it.second;
if (key.key.equals (L"silent")) dwret |= (DWORD)CMDPARAM::SILENT;
else if (key.key.equals (L"verysilent")) dwret |= (DWORD)CMDPARAM::SILENT;
else if (key.key.equals (L"multiple"))
{
if (value.type == paramtype::file)
{
auto strlist = LoadFileListW (value.value);
for (auto &it_s : strlist)
{
if (std::wnstring::empty (it_s)) continue;
std::wnstring filepath = it_s;
std::wstring listdir = GetFileDirectoryW (value.value);
if (!IsFileExists (filepath)) filepath = ProcessEnvVars (filepath);
if (!IsFileExists (filepath)) filepath = CombinePath (listdir, filepath);
if (!IsFileExists (filepath)) filepath = CombinePath (listdir, it_s);
if (!IsFileExists (filepath)) continue;
else files.insert (filepath);
}
}
}
} break;
}
}
if (files.size () > 1) dwret |= (DWORD)CMDPARAM::MULTIPLE;
return dwret;
}
HRESULT SetCurrentAppUserModelID (PCWSTR appID)
{
typedef HRESULT (WINAPI *SetAppUserModelIDFunc)(PCWSTR);
HMODULE shell32 = LoadLibraryW (L"shell32.dll");
destruct freelib ([&] () {
if (shell32) FreeLibrary (shell32);
});
try
{
if (!shell32) return E_FAIL;
auto SetAppUserModelID = (SetAppUserModelIDFunc)GetProcAddress (shell32, "SetCurrentProcessExplicitAppUserModelID");
if (!SetAppUserModelID)
{
FreeLibrary (shell32);
return E_FAIL;
}
return SetAppUserModelID (appID);
}
catch (...) { return E_FAIL; }
return E_FAIL;
}
[STAThread]
int APIENTRY wWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
SetCurrentProcessExplicitAppUserModelID (m_idenName);
SetProcessDPIAware ();
{
// ÉèÖõ±Ç°Ä¿Â¼Îª³ÌÐòËùÔÚĿ¼
std::wnstring currdir = GetCurrentProgramPathW ();
std::wnstring rootdir = GetProgramRootDirectoryW ();
if (!PathEquals (currdir, rootdir)) SetCurrentDirectoryW (rootdir.c_str ());
}
CoInitializeEx (NULL, COINIT_MULTITHREADED);
destruct relco ([] () {
CoUninitialize ();
});
System::Windows::Forms::Application::EnableVisualStyles ();
System::Windows::Forms::Application::SetCompatibleTextRenderingDefault (false);
auto mwnd = gcnew MainHtmlWnd ();
g_mainwnd = mwnd;
System::Windows::Forms::Application::Run (mwnd);
return 0;
}