in sources/Google.Solutions.IapDesktop/Program.cs [315:768]
protected override int HandleFirstInvocation(string[] args)
{
IsLoggingEnabled = this.commandLineOptions.IsLoggingEnabled;
//
// Set up process mitigations. This must be done early, otherwise it's
// ineffective.
//
try
{
ProcessMitigations.Apply();
}
catch (Exception e)
{
ApplicationTraceSource.Log.TraceError(e);
}
//
// Use the GDI-based TextRenderer class.
//
// NB. This must be set early, before the first window is created.
//
System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
System.Windows.Forms.Application.EnableVisualStyles();
#if DEBUG
ApplicationTraceSource.Log.Switch.Level = SourceLevels.Verbose;
TerminalTraceSource.Log.Switch.Level = SourceLevels.Verbose;
#endif
//
// Install patches requires for IAP.
//
try
{
SystemPatch.UnrestrictUserAgentHeader.Install();
}
catch (InvalidOperationException e)
{
ApplicationTraceSource.Log.TraceWarning(
"Installing UnrestrictUserAgentHeader patch failed: {0}", e);
}
try
{
WebSocket.RegisterPrefixes();
SystemPatch.SetUsernameAsHostHeaderForWssRequests.Install();
}
catch (Exception e)
{
ApplicationTraceSource.Log.TraceWarning(
"Installing SetUsernameAsHostHeaderForWssRequests patch failed: {0}", e);
}
//
// Set up layers. Services in a layer can lookup services in a lower layer,
// but not in a higher layer.
//
var preAuthLayer = new ServiceRegistry();
var install = new Install(Install.DefaultBaseKeyPath);
using (var profile = LoadProfileOrExit(install, this.commandLineOptions))
using (var processFactory = new Win32ChildProcessFactory(true))
{
Debug.Assert(!Install.IsExecutingTests);
//
// Load pre-auth layer: Platform abstractions, API adapters.
//
// We can only load and access services that don't require
// authorization. In particular, this means that we cannot access
// any Google APIs.
//
preAuthLayer.AddSingleton<IInstall>(install);
preAuthLayer.AddSingleton<UserAgent>(Install.UserAgent);
preAuthLayer.AddSingleton(profile);
preAuthLayer.AddSingleton<IUserProfile>(profile);
preAuthLayer.AddSingleton<IClock>(SystemClock.Default);
preAuthLayer.AddTransient<IConfirmationDialog, ConfirmationDialog>();
preAuthLayer.AddTransient<ITaskDialog, TaskDialog>();
preAuthLayer.AddTransient<ICredentialDialog, CredentialDialog>();
preAuthLayer.AddTransient<IInputDialog, InputDialog>();
preAuthLayer.AddTransient<IExceptionDialog, ExceptionDialog>();
preAuthLayer.AddTransient<IOperationProgressDialog, OperationProgressDialog>();
preAuthLayer.AddTransient<INotifyDialog, NotifyDialog>();
preAuthLayer.AddSingleton<IExternalRestClient, ExternalRestClient>();
preAuthLayer.AddTransient<HelpClient>();
preAuthLayer.AddTransient<BugReportClient>();
preAuthLayer.AddTransient<IHttpProxyAdapter, HttpProxyAdapter>();
preAuthLayer.AddSingleton<ProtocolRegistry>();
preAuthLayer.AddSingleton<IWin32ProcessFactory>(processFactory);
preAuthLayer.AddSingleton<IWin32ProcessSet>(processFactory);
preAuthLayer.AddSingleton<IKeyStore>(
new KeyStore(CngProvider.MicrosoftSoftwareKeyStorageProvider));
//
// Initialize UI, DPI mode.
//
var themeSettingsRepository = new ThemeSettingsRepository(
profile.SettingsKey.CreateSubKey("Theme"));
var dpiMode = (themeSettingsRepository.GetSettings().ScalingMode.Value) switch
{
ScalingMode.None => DpiAwarenessMode.DpiUnaware,
ScalingMode.LegacyGdi => DpiAwarenessMode.SystemAware,
_ => DpiAwarenessMode.SystemAware,
};
if (DpiAwareness.IsSupported && dpiMode != DpiAwarenessMode.DpiUnaware)
{
//
// Set DPI mode.
//
// NB. Setting the DPI mode programmatically (instead of using the
// app manifest or app.config) does causes WinForms to *not* deliver
// DPI change (WM_DPICHANGE) events, but that's ok.
//
try
{
DpiAwareness.ProcessMode = dpiMode;
}
catch (Exception e)
{
ApplicationTraceSource.Log.TraceWarning(
"Setting DPI mode to {0} failed: {1}", dpiMode, e.Message);
}
}
//
// Load settings.
//
var appSettingsRepository = new ApplicationSettingsRepository(profile);
preAuthLayer.AddSingleton<IRepository<IApplicationSettings>>(appSettingsRepository);
if (appSettingsRepository.IsPolicyPresent)
{
//
// If there are policies in place, mark the UA as
// Enterprise-managed.
//
Install.UserAgent.Extensions = "Enterprise";
}
var authSettingsRepository = new AuthSettingsRepository(
profile.SettingsKey.CreateSubKey("Auth"));
var accessSettingsRepository = new AccessSettingsRepository(
profile.SettingsKey.CreateSubKey("Application"),
profile.MachinePolicyKey?.OpenSubKey("Application"),
profile.UserPolicyKey?.OpenSubKey("Application"));
preAuthLayer.AddSingleton<IRepository<IAuthSettings>>(authSettingsRepository);
preAuthLayer.AddSingleton<IOidcOfflineCredentialStore>(authSettingsRepository);
preAuthLayer.AddSingleton<IRepository<IAccessSettings>>(accessSettingsRepository);
preAuthLayer.AddSingleton<IRepository<IThemeSettings>>(themeSettingsRepository);
preAuthLayer.AddSingleton(new ToolWindowStateRepository(
profile.SettingsKey.CreateSubKey("ToolWindows")));
preAuthLayer.AddSingleton<IBindingContext, ViewBindingContext>();
preAuthLayer.AddTransient<IBrowserProtocolRegistry, BrowserProtocolRegistry>();
//
// Load themes.
//
var themes = Themes.Load(themeSettingsRepository);
preAuthLayer.AddSingleton<ISystemDialogTheme>(themes.SystemDialog);
preAuthLayer.AddSingleton<IDialogTheme>(themes.Dialog);
preAuthLayer.AddSingleton<IToolWindowTheme>(themes.ToolWindow);
preAuthLayer.AddSingleton<IMainWindowTheme>(themes.MainWindow);
//
// Configure networking settings.
//
// NB. Until now, no network connections have been made.
//
var appSettings = appSettingsRepository.GetSettings();
//
// Override default set of TLS versions.
//
ServicePointManager.SecurityProtocol = appSettings.TlsVersions.Value;
try
{
//
// Activate proxy settings based on app settings.
//
preAuthLayer.GetService<IHttpProxyAdapter>().ActivateSettings(appSettings);
PscAndMtlsAwareHttpClientFactory.NtlmProxyAuthenticationRetries =
(ushort)appSettings.ProxyAuthenticationRetries.Value;
}
catch (Exception)
{
//
// Settings invalid -> ignore.
//
}
//
// Register and configure API client endpoints.
//
var serviceRoute = ServiceRoute.Public;
{
var accessSettings = accessSettingsRepository.GetSettings();
if (accessSettings.PrivateServiceConnectEndpoint.Value is var pscEndpoint &&
!string.IsNullOrEmpty(pscEndpoint))
{
//
// Enable PSC.
//
serviceRoute = new ServiceRoute(pscEndpoint);
}
//
// Set connection pool limit. This limit applies per endpoint.
//
ServicePointManager.DefaultConnectionLimit
= accessSettings.ConnectionLimit.Value;
}
preAuthLayer.AddSingleton(OAuthClient.ApiKey);
preAuthLayer.AddSingleton(serviceRoute);
preAuthLayer.AddSingleton(GaiaOidcClient.CreateEndpoint(serviceRoute));
preAuthLayer.AddSingleton(WorkforcePoolClient.CreateEndpoint(serviceRoute));
preAuthLayer.AddSingleton(ResourceManagerClient.CreateEndpoint(serviceRoute));
preAuthLayer.AddSingleton(ComputeEngineClient.CreateEndpoint(serviceRoute));
preAuthLayer.AddSingleton(OsLoginClient.CreateEndpoint(serviceRoute));
preAuthLayer.AddSingleton(LoggingClient.CreateEndpoint(serviceRoute));
preAuthLayer.AddSingleton(IapClient.CreateEndpoint(serviceRoute));
//
// Enable telemetry if the user allows it. Do this before
// authorization takes place.
//
TelemetryLog.Current = new AnalyticsLog(
new MeasurementClient(
MeasurementClient.CreateEndpoint(),
Install.UserAgent,
AnalyticsStream.ApiKey,
AnalyticsStream.MeasurementId),
install,
new Dictionary<string, object>()
{
{ DefaultParameters.UserAgent, Install.UserAgent.ToApplicationName() },
{ DefaultParameters.UserAgentArchitecture, ProcessEnvironment.ProcessArchitecture.ToString() },
{ DefaultParameters.UserAgentPlatformVersion, Environment.OSVersion.Version.ToString() },
{ "osdrk", SystemTheme.ShouldAppsUseDarkMode ? "1" : "0" },
{ "oscdp", DeviceCapabilities.Current.Dpi.ToString() },
{ "ossdp", DeviceCapabilities.System.Dpi.ToString() },
{ "apent", appSettingsRepository.IsPolicyPresent ? "1" : "0" },
{ "apsrt", serviceRoute == ServiceRoute.Public ? "Public" : "PSC" },
})
{
Enabled = appSettings.IsTelemetryEnabled.Value
};
preAuthLayer.AddSingleton(TelemetryLog.Current);
preAuthLayer.AddTransient<AuthorizeView>();
preAuthLayer.AddTransient<AuthorizeViewModel>();
preAuthLayer.AddTransient<AuthorizeOptionsView>();
preAuthLayer.AddTransient<AuthorizeOptionsViewModel>();
preAuthLayer.AddTransient<OAuthScopeNotGrantedView>();
preAuthLayer.AddTransient<OAuthScopeNotGrantedViewModel>();
preAuthLayer.AddTransient<PropertiesView>();
preAuthLayer.AddTransient<PropertiesViewModel>();
var authorization = AuthorizeOrExit(preAuthLayer);
//
// Authorization complete, now the main part of the application
// can be initialized.
//
// Load main layer, containing everything else (except for
// extensions).
//
var mainLayer = new ServiceRegistry(preAuthLayer);
mainLayer.AddSingleton<IAuthorization>(authorization);
mainLayer.AddTransient<IToolWindowHost, ToolWindowHost>();
var mainForm = new MainForm(mainLayer)
{
StartupUrl = this.commandLineOptions.StartupUrl,
ShowWhatsNew = this.commandLineOptions.IsPostInstall && install.PreviousVersion != null
};
mainLayer.AddSingleton<IJobHost>(mainForm);
//
// Load main services.
//
var eventService = new EventQueue(mainForm);
//
// Register API clients as singletons to ensure connection reuse.
//
mainLayer.AddSingleton<IResourceManagerClient, ResourceManagerClient>();
mainLayer.AddSingleton<IComputeEngineClient, ComputeEngineClient>();
mainLayer.AddSingleton<ILoggingClient, LoggingClient>();
mainLayer.AddSingleton<IOsLoginClient, OsLoginClient>();
mainLayer.AddSingleton<IIapClient, IapClient>();
mainLayer.AddSingleton<IReleaseFeed>(new GithubClient(
new ExternalRestClient(),
OAuthClient.RepositoryName));
mainLayer.AddTransient<IAddressResolver, AddressResolver>();
mainLayer.AddTransient<IWindowsCredentialGenerator, WindowsCredentialGenerator>();
mainLayer.AddSingleton<IJobService, JobService>();
mainLayer.AddSingleton<IEventQueue>(eventService);
mainLayer.AddSingleton<ISessionBroker, SessionBroker>();
mainLayer.AddSingleton<IBrowser>(Browser.Default);
var projectRepository = new ProjectRepository(profile.SettingsKey.CreateSubKey("Inventory"));
mainLayer.AddSingleton<IProjectRepository>(projectRepository);
mainLayer.AddSingleton<IProjectSettingsRepository>(projectRepository);
mainLayer.AddSingleton<IProjectWorkspace, ProjectWorkspace>();
mainLayer.AddTransient<ICloudConsoleClient, CloudConsoleClient>();
mainLayer.AddTransient<IUpdatePolicy, UpdatePolicy>();
mainLayer.AddSingleton<IIapTransportFactory, IapTransportFactory>();
mainLayer.AddSingleton<IDirectTransportFactory, DirectTransportFactory>();
//
// Load windows.
//
mainLayer.AddSingleton<IMainWindow>(mainForm);
mainLayer.AddSingleton<IWin32Window>(mainForm);
mainLayer.AddTransient<AccessInfoFlyoutView>();
mainLayer.AddTransient<AccessInfoViewModel>();
mainLayer.AddTransient<NewProfileView>();
mainLayer.AddTransient<NewProfileViewModel>();
mainLayer.AddTransient<IProjectPickerDialog, ProjectPickerDialog>();
mainLayer.AddTransient<ProjectPickerView>();
mainLayer.AddTransient<ProjectPickerViewModel>();
mainLayer.AddSingleton<IProjectExplorer, ProjectExplorer>();
mainLayer.AddSingleton<ProjectExplorerView>();
mainLayer.AddTransient<ProjectExplorerViewModel>();
mainLayer.AddTransient<ReleaseNotesView>();
mainLayer.AddTransient<ReleaseNotesViewModel>();
mainLayer.AddSingleton<UrlCommands>();
//
// Load extensions.
//
foreach (var extension in LoadExtensionAssemblies())
{
mainLayer.AddExtensionAssembly(extension);
}
//
// Run app.
//
this.initializedMainForm = mainForm;
this.initializedMainForm.Shown += (_, __) =>
{
//
// Form is now ready to handle subsequent invocations.
//
this.mainFormInitialized.Set();
};
this.initializedMainForm.FormClosing += (_, __) =>
{
//
// Stop handling subsequent invocations.
//
this.initializedMainForm = null;
};
using (new DebugMessageThrottle(TimeSpan.FromMilliseconds(100)))
using (var recorder = new MessageTraceRecorder(8))
{
//
// Replace the standard WinForms exception dialog.
//
System.Windows.Forms.Application.ThreadException += (_, exArgs)
=> ShowFatalErrorAndExit(exArgs.Exception, recorder.Capture());
mainForm.Shown += (_, __) =>
{
//
// Try to force the window into the foreground. This might
// not be allowed in all circumstances, but ensures that the
// window becomes visible after the user has completed a
// (browser-based) authorization.
//
TrySetForegroundWindow(Process.GetCurrentProcess().Id);
};
//
// Show the main window.
//
try
{
System.Windows.Forms.Application.Run(mainForm);
}
catch (Exception e)
{
ShowFatalErrorAndExit(e, recorder.Capture());
}
if (processFactory.ChildProcesses > 0)
{
//
// Instead of killing child processes outright, give
// them a chance to close gracefully (and possibly
// save any work).
//
try
{
WaitDialog.Wait(
null,
"Waiting for applications to close...",
async cancellationToken =>
{
//
// Give child processes a fixed time to close,
// but the user might cancel early.
//
using (var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
using (var combinedCts = timeoutCts.Token.Combine(cancellationToken))
{
await processFactory
.CloseAsync(combinedCts.Token)
.ConfigureAwait(true);
}
});
}
catch (Exception e)
{
#if DEBUG
if (!e.IsCancellation())
{
ShowFatalErrorAndExit(e, recorder.Capture());
}
#else
_ = e;
#endif
}
}
//
// Ensure logs are flushed.
//
IsLoggingEnabled = false;
return 0;
}
}
}