This repository has been archived by the owner on Jul 5, 2024. It is now read-only.
forked from Squirrel/Squirrel.Windows
-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add fallback progress dialog if there is no splash image
- Loading branch information
Showing
8 changed files
with
310 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
using System; | ||
using System.Drawing; | ||
using System.IO; | ||
using Squirrel.SimpleSplat; | ||
using Squirrel.Update.Windows; | ||
|
||
namespace Squirrel.Update | ||
{ | ||
internal class ComposedWindow : ISplashWindow | ||
{ | ||
private readonly Bitmap _img; | ||
private readonly Icon _icon; | ||
private ISplashWindow _window; | ||
|
||
public ComposedWindow(string appName, bool silent, byte[] iconBytes, byte[] splashBytes) | ||
{ | ||
try { | ||
// we only accept a byte array and convert to memorystream because | ||
// gdi needs to seek and get length which is not supported in DeflateStream | ||
if (iconBytes?.Length > 0) _icon = new Icon(new MemoryStream(iconBytes)); | ||
if (splashBytes?.Length > 0) _img = (Bitmap) Bitmap.FromStream(new MemoryStream(splashBytes)); | ||
} catch (Exception ex) { | ||
this.Log().WarnException("Unable to load splash image", ex); | ||
} | ||
|
||
// don't bother creating a window if we're in silent mode, we can't show any UI | ||
try { | ||
if (!silent) { | ||
if (_img != null) { | ||
_window = new User32SplashWindow(appName, _icon, _img); | ||
} else { | ||
_window = new ComCtlProgressDialog(appName, _icon); | ||
} | ||
} else { | ||
this.Log().Warn("Running install in SILENT mode, no prompts will be shown to the user. Dialogs will auto-respond as no/cancel."); | ||
} | ||
} catch (Exception ex) { | ||
this.Log().ErrorException("Unable to open splash window", ex); | ||
} | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
LogThrow("Failed to dispose window.", () => _window?.Dispose()); | ||
_img?.Dispose(); | ||
_icon?.Dispose(); | ||
} | ||
|
||
public void Hide() | ||
=> LogThrow("Failed to hide window.", () => _window?.Hide()); | ||
|
||
public void SetProgress(ulong completed, ulong total) | ||
=> LogThrow("Failed to set progress.", () => _window?.SetProgress(completed, total)); | ||
|
||
public void SetProgressIndeterminate() | ||
=> LogThrow("Failed to set progress indeterminate.", () => _window?.SetProgressIndeterminate()); | ||
|
||
public void Show() | ||
=> LogThrow("Failed to show window.", () => _window?.Show()); | ||
|
||
public void ShowErrorDialog(string title, string message) | ||
=> LogThrow("Failed to show error dialog.", () => _window?.ShowErrorDialog(title, message)); | ||
|
||
public void ShowInfoDialog(string title, string message) | ||
=> LogThrow("Failed to show info dialog.", () => _window?.ShowInfoDialog(title, message)); | ||
|
||
public bool ShowQuestionDialog(string title, string message) | ||
=> LogThrow("Failed to show question dialog.", () => _window?.ShowQuestionDialog(title, message) ?? false, false); | ||
|
||
private void LogThrow(string msg, Action act) | ||
{ | ||
try { act(); } catch (Exception ex) { this.Log().ErrorException(msg, ex); } | ||
} | ||
|
||
private T LogThrow<T>(string msg, Func<T> act, T errRet) | ||
{ | ||
try { | ||
return act(); | ||
} catch (Exception ex) { | ||
this.Log().ErrorException(msg, ex); | ||
return errRet; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
using System; | ||
using System.Drawing; | ||
using System.Threading; | ||
using Squirrel.SimpleSplat; | ||
using Vanara.PInvoke; | ||
using Vanara.Windows.Forms; | ||
using static Vanara.PInvoke.ComCtl32; | ||
using static Vanara.PInvoke.User32; | ||
|
||
namespace Squirrel.Update.Windows | ||
{ | ||
internal class ComCtlProgressDialog : WindowBase | ||
{ | ||
public override IntPtr Handle => _hwnd != HWND.NULL ? _hwnd.DangerousGetHandle() : IntPtr.Zero; | ||
private HWND _hwnd; | ||
private readonly Icon _icon; | ||
private readonly object _lock = new object(); | ||
private readonly ManualResetEvent _signal; | ||
private bool _marquee = true; | ||
private bool _closing = false; | ||
|
||
public ComCtlProgressDialog(string appName, Icon icon) : base(appName) | ||
{ | ||
_signal = new ManualResetEvent(false); | ||
_icon = icon; | ||
} | ||
|
||
public override void Hide() | ||
{ | ||
lock (_lock) { | ||
if (Handle == IntPtr.Zero) return; | ||
_closing = true; | ||
SendMessage(_hwnd, WindowMessage.WM_CLOSE); | ||
} | ||
} | ||
|
||
public override void SetProgress(ulong completed, ulong total) | ||
{ | ||
lock (_lock) { | ||
if (Handle == IntPtr.Zero) return; | ||
var progress = (int) Math.Round((double) completed / (double) total * 100); | ||
if (_marquee) { | ||
_marquee = false; | ||
SendMessage(_hwnd, (uint) TaskDialogMessage.TDM_SET_PROGRESS_BAR_MARQUEE, (IntPtr) 0, (IntPtr) 0); // turn off marque animation | ||
SendMessage(_hwnd, (uint) TaskDialogMessage.TDM_SET_MARQUEE_PROGRESS_BAR, (IntPtr) 0); // disable marque mode | ||
} | ||
SendMessage(_hwnd, (uint) TaskDialogMessage.TDM_SET_PROGRESS_BAR_POS, (IntPtr) progress); | ||
} | ||
} | ||
|
||
public override void SetProgressIndeterminate() | ||
{ | ||
lock (_lock) { | ||
if (Handle == IntPtr.Zero) return; | ||
if (!_marquee) { | ||
_marquee = true; | ||
SendMessage(_hwnd, (uint) TaskDialogMessage.TDM_SET_PROGRESS_BAR_POS, (IntPtr) 0); // clear current progress bar | ||
SendMessage(_hwnd, (uint) TaskDialogMessage.TDM_SET_MARQUEE_PROGRESS_BAR, (IntPtr) 1); // enable marque mode | ||
SendMessage(_hwnd, (uint) TaskDialogMessage.TDM_SET_PROGRESS_BAR_MARQUEE, (IntPtr) 1, (IntPtr) 0); // turn on marque animation | ||
} | ||
} | ||
} | ||
|
||
public override void Show() | ||
{ | ||
lock (_lock) { | ||
if (Handle != IntPtr.Zero) return; | ||
_signal.Reset(); | ||
_closing = false; | ||
_marquee = true; | ||
var t = new Thread(ShowProc); | ||
t.IsBackground = true; | ||
t.SetApartmentState(ApartmentState.STA); | ||
t.Start(); | ||
if (!_signal.WaitOne(5000)) { | ||
throw new Exception("Timeout waiting for splash window to open"); | ||
} | ||
} | ||
} | ||
|
||
private void ShowProc() | ||
{ | ||
var config = new TASKDIALOGCONFIG(); | ||
config.dwCommonButtons = TASKDIALOG_COMMON_BUTTON_FLAGS.TDCBF_CANCEL_BUTTON; | ||
config.MainInstruction = "Installing " + AppName; | ||
//config.Content = "Hello this is some content"; | ||
config.pfCallbackProc = new TaskDialogCallbackProc(CallbackProc); | ||
config.dwFlags = TASKDIALOG_FLAGS.TDF_SIZE_TO_CONTENT | TASKDIALOG_FLAGS.TDF_SHOW_PROGRESS_BAR | ||
| TASKDIALOG_FLAGS.TDF_SHOW_MARQUEE_PROGRESS_BAR; | ||
|
||
if (_icon != null) { | ||
config.dwFlags |= TASKDIALOG_FLAGS.TDF_USE_HICON_MAIN; | ||
config.mainIcon = _icon.Handle; | ||
} | ||
|
||
using (new ComCtl32v6Context()) { | ||
var hr = TaskDialogIndirect(config, out var b1, out var b2, out var b3); | ||
if (hr.Failed) | ||
this.Log().ErrorException("Failed to open task dialog.", hr.GetException()); | ||
} | ||
} | ||
|
||
private HRESULT CallbackProc(HWND hwnd, TaskDialogNotification msg, IntPtr wParam, IntPtr lParam, IntPtr refData) | ||
{ | ||
switch (msg) { | ||
|
||
case TaskDialogNotification.TDN_DIALOG_CONSTRUCTED: | ||
_hwnd = hwnd; | ||
// Start marquee animation | ||
SendMessage(hwnd, (uint) TaskDialogMessage.TDM_SET_PROGRESS_BAR_MARQUEE, (IntPtr) 1, (IntPtr) 0); | ||
break; | ||
|
||
case TaskDialogNotification.TDN_CREATED: | ||
_signal.Set(); | ||
break; | ||
|
||
case TaskDialogNotification.TDN_DESTROYED: | ||
_hwnd = HWND.NULL; | ||
break; | ||
|
||
case TaskDialogNotification.TDN_BUTTON_CLICKED: | ||
// TODO support cancellation? | ||
return _closing ? 0 : 1; | ||
|
||
} | ||
|
||
return HRESULT.S_OK; | ||
} | ||
|
||
public override void Dispose() { } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.