mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Add C++ to editorconfig & update formatting. No functional changes.
This commit is contained in:
@@ -123,3 +123,72 @@ insert_final_newline=false
|
|||||||
[*.vb]
|
[*.vb]
|
||||||
# Modifier preferences
|
# Modifier preferences
|
||||||
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
|
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
|
||||||
|
|
||||||
|
[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]
|
||||||
|
|
||||||
|
# Visual C++ Code Style settings
|
||||||
|
|
||||||
|
cpp_generate_documentation_comments = xml
|
||||||
|
|
||||||
|
# Visual C++ Formatting settings
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
cpp_indent_braces = false
|
||||||
|
cpp_indent_multi_line_relative_to = innermost_parenthesis
|
||||||
|
cpp_indent_within_parentheses = indent
|
||||||
|
cpp_indent_preserve_within_parentheses = true
|
||||||
|
cpp_indent_case_contents = true
|
||||||
|
cpp_indent_case_labels = false
|
||||||
|
cpp_indent_case_contents_when_block = false
|
||||||
|
cpp_indent_lambda_braces_when_parameter = true
|
||||||
|
cpp_indent_goto_labels = one_left
|
||||||
|
cpp_indent_preprocessor = leftmost_column
|
||||||
|
cpp_indent_access_specifiers = false
|
||||||
|
cpp_indent_namespace_contents = true
|
||||||
|
cpp_indent_preserve_comments = false
|
||||||
|
cpp_new_line_before_open_brace_namespace = new_line
|
||||||
|
cpp_new_line_before_open_brace_type = new_line
|
||||||
|
cpp_new_line_before_open_brace_function = new_line
|
||||||
|
cpp_new_line_before_open_brace_block = same_line
|
||||||
|
cpp_new_line_before_open_brace_lambda = ignore
|
||||||
|
cpp_new_line_scope_braces_on_separate_lines = false
|
||||||
|
cpp_new_line_close_brace_same_line_empty_type = false
|
||||||
|
cpp_new_line_close_brace_same_line_empty_function = false
|
||||||
|
cpp_new_line_before_catch = true
|
||||||
|
cpp_new_line_before_else = true
|
||||||
|
cpp_new_line_before_while_in_do_while = false
|
||||||
|
cpp_space_before_function_open_parenthesis = remove
|
||||||
|
cpp_space_within_parameter_list_parentheses = false
|
||||||
|
cpp_space_between_empty_parameter_list_parentheses = false
|
||||||
|
cpp_space_after_keywords_in_control_flow_statements = true
|
||||||
|
cpp_space_within_control_flow_statement_parentheses = false
|
||||||
|
cpp_space_before_lambda_open_parenthesis = false
|
||||||
|
cpp_space_within_cast_parentheses = false
|
||||||
|
cpp_space_after_cast_close_parenthesis = false
|
||||||
|
cpp_space_within_expression_parentheses = false
|
||||||
|
cpp_space_before_block_open_brace = true
|
||||||
|
cpp_space_between_empty_braces = false
|
||||||
|
cpp_space_before_initializer_list_open_brace = false
|
||||||
|
cpp_space_within_initializer_list_braces = true
|
||||||
|
cpp_space_preserve_in_initializer_list = true
|
||||||
|
cpp_space_before_open_square_bracket = false
|
||||||
|
cpp_space_within_square_brackets = false
|
||||||
|
cpp_space_before_empty_square_brackets = false
|
||||||
|
cpp_space_between_empty_square_brackets = false
|
||||||
|
cpp_space_group_square_brackets = true
|
||||||
|
cpp_space_within_lambda_brackets = false
|
||||||
|
cpp_space_between_empty_lambda_brackets = false
|
||||||
|
cpp_space_before_comma = false
|
||||||
|
cpp_space_after_comma = true
|
||||||
|
cpp_space_remove_around_member_operators = true
|
||||||
|
cpp_space_before_inheritance_colon = true
|
||||||
|
cpp_space_before_constructor_colon = true
|
||||||
|
cpp_space_remove_before_semicolon = true
|
||||||
|
cpp_space_after_semicolon = false
|
||||||
|
cpp_space_remove_around_unary_operator = true
|
||||||
|
cpp_space_around_binary_operator = insert
|
||||||
|
cpp_space_around_assignment_operator = insert
|
||||||
|
cpp_space_pointer_reference_alignment = left
|
||||||
|
cpp_space_around_ternary_operator = insert
|
||||||
|
cpp_wrap_preserve_blocks = one_liners
|
||||||
|
|||||||
@@ -5,246 +5,244 @@
|
|||||||
using std::wstring;
|
using std::wstring;
|
||||||
|
|
||||||
class ATL_NO_VTABLE CDownloadProgressCallback :
|
class ATL_NO_VTABLE CDownloadProgressCallback :
|
||||||
public CComObjectRoot,
|
public CComObjectRoot,
|
||||||
public IBindStatusCallback
|
public IBindStatusCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CDownloadProgressCallback()
|
CDownloadProgressCallback()
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
DECLARE_NOT_AGGREGATABLE(CDownloadProgressCallback)
|
DECLARE_NOT_AGGREGATABLE(CDownloadProgressCallback)
|
||||||
|
BEGIN_COM_MAP(CDownloadProgressCallback)
|
||||||
|
COM_INTERFACE_ENTRY(IBindStatusCallback)
|
||||||
|
END_COM_MAP()
|
||||||
|
DECLARE_PROTECT_FINAL_CONSTRUCT()
|
||||||
|
|
||||||
BEGIN_COM_MAP(CDownloadProgressCallback)
|
HRESULT FinalConstruct() { return S_OK; }
|
||||||
COM_INTERFACE_ENTRY(IBindStatusCallback)
|
|
||||||
END_COM_MAP()
|
|
||||||
|
|
||||||
DECLARE_PROTECT_FINAL_CONSTRUCT()
|
void FinalRelease()
|
||||||
|
{}
|
||||||
|
|
||||||
HRESULT FinalConstruct() { return S_OK; }
|
void SetProgressDialog(IProgressDialog* pd)
|
||||||
|
{
|
||||||
|
m_spProgressDialog = pd;
|
||||||
|
}
|
||||||
|
|
||||||
void FinalRelease()
|
STDMETHOD(OnProgress)(ULONG ulProgress, ULONG ulProgressMax, ULONG /*ulStatusCode*/, LPCWSTR /*szStatusText*/)
|
||||||
{
|
{
|
||||||
}
|
if (m_spProgressDialog != nullptr) {
|
||||||
|
if (m_spProgressDialog->HasUserCancelled()) {
|
||||||
|
return E_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
void SetProgressDialog(IProgressDialog* pd)
|
m_spProgressDialog->SetProgress(ulProgress, ulProgressMax);
|
||||||
{
|
}
|
||||||
m_spProgressDialog = pd;
|
|
||||||
}
|
|
||||||
|
|
||||||
STDMETHOD(OnProgress)(ULONG ulProgress, ULONG ulProgressMax, ULONG /*ulStatusCode*/, LPCWSTR /*szStatusText*/)
|
return S_OK;
|
||||||
{
|
}
|
||||||
if (m_spProgressDialog != nullptr) {
|
|
||||||
if (m_spProgressDialog->HasUserCancelled()) {
|
|
||||||
return E_ABORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_spProgressDialog->SetProgress(ulProgress, ulProgressMax);
|
STDMETHOD(OnStartBinding)(DWORD /*dwReserved*/, IBinding* pBinding) { return E_NOTIMPL; }
|
||||||
}
|
STDMETHOD(GetPriority)(LONG* pnPriority) { return E_NOTIMPL; }
|
||||||
|
STDMETHOD(OnLowResource)(DWORD /*reserved*/) { return E_NOTIMPL; }
|
||||||
return S_OK;
|
STDMETHOD(OnStopBinding)(HRESULT /*hresult*/, LPCWSTR /*szError*/) { return E_NOTIMPL; }
|
||||||
}
|
STDMETHOD(GetBindInfo)(DWORD* pgrfBINDF, BINDINFO* pbindInfo) { return E_NOTIMPL; }
|
||||||
|
STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC* /*pformatetc*/, STGMEDIUM* pstgmed) { return E_NOTIMPL; }
|
||||||
STDMETHOD(OnStartBinding)(DWORD /*dwReserved*/, IBinding *pBinding) { return E_NOTIMPL; }
|
STDMETHOD(OnObjectAvailable)(REFIID /*riid*/, IUnknown* /*punk*/) { return E_NOTIMPL; }
|
||||||
STDMETHOD(GetPriority)(LONG *pnPriority) { return E_NOTIMPL; }
|
|
||||||
STDMETHOD(OnLowResource)(DWORD /*reserved*/) { return E_NOTIMPL; }
|
|
||||||
STDMETHOD(OnStopBinding)(HRESULT /*hresult*/, LPCWSTR /*szError*/) { return E_NOTIMPL; }
|
|
||||||
STDMETHOD(GetBindInfo)(DWORD *pgrfBINDF, BINDINFO *pbindInfo) { return E_NOTIMPL; }
|
|
||||||
STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC * /*pformatetc*/, STGMEDIUM *pstgmed) { return E_NOTIMPL; }
|
|
||||||
STDMETHOD(OnObjectAvailable)(REFIID /*riid*/, IUnknown * /*punk*/) { return E_NOTIMPL; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CComPtr<IProgressDialog> m_spProgressDialog;
|
CComPtr<IProgressDialog> m_spProgressDialog;
|
||||||
};
|
};
|
||||||
|
|
||||||
HRESULT CFxHelper::InstallDotnet(const RUNTIMEINFO* runtime, bool isQuiet)
|
HRESULT CFxHelper::InstallDotnet(const RUNTIMEINFO* runtime, bool isQuiet)
|
||||||
{
|
{
|
||||||
auto runtimeName = wstring(runtime->friendlyName);
|
auto runtimeName = wstring(runtime->friendlyName);
|
||||||
auto runtimeUrl = wstring(runtime->installerUrl);
|
auto runtimeUrl = wstring(runtime->installerUrl);
|
||||||
|
|
||||||
if (!isQuiet) {
|
if (!isQuiet) {
|
||||||
CTaskDialog dlg;
|
CTaskDialog dlg;
|
||||||
TASKDIALOG_BUTTON buttons[] = {
|
TASKDIALOG_BUTTON buttons[] = {
|
||||||
{ 1, L"Install", },
|
{ 1, L"Install", },
|
||||||
{ 2, L"Cancel", },
|
{ 2, L"Cancel", },
|
||||||
};
|
};
|
||||||
|
|
||||||
wstring txtInstruction = L"Install " + runtimeName;
|
wstring txtInstruction = L"Install " + runtimeName;
|
||||||
wstring txtMain = L"This application requires " + runtimeName + L". Click the Install button to get started.";
|
wstring txtMain = L"This application requires " + runtimeName + L". Click the Install button to get started.";
|
||||||
wstring txtExpanded = L"Clicking install will download the latest version of this operating system component from Microsoft and install it on your PC. Setup can not continue until this is complete.";
|
wstring txtExpanded = L"Clicking install will download the latest version of this operating system component from Microsoft and install it on your PC. Setup can not continue until this is complete.";
|
||||||
|
|
||||||
dlg.SetButtons(buttons, 2);
|
dlg.SetButtons(buttons, 2);
|
||||||
dlg.SetMainInstructionText(txtInstruction.c_str());
|
dlg.SetMainInstructionText(txtInstruction.c_str());
|
||||||
dlg.SetContentText(txtMain.c_str());
|
dlg.SetContentText(txtMain.c_str());
|
||||||
dlg.SetMainIcon(TD_INFORMATION_ICON);
|
dlg.SetMainIcon(TD_INFORMATION_ICON);
|
||||||
|
|
||||||
dlg.SetExpandedInformationText(txtExpanded.c_str());
|
dlg.SetExpandedInformationText(txtExpanded.c_str());
|
||||||
|
|
||||||
int nButton;
|
int nButton;
|
||||||
if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton)) || nButton != 1) {
|
if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton)) || nButton != 1) {
|
||||||
return S_FALSE;
|
return S_FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT hr = E_FAIL;
|
HRESULT hr = E_FAIL;
|
||||||
WCHAR szFinalTempFileName[_MAX_PATH] = L"";
|
WCHAR szFinalTempFileName[_MAX_PATH] = L"";
|
||||||
CComPtr<IBindStatusCallback> bscb;
|
CComPtr<IBindStatusCallback> bscb;
|
||||||
CComPtr<IProgressDialog> pd;
|
CComPtr<IProgressDialog> pd;
|
||||||
SHELLEXECUTEINFO execInfo = { sizeof(execInfo), };
|
SHELLEXECUTEINFO execInfo = { sizeof(execInfo), };
|
||||||
|
|
||||||
WCHAR szTempPath[_MAX_PATH];
|
WCHAR szTempPath[_MAX_PATH];
|
||||||
DWORD dwTempPathResult = GetTempPath(_MAX_PATH, szTempPath);
|
DWORD dwTempPathResult = GetTempPath(_MAX_PATH, szTempPath);
|
||||||
|
|
||||||
if (dwTempPathResult == 0) {
|
if (dwTempPathResult == 0) {
|
||||||
hr = AtlHresultFromLastError();
|
hr = AtlHresultFromLastError();
|
||||||
goto out;
|
goto out;
|
||||||
} else if (dwTempPathResult > _MAX_PATH) {
|
}
|
||||||
hr = DISP_E_BUFFERTOOSMALL;
|
else if (dwTempPathResult > _MAX_PATH) {
|
||||||
goto out;
|
hr = DISP_E_BUFFERTOOSMALL;
|
||||||
}
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
WCHAR szTempFileName[_MAX_PATH];
|
WCHAR szTempFileName[_MAX_PATH];
|
||||||
if (!GetTempFileName(szTempPath, L"NDP", 0, szTempFileName)) {
|
if (!GetTempFileName(szTempPath, L"NDP", 0, szTempFileName)) {
|
||||||
hr = AtlHresultFromLastError();
|
hr = AtlHresultFromLastError();
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
szTempFileName[_countof(szTempFileName) - 1] = L'\0';
|
szTempFileName[_countof(szTempFileName) - 1] = L'\0';
|
||||||
if (wcscpy_s(szFinalTempFileName, _countof(szFinalTempFileName), szTempFileName) != 0) {
|
if (wcscpy_s(szFinalTempFileName, _countof(szFinalTempFileName), szTempFileName) != 0) {
|
||||||
hr = E_FAIL;
|
hr = E_FAIL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
WCHAR* pLastDot = wcsrchr(szFinalTempFileName, L'.');
|
WCHAR* pLastDot = wcsrchr(szFinalTempFileName, L'.');
|
||||||
if (pLastDot == nullptr) {
|
if (pLastDot == nullptr) {
|
||||||
if (wcscat_s(szFinalTempFileName, _countof(szFinalTempFileName), L".exe") != 0) {
|
if (wcscat_s(szFinalTempFileName, _countof(szFinalTempFileName), L".exe") != 0) {
|
||||||
hr = E_FAIL;
|
hr = E_FAIL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
if (wcscpy_s(pLastDot, _countof(szFinalTempFileName) - (pLastDot - szFinalTempFileName), L".exe") != 0) {
|
else {
|
||||||
hr = E_FAIL;
|
if (wcscpy_s(pLastDot, _countof(szFinalTempFileName) - (pLastDot - szFinalTempFileName), L".exe") != 0) {
|
||||||
goto out;
|
hr = E_FAIL;
|
||||||
}
|
goto out;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!MoveFile(szTempFileName, szFinalTempFileName)) {
|
if (!MoveFile(szTempFileName, szFinalTempFileName)) {
|
||||||
hr = AtlHresultFromLastError();
|
hr = AtlHresultFromLastError();
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isQuiet) {
|
if (!isQuiet) {
|
||||||
pd.CoCreateInstance(CLSID_ProgressDialog);
|
pd.CoCreateInstance(CLSID_ProgressDialog);
|
||||||
|
|
||||||
if (pd != nullptr) {
|
if (pd != nullptr) {
|
||||||
pd->SetTitle(L"Downloading");
|
pd->SetTitle(L"Downloading");
|
||||||
pd->SetLine(1, L"Downloading the .NET installer", FALSE, nullptr);
|
pd->SetLine(1, L"Downloading the .NET installer", FALSE, nullptr);
|
||||||
pd->StartProgressDialog(nullptr, nullptr, 0, nullptr);
|
pd->StartProgressDialog(nullptr, nullptr, 0, nullptr);
|
||||||
|
|
||||||
CComObject<CDownloadProgressCallback>* bscbObj = nullptr;
|
CComObject<CDownloadProgressCallback>* bscbObj = nullptr;
|
||||||
if (SUCCEEDED(CComObject<CDownloadProgressCallback>::CreateInstance(&bscbObj))) {
|
if (SUCCEEDED(CComObject<CDownloadProgressCallback>::CreateInstance(&bscbObj))) {
|
||||||
bscbObj->SetProgressDialog(pd);
|
bscbObj->SetProgressDialog(pd);
|
||||||
bscb = bscbObj;
|
bscb = bscbObj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = URLDownloadToFile(nullptr, runtimeUrl.c_str(), szFinalTempFileName, 0, bscb);
|
hr = URLDownloadToFile(nullptr, runtimeUrl.c_str(), szFinalTempFileName, 0, bscb);
|
||||||
if (pd != nullptr) {
|
if (pd != nullptr) {
|
||||||
pd->StopProgressDialog();
|
pd->StopProgressDialog();
|
||||||
}
|
}
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
execInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
execInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||||
execInfo.lpVerb = L"open";
|
execInfo.lpVerb = L"open";
|
||||||
execInfo.lpFile = szFinalTempFileName;
|
execInfo.lpFile = szFinalTempFileName;
|
||||||
|
|
||||||
if (isQuiet) {
|
if (isQuiet) {
|
||||||
execInfo.lpParameters = L"/q /norestart";
|
execInfo.lpParameters = L"/q /norestart";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
execInfo.lpParameters = L"/passive /norestart /showrmui";
|
execInfo.lpParameters = L"/passive /norestart /showrmui";
|
||||||
}
|
}
|
||||||
|
|
||||||
execInfo.nShow = SW_SHOW;
|
execInfo.nShow = SW_SHOW;
|
||||||
if (!ShellExecuteEx(&execInfo)) {
|
if (!ShellExecuteEx(&execInfo)) {
|
||||||
hr = AtlHresultFromLastError();
|
hr = AtlHresultFromLastError();
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
WaitForSingleObject(execInfo.hProcess, INFINITE);
|
WaitForSingleObject(execInfo.hProcess, INFINITE);
|
||||||
|
|
||||||
DWORD exitCode;
|
DWORD exitCode;
|
||||||
if (!GetExitCodeProcess(execInfo.hProcess, &exitCode)) {
|
if (!GetExitCodeProcess(execInfo.hProcess, &exitCode)) {
|
||||||
hr = AtlHresultFromLastError();
|
hr = AtlHresultFromLastError();
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exitCode == 1641 || exitCode == 3010) {
|
if (exitCode == 1641 || exitCode == 3010) {
|
||||||
// The framework installer wants a reboot before we can continue
|
// The framework installer wants a reboot before we can continue
|
||||||
// See https://msdn.microsoft.com/en-us/library/ee942965%28v=vs.110%29.aspx
|
// See https://msdn.microsoft.com/en-us/library/ee942965%28v=vs.110%29.aspx
|
||||||
// hr = HandleRebootRequirement(isQuiet);
|
// hr = HandleRebootRequirement(isQuiet);
|
||||||
// Exit as a failure, so that setup doesn't carry on now
|
// Exit as a failure, so that setup doesn't carry on now
|
||||||
hr = ERROR_SUCCESS_REBOOT_REQUIRED;
|
hr = ERROR_SUCCESS_REBOOT_REQUIRED;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
hr = exitCode != 0 ? E_FAIL : S_OK;
|
hr = exitCode != 0 ? E_FAIL : S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (execInfo.hProcess != NULL && execInfo.hProcess != INVALID_HANDLE_VALUE) {
|
if (execInfo.hProcess != NULL && execInfo.hProcess != INVALID_HANDLE_VALUE) {
|
||||||
CloseHandle(execInfo.hProcess);
|
CloseHandle(execInfo.hProcess);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*szFinalTempFileName != L'\0') {
|
if (*szFinalTempFileName != L'\0') {
|
||||||
DeleteFile(szFinalTempFileName);
|
DeleteFile(szFinalTempFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deal with the aftermath of the framework installer telling us that we need to reboot
|
// Deal with the aftermath of the framework installer telling us that we need to reboot
|
||||||
HRESULT CFxHelper::HandleRebootRequirement(bool isQuiet)
|
HRESULT CFxHelper::HandleRebootRequirement(bool isQuiet)
|
||||||
{
|
{
|
||||||
if (isQuiet) {
|
if (isQuiet) {
|
||||||
// Don't silently reboot - just error-out
|
// Don't silently reboot - just error-out
|
||||||
fprintf_s(stderr, "A reboot is required following .NET installation - reboot then run installer again.\n");
|
fprintf_s(stderr, "A reboot is required following .NET installation - reboot then run installer again.\n");
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
CTaskDialog dlg;
|
CTaskDialog dlg;
|
||||||
TASKDIALOG_BUTTON buttons[] = {
|
TASKDIALOG_BUTTON buttons[] = {
|
||||||
{ 1, L"Restart Now", },
|
{ 1, L"Restart Now", },
|
||||||
{ 2, L"Cancel", },
|
{ 2, L"Cancel", },
|
||||||
};
|
};
|
||||||
|
|
||||||
dlg.SetButtons(buttons, 2);
|
dlg.SetButtons(buttons, 2);
|
||||||
dlg.SetMainInstructionText(L"Restart System");
|
dlg.SetMainInstructionText(L"Restart System");
|
||||||
dlg.SetContentText(L"To finish installing the .NET Framework, the system now needs to restart. The installation will finish after you restart and log-in again.");
|
dlg.SetContentText(L"To finish installing the .NET Framework, the system now needs to restart. The installation will finish after you restart and log-in again.");
|
||||||
dlg.SetMainIcon(TD_INFORMATION_ICON);
|
dlg.SetMainIcon(TD_INFORMATION_ICON);
|
||||||
|
|
||||||
dlg.SetExpandedInformationText(L"If you click 'Cancel', you'll need to re-run this setup program yourself, after restarting your system.");
|
dlg.SetExpandedInformationText(L"If you click 'Cancel', you'll need to re-run this setup program yourself, after restarting your system.");
|
||||||
|
|
||||||
int nButton;
|
int nButton;
|
||||||
if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton)) || nButton != 1) {
|
if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton)) || nButton != 1) {
|
||||||
return S_FALSE;
|
return S_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to set up a runonce entry to restart this installer once the reboot has happened
|
// We need to set up a runonce entry to restart this installer once the reboot has happened
|
||||||
if (!WriteRunOnceEntry()) {
|
if (!WriteRunOnceEntry()) {
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// And now, reboot
|
// And now, reboot
|
||||||
if (!RebootSystem()) {
|
if (!RebootSystem()) {
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// About to reboot, but just in case...
|
// About to reboot, but just in case...
|
||||||
return S_FALSE;
|
return S_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -253,44 +251,44 @@ HRESULT CFxHelper::HandleRebootRequirement(bool isQuiet)
|
|||||||
//
|
//
|
||||||
bool CFxHelper::WriteRunOnceEntry()
|
bool CFxHelper::WriteRunOnceEntry()
|
||||||
{
|
{
|
||||||
ATL::CRegKey key;
|
ATL::CRegKey key;
|
||||||
|
|
||||||
if (key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", KEY_WRITE) != ERROR_SUCCESS) {
|
if (key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", KEY_WRITE) != ERROR_SUCCESS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TCHAR exePath[MAX_PATH];
|
TCHAR exePath[MAX_PATH];
|
||||||
GetModuleFileName(NULL, exePath, MAX_PATH);
|
GetModuleFileName(NULL, exePath, MAX_PATH);
|
||||||
|
|
||||||
if (key.SetStringValue(L"SquirrelInstall", exePath) != ERROR_SUCCESS) {
|
if (key.SetStringValue(L"SquirrelInstall", exePath) != ERROR_SUCCESS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CFxHelper::RebootSystem()
|
bool CFxHelper::RebootSystem()
|
||||||
{
|
{
|
||||||
// First we need to enable the SE_SHUTDOWN_NAME privilege
|
// First we need to enable the SE_SHUTDOWN_NAME privilege
|
||||||
LUID luid;
|
LUID luid;
|
||||||
if (!LookupPrivilegeValue(L"", SE_SHUTDOWN_NAME, &luid)) {
|
if (!LookupPrivilegeValue(L"", SE_SHUTDOWN_NAME, &luid)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
HANDLE hToken = NULL;
|
HANDLE hToken = NULL;
|
||||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TOKEN_PRIVILEGES tp;
|
TOKEN_PRIVILEGES tp;
|
||||||
tp.PrivilegeCount = 1;
|
tp.PrivilegeCount = 1;
|
||||||
tp.Privileges[0].Luid = luid;
|
tp.Privileges[0].Luid = luid;
|
||||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||||
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, 0)) {
|
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, 0)) {
|
||||||
CloseHandle(hToken);
|
CloseHandle(hToken);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we have that privilege, we can ask Windows to restart
|
// Now we have that privilege, we can ask Windows to restart
|
||||||
return ExitWindowsEx(EWX_REBOOT, 0) != 0;
|
return ExitWindowsEx(EWX_REBOOT, 0) != 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
|
|
||||||
class CFxHelper
|
class CFxHelper
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static HRESULT InstallDotnet(const RUNTIMEINFO* runtime, bool isQuiet);
|
static HRESULT InstallDotnet(const RUNTIMEINFO* runtime, bool isQuiet);
|
||||||
static HRESULT HandleRebootRequirement(bool isQuiet);
|
static HRESULT HandleRebootRequirement(bool isQuiet);
|
||||||
private:
|
private:
|
||||||
static bool WriteRunOnceEntry();
|
static bool WriteRunOnceEntry();
|
||||||
static bool RebootSystem();
|
static bool RebootSystem();
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -12,245 +12,226 @@ void ASSERT(void* obj) {}
|
|||||||
// https://stackoverflow.com/a/66238748/184746
|
// https://stackoverflow.com/a/66238748/184746
|
||||||
IStream* LoadImageFromResource(const wchar_t* resid, const wchar_t* restype)
|
IStream* LoadImageFromResource(const wchar_t* resid, const wchar_t* restype)
|
||||||
{
|
{
|
||||||
IStream* pStream = nullptr;
|
IStream* pStream = nullptr;
|
||||||
HGLOBAL hGlobal = nullptr;
|
HGLOBAL hGlobal = nullptr;
|
||||||
|
|
||||||
HINSTANCE hInst = GetModuleHandle(NULL);
|
HINSTANCE hInst = GetModuleHandle(NULL);
|
||||||
HRSRC hrsrc = FindResourceW(hInst, resid, restype); // get the handle to the resource
|
HRSRC hrsrc = FindResourceW(hInst, resid, restype); // get the handle to the resource
|
||||||
if (hrsrc)
|
if (hrsrc) {
|
||||||
{
|
DWORD dwResourceSize = SizeofResource(hInst, hrsrc);
|
||||||
DWORD dwResourceSize = SizeofResource(hInst, hrsrc);
|
if (dwResourceSize > 0) {
|
||||||
if (dwResourceSize > 0)
|
HGLOBAL hGlobalResource = LoadResource(hInst, hrsrc); // load it
|
||||||
{
|
if (hGlobalResource) {
|
||||||
HGLOBAL hGlobalResource = LoadResource(hInst, hrsrc); // load it
|
void* imagebytes = LockResource(hGlobalResource); // get a pointer to the file bytes
|
||||||
if (hGlobalResource)
|
|
||||||
{
|
|
||||||
void* imagebytes = LockResource(hGlobalResource); // get a pointer to the file bytes
|
|
||||||
|
|
||||||
// copy image bytes into a real hglobal memory handle
|
// copy image bytes into a real hglobal memory handle
|
||||||
hGlobal = ::GlobalAlloc(GHND, dwResourceSize);
|
hGlobal = ::GlobalAlloc(GHND, dwResourceSize);
|
||||||
if (hGlobal)
|
if (hGlobal) {
|
||||||
{
|
void* pBuffer = ::GlobalLock(hGlobal);
|
||||||
void* pBuffer = ::GlobalLock(hGlobal);
|
if (pBuffer) {
|
||||||
if (pBuffer)
|
memcpy(pBuffer, imagebytes, dwResourceSize);
|
||||||
{
|
HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
|
||||||
memcpy(pBuffer, imagebytes, dwResourceSize);
|
if (SUCCEEDED(hr)) {
|
||||||
HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
|
// pStream now owns the global handle and will invoke GlobalFree on release
|
||||||
if (SUCCEEDED(hr))
|
hGlobal = nullptr;
|
||||||
{
|
}
|
||||||
// pStream now owns the global handle and will invoke GlobalFree on release
|
}
|
||||||
hGlobal = nullptr;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hGlobal)
|
if (hGlobal) {
|
||||||
{
|
GlobalFree(hGlobal);
|
||||||
GlobalFree(hGlobal);
|
hGlobal = nullptr;
|
||||||
hGlobal = nullptr;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return pStream;
|
return pStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageEx::ImageEx(const wchar_t* resid, const wchar_t* restype)
|
ImageEx::ImageEx(const wchar_t* resid, const wchar_t* restype)
|
||||||
{
|
{
|
||||||
Initialize();
|
Initialize();
|
||||||
|
|
||||||
auto stream = LoadImageFromResource(resid, restype);
|
auto stream = LoadImageFromResource(resid, restype);
|
||||||
if (stream)
|
if (stream) {
|
||||||
{
|
nativeImage = NULL;
|
||||||
nativeImage = NULL;
|
lastResult = DllExports::GdipLoadImageFromStreamICM(stream, &nativeImage);
|
||||||
lastResult = DllExports::GdipLoadImageFromStreamICM(stream, &nativeImage);
|
m_bIsInitialized = true;
|
||||||
m_bIsInitialized = true;
|
TestForAnimatedGIF();
|
||||||
TestForAnimatedGIF();
|
stream->Release();
|
||||||
stream->Release();
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageEx::~ImageEx()
|
ImageEx::~ImageEx()
|
||||||
{
|
{
|
||||||
Destroy();
|
Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageEx::InitAnimation(HWND hWnd, CPoint pt)
|
bool ImageEx::InitAnimation(HWND hWnd, CPoint pt)
|
||||||
{
|
{
|
||||||
|
m_hWnd = hWnd;
|
||||||
|
m_pt = pt;
|
||||||
|
|
||||||
m_hWnd = hWnd;
|
if (!m_bIsInitialized) {
|
||||||
m_pt = pt;
|
TRACE(_T("GIF not initialized\n"));
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
if (!m_bIsInitialized)
|
if (IsAnimatedGIF()) {
|
||||||
{
|
if (m_hThread == NULL) {
|
||||||
TRACE(_T("GIF not initialized\n"));
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (IsAnimatedGIF())
|
unsigned int nTID = 0;
|
||||||
{
|
|
||||||
if (m_hThread == NULL)
|
|
||||||
{
|
|
||||||
|
|
||||||
unsigned int nTID = 0;
|
m_hThread = (HANDLE)_beginthreadex(NULL, 0, _ThreadAnimationProc, this, CREATE_SUSPENDED, &nTID);
|
||||||
|
|
||||||
m_hThread = (HANDLE)_beginthreadex(NULL, 0, _ThreadAnimationProc, this, CREATE_SUSPENDED, &nTID);
|
if (!m_hThread) {
|
||||||
|
TRACE(_T("Couldn't start a GIF animation thread\n"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ResumeThread(m_hThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_hThread)
|
return false;
|
||||||
{
|
|
||||||
TRACE(_T("Couldn't start a GIF animation thread\n"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ResumeThread(m_hThread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CSize ImageEx::GetSize()
|
CSize ImageEx::GetSize()
|
||||||
{
|
{
|
||||||
return CSize(GetWidth(), GetHeight());
|
return CSize(GetWidth(), GetHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageEx::TestForAnimatedGIF()
|
bool ImageEx::TestForAnimatedGIF()
|
||||||
{
|
{
|
||||||
UINT count = 0;
|
UINT count = 0;
|
||||||
count = GetFrameDimensionsCount();
|
count = GetFrameDimensionsCount();
|
||||||
GUID* pDimensionIDs = new GUID[count];
|
GUID* pDimensionIDs = new GUID[count];
|
||||||
|
|
||||||
// Get the list of frame dimensions from the Image object.
|
// Get the list of frame dimensions from the Image object.
|
||||||
GetFrameDimensionsList(pDimensionIDs, count);
|
GetFrameDimensionsList(pDimensionIDs, count);
|
||||||
|
|
||||||
// Get the number of frames in the first dimension.
|
// Get the number of frames in the first dimension.
|
||||||
m_nFrameCount = GetFrameCount(&pDimensionIDs[0]);
|
m_nFrameCount = GetFrameCount(&pDimensionIDs[0]);
|
||||||
|
|
||||||
// Assume that the image has a property item of type PropertyItemEquipMake.
|
// Assume that the image has a property item of type PropertyItemEquipMake.
|
||||||
// Get the size of that property item.
|
// Get the size of that property item.
|
||||||
int nSize = GetPropertyItemSize(PropertyTagFrameDelay);
|
int nSize = GetPropertyItemSize(PropertyTagFrameDelay);
|
||||||
|
|
||||||
// Allocate a buffer to receive the property item.
|
// Allocate a buffer to receive the property item.
|
||||||
m_pPropertyItem = (PropertyItem*)malloc(nSize);
|
m_pPropertyItem = (PropertyItem*)malloc(nSize);
|
||||||
|
|
||||||
GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem);
|
GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem);
|
||||||
|
|
||||||
delete pDimensionIDs;
|
delete pDimensionIDs;
|
||||||
|
|
||||||
return m_nFrameCount > 1;
|
return m_nFrameCount > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageEx::Initialize()
|
void ImageEx::Initialize()
|
||||||
{
|
{
|
||||||
m_nFramePosition = 0;
|
m_nFramePosition = 0;
|
||||||
m_nFrameCount = 0;
|
m_nFrameCount = 0;
|
||||||
lastResult = InvalidParameter;
|
lastResult = InvalidParameter;
|
||||||
m_hThread = NULL;
|
m_hThread = NULL;
|
||||||
m_bIsInitialized = false;
|
m_bIsInitialized = false;
|
||||||
m_pPropertyItem = NULL;
|
m_pPropertyItem = NULL;
|
||||||
|
|
||||||
#ifdef INDIGO_CTRL_PROJECT
|
#ifdef INDIGO_CTRL_PROJECT
|
||||||
m_hInst = _Module.GetResourceInstance();
|
m_hInst = _Module.GetResourceInstance();
|
||||||
#else
|
#else
|
||||||
m_hInst = GetModuleHandle(NULL);// AfxGetResourceHandle();
|
m_hInst = GetModuleHandle(NULL);// AfxGetResourceHandle();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_bPause = false;
|
m_bPause = false;
|
||||||
m_hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
m_hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
m_hPause = CreateEvent(NULL, TRUE, TRUE, NULL);
|
m_hPause = CreateEvent(NULL, TRUE, TRUE, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
UINT WINAPI ImageEx::_ThreadAnimationProc(LPVOID pParam)
|
UINT WINAPI ImageEx::_ThreadAnimationProc(LPVOID pParam)
|
||||||
{
|
{
|
||||||
ASSERT(pParam);
|
ASSERT(pParam);
|
||||||
ImageEx* pImage = reinterpret_cast<ImageEx*> (pParam);
|
ImageEx* pImage = reinterpret_cast<ImageEx*> (pParam);
|
||||||
pImage->ThreadAnimation();
|
pImage->ThreadAnimation();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageEx::ThreadAnimation()
|
void ImageEx::ThreadAnimation()
|
||||||
{
|
{
|
||||||
m_nFramePosition = 0;
|
m_nFramePosition = 0;
|
||||||
|
|
||||||
bool bExit = false;
|
bool bExit = false;
|
||||||
while (bExit == false)
|
while (bExit == false) {
|
||||||
{
|
bExit = DrawFrameGIF();
|
||||||
bExit = DrawFrameGIF();
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageEx::DrawFrameGIF()
|
bool ImageEx::DrawFrameGIF()
|
||||||
{
|
{
|
||||||
::WaitForSingleObject(m_hPause, INFINITE);
|
::WaitForSingleObject(m_hPause, INFINITE);
|
||||||
|
|
||||||
GUID pageGuid = FrameDimensionTime;
|
GUID pageGuid = FrameDimensionTime;
|
||||||
|
|
||||||
long hmWidth = GetWidth();
|
long hmWidth = GetWidth();
|
||||||
long hmHeight = GetHeight();
|
long hmHeight = GetHeight();
|
||||||
|
|
||||||
HDC hDC = GetDC(m_hWnd);
|
HDC hDC = GetDC(m_hWnd);
|
||||||
if (hDC)
|
if (hDC) {
|
||||||
{
|
Graphics graphics(hDC);
|
||||||
Graphics graphics(hDC);
|
graphics.DrawImage(this, m_pt.x, m_pt.y, hmWidth, hmHeight);
|
||||||
graphics.DrawImage(this, m_pt.x, m_pt.y, hmWidth, hmHeight);
|
ReleaseDC(m_hWnd, hDC);
|
||||||
ReleaseDC(m_hWnd, hDC);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
SelectActiveFrame(&pageGuid, m_nFramePosition++);
|
SelectActiveFrame(&pageGuid, m_nFramePosition++);
|
||||||
|
|
||||||
if (m_nFramePosition == m_nFrameCount)
|
if (m_nFramePosition == m_nFrameCount)
|
||||||
m_nFramePosition = 0;
|
m_nFramePosition = 0;
|
||||||
|
|
||||||
long lPause = ((long*)m_pPropertyItem->value)[m_nFramePosition] * 10;
|
long lPause = ((long*)m_pPropertyItem->value)[m_nFramePosition] * 10;
|
||||||
DWORD dwErr = WaitForSingleObject(m_hExitEvent, lPause);
|
DWORD dwErr = WaitForSingleObject(m_hExitEvent, lPause);
|
||||||
return dwErr == WAIT_OBJECT_0;
|
return dwErr == WAIT_OBJECT_0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageEx::SetPause(bool bPause)
|
void ImageEx::SetPause(bool bPause)
|
||||||
{
|
{
|
||||||
if (!IsAnimatedGIF())
|
if (!IsAnimatedGIF())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (bPause && !m_bPause)
|
if (bPause && !m_bPause) {
|
||||||
{
|
::ResetEvent(m_hPause);
|
||||||
::ResetEvent(m_hPause);
|
}
|
||||||
}
|
else {
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
if (m_bPause && !bPause)
|
if (m_bPause && !bPause) {
|
||||||
{
|
::SetEvent(m_hPause);
|
||||||
::SetEvent(m_hPause);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
m_bPause = bPause;
|
m_bPause = bPause;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ImageEx::Destroy()
|
void ImageEx::Destroy()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (m_hThread)
|
if (m_hThread) {
|
||||||
{
|
// If pause un pause
|
||||||
// If pause un pause
|
SetPause(false);
|
||||||
SetPause(false);
|
|
||||||
|
|
||||||
SetEvent(m_hExitEvent);
|
SetEvent(m_hExitEvent);
|
||||||
WaitForSingleObject(m_hThread, INFINITE);
|
WaitForSingleObject(m_hThread, INFINITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseHandle(m_hThread);
|
CloseHandle(m_hThread);
|
||||||
CloseHandle(m_hExitEvent);
|
CloseHandle(m_hExitEvent);
|
||||||
CloseHandle(m_hPause);
|
CloseHandle(m_hPause);
|
||||||
|
|
||||||
free(m_pPropertyItem);
|
free(m_pPropertyItem);
|
||||||
|
|
||||||
m_pPropertyItem = NULL;
|
m_pPropertyItem = NULL;
|
||||||
m_hThread = NULL;
|
m_hThread = NULL;
|
||||||
m_hExitEvent = NULL;
|
m_hExitEvent = NULL;
|
||||||
m_hPause = NULL;
|
m_hPause = NULL;
|
||||||
}
|
}
|
||||||
@@ -17,36 +17,36 @@ class ImageEx : public Image
|
|||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ImageEx(const wchar_t* resid, const wchar_t* restype);
|
ImageEx(const wchar_t* resid, const wchar_t* restype);
|
||||||
~ImageEx();
|
~ImageEx();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CSize GetSize();
|
CSize GetSize();
|
||||||
bool IsAnimatedGIF() { return m_nFrameCount > 1; }
|
bool IsAnimatedGIF() { return m_nFrameCount > 1; }
|
||||||
void SetPause(bool bPause);
|
void SetPause(bool bPause);
|
||||||
bool IsPaused() { return m_bPause; }
|
bool IsPaused() { return m_bPause; }
|
||||||
bool InitAnimation(HWND hWnd, CPoint pt);
|
bool InitAnimation(HWND hWnd, CPoint pt);
|
||||||
void Destroy();
|
void Destroy();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool TestForAnimatedGIF();
|
bool TestForAnimatedGIF();
|
||||||
void Initialize();
|
void Initialize();
|
||||||
bool DrawFrameGIF();
|
bool DrawFrameGIF();
|
||||||
|
|
||||||
void ThreadAnimation();
|
void ThreadAnimation();
|
||||||
static UINT WINAPI _ThreadAnimationProc(LPVOID pParam);
|
static UINT WINAPI _ThreadAnimationProc(LPVOID pParam);
|
||||||
|
|
||||||
HANDLE m_hThread;
|
HANDLE m_hThread;
|
||||||
HANDLE m_hPause;
|
HANDLE m_hPause;
|
||||||
HANDLE m_hExitEvent;
|
HANDLE m_hExitEvent;
|
||||||
HINSTANCE m_hInst;
|
HINSTANCE m_hInst;
|
||||||
HWND m_hWnd;
|
HWND m_hWnd;
|
||||||
UINT m_nFrameCount;
|
UINT m_nFrameCount;
|
||||||
UINT m_nFramePosition;
|
UINT m_nFramePosition;
|
||||||
bool m_bIsInitialized;
|
bool m_bIsInitialized;
|
||||||
bool m_bPause;
|
bool m_bPause;
|
||||||
PropertyItem* m_pPropertyItem;
|
PropertyItem* m_pPropertyItem;
|
||||||
CPoint m_pt;
|
CPoint m_pt;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,64 +4,65 @@
|
|||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include <sddl.h>
|
#include <sddl.h>
|
||||||
|
|
||||||
bool directoryExists(wchar_t* path) {
|
bool directoryExists(wchar_t* path)
|
||||||
DWORD dwResult = GetFileAttributes(path);
|
{
|
||||||
|
DWORD dwResult = GetFileAttributes(path);
|
||||||
|
|
||||||
if (dwResult != INVALID_FILE_ATTRIBUTES) {
|
if (dwResult != INVALID_FILE_ATTRIBUTES) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: The directory could exist but we can't access it, let's check
|
// NB: The directory could exist but we can't access it, let's check
|
||||||
DWORD dwLastError = GetLastError();
|
DWORD dwLastError = GetLastError();
|
||||||
if (dwLastError == ERROR_FILE_NOT_FOUND) return false;
|
if (dwLastError == ERROR_FILE_NOT_FOUND) return false;
|
||||||
if (dwLastError == ERROR_PATH_NOT_FOUND) return false;
|
if (dwLastError == ERROR_PATH_NOT_FOUND) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MachineInstaller::ShouldSilentInstall()
|
bool MachineInstaller::ShouldSilentInstall()
|
||||||
{
|
{
|
||||||
// Figure out the package name from our own EXE name
|
// Figure out the package name from our own EXE name
|
||||||
// The name consist of [$pkgName]DeploymentTool.exe
|
// The name consist of [$pkgName]DeploymentTool.exe
|
||||||
wchar_t ourFile[MAX_PATH];
|
wchar_t ourFile[MAX_PATH];
|
||||||
HMODULE hMod = GetModuleHandle(NULL);
|
HMODULE hMod = GetModuleHandle(NULL);
|
||||||
GetModuleFileName(hMod, ourFile, _countof(ourFile));
|
GetModuleFileName(hMod, ourFile, _countof(ourFile));
|
||||||
|
|
||||||
CString fullPath = CString(ourFile);
|
CString fullPath = CString(ourFile);
|
||||||
CString pkgName = CString(ourFile + fullPath.ReverseFind(L'\\'));
|
CString pkgName = CString(ourFile + fullPath.ReverseFind(L'\\'));
|
||||||
pkgName.Replace(L"DeploymentTool.exe", L"");
|
pkgName.Replace(L"DeploymentTool.exe", L"");
|
||||||
|
|
||||||
wchar_t installFolder[MAX_PATH];
|
|
||||||
|
|
||||||
// NB: Users often get into the sitch where they install the MSI, then try to
|
wchar_t installFolder[MAX_PATH];
|
||||||
// install the standalone package on top of that. In previous versions we tried
|
|
||||||
// to detect if the app was properly installed, but now we're taking the much
|
|
||||||
// more conservative approach, that if the package dir exists in any way, we're
|
|
||||||
// bailing out
|
|
||||||
|
|
||||||
// C:\Users\Username\AppData\Local\$pkgName
|
// NB: Users often get into the sitch where they install the MSI, then try to
|
||||||
SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, installFolder);
|
// install the standalone package on top of that. In previous versions we tried
|
||||||
wcscat(installFolder, L"\\");
|
// to detect if the app was properly installed, but now we're taking the much
|
||||||
wcscat(installFolder, pkgName);
|
// more conservative approach, that if the package dir exists in any way, we're
|
||||||
|
// bailing out
|
||||||
|
|
||||||
if (directoryExists(installFolder)) {
|
// C:\Users\Username\AppData\Local\$pkgName
|
||||||
return false;
|
SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, installFolder);
|
||||||
}
|
wcscat(installFolder, L"\\");
|
||||||
|
wcscat(installFolder, pkgName);
|
||||||
|
|
||||||
// C:\ProgramData\$pkgName\$username
|
if (directoryExists(installFolder)) {
|
||||||
wchar_t username[512];
|
return false;
|
||||||
DWORD unamesize = _countof(username);
|
}
|
||||||
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, installFolder);
|
|
||||||
GetUserName(username, &unamesize);
|
|
||||||
wcscat(installFolder, L"\\");
|
|
||||||
wcscat(installFolder, pkgName);
|
|
||||||
wcscat(installFolder, L"\\");
|
|
||||||
wcscat(installFolder, username);
|
|
||||||
|
|
||||||
if (directoryExists(installFolder)) {
|
// C:\ProgramData\$pkgName\$username
|
||||||
return false;
|
wchar_t username[512];
|
||||||
}
|
DWORD unamesize = _countof(username);
|
||||||
|
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, installFolder);
|
||||||
|
GetUserName(username, &unamesize);
|
||||||
|
wcscat(installFolder, L"\\");
|
||||||
|
wcscat(installFolder, pkgName);
|
||||||
|
wcscat(installFolder, L"\\");
|
||||||
|
wcscat(installFolder, username);
|
||||||
|
|
||||||
// None of these exist, we should install
|
if (directoryExists(installFolder)) {
|
||||||
return true;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// None of these exist, we should install
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
class MachineInstaller
|
class MachineInstaller
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static bool ShouldSilentInstall();
|
static bool ShouldSilentInstall();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,104 +9,104 @@ using std::vector;
|
|||||||
RUNTIMEINFO supported_runtimes[] =
|
RUNTIMEINFO supported_runtimes[] =
|
||||||
{
|
{
|
||||||
|
|
||||||
// net45 through net46 is supported on Vista SP2 and newer
|
// net45 through net46 is supported on Vista SP2 and newer
|
||||||
|
|
||||||
{
|
{
|
||||||
_WIN32_WINNT_VISTA, 2,
|
_WIN32_WINNT_VISTA, 2,
|
||||||
L"net45", L".NET Framework 4.5",
|
L"net45", L".NET Framework 4.5",
|
||||||
L"http://go.microsoft.com/fwlink/?LinkId=397707",
|
L"http://go.microsoft.com/fwlink/?LinkId=397707",
|
||||||
378389
|
378389
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
_WIN32_WINNT_VISTA, 2,
|
_WIN32_WINNT_VISTA, 2,
|
||||||
L"net451", L".NET Framework 4.5.1",
|
L"net451", L".NET Framework 4.5.1",
|
||||||
L"http://go.microsoft.com/fwlink/?LinkId=397707",
|
L"http://go.microsoft.com/fwlink/?LinkId=397707",
|
||||||
378675
|
378675
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
_WIN32_WINNT_VISTA, 2,
|
_WIN32_WINNT_VISTA, 2,
|
||||||
L"net452", L".NET Framework 4.5.2",
|
L"net452", L".NET Framework 4.5.2",
|
||||||
L"http://go.microsoft.com/fwlink/?LinkId=397707",
|
L"http://go.microsoft.com/fwlink/?LinkId=397707",
|
||||||
379893
|
379893
|
||||||
},
|
},
|
||||||
|
|
||||||
// net461 through net48 supports Windows 7 and newer
|
// net461 through net48 supports Windows 7 and newer
|
||||||
|
|
||||||
{
|
{
|
||||||
_WIN32_WINNT_WIN7, 0,
|
_WIN32_WINNT_WIN7, 0,
|
||||||
L"net46", L".NET Framework 4.6",
|
L"net46", L".NET Framework 4.6",
|
||||||
L"http://go.microsoft.com/fwlink/?LinkId=780596",
|
L"http://go.microsoft.com/fwlink/?LinkId=780596",
|
||||||
393295
|
393295
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
_WIN32_WINNT_WIN7, 0,
|
_WIN32_WINNT_WIN7, 0,
|
||||||
L"net461", L".NET Framework 4.6.1",
|
L"net461", L".NET Framework 4.6.1",
|
||||||
L"http://go.microsoft.com/fwlink/?LinkId=780596",
|
L"http://go.microsoft.com/fwlink/?LinkId=780596",
|
||||||
394254
|
394254
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
_WIN32_WINNT_WIN7, 0,
|
_WIN32_WINNT_WIN7, 0,
|
||||||
L"net462", L".NET Framework 4.6.2",
|
L"net462", L".NET Framework 4.6.2",
|
||||||
L"http://go.microsoft.com/fwlink/?LinkId=780596",
|
L"http://go.microsoft.com/fwlink/?LinkId=780596",
|
||||||
394802
|
394802
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
_WIN32_WINNT_WIN7, 0,
|
_WIN32_WINNT_WIN7, 0,
|
||||||
L"net47", L".NET Framework 4.7",
|
L"net47", L".NET Framework 4.7",
|
||||||
L"http://go.microsoft.com/fwlink/?LinkId=863262",
|
L"http://go.microsoft.com/fwlink/?LinkId=863262",
|
||||||
460798
|
460798
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
_WIN32_WINNT_WIN7, 0,
|
_WIN32_WINNT_WIN7, 0,
|
||||||
L"net471", L".NET Framework 4.7.1",
|
L"net471", L".NET Framework 4.7.1",
|
||||||
L"http://go.microsoft.com/fwlink/?LinkId=863262",
|
L"http://go.microsoft.com/fwlink/?LinkId=863262",
|
||||||
461308
|
461308
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
_WIN32_WINNT_WIN7, 0,
|
_WIN32_WINNT_WIN7, 0,
|
||||||
L"net472", L".NET Framework 4.7.2",
|
L"net472", L".NET Framework 4.7.2",
|
||||||
L"http://go.microsoft.com/fwlink/?LinkId=863262",
|
L"http://go.microsoft.com/fwlink/?LinkId=863262",
|
||||||
461808
|
461808
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
_WIN32_WINNT_WIN7, 0,
|
_WIN32_WINNT_WIN7, 0,
|
||||||
L"net48", L".NET Framework 4.8",
|
L"net48", L".NET Framework 4.8",
|
||||||
L"https://go.microsoft.com/fwlink/?LinkId=2085155",
|
L"https://go.microsoft.com/fwlink/?LinkId=2085155",
|
||||||
528040
|
528040
|
||||||
},
|
},
|
||||||
|
|
||||||
// dotnet core is supported on Windows 7 SP1 and newer.
|
// dotnet core is supported on Windows 7 SP1 and newer.
|
||||||
// update this list periodically from https://dotnet.microsoft.com/download/dotnet
|
// update this list periodically from https://dotnet.microsoft.com/download/dotnet
|
||||||
// we could add support for 2.0/2.1/2.2 but since those runtimes didn't ship with desktop support it is probably not needed.
|
// we could add support for 2.0/2.1/2.2 but since those runtimes didn't ship with desktop support it is probably not needed.
|
||||||
|
|
||||||
{
|
{
|
||||||
_WIN32_WINNT_WIN7, 1,
|
_WIN32_WINNT_WIN7, 1,
|
||||||
L"netcoreapp3", L".NET Core 3.0.3",
|
L"netcoreapp3", L".NET Core 3.0.3",
|
||||||
L"https://download.visualstudio.microsoft.com/download/pr/c525a2bb-6e98-4e6e-849e-45241d0db71c/d21612f02b9cae52fa50eb54de905986/windowsdesktop-runtime-3.0.3-win-x64.exe",
|
L"https://download.visualstudio.microsoft.com/download/pr/c525a2bb-6e98-4e6e-849e-45241d0db71c/d21612f02b9cae52fa50eb54de905986/windowsdesktop-runtime-3.0.3-win-x64.exe",
|
||||||
0, L"WindowsDesktop.App 3.0"
|
0, L"WindowsDesktop.App 3.0"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
_WIN32_WINNT_WIN7, 1,
|
_WIN32_WINNT_WIN7, 1,
|
||||||
L"netcoreapp31", L".NET Core 3.1.18",
|
L"netcoreapp31", L".NET Core 3.1.18",
|
||||||
L"https://download.visualstudio.microsoft.com/download/pr/aa240732-82d7-4bd1-9701-e4014d7dc735/41a460f5e9c343f7b9207f64ab0e4c90/windowsdesktop-runtime-3.1.18-win-x64.exe",
|
L"https://download.visualstudio.microsoft.com/download/pr/aa240732-82d7-4bd1-9701-e4014d7dc735/41a460f5e9c343f7b9207f64ab0e4c90/windowsdesktop-runtime-3.1.18-win-x64.exe",
|
||||||
0, L"WindowsDesktop.App 3.1"
|
0, L"WindowsDesktop.App 3.1"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
_WIN32_WINNT_WIN7, 1,
|
_WIN32_WINNT_WIN7, 1,
|
||||||
L"net5", L".NET 5.0.9",
|
L"net5", L".NET 5.0.9",
|
||||||
L"https://download.visualstudio.microsoft.com/download/pr/8bc41df1-cbb4-4da6-944f-6652378e9196/1014aacedc80bbcc030dabb168d2532f/windowsdesktop-runtime-5.0.9-win-x64.exe",
|
L"https://download.visualstudio.microsoft.com/download/pr/8bc41df1-cbb4-4da6-944f-6652378e9196/1014aacedc80bbcc030dabb168d2532f/windowsdesktop-runtime-5.0.9-win-x64.exe",
|
||||||
0, L"WindowsDesktop.App 5.0"
|
0, L"WindowsDesktop.App 5.0"
|
||||||
},
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -114,126 +114,123 @@ RUNTIMEINFO supported_runtimes[] =
|
|||||||
|
|
||||||
const RUNTIMEINFO* GetRuntimeByName(wstring name)
|
const RUNTIMEINFO* GetRuntimeByName(wstring name)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < NUM_RUNTIMES; i++)
|
for (int i = 0; i < NUM_RUNTIMES; i++) {
|
||||||
{
|
const RUNTIMEINFO* item = &supported_runtimes[i];
|
||||||
const RUNTIMEINFO* item = &supported_runtimes[i];
|
auto itemName = wstring(item->name);
|
||||||
auto itemName = wstring(item->name);
|
|
||||||
|
|
||||||
if (name == itemName)
|
if (name == itemName)
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsRuntimeSupported(const RUNTIMEINFO* runtime)
|
bool IsRuntimeSupported(const RUNTIMEINFO* runtime)
|
||||||
{
|
{
|
||||||
return IsWindowsVersionOrGreater(HIBYTE(runtime->minOS), LOBYTE(runtime->minOS), runtime->minSP);
|
return IsWindowsVersionOrGreater(HIBYTE(runtime->minOS), LOBYTE(runtime->minOS), runtime->minSP);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const wchar_t* ndpPath = L"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full";
|
static const wchar_t* ndpPath = L"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full";
|
||||||
bool IsFullNetFrameworkInstalled(DWORD requiredVersion)
|
bool IsFullNetFrameworkInstalled(DWORD requiredVersion)
|
||||||
{
|
{
|
||||||
ATL::CRegKey key;
|
ATL::CRegKey key;
|
||||||
|
|
||||||
if (key.Open(HKEY_LOCAL_MACHINE, ndpPath, KEY_READ) != ERROR_SUCCESS) {
|
if (key.Open(HKEY_LOCAL_MACHINE, ndpPath, KEY_READ) != ERROR_SUCCESS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD dwReleaseInfo = 0;
|
DWORD dwReleaseInfo = 0;
|
||||||
if (key.QueryDWORDValue(L"Release", dwReleaseInfo) != ERROR_SUCCESS ||
|
if (key.QueryDWORDValue(L"Release", dwReleaseInfo) != ERROR_SUCCESS ||
|
||||||
dwReleaseInfo < requiredVersion) {
|
dwReleaseInfo < requiredVersion) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO this is extremely messy, it should certainly be re-written.
|
// TODO this is extremely messy, it should certainly be re-written.
|
||||||
wstring exec(const char* cmd) {
|
wstring exec(const char* cmd)
|
||||||
char buffer[128];
|
{
|
||||||
string result = "";
|
char buffer[128];
|
||||||
FILE* pipe = _popen(cmd, "r");
|
string result = "";
|
||||||
if (!pipe)
|
FILE* pipe = _popen(cmd, "r");
|
||||||
return L"";
|
if (!pipe)
|
||||||
try {
|
return L"";
|
||||||
while (fgets(buffer, sizeof buffer, pipe) != NULL) {
|
try {
|
||||||
result += buffer;
|
while (fgets(buffer, sizeof buffer, pipe) != NULL) {
|
||||||
}
|
result += buffer;
|
||||||
}
|
}
|
||||||
catch (...) {
|
}
|
||||||
_pclose(pipe);
|
catch (...) {
|
||||||
return L"";
|
_pclose(pipe);
|
||||||
}
|
return L"";
|
||||||
_pclose(pipe);
|
}
|
||||||
|
_pclose(pipe);
|
||||||
|
|
||||||
// https://stackoverflow.com/a/8969776/184746
|
// https://stackoverflow.com/a/8969776/184746
|
||||||
std::wstring wsTmp(result.begin(), result.end());
|
std::wstring wsTmp(result.begin(), result.end());
|
||||||
return wsTmp;
|
return wsTmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsDotNetCoreInstalled(wstring searchString)
|
bool IsDotNetCoreInstalled(wstring searchString)
|
||||||
{
|
{
|
||||||
// it is possible to parse this registry entry, but it only returns the newest version
|
// it is possible to parse this registry entry, but it only returns the newest version
|
||||||
// and it might be necessary to install an older version of the runtime if it's not installed,
|
// and it might be necessary to install an older version of the runtime if it's not installed,
|
||||||
// so we need a full list of installed runtimes.
|
// so we need a full list of installed runtimes.
|
||||||
// static const wchar_t* dncPath = L"SOFTWARE\\dotnet\\Setup\\InstalledVersions";
|
// static const wchar_t* dncPath = L"SOFTWARE\\dotnet\\Setup\\InstalledVersions";
|
||||||
|
|
||||||
// note, dotnet cli will only return x64 results.
|
// note, dotnet cli will only return x64 results.
|
||||||
//auto runtimes = exec("dotnet --list-runtimes");
|
//auto runtimes = exec("dotnet --list-runtimes");
|
||||||
auto runtimes = exec("dotnet --info");
|
auto runtimes = exec("dotnet --info");
|
||||||
return runtimes.find(searchString) != std::string::npos;
|
return runtimes.find(searchString) != std::string::npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsRuntimeInstalled(const RUNTIMEINFO* runtime)
|
bool IsRuntimeInstalled(const RUNTIMEINFO* runtime)
|
||||||
{
|
{
|
||||||
if (runtime->fxReleaseVersion > 0)
|
if (runtime->fxReleaseVersion > 0) {
|
||||||
{
|
return IsFullNetFrameworkInstalled(runtime->fxReleaseVersion);
|
||||||
return IsFullNetFrameworkInstalled(runtime->fxReleaseVersion);
|
}
|
||||||
}
|
else {
|
||||||
else
|
return IsDotNetCoreInstalled(wstring(runtime->dncRuntimeVersionName));
|
||||||
{
|
}
|
||||||
return IsDotNetCoreInstalled(wstring(runtime->dncRuntimeVersionName));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ParseRuntimeString(std::wstring version, vector<const RUNTIMEINFO*>& runtimes)
|
int ParseRuntimeString(std::wstring version, vector<const RUNTIMEINFO*>& runtimes)
|
||||||
{
|
{
|
||||||
// split version string by comma
|
// split version string by comma
|
||||||
int ret = S_OK;
|
int ret = S_OK;
|
||||||
wstring temp;
|
wstring temp;
|
||||||
std::wstringstream wss(version);
|
std::wstringstream wss(version);
|
||||||
while (std::getline(wss, temp, L','))
|
while (std::getline(wss, temp, L',')) {
|
||||||
{
|
const RUNTIMEINFO* rt = GetRuntimeByName(temp);
|
||||||
const RUNTIMEINFO* rt = GetRuntimeByName(temp);
|
if (rt != nullptr)
|
||||||
if (rt != nullptr)
|
runtimes.push_back(rt);
|
||||||
runtimes.push_back(rt);
|
else
|
||||||
else
|
ret = S_FALSE;
|
||||||
ret = S_FALSE;
|
}
|
||||||
}
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<const RUNTIMEINFO*> GetRequiredRuntimes()
|
vector<const RUNTIMEINFO*> GetRequiredRuntimes()
|
||||||
{
|
{
|
||||||
vector<const RUNTIMEINFO*> runtimes;
|
vector<const RUNTIMEINFO*> runtimes;
|
||||||
|
|
||||||
// get comma-delimited version string from exe resources
|
// get comma-delimited version string from exe resources
|
||||||
wchar_t* versionFlag = (wchar_t*)LoadResource(NULL, FindResource(NULL, (LPCWSTR)IDR_FX_VERSION_FLAG, L"FLAGS"));
|
wchar_t* versionFlag = (wchar_t*)LoadResource(NULL, FindResource(NULL, (LPCWSTR)IDR_FX_VERSION_FLAG, L"FLAGS"));
|
||||||
if (versionFlag == nullptr)
|
if (versionFlag == nullptr)
|
||||||
return runtimes;
|
return runtimes;
|
||||||
|
|
||||||
wstring version(versionFlag);
|
wstring version(versionFlag);
|
||||||
if (version.length() == 0)
|
if (version.length() == 0)
|
||||||
return runtimes;
|
return runtimes;
|
||||||
|
|
||||||
ParseRuntimeString(version, runtimes);
|
ParseRuntimeString(version, runtimes);
|
||||||
return runtimes;
|
return runtimes;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VerifyRuntimeString(std::wstring version)
|
int VerifyRuntimeString(std::wstring version)
|
||||||
{
|
{
|
||||||
vector<const RUNTIMEINFO*> runtimes;
|
vector<const RUNTIMEINFO*> runtimes;
|
||||||
return ParseRuntimeString(version, runtimes);
|
return ParseRuntimeString(version, runtimes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,13 @@
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
WORD minOS;
|
WORD minOS;
|
||||||
WORD minSP;
|
WORD minSP;
|
||||||
wchar_t name[32];
|
wchar_t name[32];
|
||||||
wchar_t friendlyName[32];
|
wchar_t friendlyName[32];
|
||||||
wchar_t installerUrl[256];
|
wchar_t installerUrl[256];
|
||||||
DWORD fxReleaseVersion;
|
DWORD fxReleaseVersion;
|
||||||
wchar_t dncRuntimeVersionName[32];
|
wchar_t dncRuntimeVersionName[32];
|
||||||
} RUNTIMEINFO;
|
} RUNTIMEINFO;
|
||||||
|
|
||||||
const RUNTIMEINFO* GetRuntimeByName(std::wstring name);
|
const RUNTIMEINFO* GetRuntimeByName(std::wstring name);
|
||||||
|
|||||||
@@ -45,28 +45,23 @@ void CSplashWnd::SetImage(const wchar_t* resid, const wchar_t* restype)
|
|||||||
|
|
||||||
void CSplashWnd::Show()
|
void CSplashWnd::Show()
|
||||||
{
|
{
|
||||||
if (m_hThread == NULL)
|
if (m_hThread == NULL) {
|
||||||
{
|
|
||||||
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
m_hThread = (HANDLE)_beginthreadex(NULL, 0, SplashThreadProc, static_cast<LPVOID>(this), 0, &m_ThreadId);
|
m_hThread = (HANDLE)_beginthreadex(NULL, 0, SplashThreadProc, static_cast<LPVOID>(this), 0, &m_ThreadId);
|
||||||
if (WaitForSingleObject(m_hEvent, 5000) == WAIT_TIMEOUT)
|
if (WaitForSingleObject(m_hEvent, 5000) == WAIT_TIMEOUT) {
|
||||||
{
|
|
||||||
OutputDebugString(L"Error starting SplashThread\n");
|
OutputDebugString(L"Error starting SplashThread\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
PostThreadMessage(m_ThreadId, WM_ACTIVATE, WA_CLICKACTIVE, 0);
|
PostThreadMessage(m_ThreadId, WM_ACTIVATE, WA_CLICKACTIVE, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSplashWnd::Hide()
|
void CSplashWnd::Hide()
|
||||||
{
|
{
|
||||||
if (m_hThread)
|
if (m_hThread) {
|
||||||
{
|
|
||||||
PostThreadMessage(m_ThreadId, WM_QUIT, 0, 0);
|
PostThreadMessage(m_ThreadId, WM_QUIT, 0, 0);
|
||||||
if (WaitForSingleObject(m_hThread, 9000) == WAIT_TIMEOUT)
|
if (WaitForSingleObject(m_hThread, 9000) == WAIT_TIMEOUT) {
|
||||||
{
|
|
||||||
::TerminateThread(m_hThread, 2222);
|
::TerminateThread(m_hThread, 2222);
|
||||||
}
|
}
|
||||||
CloseHandle(m_hThread);
|
CloseHandle(m_hThread);
|
||||||
@@ -101,8 +96,7 @@ unsigned int __stdcall CSplashWnd::SplashThreadProc(void* lpParameter)
|
|||||||
wndcls.lpszClassName = L"SplashWnd";
|
wndcls.lpszClassName = L"SplashWnd";
|
||||||
wndcls.hIcon = LoadIcon(wndcls.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
|
wndcls.hIcon = LoadIcon(wndcls.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
|
||||||
|
|
||||||
if (!RegisterClass(&wndcls))
|
if (!RegisterClass(&wndcls)) {
|
||||||
{
|
|
||||||
if (GetLastError() != 0x00000582) // already registered)
|
if (GetLastError() != 0x00000582) // already registered)
|
||||||
{
|
{
|
||||||
OutputDebugString(L"Unable to register class SplashWnd\n");
|
OutputDebugString(L"Unable to register class SplashWnd\n");
|
||||||
@@ -118,13 +112,11 @@ unsigned int __stdcall CSplashWnd::SplashThreadProc(void* lpParameter)
|
|||||||
|
|
||||||
::GetCursorPos(&point);
|
::GetCursorPos(&point);
|
||||||
hMonitor = ::MonitorFromPoint(point, MONITOR_DEFAULTTONEAREST);
|
hMonitor = ::MonitorFromPoint(point, MONITOR_DEFAULTTONEAREST);
|
||||||
if (::GetMonitorInfo(hMonitor, &mi))
|
if (::GetMonitorInfo(hMonitor, &mi)) {
|
||||||
{
|
|
||||||
rcArea.left = (mi.rcMonitor.right + mi.rcMonitor.left - static_cast<long>(pThis->m_pImage->GetWidth())) / 2;
|
rcArea.left = (mi.rcMonitor.right + mi.rcMonitor.left - static_cast<long>(pThis->m_pImage->GetWidth())) / 2;
|
||||||
rcArea.top = (mi.rcMonitor.top + mi.rcMonitor.bottom - static_cast<long>(pThis->m_pImage->GetHeight())) / 2;
|
rcArea.top = (mi.rcMonitor.top + mi.rcMonitor.bottom - static_cast<long>(pThis->m_pImage->GetHeight())) / 2;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcArea, NULL);
|
SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcArea, NULL);
|
||||||
rcArea.left = (rcArea.right + rcArea.left - pThis->m_pImage->GetWidth()) / 2;
|
rcArea.left = (rcArea.right + rcArea.left - pThis->m_pImage->GetWidth()) / 2;
|
||||||
rcArea.top = (rcArea.top + rcArea.bottom - pThis->m_pImage->GetHeight()) / 2;
|
rcArea.top = (rcArea.top + rcArea.bottom - pThis->m_pImage->GetHeight()) / 2;
|
||||||
@@ -141,8 +133,7 @@ unsigned int __stdcall CSplashWnd::SplashThreadProc(void* lpParameter)
|
|||||||
wndcls.hInstance,
|
wndcls.hInstance,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (!pThis->m_hSplashWnd)
|
if (!pThis->m_hSplashWnd) {
|
||||||
{
|
|
||||||
OutputDebugString(L"Unable to create SplashWnd\n");
|
OutputDebugString(L"Unable to create SplashWnd\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -159,16 +150,13 @@ unsigned int __stdcall CSplashWnd::SplashThreadProc(void* lpParameter)
|
|||||||
PeekMessage(&msg, NULL, 0, 0, 0); // invoke creating message queue
|
PeekMessage(&msg, NULL, 0, 0, 0); // invoke creating message queue
|
||||||
SetEvent(pThis->m_hEvent);
|
SetEvent(pThis->m_hEvent);
|
||||||
|
|
||||||
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
|
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
|
||||||
{
|
|
||||||
if (msg.message == WM_QUIT) break;
|
if (msg.message == WM_QUIT) break;
|
||||||
|
|
||||||
if (bRet == -1)
|
if (bRet == -1) {
|
||||||
{
|
|
||||||
// handle the error and possibly exit
|
// handle the error and possibly exit
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
TranslateMessage(&msg);
|
TranslateMessage(&msg);
|
||||||
DispatchMessage(&msg);
|
DispatchMessage(&msg);
|
||||||
}
|
}
|
||||||
@@ -181,24 +169,19 @@ unsigned int __stdcall CSplashWnd::SplashThreadProc(void* lpParameter)
|
|||||||
LRESULT CALLBACK CSplashWnd::SplashWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
LRESULT CALLBACK CSplashWnd::SplashWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
CSplashWnd* pInstance = reinterpret_cast<CSplashWnd*>(GetWindowLongPtr(hwnd, GWL_USERDATA));
|
CSplashWnd* pInstance = reinterpret_cast<CSplashWnd*>(GetWindowLongPtr(hwnd, GWL_USERDATA));
|
||||||
if (pInstance == NULL)
|
if (pInstance == NULL) {
|
||||||
{
|
|
||||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (uMsg)
|
switch (uMsg) {
|
||||||
{
|
|
||||||
|
|
||||||
case WM_PAINT:
|
case WM_PAINT:
|
||||||
{
|
{
|
||||||
if (pInstance->m_pImage)
|
if (pInstance->m_pImage) {
|
||||||
{
|
if (pInstance->m_pImage->IsAnimatedGIF()) {
|
||||||
if (pInstance->m_pImage->IsAnimatedGIF())
|
|
||||||
{
|
|
||||||
// do nothing, the gif will be drawn by it's own thread.
|
// do nothing, the gif will be drawn by it's own thread.
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
Gdiplus::Graphics gdip(hwnd);
|
Gdiplus::Graphics gdip(hwnd);
|
||||||
gdip.DrawImage(pInstance->m_pImage, 0, 0, pInstance->m_pImage->GetWidth(), pInstance->m_pImage->GetHeight());
|
gdip.DrawImage(pInstance->m_pImage, 0, 0, pInstance->m_pImage->GetWidth(), pInstance->m_pImage->GetHeight());
|
||||||
}
|
}
|
||||||
@@ -216,8 +199,7 @@ LRESULT CALLBACK CSplashWnd::SplashWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
|
|||||||
|
|
||||||
case WM_MOUSEMOVE:
|
case WM_MOUSEMOVE:
|
||||||
{
|
{
|
||||||
if (GetCapture() == hwnd)
|
if (GetCapture() == hwnd) {
|
||||||
{
|
|
||||||
RECT rcWnd;
|
RECT rcWnd;
|
||||||
GetWindowRect(hwnd, &rcWnd);
|
GetWindowRect(hwnd, &rcWnd);
|
||||||
|
|
||||||
|
|||||||
@@ -24,19 +24,18 @@ private:
|
|||||||
CSplashWnd& operator=(const CSplashWnd&) {};
|
CSplashWnd& operator=(const CSplashWnd&) {};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
HANDLE m_hThread;
|
HANDLE m_hThread;
|
||||||
unsigned int m_ThreadId;
|
unsigned int m_ThreadId;
|
||||||
HANDLE m_hEvent;
|
HANDLE m_hEvent;
|
||||||
ImageEx* m_pImage;
|
ImageEx* m_pImage;
|
||||||
HWND m_hSplashWnd;
|
HWND m_hSplashWnd;
|
||||||
HWND m_hParentWnd;
|
HWND m_hParentWnd;
|
||||||
POINT m_ptMouseDown;
|
POINT m_ptMouseDown;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CSplashWnd(HWND hParent = NULL);
|
CSplashWnd(HWND hParent = NULL);
|
||||||
~CSplashWnd();
|
~CSplashWnd();
|
||||||
void SetImage(const wchar_t* resid, const wchar_t* restype);
|
void SetImage(const wchar_t* resid, const wchar_t* restype);
|
||||||
void SetWindowName(const wchar_t* windowName);
|
|
||||||
void Show();
|
void Show();
|
||||||
void Hide();
|
void Hide();
|
||||||
|
|
||||||
|
|||||||
@@ -6,296 +6,297 @@
|
|||||||
|
|
||||||
void CUpdateRunner::DisplayErrorMessage(CString& errorMessage, wchar_t* logFile)
|
void CUpdateRunner::DisplayErrorMessage(CString& errorMessage, wchar_t* logFile)
|
||||||
{
|
{
|
||||||
CTaskDialog dlg;
|
CTaskDialog dlg;
|
||||||
TASKDIALOG_BUTTON buttons[] = {
|
TASKDIALOG_BUTTON buttons[] = {
|
||||||
{ 1, L"Open Setup Log", },
|
{ 1, L"Open Setup Log", },
|
||||||
{ 2, L"Close", },
|
{ 2, L"Close", },
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Something about contacting support?
|
// TODO: Something about contacting support?
|
||||||
if (logFile == NULL) {
|
if (logFile == NULL) {
|
||||||
dlg.SetButtons(&buttons[1], 1, 1);
|
dlg.SetButtons(&buttons[1], 1, 1);
|
||||||
} else {
|
}
|
||||||
dlg.SetButtons(buttons, 2, 1);
|
else {
|
||||||
}
|
dlg.SetButtons(buttons, 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
dlg.SetMainInstructionText(L"Installation has failed");
|
dlg.SetMainInstructionText(L"Installation has failed");
|
||||||
dlg.SetContentText(errorMessage);
|
dlg.SetContentText(errorMessage);
|
||||||
dlg.SetMainIcon(TD_ERROR_ICON);
|
dlg.SetMainIcon(TD_ERROR_ICON);
|
||||||
|
|
||||||
int nButton;
|
int nButton;
|
||||||
|
|
||||||
if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton))) {
|
if (FAILED(dlg.DoModal(::GetActiveWindow(), &nButton))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nButton == 1 && logFile != NULL) {
|
if (nButton == 1 && logFile != NULL) {
|
||||||
ShellExecute(NULL, NULL, logFile, NULL, NULL, SW_SHOW);
|
ShellExecute(NULL, NULL, logFile, NULL, NULL, SW_SHOW);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT CUpdateRunner::AreWeUACElevated()
|
HRESULT CUpdateRunner::AreWeUACElevated()
|
||||||
{
|
{
|
||||||
HANDLE hProcess = GetCurrentProcess();
|
HANDLE hProcess = GetCurrentProcess();
|
||||||
HANDLE hToken = 0;
|
HANDLE hToken = 0;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {
|
if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {
|
||||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
TOKEN_ELEVATION_TYPE elevType;
|
TOKEN_ELEVATION_TYPE elevType;
|
||||||
DWORD dontcare;
|
DWORD dontcare;
|
||||||
if (!GetTokenInformation(hToken, TokenElevationType, &elevType, sizeof(TOKEN_ELEVATION_TYPE), &dontcare)) {
|
if (!GetTokenInformation(hToken, TokenElevationType, &elevType, sizeof(TOKEN_ELEVATION_TYPE), &dontcare)) {
|
||||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = (elevType == TokenElevationTypeFull ? S_OK : S_FALSE);
|
hr = (elevType == TokenElevationTypeFull ? S_OK : S_FALSE);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (hToken) {
|
if (hToken) {
|
||||||
CloseHandle(hToken);
|
CloseHandle(hToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT FindDesktopFolderView(REFIID riid, void **ppv)
|
HRESULT FindDesktopFolderView(REFIID riid, void** ppv)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
CComPtr<IShellWindows> spShellWindows;
|
CComPtr<IShellWindows> spShellWindows;
|
||||||
spShellWindows.CoCreateInstance(CLSID_ShellWindows);
|
spShellWindows.CoCreateInstance(CLSID_ShellWindows);
|
||||||
|
|
||||||
CComVariant vtLoc(CSIDL_DESKTOP);
|
CComVariant vtLoc(CSIDL_DESKTOP);
|
||||||
CComVariant vtEmpty;
|
CComVariant vtEmpty;
|
||||||
long lhwnd;
|
long lhwnd;
|
||||||
CComPtr<IDispatch> spdisp;
|
CComPtr<IDispatch> spdisp;
|
||||||
|
|
||||||
hr = spShellWindows->FindWindowSW(
|
hr = spShellWindows->FindWindowSW(
|
||||||
&vtLoc, &vtEmpty,
|
&vtLoc, &vtEmpty,
|
||||||
SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);
|
SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);
|
||||||
if (FAILED(hr)) return hr;
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
CComPtr<IShellBrowser> spBrowser;
|
CComPtr<IShellBrowser> spBrowser;
|
||||||
hr = CComQIPtr<IServiceProvider>(spdisp)->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&spBrowser));
|
hr = CComQIPtr<IServiceProvider>(spdisp)->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&spBrowser));
|
||||||
if (FAILED(hr)) return hr;
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
CComPtr<IShellView> spView;
|
CComPtr<IShellView> spView;
|
||||||
hr = spBrowser->QueryActiveShellView(&spView);
|
hr = spBrowser->QueryActiveShellView(&spView);
|
||||||
if (FAILED(hr)) return hr;
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
hr = spView->QueryInterface(riid, ppv);
|
hr = spView->QueryInterface(riid, ppv);
|
||||||
if (FAILED(hr)) return hr;
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT GetDesktopAutomationObject(REFIID riid, void **ppv)
|
HRESULT GetDesktopAutomationObject(REFIID riid, void** ppv)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
CComPtr<IShellView> spsv;
|
CComPtr<IShellView> spsv;
|
||||||
hr = FindDesktopFolderView(IID_PPV_ARGS(&spsv));
|
hr = FindDesktopFolderView(IID_PPV_ARGS(&spsv));
|
||||||
if (FAILED(hr)) return hr;
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
CComPtr<IDispatch> spdispView;
|
CComPtr<IDispatch> spdispView;
|
||||||
hr = spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
|
hr = spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
|
||||||
if (FAILED(hr)) return hr;
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
return spdispView->QueryInterface(riid, ppv);
|
return spdispView->QueryInterface(riid, ppv);
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT CUpdateRunner::ShellExecuteFromExplorer(LPWSTR pszFile, LPWSTR pszParameters)
|
HRESULT CUpdateRunner::ShellExecuteFromExplorer(LPWSTR pszFile, LPWSTR pszParameters)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
CComPtr<IShellFolderViewDual> spFolderView;
|
CComPtr<IShellFolderViewDual> spFolderView;
|
||||||
hr = GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView));
|
hr = GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView));
|
||||||
if (FAILED(hr)) return hr;
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
CComPtr<IDispatch> spdispShell;
|
CComPtr<IDispatch> spdispShell;
|
||||||
hr = spFolderView->get_Application(&spdispShell);
|
hr = spFolderView->get_Application(&spdispShell);
|
||||||
if (FAILED(hr)) return hr;
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
return CComQIPtr<IShellDispatch2>(spdispShell)->ShellExecute(
|
return CComQIPtr<IShellDispatch2>(spdispShell)->ShellExecute(
|
||||||
CComBSTR(pszFile),
|
CComBSTR(pszFile),
|
||||||
CComVariant(pszParameters ? pszParameters : L""),
|
CComVariant(pszParameters ? pszParameters : L""),
|
||||||
CComVariant(L""),
|
CComVariant(L""),
|
||||||
CComVariant(L""),
|
CComVariant(L""),
|
||||||
CComVariant(SW_SHOWDEFAULT));
|
CComVariant(SW_SHOWDEFAULT));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CUpdateRunner::DirectoryExists(wchar_t* szPath)
|
bool CUpdateRunner::DirectoryExists(wchar_t* szPath)
|
||||||
{
|
{
|
||||||
DWORD dwAttrib = GetFileAttributes(szPath);
|
DWORD dwAttrib = GetFileAttributes(szPath);
|
||||||
|
|
||||||
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
|
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
|
||||||
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
|
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CUpdateRunner::DirectoryIsWritable(wchar_t * szPath)
|
bool CUpdateRunner::DirectoryIsWritable(wchar_t* szPath)
|
||||||
{
|
{
|
||||||
wchar_t szTempFileName[MAX_PATH];
|
wchar_t szTempFileName[MAX_PATH];
|
||||||
UINT uRetVal = GetTempFileNameW(szPath, L"Squirrel", 0, szTempFileName);
|
UINT uRetVal = GetTempFileNameW(szPath, L"Squirrel", 0, szTempFileName);
|
||||||
if (uRetVal == 0) {
|
if (uRetVal == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
DeleteFile(szTempFileName);
|
DeleteFile(szTempFileName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CUpdateRunner::ExtractUpdaterAndRun(wchar_t* lpCommandLine, bool useFallbackDir, std::function<void()>& callback)
|
int CUpdateRunner::ExtractUpdaterAndRun(wchar_t* lpCommandLine, bool useFallbackDir, std::function<void()>& callback)
|
||||||
{
|
{
|
||||||
PROCESS_INFORMATION pi = { 0 };
|
PROCESS_INFORMATION pi = { 0 };
|
||||||
STARTUPINFO si = { 0 };
|
STARTUPINFO si = { 0 };
|
||||||
CResource zipResource;
|
CResource zipResource;
|
||||||
wchar_t targetDir[MAX_PATH] = { 0 };
|
wchar_t targetDir[MAX_PATH] = { 0 };
|
||||||
wchar_t logFile[MAX_PATH];
|
wchar_t logFile[MAX_PATH];
|
||||||
DWORD dwExitCode = 0;
|
DWORD dwExitCode = 0;
|
||||||
|
|
||||||
std::vector<CString> to_delete;
|
std::vector<CString> to_delete;
|
||||||
|
|
||||||
wchar_t* envSquirrelTemp = _wgetenv(L"SQUIRREL_TEMP");
|
wchar_t* envSquirrelTemp = _wgetenv(L"SQUIRREL_TEMP");
|
||||||
if (envSquirrelTemp &&
|
if (envSquirrelTemp &&
|
||||||
DirectoryExists(envSquirrelTemp) &&
|
DirectoryExists(envSquirrelTemp) &&
|
||||||
DirectoryIsWritable(envSquirrelTemp) &&
|
DirectoryIsWritable(envSquirrelTemp) &&
|
||||||
!PathIsUNCW(envSquirrelTemp)) {
|
!PathIsUNCW(envSquirrelTemp)) {
|
||||||
_swprintf_c(targetDir, _countof(targetDir), L"%s", envSquirrelTemp);
|
_swprintf_c(targetDir, _countof(targetDir), L"%s", envSquirrelTemp);
|
||||||
goto gotADir;
|
goto gotADir;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!useFallbackDir) {
|
if (!useFallbackDir) {
|
||||||
SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, targetDir);
|
SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, targetDir);
|
||||||
goto gotADir;
|
goto gotADir;
|
||||||
}
|
}
|
||||||
|
|
||||||
wchar_t username[512];
|
wchar_t username[512];
|
||||||
wchar_t appDataDir[MAX_PATH];
|
wchar_t appDataDir[MAX_PATH];
|
||||||
ULONG unameSize = _countof(username);
|
ULONG unameSize = _countof(username);
|
||||||
|
|
||||||
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, appDataDir);
|
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, appDataDir);
|
||||||
GetUserName(username, &unameSize);
|
GetUserName(username, &unameSize);
|
||||||
|
|
||||||
_swprintf_c(targetDir, _countof(targetDir), L"%s\\%s", appDataDir, username);
|
_swprintf_c(targetDir, _countof(targetDir), L"%s\\%s", appDataDir, username);
|
||||||
|
|
||||||
if (!CreateDirectory(targetDir, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
if (!CreateDirectory(targetDir, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||||
wchar_t err[4096];
|
wchar_t err[4096];
|
||||||
_swprintf_c(err, _countof(err), L"Unable to write to %s - IT policies may be restricting access to this folder", targetDir);
|
_swprintf_c(err, _countof(err), L"Unable to write to %s - IT policies may be restricting access to this folder", targetDir);
|
||||||
callback();
|
callback();
|
||||||
DisplayErrorMessage(CString(err), NULL);
|
DisplayErrorMessage(CString(err), NULL);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
gotADir:
|
gotADir:
|
||||||
|
|
||||||
wcscat_s(targetDir, _countof(targetDir), L"\\SquirrelTemp");
|
wcscat_s(targetDir, _countof(targetDir), L"\\SquirrelTemp");
|
||||||
|
|
||||||
if (!CreateDirectory(targetDir, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
if (!CreateDirectory(targetDir, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||||
wchar_t err[4096];
|
wchar_t err[4096];
|
||||||
_swprintf_c(err, _countof(err), L"Unable to write to %s - IT policies may be restricting access to this folder", targetDir);
|
_swprintf_c(err, _countof(err), L"Unable to write to %s - IT policies may be restricting access to this folder", targetDir);
|
||||||
|
|
||||||
if (useFallbackDir) {
|
if (useFallbackDir) {
|
||||||
callback();
|
callback();
|
||||||
DisplayErrorMessage(CString(err), NULL);
|
DisplayErrorMessage(CString(err), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
goto failedExtract;
|
goto failedExtract;
|
||||||
}
|
}
|
||||||
|
|
||||||
swprintf_s(logFile, L"%s\\SquirrelSetup.log", targetDir);
|
swprintf_s(logFile, L"%s\\SquirrelSetup.log", targetDir);
|
||||||
|
|
||||||
if (!zipResource.Load(L"DATA", IDR_UPDATE_ZIP)) {
|
if (!zipResource.Load(L"DATA", IDR_UPDATE_ZIP)) {
|
||||||
goto failedExtract;
|
goto failedExtract;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD dwSize = zipResource.GetSize();
|
DWORD dwSize = zipResource.GetSize();
|
||||||
if (dwSize < 0x100) {
|
if (dwSize < 0x100) {
|
||||||
goto failedExtract;
|
goto failedExtract;
|
||||||
}
|
}
|
||||||
|
|
||||||
BYTE* pData = (BYTE*)zipResource.Lock();
|
BYTE* pData = (BYTE*)zipResource.Lock();
|
||||||
HZIP zipFile = OpenZip(pData, dwSize, NULL);
|
HZIP zipFile = OpenZip(pData, dwSize, NULL);
|
||||||
SetUnzipBaseDir(zipFile, targetDir);
|
SetUnzipBaseDir(zipFile, targetDir);
|
||||||
|
|
||||||
// NB: This library is kind of a disaster
|
// NB: This library is kind of a disaster
|
||||||
ZRESULT zr;
|
ZRESULT zr;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
do {
|
do {
|
||||||
ZIPENTRY zentry;
|
ZIPENTRY zentry;
|
||||||
wchar_t targetFile[MAX_PATH];
|
wchar_t targetFile[MAX_PATH];
|
||||||
|
|
||||||
zr = GetZipItem(zipFile, index, &zentry);
|
zr = GetZipItem(zipFile, index, &zentry);
|
||||||
if (zr != ZR_OK && zr != ZR_MORE) {
|
if (zr != ZR_OK && zr != ZR_MORE) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: UnzipItem won't overwrite data, we need to do it ourselves
|
// NB: UnzipItem won't overwrite data, we need to do it ourselves
|
||||||
swprintf_s(targetFile, L"%s\\%s", targetDir, zentry.name);
|
swprintf_s(targetFile, L"%s\\%s", targetDir, zentry.name);
|
||||||
DeleteFile(targetFile);
|
DeleteFile(targetFile);
|
||||||
|
|
||||||
if (UnzipItem(zipFile, index, zentry.name) != ZR_OK) break;
|
if (UnzipItem(zipFile, index, zentry.name) != ZR_OK) break;
|
||||||
to_delete.push_back(CString(targetFile));
|
to_delete.push_back(CString(targetFile));
|
||||||
index++;
|
index++;
|
||||||
} while (zr == ZR_MORE || zr == ZR_OK);
|
} while (zr == ZR_MORE || zr == ZR_OK);
|
||||||
|
|
||||||
CloseZip(zipFile);
|
CloseZip(zipFile);
|
||||||
zipResource.Release();
|
zipResource.Release();
|
||||||
|
|
||||||
// nfi if the zip extract actually worked, check for Update.exe
|
// nfi if the zip extract actually worked, check for Update.exe
|
||||||
wchar_t updateExePath[MAX_PATH];
|
wchar_t updateExePath[MAX_PATH];
|
||||||
swprintf_s(updateExePath, L"%s\\%s", targetDir, L"Update.exe");
|
swprintf_s(updateExePath, L"%s\\%s", targetDir, L"Update.exe");
|
||||||
|
|
||||||
if (GetFileAttributes(updateExePath) == INVALID_FILE_ATTRIBUTES) {
|
if (GetFileAttributes(updateExePath) == INVALID_FILE_ATTRIBUTES) {
|
||||||
goto failedExtract;
|
goto failedExtract;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run Update.exe
|
// Run Update.exe
|
||||||
si.cb = sizeof(STARTUPINFO);
|
si.cb = sizeof(STARTUPINFO);
|
||||||
si.wShowWindow = SW_SHOW;
|
si.wShowWindow = SW_SHOW;
|
||||||
si.dwFlags = STARTF_USESHOWWINDOW;
|
si.dwFlags = STARTF_USESHOWWINDOW;
|
||||||
|
|
||||||
if (!lpCommandLine || wcsnlen_s(lpCommandLine, MAX_PATH) < 1) {
|
if (!lpCommandLine || wcsnlen_s(lpCommandLine, MAX_PATH) < 1) {
|
||||||
lpCommandLine = L"";
|
lpCommandLine = L"";
|
||||||
}
|
}
|
||||||
|
|
||||||
wchar_t cmd[MAX_PATH];
|
wchar_t cmd[MAX_PATH];
|
||||||
swprintf_s(cmd, L"\"%s\" --install . %s", updateExePath, lpCommandLine);
|
swprintf_s(cmd, L"\"%s\" --install . %s", updateExePath, lpCommandLine);
|
||||||
|
|
||||||
if (!CreateProcess(NULL, cmd, NULL, NULL, false, 0, NULL, targetDir, &si, &pi)) {
|
if (!CreateProcess(NULL, cmd, NULL, NULL, false, 0, NULL, targetDir, &si, &pi)) {
|
||||||
goto failedExtract;
|
goto failedExtract;
|
||||||
}
|
}
|
||||||
|
|
||||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||||
|
|
||||||
if (!GetExitCodeProcess(pi.hProcess, &dwExitCode)) {
|
if (!GetExitCodeProcess(pi.hProcess, &dwExitCode)) {
|
||||||
dwExitCode = (DWORD)-1;
|
dwExitCode = (DWORD)-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dwExitCode != 0) {
|
if (dwExitCode != 0) {
|
||||||
callback();
|
callback();
|
||||||
DisplayErrorMessage(CString(
|
DisplayErrorMessage(CString(
|
||||||
L"There was an error while installing the application. "
|
L"There was an error while installing the application. "
|
||||||
L"Check the setup log for more information and contact the author."), logFile);
|
L"Check the setup log for more information and contact the author."), logFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < to_delete.size(); i++) {
|
for (unsigned int i = 0; i < to_delete.size(); i++) {
|
||||||
DeleteFile(to_delete[i]);
|
DeleteFile(to_delete[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseHandle(pi.hProcess);
|
CloseHandle(pi.hProcess);
|
||||||
CloseHandle(pi.hThread);
|
CloseHandle(pi.hThread);
|
||||||
return (int) dwExitCode;
|
return (int)dwExitCode;
|
||||||
|
|
||||||
failedExtract:
|
failedExtract:
|
||||||
if (!useFallbackDir) {
|
if (!useFallbackDir) {
|
||||||
// Take another pass at it, using C:\ProgramData instead
|
// Take another pass at it, using C:\ProgramData instead
|
||||||
return ExtractUpdaterAndRun(lpCommandLine, true, callback);
|
return ExtractUpdaterAndRun(lpCommandLine, true, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
DisplayErrorMessage(CString(L"Failed to extract installer"), NULL);
|
DisplayErrorMessage(CString(L"Failed to extract installer"), NULL);
|
||||||
return (int) dwExitCode;
|
return (int)dwExitCode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ class CUpdateRunner
|
|||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void DisplayErrorMessage(CString& errorMessage, wchar_t* logFile);
|
static void DisplayErrorMessage(CString& errorMessage, wchar_t* logFile);
|
||||||
static HRESULT AreWeUACElevated();
|
static HRESULT AreWeUACElevated();
|
||||||
static HRESULT ShellExecuteFromExplorer(LPWSTR pszFile, LPWSTR pszParameters);
|
static HRESULT ShellExecuteFromExplorer(LPWSTR pszFile, LPWSTR pszParameters);
|
||||||
static bool DirectoryExists(wchar_t* szPath);
|
static bool DirectoryExists(wchar_t* szPath);
|
||||||
static bool DirectoryIsWritable(wchar_t* szPath);
|
static bool DirectoryIsWritable(wchar_t* szPath);
|
||||||
static int ExtractUpdaterAndRun(wchar_t* lpCommandLine, bool useFallbackDir, std::function<void()>& callback);
|
static int ExtractUpdaterAndRun(wchar_t* lpCommandLine, bool useFallbackDir, std::function<void()>& callback);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
4875
src/Setup/unzip.cpp
4875
src/Setup/unzip.cpp
File diff suppressed because it is too large
Load Diff
@@ -18,18 +18,19 @@ typedef DWORD ZRESULT;
|
|||||||
// return codes from any of the zip functions. Listed later.
|
// return codes from any of the zip functions. Listed later.
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{ int index; // index of this file within the zip
|
{
|
||||||
TCHAR name[MAX_PATH]; // filename within the zip
|
int index; // index of this file within the zip
|
||||||
DWORD attr; // attributes, as in GetFileAttributes.
|
TCHAR name[MAX_PATH]; // filename within the zip
|
||||||
FILETIME atime,ctime,mtime;// access, create, modify filetimes
|
DWORD attr; // attributes, as in GetFileAttributes.
|
||||||
long comp_size; // sizes of item, compressed and uncompressed. These
|
FILETIME atime, ctime, mtime;// access, create, modify filetimes
|
||||||
long unc_size; // may be -1 if not yet known (e.g. being streamed in)
|
long comp_size; // sizes of item, compressed and uncompressed. These
|
||||||
|
long unc_size; // may be -1 if not yet known (e.g. being streamed in)
|
||||||
} ZIPENTRY;
|
} ZIPENTRY;
|
||||||
|
|
||||||
|
|
||||||
HZIP OpenZip(const TCHAR *fn, const char *password);
|
HZIP OpenZip(const TCHAR* fn, const char* password);
|
||||||
HZIP OpenZip(void *z,unsigned int len, const char *password);
|
HZIP OpenZip(void* z, unsigned int len, const char* password);
|
||||||
HZIP OpenZipHandle(HANDLE h, const char *password);
|
HZIP OpenZipHandle(HANDLE h, const char* password);
|
||||||
// OpenZip - opens a zip file and returns a handle with which you can
|
// OpenZip - opens a zip file and returns a handle with which you can
|
||||||
// subsequently examine its contents. You can open a zip file from:
|
// subsequently examine its contents. You can open a zip file from:
|
||||||
// from a pipe: OpenZipHandle(hpipe_read,0);
|
// from a pipe: OpenZipHandle(hpipe_read,0);
|
||||||
@@ -46,7 +47,7 @@ HZIP OpenZipHandle(HANDLE h, const char *password);
|
|||||||
// but for real windows, the zip makes its own copy of your handle, so you
|
// but for real windows, the zip makes its own copy of your handle, so you
|
||||||
// can close yours anytime.
|
// can close yours anytime.
|
||||||
|
|
||||||
ZRESULT GetZipItem(HZIP hz, int index, ZIPENTRY *ze);
|
ZRESULT GetZipItem(HZIP hz, int index, ZIPENTRY* ze);
|
||||||
// GetZipItem - call this to get information about an item in the zip.
|
// GetZipItem - call this to get information about an item in the zip.
|
||||||
// If index is -1 and the file wasn't opened through a pipe,
|
// If index is -1 and the file wasn't opened through a pipe,
|
||||||
// then it returns information about the whole zipfile
|
// then it returns information about the whole zipfile
|
||||||
@@ -61,14 +62,14 @@ ZRESULT GetZipItem(HZIP hz, int index, ZIPENTRY *ze);
|
|||||||
// then then comp_size and sometimes unc_size as well may not be known until
|
// then then comp_size and sometimes unc_size as well may not be known until
|
||||||
// after the item has been unzipped.
|
// after the item has been unzipped.
|
||||||
|
|
||||||
ZRESULT FindZipItem(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze);
|
ZRESULT FindZipItem(HZIP hz, const TCHAR* name, bool ic, int* index, ZIPENTRY* ze);
|
||||||
// FindZipItem - finds an item by name. ic means 'insensitive to case'.
|
// FindZipItem - finds an item by name. ic means 'insensitive to case'.
|
||||||
// It returns the index of the item, and returns information about it.
|
// It returns the index of the item, and returns information about it.
|
||||||
// If nothing was found, then index is set to -1 and the function returns
|
// If nothing was found, then index is set to -1 and the function returns
|
||||||
// an error code.
|
// an error code.
|
||||||
|
|
||||||
ZRESULT UnzipItem(HZIP hz, int index, const TCHAR *fn);
|
ZRESULT UnzipItem(HZIP hz, int index, const TCHAR* fn);
|
||||||
ZRESULT UnzipItem(HZIP hz, int index, void *z,unsigned int len);
|
ZRESULT UnzipItem(HZIP hz, int index, void* z, unsigned int len);
|
||||||
ZRESULT UnzipItemHandle(HZIP hz, int index, HANDLE h);
|
ZRESULT UnzipItemHandle(HZIP hz, int index, HANDLE h);
|
||||||
// UnzipItem - given an index to an item, unzips it. You can unzip to:
|
// UnzipItem - given an index to an item, unzips it. You can unzip to:
|
||||||
// to a pipe: UnzipItemHandle(hz,i, hpipe_write);
|
// to a pipe: UnzipItemHandle(hz,i, hpipe_write);
|
||||||
@@ -85,7 +86,7 @@ ZRESULT UnzipItemHandle(HZIP hz, int index, HANDLE h);
|
|||||||
// If you unzip a directory with ZIP_FILENAME, then the directory gets created.
|
// If you unzip a directory with ZIP_FILENAME, then the directory gets created.
|
||||||
// If you unzip it to a handle or a memory block, then nothing gets created
|
// If you unzip it to a handle or a memory block, then nothing gets created
|
||||||
// and it emits 0 bytes.
|
// and it emits 0 bytes.
|
||||||
ZRESULT SetUnzipBaseDir(HZIP hz, const TCHAR *dir);
|
ZRESULT SetUnzipBaseDir(HZIP hz, const TCHAR* dir);
|
||||||
// if unzipping to a filename, and it's a relative filename, then it will be relative to here.
|
// if unzipping to a filename, and it's a relative filename, then it will be relative to here.
|
||||||
// (defaults to current-directory).
|
// (defaults to current-directory).
|
||||||
|
|
||||||
@@ -93,7 +94,7 @@ ZRESULT SetUnzipBaseDir(HZIP hz, const TCHAR *dir);
|
|||||||
ZRESULT CloseZip(HZIP hz);
|
ZRESULT CloseZip(HZIP hz);
|
||||||
// CloseZip - the zip handle must be closed with this function.
|
// CloseZip - the zip handle must be closed with this function.
|
||||||
|
|
||||||
unsigned int FormatZipMessage(ZRESULT code, TCHAR *buf,unsigned int len);
|
unsigned int FormatZipMessage(ZRESULT code, TCHAR* buf, unsigned int len);
|
||||||
// FormatZipMessage - given an error code, formats it as a string.
|
// FormatZipMessage - given an error code, formats it as a string.
|
||||||
// It returns the length of the error message. If buf/len points
|
// It returns the length of the error message. If buf/len points
|
||||||
// to a real buffer, then it also writes as much as possible into there.
|
// to a real buffer, then it also writes as much as possible into there.
|
||||||
@@ -199,7 +200,7 @@ unsigned int FormatZipMessage(ZRESULT code, TCHAR *buf,unsigned int len);
|
|||||||
// one or the other of them based on a dynamic choice. If the header file
|
// one or the other of them based on a dynamic choice. If the header file
|
||||||
// for only one is present, then we will bind to that particular one.
|
// for only one is present, then we will bind to that particular one.
|
||||||
ZRESULT CloseZipU(HZIP hz);
|
ZRESULT CloseZipU(HZIP hz);
|
||||||
unsigned int FormatZipMessageU(ZRESULT code, TCHAR *buf,unsigned int len);
|
unsigned int FormatZipMessageU(ZRESULT code, TCHAR* buf, unsigned int len);
|
||||||
bool IsZipHandleU(HZIP hz);
|
bool IsZipHandleU(HZIP hz);
|
||||||
#ifdef _zip_H
|
#ifdef _zip_H
|
||||||
#undef CloseZip
|
#undef CloseZip
|
||||||
|
|||||||
@@ -22,156 +22,146 @@ typedef BOOL(WINAPI* SetDefaultDllDirectoriesFunction)(DWORD DirectoryFlags);
|
|||||||
// If we pre-load them with an absolute path then we are good.
|
// If we pre-load them with an absolute path then we are good.
|
||||||
void PreloadLibs()
|
void PreloadLibs()
|
||||||
{
|
{
|
||||||
wchar_t sys32Folder[MAX_PATH];
|
wchar_t sys32Folder[MAX_PATH];
|
||||||
GetSystemDirectory(sys32Folder, MAX_PATH);
|
GetSystemDirectory(sys32Folder, MAX_PATH);
|
||||||
|
|
||||||
std::wstring version = (std::wstring(sys32Folder) + L"\\version.dll");
|
std::wstring version = (std::wstring(sys32Folder) + L"\\version.dll");
|
||||||
std::wstring logoncli = (std::wstring(sys32Folder) + L"\\logoncli.dll");
|
std::wstring logoncli = (std::wstring(sys32Folder) + L"\\logoncli.dll");
|
||||||
std::wstring sspicli = (std::wstring(sys32Folder) + L"\\sspicli.dll");
|
std::wstring sspicli = (std::wstring(sys32Folder) + L"\\sspicli.dll");
|
||||||
|
|
||||||
LoadLibrary(version.c_str());
|
LoadLibrary(version.c_str());
|
||||||
LoadLibrary(logoncli.c_str());
|
LoadLibrary(logoncli.c_str());
|
||||||
LoadLibrary(sspicli.c_str());
|
LoadLibrary(sspicli.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MitigateDllHijacking()
|
void MitigateDllHijacking()
|
||||||
{
|
{
|
||||||
// Set the default DLL lookup directory to System32 for ourselves and kernel32.dll
|
// Set the default DLL lookup directory to System32 for ourselves and kernel32.dll
|
||||||
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32);
|
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||||
|
|
||||||
HMODULE hKernel32 = LoadLibrary(L"kernel32.dll");
|
HMODULE hKernel32 = LoadLibrary(L"kernel32.dll");
|
||||||
ATLASSERT(hKernel32 != NULL);
|
ATLASSERT(hKernel32 != NULL);
|
||||||
|
|
||||||
SetDefaultDllDirectoriesFunction pfn = (SetDefaultDllDirectoriesFunction)GetProcAddress(hKernel32, "SetDefaultDllDirectories");
|
SetDefaultDllDirectoriesFunction pfn = (SetDefaultDllDirectoriesFunction)GetProcAddress(hKernel32, "SetDefaultDllDirectories");
|
||||||
if (pfn) { (*pfn)(LOAD_LIBRARY_SEARCH_SYSTEM32); }
|
if (pfn) { (*pfn)(LOAD_LIBRARY_SEARCH_SYSTEM32); }
|
||||||
|
|
||||||
PreloadLibs();
|
PreloadLibs();
|
||||||
}
|
}
|
||||||
|
|
||||||
int APIENTRY wWinMain(
|
int APIENTRY wWinMain(
|
||||||
_In_ HINSTANCE hInstance,
|
_In_ HINSTANCE hInstance,
|
||||||
_In_opt_ HINSTANCE hPrevInstance,
|
_In_opt_ HINSTANCE hPrevInstance,
|
||||||
_In_ LPWSTR lpCmdLine,
|
_In_ LPWSTR lpCmdLine,
|
||||||
_In_ int nCmdShow)
|
_In_ int nCmdShow)
|
||||||
{
|
{
|
||||||
MitigateDllHijacking();
|
MitigateDllHijacking();
|
||||||
|
|
||||||
int exitCode = -1;
|
int exitCode = -1;
|
||||||
CString cmdLine(lpCmdLine);
|
CString cmdLine(lpCmdLine);
|
||||||
|
|
||||||
// see if the requested framework(s) are supported and exit.
|
// see if the requested framework(s) are supported and exit.
|
||||||
int chkFrameworkIdx = cmdLine.Find(L"--checkFramework");
|
int chkFrameworkIdx = cmdLine.Find(L"--checkFramework");
|
||||||
if (chkFrameworkIdx >= 0) {
|
if (chkFrameworkIdx >= 0)
|
||||||
return VerifyRuntimeString(std::wstring(cmdLine).substr(chkFrameworkIdx + 17));
|
return VerifyRuntimeString(std::wstring(cmdLine).substr(chkFrameworkIdx + 17));
|
||||||
}
|
|
||||||
|
|
||||||
if (cmdLine.Find(L"--checkInstall") >= 0) {
|
if (cmdLine.Find(L"--checkInstall") >= 0) {
|
||||||
// If we're already installed, exit as fast as possible
|
// If we're already installed, exit as fast as possible
|
||||||
if (!MachineInstaller::ShouldSilentInstall()) {
|
if (!MachineInstaller::ShouldSilentInstall())
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure update.exe gets silent
|
// Make sure update.exe gets silent
|
||||||
wcscat(lpCmdLine, L" --silent");
|
wcscat(lpCmdLine, L" --silent");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize COM
|
// Initialize COM
|
||||||
HRESULT hr = ::CoInitialize(NULL);
|
HRESULT hr = ::CoInitialize(NULL);
|
||||||
ATLASSERT(SUCCEEDED(hr));
|
ATLASSERT(SUCCEEDED(hr));
|
||||||
AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES);
|
AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES);
|
||||||
|
|
||||||
// Initialize ATL
|
// Initialize ATL
|
||||||
_Module = new CAppModule();
|
_Module = new CAppModule();
|
||||||
hr = _Module->Init(NULL, hInstance);
|
hr = _Module->Init(NULL, hInstance);
|
||||||
|
|
||||||
// Initialize GDI
|
// Initialize GDI
|
||||||
ULONG_PTR gdiplusToken;
|
ULONG_PTR gdiplusToken;
|
||||||
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
|
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
|
||||||
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
|
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
|
||||||
CSplashWnd splash;
|
CSplashWnd splash;
|
||||||
std::function<void()> closeSplashFn([&]() { splash.Hide(); });
|
std::function<void()> closeSplashFn([&]() { splash.Hide(); });
|
||||||
|
|
||||||
bool isQuiet = (cmdLine.Find(L"-s") >= 0);
|
bool isQuiet = (cmdLine.Find(L"-s") >= 0);
|
||||||
bool weAreUACElevated = CUpdateRunner::AreWeUACElevated() == S_OK;
|
bool weAreUACElevated = CUpdateRunner::AreWeUACElevated() == S_OK;
|
||||||
bool attemptingToRerun = (cmdLine.Find(L"--rerunningWithoutUAC") >= 0);
|
bool attemptingToRerun = (cmdLine.Find(L"--rerunningWithoutUAC") >= 0);
|
||||||
|
|
||||||
// todo: make min windows version configurable (eg, > windows 10 18362)
|
// todo: make min windows version configurable (eg, > windows 10 18362)
|
||||||
auto runtimes = GetRequiredRuntimes();
|
auto runtimes = GetRequiredRuntimes();
|
||||||
|
|
||||||
if (weAreUACElevated && attemptingToRerun) {
|
if (weAreUACElevated && attemptingToRerun) {
|
||||||
CUpdateRunner::DisplayErrorMessage(CString(L"Please re-run this installer as a normal user instead of \"Run as Administrator\"."), NULL);
|
CUpdateRunner::DisplayErrorMessage(CString(L"Please re-run this installer as a normal user instead of \"Run as Administrator\"."), NULL);
|
||||||
exitCode = E_FAIL;
|
exitCode = E_FAIL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rebootRequired = false;
|
bool rebootRequired = false;
|
||||||
|
|
||||||
// check all required runtimes against the current operating system version
|
// check all required runtimes against the current operating system version
|
||||||
for (auto rt : runtimes)
|
for (auto rt : runtimes) {
|
||||||
{
|
if (!IsRuntimeSupported(rt)) {
|
||||||
if (!IsRuntimeSupported(rt))
|
// Explain this as nicely as possible and give up.
|
||||||
{
|
MessageBox(0L, L"This program cannot run on this computer; it requires a later version of Windows.", L"Incompatible Operating System", 0);
|
||||||
// Explain this as nicely as possible and give up.
|
exitCode = E_FAIL;
|
||||||
MessageBox(0L, L"This program cannot run on this computer; it requires a later version of Windows.", L"Incompatible Operating System", 0);
|
goto out;
|
||||||
exitCode = E_FAIL;
|
}
|
||||||
goto out;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// install any missing runtimes
|
// install any missing runtimes
|
||||||
for (auto rt : runtimes)
|
for (auto rt : runtimes) {
|
||||||
{
|
if (!IsRuntimeInstalled(rt)) {
|
||||||
if (!IsRuntimeInstalled(rt))
|
HRESULT hr = CFxHelper::InstallDotnet(rt, isQuiet);
|
||||||
{
|
if (hr == ERROR_SUCCESS_REBOOT_REQUIRED) {
|
||||||
HRESULT hr = CFxHelper::InstallDotnet(rt, isQuiet);
|
// we will reboot after installing all required runtimes.
|
||||||
if (hr == ERROR_SUCCESS_REBOOT_REQUIRED)
|
rebootRequired = true;
|
||||||
{
|
}
|
||||||
// we will reboot after installing all required runtimes.
|
else if (FAILED(hr)) {
|
||||||
rebootRequired = true;
|
exitCode = hr; // #yolo
|
||||||
}
|
CUpdateRunner::DisplayErrorMessage(CString(L"Failed to install .NET, you can try installing it manually."), NULL);
|
||||||
else if (FAILED(hr))
|
goto out;
|
||||||
{
|
}
|
||||||
exitCode = hr; // #yolo
|
// S_FALSE isn't failure, but we still shouldn't try to install
|
||||||
CUpdateRunner::DisplayErrorMessage(CString(L"Failed to install .NET, you can try installing it manually."), NULL);
|
else if (hr != S_OK) {
|
||||||
goto out;
|
exitCode = 0;
|
||||||
}
|
goto out;
|
||||||
// S_FALSE isn't failure, but we still shouldn't try to install
|
}
|
||||||
else if (hr != S_OK)
|
}
|
||||||
{
|
}
|
||||||
exitCode = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if any runtimes indicated a reboot is required, let's do that now
|
// if any runtimes indicated a reboot is required, let's do that now
|
||||||
if (rebootRequired)
|
if (rebootRequired) {
|
||||||
{
|
exitCode = CFxHelper::HandleRebootRequirement(isQuiet);
|
||||||
exitCode = CFxHelper::HandleRebootRequirement(isQuiet);
|
goto out;
|
||||||
goto out;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// If we're UAC-elevated, we shouldn't be because it will give us permissions
|
// If we're UAC-elevated, we shouldn't be because it will give us permissions
|
||||||
// problems later. Just silently rerun ourselves.
|
// problems later. Just silently rerun ourselves.
|
||||||
if (weAreUACElevated) {
|
if (weAreUACElevated) {
|
||||||
wchar_t buf[4096];
|
wchar_t buf[4096];
|
||||||
HMODULE hMod = GetModuleHandle(NULL);
|
HMODULE hMod = GetModuleHandle(NULL);
|
||||||
GetModuleFileNameW(hMod, buf, 4096);
|
GetModuleFileNameW(hMod, buf, 4096);
|
||||||
wcscat(lpCmdLine, L" --rerunningWithoutUAC");
|
wcscat(lpCmdLine, L" --rerunningWithoutUAC");
|
||||||
|
|
||||||
CUpdateRunner::ShellExecuteFromExplorer(buf, lpCmdLine);
|
CUpdateRunner::ShellExecuteFromExplorer(buf, lpCmdLine);
|
||||||
exitCode = 0;
|
exitCode = 0;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
splash.SetImage(MAKEINTRESOURCE(IDR_SPLASH_IMG), L"DATA");
|
splash.SetImage(MAKEINTRESOURCE(IDR_SPLASH_IMG), L"DATA");
|
||||||
splash.Show();
|
splash.Show();
|
||||||
|
|
||||||
// run updater
|
// run updater
|
||||||
exitCode = CUpdateRunner::ExtractUpdaterAndRun(lpCmdLine, false, closeSplashFn);
|
exitCode = CUpdateRunner::ExtractUpdaterAndRun(lpCmdLine, false, closeSplashFn);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
splash.Hide();
|
splash.Hide();
|
||||||
_Module->Term();
|
_Module->Term();
|
||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,82 +31,91 @@ SOFTWARE.
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace version {
|
namespace version
|
||||||
|
{
|
||||||
|
|
||||||
namespace {
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
// Compare normal version identifiers.
|
// Compare normal version identifiers.
|
||||||
int compare_normal(const Version_data& l, const Version_data& r) {
|
int compare_normal(const Version_data& l, const Version_data& r)
|
||||||
if (l.major > r.major) return 1;
|
{
|
||||||
if (l.major < r.major) return -1;
|
if (l.major > r.major) return 1;
|
||||||
if (l.minor > r.minor) return 1;
|
if (l.major < r.major) return -1;
|
||||||
if (l.minor < r.minor) return -1;
|
if (l.minor > r.minor) return 1;
|
||||||
if (l.patch > r.patch) return 1;
|
if (l.minor < r.minor) return -1;
|
||||||
if (l.patch < r.patch) return -1;
|
if (l.patch > r.patch) return 1;
|
||||||
return 0;
|
if (l.patch < r.patch) return -1;
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Compare alphanumeric prerelease identifiers.
|
// Compare alphanumeric prerelease identifiers.
|
||||||
inline int cmp_alnum_prerel_ids(const string& l, const string& r) {
|
inline int cmp_alnum_prerel_ids(const string& l, const string& r)
|
||||||
auto cmp = l.compare(r);
|
{
|
||||||
if (cmp == 0) {
|
auto cmp = l.compare(r);
|
||||||
return cmp;
|
if (cmp == 0) {
|
||||||
} else {
|
return cmp;
|
||||||
return cmp > 0 ? 1 : -1;
|
}
|
||||||
}
|
else {
|
||||||
}
|
return cmp > 0 ? 1 : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Compare numeric prerelease identifiers.
|
// Compare numeric prerelease identifiers.
|
||||||
inline int cmp_num_prerel_ids(const string& l, const string& r) {
|
inline int cmp_num_prerel_ids(const string& l, const string& r)
|
||||||
long long li = stoll(l);
|
{
|
||||||
long long ri = stoll(r);
|
long long li = stoll(l);
|
||||||
if (li == ri) return 0;
|
long long ri = stoll(r);
|
||||||
return li > ri ? 1 : -1;
|
if (li == ri) return 0;
|
||||||
}
|
return li > ri ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
using Prerel_type_pair = pair<Id_type, Id_type>;
|
using Prerel_type_pair = pair<Id_type, Id_type>;
|
||||||
using Prerel_id_comparator = function<int(const string&, const string&)>;
|
using Prerel_id_comparator = function<int(const string&, const string&)>;
|
||||||
const map<Prerel_type_pair, Prerel_id_comparator> comparators = {
|
const map<Prerel_type_pair, Prerel_id_comparator> comparators = {
|
||||||
{ { Id_type::alnum, Id_type::alnum }, cmp_alnum_prerel_ids },
|
{ { Id_type::alnum, Id_type::alnum }, cmp_alnum_prerel_ids },
|
||||||
{ { Id_type::alnum, Id_type::num }, [](const string&, const string&) {return 1;} },
|
{ { Id_type::alnum, Id_type::num }, [](const string&, const string&) {return 1;} },
|
||||||
{ { Id_type::num, Id_type::alnum }, [](const string&, const string&) {return -1;} },
|
{ { Id_type::num, Id_type::alnum }, [](const string&, const string&) {return -1;} },
|
||||||
{ { Id_type::num, Id_type::num }, cmp_num_prerel_ids }
|
{ { Id_type::num, Id_type::num }, cmp_num_prerel_ids }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compare prerelease identifiers based on their types.
|
// Compare prerelease identifiers based on their types.
|
||||||
inline int compare_prerel_identifiers(const Prerelease_identifier& l, const Prerelease_identifier& r) {
|
inline int compare_prerel_identifiers(const Prerelease_identifier& l, const Prerelease_identifier& r)
|
||||||
auto cmp = comparators.at({ l.second, r.second });
|
{
|
||||||
return cmp(l.first, r.first);
|
auto cmp = comparators.at({ l.second, r.second });
|
||||||
}
|
return cmp(l.first, r.first);
|
||||||
|
}
|
||||||
|
|
||||||
inline int cmp_rel_prerel(const Prerelease_identifiers& l, const Prerelease_identifiers& r) {
|
inline int cmp_rel_prerel(const Prerelease_identifiers& l, const Prerelease_identifiers& r)
|
||||||
if (l.empty() && !r.empty()) return 1;
|
{
|
||||||
if (r.empty() && !l.empty()) return -1;
|
if (l.empty() && !r.empty()) return 1;
|
||||||
return 0;
|
if (r.empty() && !l.empty()) return -1;
|
||||||
}
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int Semver200_comparator::compare(const Version_data& l, const Version_data& r) const {
|
int Semver200_comparator::compare(const Version_data& l, const Version_data& r) const
|
||||||
// Compare normal version components.
|
{
|
||||||
int cmp = compare_normal(l, r);
|
// Compare normal version components.
|
||||||
if (cmp != 0) return cmp;
|
int cmp = compare_normal(l, r);
|
||||||
|
if (cmp != 0) return cmp;
|
||||||
|
|
||||||
// Compare if one version is release and the other prerelease - release is always higher.
|
// Compare if one version is release and the other prerelease - release is always higher.
|
||||||
cmp = cmp_rel_prerel(l.prerelease_ids, r.prerelease_ids);
|
cmp = cmp_rel_prerel(l.prerelease_ids, r.prerelease_ids);
|
||||||
if (cmp != 0) return cmp;
|
if (cmp != 0) return cmp;
|
||||||
|
|
||||||
// Compare prerelease by looking at each identifier: numeric ones are compared as numbers,
|
// Compare prerelease by looking at each identifier: numeric ones are compared as numbers,
|
||||||
// alphanum as ASCII strings.
|
// alphanum as ASCII strings.
|
||||||
auto shorter = min(l.prerelease_ids.size(), r.prerelease_ids.size());
|
auto shorter = min(l.prerelease_ids.size(), r.prerelease_ids.size());
|
||||||
for (size_t i = 0; i < shorter; i++) {
|
for (size_t i = 0; i < shorter; i++) {
|
||||||
cmp = compare_prerel_identifiers(l.prerelease_ids[i], r.prerelease_ids[i]);
|
cmp = compare_prerel_identifiers(l.prerelease_ids[i], r.prerelease_ids[i]);
|
||||||
if (cmp != 0) return cmp;
|
if (cmp != 0) return cmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prerelease identifiers are the same, to the length of the shorter version string;
|
// Prerelease identifiers are the same, to the length of the shorter version string;
|
||||||
// if they are the same length, then versions are equal, otherwise, longer one wins.
|
// if they are the same length, then versions are equal, otherwise, longer one wins.
|
||||||
if (l.prerelease_ids.size() == r.prerelease_ids.size()) return 0;
|
if (l.prerelease_ids.size() == r.prerelease_ids.size()) return 0;
|
||||||
return l.prerelease_ids.size() > r.prerelease_ids.size() ? 1 : -1;
|
return l.prerelease_ids.size() > r.prerelease_ids.size() ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,176 +35,190 @@ SOFTWARE.
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace version {
|
namespace version
|
||||||
|
{
|
||||||
|
|
||||||
namespace {
|
namespace
|
||||||
enum class Parser_state {
|
{
|
||||||
major, minor, patch, prerelease, build
|
enum class Parser_state
|
||||||
};
|
{
|
||||||
|
major, minor, patch, prerelease, build
|
||||||
|
};
|
||||||
|
|
||||||
using Validator = function<void(const string&, const char)>;
|
using Validator = function<void(const string&, const char)>;
|
||||||
using State_transition_hook = function<void(string&)>;
|
using State_transition_hook = function<void(string&)>;
|
||||||
/// State transition is described by a character that triggers it, a state to transition to and
|
/// State transition is described by a character that triggers it, a state to transition to and
|
||||||
/// optional hook to be invoked on transition.
|
/// optional hook to be invoked on transition.
|
||||||
using Transition = tuple<const char, Parser_state, State_transition_hook>;
|
using Transition = tuple<const char, Parser_state, State_transition_hook>;
|
||||||
using Transitions = vector<Transition>;
|
using Transitions = vector<Transition>;
|
||||||
using State = tuple<Transitions, string&, Validator>;
|
using State = tuple<Transitions, string&, Validator>;
|
||||||
using State_machine = map<Parser_state, State>;
|
using State_machine = map<Parser_state, State>;
|
||||||
|
|
||||||
// Ranges of characters allowed in prerelease and build identifiers.
|
// Ranges of characters allowed in prerelease and build identifiers.
|
||||||
const vector<pair<char, char>> allowed_prerel_id_chars = {
|
const vector<pair<char, char>> allowed_prerel_id_chars = {
|
||||||
{ '0', '9' },{ 'A','Z' },{ 'a','z' },{ '-','-' }
|
{ '0', '9' },{ 'A','Z' },{ 'a','z' },{ '-','-' }
|
||||||
};
|
};
|
||||||
|
|
||||||
inline Transition mkx(const char c, Parser_state p, State_transition_hook pth) {
|
inline Transition mkx(const char c, Parser_state p, State_transition_hook pth)
|
||||||
return make_tuple(c, p, pth);
|
{
|
||||||
}
|
return make_tuple(c, p, pth);
|
||||||
|
}
|
||||||
|
|
||||||
/// Advance parser state machine by a single step.
|
/// Advance parser state machine by a single step.
|
||||||
/**
|
/**
|
||||||
Perform single step of parser state machine: if character matches one from transition tables -
|
Perform single step of parser state machine: if character matches one from transition tables -
|
||||||
trigger transition to next state; otherwise, validate if current token is in legal state
|
trigger transition to next state; otherwise, validate if current token is in legal state
|
||||||
(throw Parse_error if not) and then add character to current token; State transition includes
|
(throw Parse_error if not) and then add character to current token; State transition includes
|
||||||
preparing various vars for next state and invoking state transition hook (if specified) which is
|
preparing various vars for next state and invoking state transition hook (if specified) which is
|
||||||
where whole tokens are validated.
|
where whole tokens are validated.
|
||||||
*/
|
*/
|
||||||
inline void process_char(const char c, Parser_state& cstate, Parser_state& pstate,
|
inline void process_char(const char c, Parser_state& cstate, Parser_state& pstate,
|
||||||
const Transitions& transitions, string& target, Validator validate) {
|
const Transitions& transitions, string& target, Validator validate)
|
||||||
for (const auto& transition : transitions) {
|
{
|
||||||
if (c == get<0>(transition)) {
|
for (const auto& transition : transitions) {
|
||||||
if (get<2>(transition)) get<2>(transition)(target);
|
if (c == get<0>(transition)) {
|
||||||
pstate = cstate;
|
if (get<2>(transition)) get<2>(transition)(target);
|
||||||
cstate = get<1>(transition);
|
pstate = cstate;
|
||||||
return;
|
cstate = get<1>(transition);
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
validate(target, c);
|
}
|
||||||
target.push_back(c);
|
validate(target, c);
|
||||||
}
|
target.push_back(c);
|
||||||
|
}
|
||||||
|
|
||||||
/// Validate normal (major, minor, patch) version components.
|
/// Validate normal (major, minor, patch) version components.
|
||||||
inline void normal_version_validator(const string& tgt, const char c) {
|
inline void normal_version_validator(const string& tgt, const char c)
|
||||||
if (c < '0' || c > '9') throw Parse_error("invalid character encountered: " + string(1, c));
|
{
|
||||||
if (tgt.compare(0, 1, "0") == 0) throw Parse_error("leading 0 not allowed");
|
if (c < '0' || c > '9') throw Parse_error("invalid character encountered: " + string(1, c));
|
||||||
}
|
if (tgt.compare(0, 1, "0") == 0) throw Parse_error("leading 0 not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
/// Validate that prerelease and build version identifiers are comprised of allowed chars only.
|
/// Validate that prerelease and build version identifiers are comprised of allowed chars only.
|
||||||
inline void prerelease_version_validator(const string&, const char c) {
|
inline void prerelease_version_validator(const string&, const char c)
|
||||||
bool res = false;
|
{
|
||||||
for (const auto& r : allowed_prerel_id_chars) {
|
bool res = false;
|
||||||
res |= (c >= r.first && c <= r.second);
|
for (const auto& r : allowed_prerel_id_chars) {
|
||||||
}
|
res |= (c >= r.first && c <= r.second);
|
||||||
if (!res)
|
}
|
||||||
throw Parse_error("invalid character encountered: " + string(1, c));
|
if (!res)
|
||||||
}
|
throw Parse_error("invalid character encountered: " + string(1, c));
|
||||||
|
}
|
||||||
|
|
||||||
inline bool is_identifier_numeric(const string& id) {
|
inline bool is_identifier_numeric(const string& id)
|
||||||
return id.find_first_not_of("0123456789") == string::npos;
|
{
|
||||||
}
|
return id.find_first_not_of("0123456789") == string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool check_for_leading_0(const string& str) {
|
inline bool check_for_leading_0(const string& str)
|
||||||
return str.length() > 1 && str[0] == '0';
|
{
|
||||||
}
|
return str.length() > 1 && str[0] == '0';
|
||||||
|
}
|
||||||
|
|
||||||
/// Validate every individual prerelease identifier, determine it's type and add it to collection.
|
/// Validate every individual prerelease identifier, determine it's type and add it to collection.
|
||||||
void prerelease_hook_impl(string& id, Prerelease_identifiers& prerelease) {
|
void prerelease_hook_impl(string& id, Prerelease_identifiers& prerelease)
|
||||||
if (id.empty()) throw Parse_error("version identifier cannot be empty");
|
{
|
||||||
Id_type t = Id_type::alnum;
|
if (id.empty()) throw Parse_error("version identifier cannot be empty");
|
||||||
if (is_identifier_numeric(id)) {
|
Id_type t = Id_type::alnum;
|
||||||
t = Id_type::num;
|
if (is_identifier_numeric(id)) {
|
||||||
if (check_for_leading_0(id)) {
|
t = Id_type::num;
|
||||||
throw Parse_error("numeric identifiers cannot have leading 0");
|
if (check_for_leading_0(id)) {
|
||||||
}
|
throw Parse_error("numeric identifiers cannot have leading 0");
|
||||||
}
|
}
|
||||||
prerelease.push_back(Prerelease_identifier(id, t));
|
}
|
||||||
id.clear();
|
prerelease.push_back(Prerelease_identifier(id, t));
|
||||||
}
|
id.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/// Validate every individual build identifier and add it to collection.
|
/// Validate every individual build identifier and add it to collection.
|
||||||
void build_hook_impl(string& id, Parser_state& pstate, Build_identifiers& build,
|
void build_hook_impl(string& id, Parser_state& pstate, Build_identifiers& build,
|
||||||
std::string& prerelease_id, Prerelease_identifiers& prerelease) {
|
std::string& prerelease_id, Prerelease_identifiers& prerelease)
|
||||||
// process last token left from parsing prerelease data
|
{
|
||||||
if (pstate == Parser_state::prerelease) prerelease_hook_impl(prerelease_id, prerelease);
|
// process last token left from parsing prerelease data
|
||||||
if (id.empty()) throw Parse_error("version identifier cannot be empty");
|
if (pstate == Parser_state::prerelease) prerelease_hook_impl(prerelease_id, prerelease);
|
||||||
build.push_back(id);
|
if (id.empty()) throw Parse_error("version identifier cannot be empty");
|
||||||
id.clear();
|
build.push_back(id);
|
||||||
}
|
id.clear();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse semver 2.0.0-compatible string to Version_data structure.
|
/// Parse semver 2.0.0-compatible string to Version_data structure.
|
||||||
/**
|
/**
|
||||||
Version text parser is implemented as a state machine. In each step one successive character from version
|
Version text parser is implemented as a state machine. In each step one successive character from version
|
||||||
string is consumed and is either added to current token or triggers state transition. Hooks can be
|
string is consumed and is either added to current token or triggers state transition. Hooks can be
|
||||||
injected into state transitions for validation/customization purposes.
|
injected into state transitions for validation/customization purposes.
|
||||||
*/
|
*/
|
||||||
Version_data Semver200_parser::parse(const string& s) const {
|
Version_data Semver200_parser::parse(const string& s) const
|
||||||
string major;
|
{
|
||||||
string minor;
|
string major;
|
||||||
string patch;
|
string minor;
|
||||||
string prerelease_id;
|
string patch;
|
||||||
string build_id;
|
string prerelease_id;
|
||||||
Prerelease_identifiers prerelease;
|
string build_id;
|
||||||
Build_identifiers build;
|
Prerelease_identifiers prerelease;
|
||||||
Parser_state cstate{ Parser_state::major };
|
Build_identifiers build;
|
||||||
Parser_state pstate;
|
Parser_state cstate{ Parser_state::major };
|
||||||
|
Parser_state pstate;
|
||||||
|
|
||||||
auto prerelease_hook = [&](string& id) {
|
auto prerelease_hook = [&](string& id) {
|
||||||
prerelease_hook_impl(id, prerelease);
|
prerelease_hook_impl(id, prerelease);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto build_hook = [&](string& id) {
|
auto build_hook = [&](string& id) {
|
||||||
build_hook_impl(id, pstate, build, prerelease_id, prerelease);
|
build_hook_impl(id, pstate, build, prerelease_id, prerelease);
|
||||||
};
|
};
|
||||||
|
|
||||||
// State transition tables
|
// State transition tables
|
||||||
auto major_trans = {
|
auto major_trans = {
|
||||||
mkx('.', Parser_state::minor, {})
|
mkx('.', Parser_state::minor, {})
|
||||||
};
|
};
|
||||||
auto minor_trans = {
|
auto minor_trans = {
|
||||||
mkx('.', Parser_state::patch, {})
|
mkx('.', Parser_state::patch, {})
|
||||||
};
|
};
|
||||||
auto patch_trans = {
|
auto patch_trans = {
|
||||||
mkx('-', Parser_state::prerelease, {}),
|
mkx('-', Parser_state::prerelease, {}),
|
||||||
mkx('+', Parser_state::build, {})
|
mkx('+', Parser_state::build, {})
|
||||||
};
|
};
|
||||||
auto prerelease_trans = {
|
auto prerelease_trans = {
|
||||||
// When identifier separator (.) is found, stay in the same state but invoke hook
|
// When identifier separator (.) is found, stay in the same state but invoke hook
|
||||||
// in order to process each individual identifier separately.
|
// in order to process each individual identifier separately.
|
||||||
mkx('.', Parser_state::prerelease, prerelease_hook),
|
mkx('.', Parser_state::prerelease, prerelease_hook),
|
||||||
mkx('+', Parser_state::build, {})
|
mkx('+', Parser_state::build, {})
|
||||||
};
|
};
|
||||||
auto build_trans = {
|
auto build_trans = {
|
||||||
// Same stay-in-the-same-state-but-invoke-hook trick from above.
|
// Same stay-in-the-same-state-but-invoke-hook trick from above.
|
||||||
mkx('.', Parser_state::build, build_hook)
|
mkx('.', Parser_state::build, build_hook)
|
||||||
};
|
};
|
||||||
|
|
||||||
State_machine state_machine = {
|
State_machine state_machine = {
|
||||||
{Parser_state::major, State{major_trans, major, normal_version_validator}},
|
{Parser_state::major, State{major_trans, major, normal_version_validator}},
|
||||||
{Parser_state::minor, State{minor_trans, minor, normal_version_validator}},
|
{Parser_state::minor, State{minor_trans, minor, normal_version_validator}},
|
||||||
{Parser_state::patch, State{patch_trans, patch, normal_version_validator}},
|
{Parser_state::patch, State{patch_trans, patch, normal_version_validator}},
|
||||||
{Parser_state::prerelease, State{prerelease_trans, prerelease_id, prerelease_version_validator}},
|
{Parser_state::prerelease, State{prerelease_trans, prerelease_id, prerelease_version_validator}},
|
||||||
{Parser_state::build, State{build_trans, build_id, prerelease_version_validator}}
|
{Parser_state::build, State{build_trans, build_id, prerelease_version_validator}}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Main loop.
|
// Main loop.
|
||||||
for (const auto& c : s) {
|
for (const auto& c : s) {
|
||||||
auto state = state_machine.at(cstate);
|
auto state = state_machine.at(cstate);
|
||||||
process_char(c, cstate, pstate, get<0>(state), get<1>(state), get<2>(state));
|
process_char(c, cstate, pstate, get<0>(state), get<1>(state), get<2>(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger appropriate hooks in order to process last token, because no state transition was
|
// Trigger appropriate hooks in order to process last token, because no state transition was
|
||||||
// triggered for it.
|
// triggered for it.
|
||||||
if (cstate == Parser_state::prerelease) {
|
if (cstate == Parser_state::prerelease) {
|
||||||
prerelease_hook(prerelease_id);
|
prerelease_hook(prerelease_id);
|
||||||
} else if (cstate == Parser_state::build) {
|
}
|
||||||
build_hook(build_id);
|
else if (cstate == Parser_state::build) {
|
||||||
}
|
build_hook(build_id);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Version_data{ stoi(major), stoi(minor), stoi(patch), prerelease, build };
|
return Version_data{ stoi(major), stoi(minor), stoi(patch), prerelease, build };
|
||||||
} catch (invalid_argument& ex) {
|
}
|
||||||
throw Parse_error(ex.what());
|
catch (invalid_argument& ex) {
|
||||||
}
|
throw Parse_error(ex.what());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,81 +8,81 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
wchar_t* FindRootAppDir()
|
wchar_t* FindRootAppDir()
|
||||||
{
|
{
|
||||||
wchar_t* ourDirectory = new wchar_t[MAX_PATH];
|
wchar_t* ourDirectory = new wchar_t[MAX_PATH];
|
||||||
|
|
||||||
GetModuleFileName(GetModuleHandle(NULL), ourDirectory, MAX_PATH);
|
GetModuleFileName(GetModuleHandle(NULL), ourDirectory, MAX_PATH);
|
||||||
wchar_t* lastSlash = wcsrchr(ourDirectory, L'\\');
|
wchar_t* lastSlash = wcsrchr(ourDirectory, L'\\');
|
||||||
if (!lastSlash) {
|
if (!lastSlash) {
|
||||||
delete[] ourDirectory;
|
delete[] ourDirectory;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Null-terminate the string at the slash so now it's a directory
|
// Null-terminate the string at the slash so now it's a directory
|
||||||
*lastSlash = 0x0;
|
*lastSlash = 0x0;
|
||||||
return ourDirectory;
|
return ourDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
wchar_t* FindOwnExecutableName()
|
wchar_t* FindOwnExecutableName()
|
||||||
{
|
{
|
||||||
wchar_t* ourDirectory = new wchar_t[MAX_PATH];
|
wchar_t* ourDirectory = new wchar_t[MAX_PATH];
|
||||||
|
|
||||||
GetModuleFileName(GetModuleHandle(NULL), ourDirectory, MAX_PATH);
|
GetModuleFileName(GetModuleHandle(NULL), ourDirectory, MAX_PATH);
|
||||||
wchar_t* lastSlash = wcsrchr(ourDirectory, L'\\');
|
wchar_t* lastSlash = wcsrchr(ourDirectory, L'\\');
|
||||||
if (!lastSlash) {
|
if (!lastSlash) {
|
||||||
delete[] ourDirectory;
|
delete[] ourDirectory;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
wchar_t* ret = _wcsdup(lastSlash + 1);
|
wchar_t* ret = _wcsdup(lastSlash + 1);
|
||||||
delete[] ourDirectory;
|
delete[] ourDirectory;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring FindLatestAppDir()
|
std::wstring FindLatestAppDir()
|
||||||
{
|
{
|
||||||
std::wstring ourDir;
|
std::wstring ourDir;
|
||||||
ourDir.assign(FindRootAppDir());
|
ourDir.assign(FindRootAppDir());
|
||||||
|
|
||||||
ourDir += L"\\app-*";
|
ourDir += L"\\app-*";
|
||||||
|
|
||||||
WIN32_FIND_DATA fileInfo = { 0 };
|
WIN32_FIND_DATA fileInfo = { 0 };
|
||||||
HANDLE hFile = FindFirstFile(ourDir.c_str(), &fileInfo);
|
HANDLE hFile = FindFirstFile(ourDir.c_str(), &fileInfo);
|
||||||
if (hFile == INVALID_HANDLE_VALUE) {
|
if (hFile == INVALID_HANDLE_VALUE) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
version::Semver200_version acc("0.0.0");
|
version::Semver200_version acc("0.0.0");
|
||||||
std::wstring acc_s;
|
std::wstring acc_s;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
std::wstring appVer = fileInfo.cFileName;
|
std::wstring appVer = fileInfo.cFileName;
|
||||||
appVer = appVer.substr(4); // Skip 'app-'
|
appVer = appVer.substr(4); // Skip 'app-'
|
||||||
if (!(fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
if (!(fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string s(appVer.begin(), appVer.end());
|
std::string s(appVer.begin(), appVer.end());
|
||||||
|
|
||||||
version::Semver200_version thisVer(s);
|
version::Semver200_version thisVer(s);
|
||||||
|
|
||||||
if (thisVer > acc) {
|
if (thisVer > acc) {
|
||||||
acc = thisVer;
|
acc = thisVer;
|
||||||
acc_s = appVer;
|
acc_s = appVer;
|
||||||
}
|
}
|
||||||
} while (FindNextFile(hFile, &fileInfo));
|
} while (FindNextFile(hFile, &fileInfo));
|
||||||
|
|
||||||
if (acc == version::Semver200_version("0.0.0")) {
|
if (acc == version::Semver200_version("0.0.0")) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ourDir.assign(FindRootAppDir());
|
ourDir.assign(FindRootAppDir());
|
||||||
std::wstringstream ret;
|
std::wstringstream ret;
|
||||||
ret << ourDir << L"\\app-" << acc_s;
|
ret << ourDir << L"\\app-" << acc_s;
|
||||||
|
|
||||||
FindClose(hFile);
|
FindClose(hFile);
|
||||||
return ret.str();
|
return ret.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
|
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
|
||||||
@@ -90,31 +90,31 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
|
|||||||
_In_ LPWSTR lpCmdLine,
|
_In_ LPWSTR lpCmdLine,
|
||||||
_In_ int nCmdShow)
|
_In_ int nCmdShow)
|
||||||
{
|
{
|
||||||
std::wstring appName;
|
std::wstring appName;
|
||||||
appName.assign(FindOwnExecutableName());
|
appName.assign(FindOwnExecutableName());
|
||||||
|
|
||||||
std::wstring workingDir(FindLatestAppDir());
|
std::wstring workingDir(FindLatestAppDir());
|
||||||
std::wstring fullPath(workingDir + L"\\" + appName);
|
std::wstring fullPath(workingDir + L"\\" + appName);
|
||||||
|
|
||||||
STARTUPINFO si = { 0 };
|
STARTUPINFO si = { 0 };
|
||||||
PROCESS_INFORMATION pi = { 0 };
|
PROCESS_INFORMATION pi = { 0 };
|
||||||
|
|
||||||
si.cb = sizeof(si);
|
si.cb = sizeof(si);
|
||||||
si.dwFlags = STARTF_USESHOWWINDOW;
|
si.dwFlags = STARTF_USESHOWWINDOW;
|
||||||
si.wShowWindow = nCmdShow;
|
si.wShowWindow = nCmdShow;
|
||||||
|
|
||||||
std::wstring cmdLine(L"\"");
|
std::wstring cmdLine(L"\"");
|
||||||
cmdLine += fullPath;
|
cmdLine += fullPath;
|
||||||
cmdLine += L"\" ";
|
cmdLine += L"\" ";
|
||||||
cmdLine += lpCmdLine;
|
cmdLine += lpCmdLine;
|
||||||
|
|
||||||
wchar_t* lpCommandLine = _wcsdup(cmdLine.c_str());
|
wchar_t* lpCommandLine = _wcsdup(cmdLine.c_str());
|
||||||
wchar_t* lpCurrentDirectory = _wcsdup(workingDir.c_str());
|
wchar_t* lpCurrentDirectory = _wcsdup(workingDir.c_str());
|
||||||
if (!CreateProcess(NULL, lpCommandLine, NULL, NULL, true, 0, NULL, lpCurrentDirectory, &si, &pi)) {
|
if (!CreateProcess(NULL, lpCommandLine, NULL, NULL, true, 0, NULL, lpCurrentDirectory, &si, &pi)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
AllowSetForegroundWindow(pi.dwProcessId);
|
AllowSetForegroundWindow(pi.dwProcessId);
|
||||||
WaitForInputIdle(pi.hProcess, 5 * 1000);
|
WaitForInputIdle(pi.hProcess, 5 * 1000);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,26 +26,32 @@ SOFTWARE.
|
|||||||
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
namespace version {
|
namespace version
|
||||||
|
{
|
||||||
|
|
||||||
/// Parse string into Version_data structure according to semantic versioning 2.0.0 rules.
|
/// Parse string into Version_data structure according to semantic versioning 2.0.0 rules.
|
||||||
struct Semver200_parser {
|
struct Semver200_parser
|
||||||
Version_data parse(const std::string&) const;
|
{
|
||||||
};
|
Version_data parse(const std::string&) const;
|
||||||
|
};
|
||||||
|
|
||||||
/// Compare Version_data to another using semantic versioning 2.0.0 rules.
|
/// Compare Version_data to another using semantic versioning 2.0.0 rules.
|
||||||
struct Semver200_comparator {
|
struct Semver200_comparator
|
||||||
int compare(const Version_data&, const Version_data&) const;
|
{
|
||||||
};
|
int compare(const Version_data&, const Version_data&) const;
|
||||||
|
};
|
||||||
|
|
||||||
/// Concrete version class that binds all semver 2.0.0 functionality together.
|
/// Concrete version class that binds all semver 2.0.0 functionality together.
|
||||||
class Semver200_version : public Basic_version<Semver200_parser, Semver200_comparator> {
|
class Semver200_version : public Basic_version<Semver200_parser, Semver200_comparator>
|
||||||
public:
|
{
|
||||||
Semver200_version()
|
public:
|
||||||
: Basic_version{ Semver200_parser(), Semver200_comparator() } {}
|
Semver200_version()
|
||||||
|
: Basic_version{ Semver200_parser(), Semver200_comparator() }
|
||||||
|
{}
|
||||||
|
|
||||||
Semver200_version(const std::string& v)
|
Semver200_version(const std::string& v)
|
||||||
: Basic_version{ v, Semver200_parser(), Semver200_comparator() } {}
|
: Basic_version{ v, Semver200_parser(), Semver200_comparator() }
|
||||||
};
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -28,129 +28,134 @@ SOFTWARE.
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace version {
|
namespace version
|
||||||
|
{
|
||||||
|
|
||||||
/// Any error in parsing or validation of version string will result in Parse_error exception being thrown.
|
/// Any error in parsing or validation of version string will result in Parse_error exception being thrown.
|
||||||
class Parse_error : public std::runtime_error {
|
class Parse_error : public std::runtime_error
|
||||||
using std::runtime_error::runtime_error;
|
{
|
||||||
};
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
/// Type of prerelease identifier: alphanumeric or numeric.
|
/// Type of prerelease identifier: alphanumeric or numeric.
|
||||||
/**
|
/**
|
||||||
Type of identifier affects comparison: alphanumeric identifiers are compared as ASCII strings, while
|
Type of identifier affects comparison: alphanumeric identifiers are compared as ASCII strings, while
|
||||||
numeric identifiers are compared as numbers.
|
numeric identifiers are compared as numbers.
|
||||||
*/
|
*/
|
||||||
enum class Id_type {
|
enum class Id_type
|
||||||
alnum, ///< Identifier is alphanumerical
|
{
|
||||||
num ///< Identifier is numeric
|
alnum, ///< Identifier is alphanumerical
|
||||||
};
|
num ///< Identifier is numeric
|
||||||
|
};
|
||||||
|
|
||||||
/// Container for prerelease identifier value and it's type.
|
/// Container for prerelease identifier value and it's type.
|
||||||
/**
|
/**
|
||||||
Prerelease version string consist of an optional series of dot-separated identifiers.
|
Prerelease version string consist of an optional series of dot-separated identifiers.
|
||||||
These identifiers can be either numerical or alphanumerical.
|
These identifiers can be either numerical or alphanumerical.
|
||||||
This structure describes one such identifier.
|
This structure describes one such identifier.
|
||||||
*/
|
*/
|
||||||
using Prerelease_identifier = std::pair<std::string, Id_type>;
|
using Prerelease_identifier = std::pair<std::string, Id_type>;
|
||||||
|
|
||||||
/// Container for all prerelease identifiers for a given version string.
|
/// Container for all prerelease identifiers for a given version string.
|
||||||
using Prerelease_identifiers = std::vector<Prerelease_identifier>;
|
using Prerelease_identifiers = std::vector<Prerelease_identifier>;
|
||||||
|
|
||||||
/// Build identifier is arbitrary string with no special meaning with regards to version precedence.
|
/// Build identifier is arbitrary string with no special meaning with regards to version precedence.
|
||||||
using Build_identifier = std::string;
|
using Build_identifier = std::string;
|
||||||
|
|
||||||
/// Container for all build identifiers of a given version string.
|
/// Container for all build identifiers of a given version string.
|
||||||
using Build_identifiers = std::vector<Build_identifier>;
|
using Build_identifiers = std::vector<Build_identifier>;
|
||||||
|
|
||||||
/// Description of version broken into parts, as per semantic versioning specification.
|
/// Description of version broken into parts, as per semantic versioning specification.
|
||||||
struct Version_data {
|
struct Version_data
|
||||||
int major; ///< Major version, change only on incompatible API modifications.
|
{
|
||||||
int minor; ///< Minor version, change on backwards-compatible API modifications.
|
int major; ///< Major version, change only on incompatible API modifications.
|
||||||
int patch; ///< Patch version, change only on bugfixes.
|
int minor; ///< Minor version, change on backwards-compatible API modifications.
|
||||||
|
int patch; ///< Patch version, change only on bugfixes.
|
||||||
|
|
||||||
/// Optional series of prerelease identifiers.
|
/// Optional series of prerelease identifiers.
|
||||||
Prerelease_identifiers prerelease_ids;
|
Prerelease_identifiers prerelease_ids;
|
||||||
|
|
||||||
/// Optional series of build identifiers.
|
/// Optional series of build identifiers.
|
||||||
Build_identifiers build_ids;
|
Build_identifiers build_ids;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Forward declaration required for operators' template declarations.
|
// Forward declaration required for operators' template declarations.
|
||||||
template<typename Parser, typename Comparator>
|
template<typename Parser, typename Comparator>
|
||||||
class Basic_version;
|
class Basic_version;
|
||||||
|
|
||||||
/// Test if left-hand version operand is of lower precedence than the right-hand version.
|
/// Test if left-hand version operand is of lower precedence than the right-hand version.
|
||||||
template<typename Parser, typename Comparator>
|
template<typename Parser, typename Comparator>
|
||||||
bool operator<(const Basic_version<Parser, Comparator>&,
|
bool operator<(const Basic_version<Parser, Comparator>&,
|
||||||
const Basic_version<Parser, Comparator>&);
|
const Basic_version<Parser, Comparator>&);
|
||||||
|
|
||||||
/// Test if left-hand version operand if of equal precedence as the right-hand version.
|
/// Test if left-hand version operand if of equal precedence as the right-hand version.
|
||||||
template<typename Parser, typename Comparator>
|
template<typename Parser, typename Comparator>
|
||||||
bool operator==(const Basic_version<Parser, Comparator>&,
|
bool operator==(const Basic_version<Parser, Comparator>&,
|
||||||
const Basic_version<Parser, Comparator>&);
|
const Basic_version<Parser, Comparator>&);
|
||||||
|
|
||||||
/// Output version object to stream using standard semver format (X.Y.Z-PR+B).
|
/// Output version object to stream using standard semver format (X.Y.Z-PR+B).
|
||||||
template<typename Parser, typename Comparator>
|
template<typename Parser, typename Comparator>
|
||||||
std::ostream& operator<<(std::ostream&,
|
std::ostream& operator<<(std::ostream&,
|
||||||
const Basic_version<Parser, Comparator>&);
|
const Basic_version<Parser, Comparator>&);
|
||||||
|
|
||||||
/// Test if left-hand version and right-hand version are of different precedence.
|
/// Test if left-hand version and right-hand version are of different precedence.
|
||||||
template<typename Parser, typename Comparator>
|
template<typename Parser, typename Comparator>
|
||||||
bool operator!=(const Basic_version<Parser, Comparator>&,
|
bool operator!=(const Basic_version<Parser, Comparator>&,
|
||||||
const Basic_version<Parser, Comparator>&);
|
const Basic_version<Parser, Comparator>&);
|
||||||
|
|
||||||
/// Test if left-hand version operand is of higher precedence than the right-hand version.
|
/// Test if left-hand version operand is of higher precedence than the right-hand version.
|
||||||
template<typename Parser, typename Comparator>
|
template<typename Parser, typename Comparator>
|
||||||
bool operator>(const Basic_version<Parser, Comparator>&,
|
bool operator>(const Basic_version<Parser, Comparator>&,
|
||||||
const Basic_version<Parser, Comparator>&);
|
const Basic_version<Parser, Comparator>&);
|
||||||
|
|
||||||
/// Test if left-hand version operand is of higher or equal precedence as the right-hand version.
|
/// Test if left-hand version operand is of higher or equal precedence as the right-hand version.
|
||||||
template<typename Parser, typename Comparator>
|
template<typename Parser, typename Comparator>
|
||||||
bool operator>=(const Basic_version<Parser, Comparator>&,
|
bool operator>=(const Basic_version<Parser, Comparator>&,
|
||||||
const Basic_version<Parser, Comparator>&);
|
const Basic_version<Parser, Comparator>&);
|
||||||
|
|
||||||
/// Test if left-hand version operand is of lower or equal precedence as the right-hand version.
|
/// Test if left-hand version operand is of lower or equal precedence as the right-hand version.
|
||||||
template<typename Parser, typename Comparator>
|
template<typename Parser, typename Comparator>
|
||||||
bool operator<=(const Basic_version<Parser, Comparator>&,
|
bool operator<=(const Basic_version<Parser, Comparator>&,
|
||||||
const Basic_version<Parser, Comparator>&);
|
const Basic_version<Parser, Comparator>&);
|
||||||
|
|
||||||
|
|
||||||
/// Base class for various version parsing and precedence ordering schemes.
|
/// Base class for various version parsing and precedence ordering schemes.
|
||||||
/**
|
/**
|
||||||
Basic_version class describes general version object without prescribing parsing,
|
Basic_version class describes general version object without prescribing parsing,
|
||||||
validation and comparison rules. These rules are implemented by supplied Parser and
|
validation and comparison rules. These rules are implemented by supplied Parser and
|
||||||
Comparator objects.
|
Comparator objects.
|
||||||
*/
|
*/
|
||||||
template<typename Parser, typename Comparator>
|
template<typename Parser, typename Comparator>
|
||||||
class Basic_version {
|
class Basic_version
|
||||||
public:
|
{
|
||||||
/// Construct Basic_version object using Parser object to parse default ("0.0.0") version string and Comparator for comparison.
|
public:
|
||||||
Basic_version(Parser, Comparator);
|
/// Construct Basic_version object using Parser object to parse default ("0.0.0") version string and Comparator for comparison.
|
||||||
|
Basic_version(Parser, Comparator);
|
||||||
|
|
||||||
/// Construct Basic_version object using Parser to parse supplied version string and Comparator for comparison.
|
/// Construct Basic_version object using Parser to parse supplied version string and Comparator for comparison.
|
||||||
Basic_version(const std::string&, Parser, Comparator);
|
Basic_version(const std::string&, Parser, Comparator);
|
||||||
|
|
||||||
/// Construct Basic_version by copying data from another one.
|
/// Construct Basic_version by copying data from another one.
|
||||||
Basic_version(const Basic_version&);
|
Basic_version(const Basic_version&);
|
||||||
|
|
||||||
/// Copy version data from another Basic_version to this one.
|
/// Copy version data from another Basic_version to this one.
|
||||||
Basic_version& operator=(const Basic_version&);
|
Basic_version& operator=(const Basic_version&);
|
||||||
|
|
||||||
int major() const; ///< Get major version.
|
int major() const; ///< Get major version.
|
||||||
int minor() const; ///< Get minor version.
|
int minor() const; ///< Get minor version.
|
||||||
int patch() const; ///< Get patch version.
|
int patch() const; ///< Get patch version.
|
||||||
const std::string prerelease() const; ///< Get prerelease version string.
|
const std::string prerelease() const; ///< Get prerelease version string.
|
||||||
const std::string build() const; ///< Get build version string.
|
const std::string build() const; ///< Get build version string.
|
||||||
|
|
||||||
friend bool operator< <>(const Basic_version&, const Basic_version&);
|
friend bool operator< <>(const Basic_version&, const Basic_version&);
|
||||||
friend bool operator== <>(const Basic_version&, const Basic_version&);
|
friend bool operator== <>(const Basic_version&, const Basic_version&);
|
||||||
friend std::ostream& operator<< <>(std::ostream&s, const Basic_version&);
|
friend std::ostream& operator<< <>(std::ostream& s, const Basic_version&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Parser parser_;
|
Parser parser_;
|
||||||
Comparator comparator_;
|
Comparator comparator_;
|
||||||
Version_data ver_;
|
Version_data ver_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "version.inl"
|
#include "version.inl"
|
||||||
|
|||||||
@@ -13,171 +13,171 @@ using namespace std;
|
|||||||
|
|
||||||
BOOL CALLBACK EnumResLangProc(HMODULE hModule, LPCTSTR lpszType, LPCTSTR lpszName, WORD wIDLanguage, LONG_PTR lParam)
|
BOOL CALLBACK EnumResLangProc(HMODULE hModule, LPCTSTR lpszType, LPCTSTR lpszName, WORD wIDLanguage, LONG_PTR lParam)
|
||||||
{
|
{
|
||||||
HANDLE hUpdate = (HANDLE)lParam;
|
HANDLE hUpdate = (HANDLE)lParam;
|
||||||
HRSRC hFindItAgain = FindResourceEx(hModule, lpszType, lpszName, wIDLanguage);
|
HRSRC hFindItAgain = FindResourceEx(hModule, lpszType, lpszName, wIDLanguage);
|
||||||
|
|
||||||
HGLOBAL hGlobal = LoadResource(hModule, hFindItAgain);
|
HGLOBAL hGlobal = LoadResource(hModule, hFindItAgain);
|
||||||
if (!hGlobal) return true;
|
if (!hGlobal) return true;
|
||||||
|
|
||||||
UpdateResource(hUpdate, lpszType, lpszName, wIDLanguage, LockResource(hGlobal), SizeofResource(hModule, hFindItAgain));
|
UpdateResource(hUpdate, lpszType, lpszName, wIDLanguage, LockResource(hGlobal), SizeofResource(hModule, hFindItAgain));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL CALLBACK EnumResNameProc(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
|
BOOL CALLBACK EnumResNameProc(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
|
||||||
{
|
{
|
||||||
HANDLE hUpdate = (HANDLE)lParam;
|
HANDLE hUpdate = (HANDLE)lParam;
|
||||||
|
|
||||||
EnumResourceLanguages(hModule, lpszType, lpszName, EnumResLangProc, (LONG_PTR)hUpdate);
|
EnumResourceLanguages(hModule, lpszType, lpszName, EnumResLangProc, (LONG_PTR)hUpdate);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL CALLBACK EnumResTypeProc(HMODULE hMod, LPTSTR lpszType, LONG_PTR lParam)
|
BOOL CALLBACK EnumResTypeProc(HMODULE hMod, LPTSTR lpszType, LONG_PTR lParam)
|
||||||
{
|
{
|
||||||
std::vector<wchar_t*>* typeList = (std::vector<wchar_t*>*)lParam;
|
std::vector<wchar_t*>* typeList = (std::vector<wchar_t*>*)lParam;
|
||||||
if (IS_INTRESOURCE(lpszType)) {
|
if (IS_INTRESOURCE(lpszType)) {
|
||||||
typeList->push_back(lpszType);
|
typeList->push_back(lpszType);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
typeList->push_back(_wcsdup(lpszType));
|
typeList->push_back(_wcsdup(lpszType));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CopyResourcesToStubExecutable(wchar_t* src, wchar_t* dest)
|
int CopyResourcesToStubExecutable(wchar_t* src, wchar_t* dest)
|
||||||
{
|
{
|
||||||
HMODULE hSrc = LoadLibraryEx(src, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
HMODULE hSrc = LoadLibraryEx(src, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
||||||
if (!hSrc) return GetLastError();
|
if (!hSrc) return GetLastError();
|
||||||
|
|
||||||
HANDLE hUpdate = BeginUpdateResource(dest, true);
|
HANDLE hUpdate = BeginUpdateResource(dest, true);
|
||||||
if (!hUpdate) return GetLastError();
|
if (!hUpdate) return GetLastError();
|
||||||
|
|
||||||
std::vector<wchar_t*> typeList;
|
std::vector<wchar_t*> typeList;
|
||||||
EnumResourceTypes(hSrc, EnumResTypeProc, (LONG_PTR)&typeList);
|
EnumResourceTypes(hSrc, EnumResTypeProc, (LONG_PTR)&typeList);
|
||||||
|
|
||||||
for (auto& type : typeList) {
|
for (auto& type : typeList) {
|
||||||
EnumResourceNames(hSrc, type, EnumResNameProc, (LONG_PTR)hUpdate);
|
EnumResourceNames(hSrc, type, EnumResNameProc, (LONG_PTR)hUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
EndUpdateResource(hUpdate, false);
|
EndUpdateResource(hUpdate, false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LoadFileIntoMemory(wstring fpath, BYTE** pBuf, int* cBuf)
|
int LoadFileIntoMemory(wstring fpath, BYTE** pBuf, int* cBuf)
|
||||||
{
|
{
|
||||||
HANDLE hFile = CreateFile(fpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
HANDLE hFile = CreateFile(fpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
if (hFile == INVALID_HANDLE_VALUE) {
|
if (hFile == INVALID_HANDLE_VALUE) {
|
||||||
printf("Can't open file\n");
|
printf("Can't open file\n");
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
BY_HANDLE_FILE_INFORMATION fileInfo;
|
BY_HANDLE_FILE_INFORMATION fileInfo;
|
||||||
if (!GetFileInformationByHandle(hFile, &fileInfo)) {
|
if (!GetFileInformationByHandle(hFile, &fileInfo)) {
|
||||||
printf("Can't read file handle\n");
|
printf("Can't read file handle\n");
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*cBuf = fileInfo.nFileSizeLow;
|
*cBuf = fileInfo.nFileSizeLow;
|
||||||
*pBuf = new BYTE[fileInfo.nFileSizeLow + 0x1000];
|
*pBuf = new BYTE[fileInfo.nFileSizeLow + 0x1000];
|
||||||
|
|
||||||
BYTE* pCurrent = *pBuf;
|
BYTE* pCurrent = *pBuf;
|
||||||
DWORD dwBytesRead;
|
DWORD dwBytesRead;
|
||||||
|
|
||||||
printf("Starting to read file!\n");
|
printf("Starting to read file!\n");
|
||||||
do {
|
do {
|
||||||
if (!ReadFile(hFile, pCurrent, 0x1000, &dwBytesRead, NULL)) {
|
if (!ReadFile(hFile, pCurrent, 0x1000, &dwBytesRead, NULL)) {
|
||||||
printf("Failed to read file! 0x%u\n", GetLastError());
|
printf("Failed to read file! 0x%u\n", GetLastError());
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pCurrent += dwBytesRead;
|
pCurrent += dwBytesRead;
|
||||||
} while (dwBytesRead > 0);
|
} while (dwBytesRead > 0);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fail()
|
int fail()
|
||||||
{
|
{
|
||||||
printf("Usage: WriteZipToSetup [Setup.exe template] [Zip File]\n");
|
printf("Usage: WriteZipToSetup [Setup.exe template] [Zip File]\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int wmain(int argc, wchar_t* argv[])
|
int wmain(int argc, wchar_t* argv[])
|
||||||
{
|
{
|
||||||
// short circuit exit for stub executable
|
// short circuit exit for stub executable
|
||||||
if (argc > 1 && wcscmp(argv[1], L"--copy-stub-resources") == 0) {
|
if (argc > 1 && wcscmp(argv[1], L"--copy-stub-resources") == 0) {
|
||||||
if (argc != 4) return fail();
|
if (argc != 4) return fail();
|
||||||
return CopyResourcesToStubExecutable(argv[2], argv[3]);
|
return CopyResourcesToStubExecutable(argv[2], argv[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse command line arguments
|
// parse command line arguments
|
||||||
const flags::args args(argc, argv);
|
const flags::args args(argc, argv);
|
||||||
const auto& parg = args.positional();
|
const auto& parg = args.positional();
|
||||||
if (parg.size() != 2) {
|
if (parg.size() != 2) {
|
||||||
return fail();
|
return fail();
|
||||||
}
|
}
|
||||||
const auto setupFile = wstring(parg[0]);
|
const auto setupFile = wstring(parg[0]);
|
||||||
const auto zipFile = wstring(parg[1]);
|
const auto zipFile = wstring(parg[1]);
|
||||||
const auto requiredFramework = args.get<wstring>(L"set-required-framework");
|
const auto requiredFramework = args.get<wstring>(L"set-required-framework");
|
||||||
const auto splashImage = args.get<wstring>(L"set-splash");
|
const auto splashImage = args.get<wstring>(L"set-splash");
|
||||||
|
|
||||||
wprintf(L"Setup: %s, Zip: %s\n", setupFile.c_str(), zipFile.c_str());
|
wprintf(L"Setup: %s, Zip: %s\n", setupFile.c_str(), zipFile.c_str());
|
||||||
|
|
||||||
// Read the entire zip file into memory, yolo
|
// Read the entire zip file into memory, yolo
|
||||||
BYTE *pZipBuf, *pSplashBuf;
|
BYTE* pZipBuf, * pSplashBuf;
|
||||||
int cZipBuf, cSplashBuf;
|
int cZipBuf, cSplashBuf;
|
||||||
|
|
||||||
if (FAILED(LoadFileIntoMemory(zipFile, &pZipBuf, &cZipBuf))) {
|
if (FAILED(LoadFileIntoMemory(zipFile, &pZipBuf, &cZipBuf))) {
|
||||||
printf("Couldn't read zip file.\n");
|
printf("Couldn't read zip file.\n");
|
||||||
return fail();
|
return fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Updating Resource!\n");
|
printf("Updating Resource!\n");
|
||||||
HANDLE hRes = BeginUpdateResource(setupFile.c_str(), false);
|
HANDLE hRes = BeginUpdateResource(setupFile.c_str(), false);
|
||||||
if (!hRes) {
|
if (!hRes) {
|
||||||
printf("Couldn't open setup.exe for writing\n");
|
printf("Couldn't open setup.exe for writing\n");
|
||||||
return fail();
|
return fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!UpdateResource(hRes, L"DATA", MAKEINTRESOURCE(IDR_UPDATE_ZIP), RESOURCE_LANG, pZipBuf, cZipBuf)) {
|
if (!UpdateResource(hRes, L"DATA", MAKEINTRESOURCE(IDR_UPDATE_ZIP), RESOURCE_LANG, pZipBuf, cZipBuf)) {
|
||||||
printf("Failed to update zip resource\n");
|
printf("Failed to update zip resource\n");
|
||||||
return fail();
|
return fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requiredFramework.has_value()) {
|
if (requiredFramework.has_value()) {
|
||||||
wstring sReq = requiredFramework.value();
|
wstring sReq = requiredFramework.value();
|
||||||
LPVOID pReq = &sReq[0];
|
LPVOID pReq = &sReq[0];
|
||||||
if (!UpdateResource(hRes, L"FLAGS", MAKEINTRESOURCE(IDR_FX_VERSION_FLAG), RESOURCE_LANG, pReq, (sReq.length() + 1) * sizeof(wchar_t))) {
|
if (!UpdateResource(hRes, L"FLAGS", MAKEINTRESOURCE(IDR_FX_VERSION_FLAG), RESOURCE_LANG, pReq, (sReq.length() + 1) * sizeof(wchar_t))) {
|
||||||
printf("Failed to update required version resource\n");
|
printf("Failed to update required version resource\n");
|
||||||
return fail();
|
return fail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (splashImage.has_value()) {
|
if (splashImage.has_value()) {
|
||||||
if (FAILED(LoadFileIntoMemory(splashImage.value(), &pSplashBuf, &cSplashBuf))) {
|
if (FAILED(LoadFileIntoMemory(splashImage.value(), &pSplashBuf, &cSplashBuf))) {
|
||||||
printf("Couldn't read splash image.\n");
|
printf("Couldn't read splash image.\n");
|
||||||
return fail();
|
return fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!UpdateResource(hRes, L"DATA", MAKEINTRESOURCE(IDR_SPLASH_IMG), RESOURCE_LANG, pSplashBuf, cSplashBuf)) {
|
if (!UpdateResource(hRes, L"DATA", MAKEINTRESOURCE(IDR_SPLASH_IMG), RESOURCE_LANG, pSplashBuf, cSplashBuf)) {
|
||||||
printf("Failed to update splash resource\n");
|
printf("Failed to update splash resource\n");
|
||||||
return fail();
|
return fail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// if the user hasn't given us a splash screen, let's remove the default (there will be no splash at all)
|
// if the user hasn't given us a splash screen, let's remove the default (there will be no splash at all)
|
||||||
if (!UpdateResource(hRes, L"DATA", MAKEINTRESOURCE(IDR_SPLASH_IMG), RESOURCE_LANG, 0, 0)) {
|
if (!UpdateResource(hRes, L"DATA", MAKEINTRESOURCE(IDR_SPLASH_IMG), RESOURCE_LANG, 0, 0)) {
|
||||||
printf("Failed to update splash resource\n");
|
printf("Failed to update splash resource\n");
|
||||||
return fail();
|
return fail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Finished!\n");
|
printf("Finished!\n");
|
||||||
if (!EndUpdateResource(hRes, false)) {
|
if (!EndUpdateResource(hRes, false)) {
|
||||||
printf("Failed to update resource\n");
|
printf("Failed to update resource\n");
|
||||||
return fail();
|
return fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("It worked!\n");
|
printf("It worked!\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,10 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace flags {
|
namespace flags
|
||||||
namespace detail {
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
using argument_map =
|
using argument_map =
|
||||||
std::unordered_map<std::wstring_view, std::optional<std::wstring_view>>;
|
std::unordered_map<std::wstring_view, std::optional<std::wstring_view>>;
|
||||||
|
|
||||||
@@ -21,8 +23,10 @@ namespace flags {
|
|||||||
// * If the token does not begin with a -, it will be considered a value for the
|
// * If the token does not begin with a -, it will be considered a value for the
|
||||||
// previous option. If there was no previous option, it will be considered a
|
// previous option. If there was no previous option, it will be considered a
|
||||||
// positional argument.
|
// positional argument.
|
||||||
struct parser {
|
struct parser
|
||||||
parser(const int argc, wchar_t** argv) {
|
{
|
||||||
|
parser(const int argc, wchar_t** argv)
|
||||||
|
{
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
churn(argv[i]);
|
churn(argv[i]);
|
||||||
}
|
}
|
||||||
@@ -32,22 +36,26 @@ namespace flags {
|
|||||||
parser& operator=(const parser&) = delete;
|
parser& operator=(const parser&) = delete;
|
||||||
|
|
||||||
const argument_map& options() const { return options_; }
|
const argument_map& options() const { return options_; }
|
||||||
const std::vector<std::wstring_view>& positional_arguments() const {
|
const std::vector<std::wstring_view>& positional_arguments() const
|
||||||
|
{
|
||||||
return positional_arguments_;
|
return positional_arguments_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Advance the state machine for the current token.
|
// Advance the state machine for the current token.
|
||||||
void churn(const std::wstring_view& item) {
|
void churn(const std::wstring_view& item)
|
||||||
|
{
|
||||||
item.at(0) == '-' ? on_option(item) : on_value(item);
|
item.at(0) == '-' ? on_option(item) : on_value(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consumes the current option if there is one.
|
// Consumes the current option if there is one.
|
||||||
void flush() {
|
void flush()
|
||||||
|
{
|
||||||
if (current_option_) on_value();
|
if (current_option_) on_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_option(const std::wstring_view& option) {
|
void on_option(const std::wstring_view& option)
|
||||||
|
{
|
||||||
// Consume the current_option and reassign it to the new option while
|
// Consume the current_option and reassign it to the new option while
|
||||||
// removing all leading dashes.
|
// removing all leading dashes.
|
||||||
flush();
|
flush();
|
||||||
@@ -64,7 +72,8 @@ namespace flags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_value(const std::optional<std::wstring_view>& value = std::nullopt) {
|
void on_value(const std::optional<std::wstring_view>& value = std::nullopt)
|
||||||
|
{
|
||||||
// If there's not an option preceding the value, it's a positional argument.
|
// If there's not an option preceding the value, it's a positional argument.
|
||||||
if (!current_option_) {
|
if (!current_option_) {
|
||||||
if (value) positional_arguments_.emplace_back(*value);
|
if (value) positional_arguments_.emplace_back(*value);
|
||||||
@@ -82,7 +91,8 @@ namespace flags {
|
|||||||
|
|
||||||
// If a key exists, return an optional populated with its value.
|
// If a key exists, return an optional populated with its value.
|
||||||
inline std::optional<std::wstring_view> get_value(
|
inline std::optional<std::wstring_view> get_value(
|
||||||
const argument_map& options, const std::wstring_view& option) {
|
const argument_map& options, const std::wstring_view& option)
|
||||||
|
{
|
||||||
if (const auto it = options.find(option); it != options.end()) {
|
if (const auto it = options.find(option); it != options.end()) {
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
@@ -94,7 +104,8 @@ namespace flags {
|
|||||||
// nullopt.
|
// nullopt.
|
||||||
template <class T>
|
template <class T>
|
||||||
std::optional<T> get(const argument_map& options,
|
std::optional<T> get(const argument_map& options,
|
||||||
const std::wstring_view& option) {
|
const std::wstring_view& option)
|
||||||
|
{
|
||||||
if (const auto view = get_value(options, option)) {
|
if (const auto view = get_value(options, option)) {
|
||||||
if (T value; std::istringstream(std::wstring(*view)) >> value) return value;
|
if (T value; std::istringstream(std::wstring(*view)) >> value) return value;
|
||||||
}
|
}
|
||||||
@@ -104,13 +115,15 @@ namespace flags {
|
|||||||
// Since the values are already stored as strings, there's no need to use `>>`.
|
// Since the values are already stored as strings, there's no need to use `>>`.
|
||||||
template <>
|
template <>
|
||||||
inline std::optional<std::wstring_view> get(const argument_map& options,
|
inline std::optional<std::wstring_view> get(const argument_map& options,
|
||||||
const std::wstring_view& option) {
|
const std::wstring_view& option)
|
||||||
|
{
|
||||||
return get_value(options, option);
|
return get_value(options, option);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline std::optional<std::wstring> get(const argument_map& options,
|
inline std::optional<std::wstring> get(const argument_map& options,
|
||||||
const std::wstring_view& option) {
|
const std::wstring_view& option)
|
||||||
|
{
|
||||||
if (const auto view = get<std::wstring_view>(options, option)) {
|
if (const auto view = get<std::wstring_view>(options, option)) {
|
||||||
return std::wstring(*view);
|
return std::wstring(*view);
|
||||||
}
|
}
|
||||||
@@ -123,7 +136,8 @@ namespace flags {
|
|||||||
constexpr std::array<const wchar_t*, 5> falsities{ {L"0", L"n", L"no", L"f", L"false"} };
|
constexpr std::array<const wchar_t*, 5> falsities{ {L"0", L"n", L"no", L"f", L"false"} };
|
||||||
template <>
|
template <>
|
||||||
inline std::optional<bool> get(const argument_map& options,
|
inline std::optional<bool> get(const argument_map& options,
|
||||||
const std::wstring_view& option) {
|
const std::wstring_view& option)
|
||||||
|
{
|
||||||
if (const auto value = get_value(options, option)) {
|
if (const auto value = get_value(options, option)) {
|
||||||
return std::none_of(falsities.begin(), falsities.end(),
|
return std::none_of(falsities.begin(), falsities.end(),
|
||||||
[&value](auto falsity) { return *value == falsity; });
|
[&value](auto falsity) { return *value == falsity; });
|
||||||
@@ -137,7 +151,8 @@ namespace flags {
|
|||||||
// nullopt.
|
// nullopt.
|
||||||
template <class T>
|
template <class T>
|
||||||
std::optional<T> get(const std::vector<std::wstring_view>& positional_arguments,
|
std::optional<T> get(const std::vector<std::wstring_view>& positional_arguments,
|
||||||
size_t positional_index) {
|
size_t positional_index)
|
||||||
|
{
|
||||||
if (positional_index < positional_arguments.size()) {
|
if (positional_index < positional_arguments.size()) {
|
||||||
if (T value; std::istringstream(
|
if (T value; std::istringstream(
|
||||||
std::wstring(positional_arguments[positional_index])) >>
|
std::wstring(positional_arguments[positional_index])) >>
|
||||||
@@ -151,7 +166,8 @@ namespace flags {
|
|||||||
template <>
|
template <>
|
||||||
inline std::optional<std::wstring_view> get(
|
inline std::optional<std::wstring_view> get(
|
||||||
const std::vector<std::wstring_view>& positional_arguments,
|
const std::vector<std::wstring_view>& positional_arguments,
|
||||||
size_t positional_index) {
|
size_t positional_index)
|
||||||
|
{
|
||||||
if (positional_index < positional_arguments.size()) {
|
if (positional_index < positional_arguments.size()) {
|
||||||
return positional_arguments[positional_index];
|
return positional_arguments[positional_index];
|
||||||
}
|
}
|
||||||
@@ -161,7 +177,8 @@ namespace flags {
|
|||||||
template <>
|
template <>
|
||||||
inline std::optional<std::wstring> get(
|
inline std::optional<std::wstring> get(
|
||||||
const std::vector<std::wstring_view>& positional_arguments,
|
const std::vector<std::wstring_view>& positional_arguments,
|
||||||
size_t positional_index) {
|
size_t positional_index)
|
||||||
|
{
|
||||||
if (positional_index < positional_arguments.size()) {
|
if (positional_index < positional_arguments.size()) {
|
||||||
return std::wstring(positional_arguments[positional_index]);
|
return std::wstring(positional_arguments[positional_index]);
|
||||||
}
|
}
|
||||||
@@ -169,30 +186,36 @@ namespace flags {
|
|||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
struct args {
|
struct args
|
||||||
|
{
|
||||||
args(const int argc, wchar_t** argv) : parser_(argc, argv) {}
|
args(const int argc, wchar_t** argv) : parser_(argc, argv) {}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
std::optional<T> get(const std::wstring_view& option) const {
|
std::optional<T> get(const std::wstring_view& option) const
|
||||||
|
{
|
||||||
return detail::get<T>(parser_.options(), option);
|
return detail::get<T>(parser_.options(), option);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
T get(const std::wstring_view& option, T&& default_value) const {
|
T get(const std::wstring_view& option, T&& default_value) const
|
||||||
|
{
|
||||||
return get<T>(option).value_or(default_value);
|
return get<T>(option).value_or(default_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
std::optional<T> get(size_t positional_index) const {
|
std::optional<T> get(size_t positional_index) const
|
||||||
|
{
|
||||||
return detail::get<T>(parser_.positional_arguments(), positional_index);
|
return detail::get<T>(parser_.positional_arguments(), positional_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
T get(size_t positional_index, T&& default_value) const {
|
T get(size_t positional_index, T&& default_value) const
|
||||||
|
{
|
||||||
return get<T>(positional_index).value_or(default_value);
|
return get<T>(positional_index).value_or(default_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::wstring_view>& positional() const {
|
const std::vector<std::wstring_view>& positional() const
|
||||||
|
{
|
||||||
return parser_.positional_arguments();
|
return parser_.positional_arguments();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user