The launcher drops RPC sessions idle >30s (easily hit while the operator sits in the install dialogs), but a dropped socket still reports Connected until an I/O fails. InstallProductWorker then skipped its reopen, streamed the archive on the fresh out-of-band connection, and died on the first progress poll — and the install-completed handler registered launch entries without checking e.Error, so AddApp's disconnected-guard exception crashed the whole console. - PodInfo.EnsureConnectionAlive: probe an "open" connection with a Ping and reconnect when it is dead; used by the install and uninstall workers. - SiteManagement.PodInfo_InstallProductCompleted: register launch entries only on success, and surface registration failures as the row's Install Failed state instead of an unhandled exception. - Regression test pins the premise + recovery against vPOD's launcher server (stale socket reports open, Ping exposes it, reconnect restores service). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
938 lines
26 KiB
C#
938 lines
26 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Net;
|
|
using System.ServiceProcess;
|
|
using System.Threading;
|
|
using Tesla.Net;
|
|
|
|
namespace TeslaConsole;
|
|
|
|
public class PodInfo : Component
|
|
{
|
|
private delegate void ConnectWorkerEventHandler(AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate);
|
|
|
|
private delegate void LaunchAppWorkerEventHandler(Guid appId, AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate);
|
|
|
|
private delegate void KillAppWorkerEventHandler(Guid appId, AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate);
|
|
|
|
private delegate void ShutdownWorkerEventHandler(bool restart, AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate);
|
|
|
|
private delegate void InstallProductWorkerEventHandler(string uncPath, SendOrPostCallback progressCallback, AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate);
|
|
|
|
private delegate void UninstallProductWorkerEventHandler(AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate);
|
|
|
|
private delegate void ServiceWorkerEventHandler(string machineName, AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate);
|
|
|
|
private const int ManagePort = 53290;
|
|
|
|
private static Dictionary<Guid, PodInfo> mPods = new Dictionary<Guid, PodInfo>();
|
|
|
|
private AsyncDelegates<ConnectWorkerEventHandler> connectDelegates;
|
|
|
|
private SendOrPostCallback onLaunchAppCompletedDelegate;
|
|
|
|
private SendOrPostCallback launchAppCompletionMethodDelegate;
|
|
|
|
private SendOrPostCallback onKillAppCompletedDelegate;
|
|
|
|
private SendOrPostCallback killAppCompletionMethodDelegate;
|
|
|
|
private AsyncDelegates<InstallProductWorkerEventHandler> installProductDelegates;
|
|
|
|
private AsyncDelegates<UninstallProductWorkerEventHandler> uninstallProductDelegates;
|
|
|
|
private AsyncDelegates<ServiceWorkerEventHandler> serviceGetDelegates;
|
|
|
|
private AsyncDelegates<ServiceWorkerEventHandler> serviceStartDelegates;
|
|
|
|
private AsyncDelegates<ServiceWorkerEventHandler> serviceStopDelegates;
|
|
|
|
private Pod mPod;
|
|
|
|
private IPodManagerConnection mConnection = new PodManagerConnection();
|
|
|
|
private object mConnectSyncRoot = new object();
|
|
|
|
private AsyncOperation mConnectAsyncOp;
|
|
|
|
private Exception mLastConnectionException;
|
|
|
|
private Cached<LaunchData[]> mAllApps = new Cached<LaunchData[]>(TimeSpan.FromMinutes(30.0));
|
|
|
|
private Cached<List<LaunchedAppData>> mRunningApps = new Cached<List<LaunchedAppData>>(TimeSpan.FromSeconds(60.0));
|
|
|
|
private float mVolume = float.NaN;
|
|
|
|
private readonly List<Action<PodInfo>> rConnectActions = new List<Action<PodInfo>>();
|
|
|
|
private IContainer components;
|
|
|
|
public Pod Pod => mPod;
|
|
|
|
public bool IsConnecting => mConnectAsyncOp != null;
|
|
|
|
public bool IsConnected
|
|
{
|
|
get
|
|
{
|
|
if (!IsConnecting && mConnection != null)
|
|
{
|
|
return mConnection.IsOpen;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public Exception LastConnectionException => mLastConnectionException;
|
|
|
|
public LaunchData[] AllApplications
|
|
{
|
|
get
|
|
{
|
|
if (mAllApps.IsExpired && mConnection.IsOpen)
|
|
{
|
|
try
|
|
{
|
|
mAllApps.Item = mConnection.GetInstalledApps();
|
|
}
|
|
catch
|
|
{
|
|
mConnection.Close();
|
|
mAllApps.Expire();
|
|
return new LaunchData[0];
|
|
}
|
|
}
|
|
return mAllApps.Item;
|
|
}
|
|
}
|
|
|
|
public LaunchedAppData[] RunningApplications
|
|
{
|
|
get
|
|
{
|
|
if (!mConnection.IsOpen)
|
|
{
|
|
return new LaunchedAppData[0];
|
|
}
|
|
if (mRunningApps.IsExpired)
|
|
{
|
|
try
|
|
{
|
|
lock (mRunningApps.SyncRoot)
|
|
{
|
|
mRunningApps.Item = new List<LaunchedAppData>(mConnection.GetLaunchedApps());
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
mConnection.Close();
|
|
lock (mRunningApps.SyncRoot)
|
|
{
|
|
mRunningApps.Expire();
|
|
}
|
|
return new LaunchedAppData[0];
|
|
}
|
|
}
|
|
if (mRunningApps.Item == null)
|
|
{
|
|
return null;
|
|
}
|
|
return mRunningApps.Item.ToArray();
|
|
}
|
|
}
|
|
|
|
public float Volume
|
|
{
|
|
get
|
|
{
|
|
if (float.IsNaN(mVolume) && mConnection.IsOpen)
|
|
{
|
|
try
|
|
{
|
|
mVolume = mConnection.VolumeLevel;
|
|
}
|
|
catch
|
|
{
|
|
mConnection.Close();
|
|
mVolume = float.NaN;
|
|
}
|
|
}
|
|
return mVolume;
|
|
}
|
|
set
|
|
{
|
|
if (mConnection.IsOpen)
|
|
{
|
|
mVolume = value;
|
|
try
|
|
{
|
|
mConnection.VolumeLevel = value;
|
|
}
|
|
catch
|
|
{
|
|
mConnection.Close();
|
|
mVolume = float.NaN;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsServiceRunning
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
ServiceController serviceController = new ServiceController("TeslaLauncherService", mPod.IPAddress.ToString());
|
|
return serviceController.Status == ServiceControllerStatus.Running;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public event EventHandler<AsyncCompletedEventArgs> ConnectCompleted;
|
|
|
|
public event EventHandler<ApplicationCompletedEventArgs> LaunchApplicationCompleted;
|
|
|
|
public event EventHandler<ApplicationCompletedEventArgs> KillApplicationCompleted;
|
|
|
|
public event EventHandler<InstallProductProgressEventArgs> InstallProductProgress;
|
|
|
|
public event EventHandler<InstallProductCompletedEventArgs> InstallProductCompleted;
|
|
|
|
public event EventHandler<AsyncCompletedEventArgs> UninstallProductCompleted;
|
|
|
|
public event EventHandler<ServiceStatusEventArgs> ServiceStatusGetCompleted;
|
|
|
|
public event EventHandler<AsyncCompletedEventArgs> ServiceStartCompleted;
|
|
|
|
public event EventHandler<AsyncCompletedEventArgs> ServiceStopCompleted;
|
|
|
|
public PodInfo()
|
|
{
|
|
InitializeComponent();
|
|
InitializeDelegates();
|
|
}
|
|
|
|
public PodInfo(IContainer container)
|
|
{
|
|
container.Add(this);
|
|
InitializeComponent();
|
|
InitializeDelegates();
|
|
}
|
|
|
|
public PodInfo(Pod pod)
|
|
: this(pod, register: false)
|
|
{
|
|
}
|
|
|
|
public PodInfo(Pod pod, bool register)
|
|
: this()
|
|
{
|
|
mPod = pod;
|
|
if (register)
|
|
{
|
|
RegisterPod(this);
|
|
}
|
|
}
|
|
|
|
protected virtual void InitializeDelegates()
|
|
{
|
|
connectDelegates = new AsyncDelegates<ConnectWorkerEventHandler>(ConnectCompletedInternal, ConnectCompletionMethod);
|
|
onLaunchAppCompletedDelegate = LaunchAppCompleted;
|
|
launchAppCompletionMethodDelegate = LaunchAppCompletionMethod;
|
|
onKillAppCompletedDelegate = KillAppCompleted;
|
|
killAppCompletionMethodDelegate = KillAppCompletionMethod;
|
|
installProductDelegates = new AsyncDelegates<InstallProductWorkerEventHandler>(InstallProductReportProgress, InstallProductInternalCompleted, InstallProductCompletionMethod);
|
|
uninstallProductDelegates = new AsyncDelegates<UninstallProductWorkerEventHandler>(UninstallProductInternalCompleted, UninstallProductCompletionMethod);
|
|
serviceGetDelegates = new AsyncDelegates<ServiceWorkerEventHandler>(ServiceStatusGetCompletedInternal, ServiceStatusGetCompletionMethod);
|
|
serviceStartDelegates = new AsyncDelegates<ServiceWorkerEventHandler>(ServiceStartCompletedInternal, ServiceStartCompletionMethod);
|
|
serviceStopDelegates = new AsyncDelegates<ServiceWorkerEventHandler>(ServiceStopCompletedInternal, ServiceStopCompletionMethod);
|
|
}
|
|
|
|
public static void RegisterPod(PodInfo pod)
|
|
{
|
|
mPods[pod.Pod.ID] = pod;
|
|
}
|
|
|
|
public static PodInfo GetPod(Guid id)
|
|
{
|
|
PodInfo value = null;
|
|
if (mPods.TryGetValue(id, out value))
|
|
{
|
|
return value;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public bool EnqueueConnectAction(Action<PodInfo> action)
|
|
{
|
|
if (IsConnected)
|
|
{
|
|
action(this);
|
|
return true;
|
|
}
|
|
rConnectActions.Add(action);
|
|
return false;
|
|
}
|
|
|
|
public void Disconnect()
|
|
{
|
|
if (mConnectAsyncOp != null)
|
|
{
|
|
CancelConnectAsync();
|
|
}
|
|
else if (IsConnected)
|
|
{
|
|
mConnection.Close();
|
|
}
|
|
mLastConnectionException = null;
|
|
mAllApps.Expire();
|
|
mRunningApps.Expire();
|
|
mVolume = float.NaN;
|
|
}
|
|
|
|
public void Refresh()
|
|
{
|
|
if (!IsConnected)
|
|
{
|
|
throw new InvalidOperationException("Cannot refresh pod info while disconnected.");
|
|
}
|
|
try
|
|
{
|
|
FullUpdateData fullUpdateData = mConnection.FullUpdate();
|
|
mAllApps.Item = fullUpdateData.InstalledApps;
|
|
lock (mRunningApps.SyncRoot)
|
|
{
|
|
mRunningApps.Item = new List<LaunchedAppData>(fullUpdateData.LaunchedApps);
|
|
}
|
|
mVolume = fullUpdateData.VolumeLevel;
|
|
}
|
|
catch
|
|
{
|
|
mConnection.Close();
|
|
mAllApps.Expire();
|
|
lock (mRunningApps.SyncRoot)
|
|
{
|
|
mRunningApps.Expire();
|
|
}
|
|
mVolume = float.NaN;
|
|
}
|
|
}
|
|
|
|
public bool IsAppRunning(Guid appId)
|
|
{
|
|
LaunchedAppData[] runningApplications = RunningApplications;
|
|
return Array.Exists(runningApplications, (LaunchedAppData app) => app.LaunchKey == appId);
|
|
}
|
|
|
|
public void AddApp(LaunchData appData)
|
|
{
|
|
if (!IsConnected)
|
|
{
|
|
throw new InvalidOperationException("Application cannot be added while disconnected.");
|
|
}
|
|
try
|
|
{
|
|
mConnection.InstallApp(appData);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Program.LogException(ex, "Add Application Failed.");
|
|
mConnection.Close();
|
|
}
|
|
}
|
|
|
|
public void Shutdown(bool restart)
|
|
{
|
|
try
|
|
{
|
|
mConnection.Shutdown(restart);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Program.LogExceptionAndShowDialog(ex, "Remote Shutdown Failed.", "Remote Shutdown Failed.");
|
|
}
|
|
}
|
|
|
|
public void ClearStore()
|
|
{
|
|
mConnection.ClearStore();
|
|
}
|
|
|
|
private void ConnectCompletedInternal(object operationState)
|
|
{
|
|
AsyncCompletedEventArgs e = operationState as AsyncCompletedEventArgs;
|
|
OnConnectCompleted(e);
|
|
}
|
|
|
|
protected void OnConnectCompleted(AsyncCompletedEventArgs e)
|
|
{
|
|
if (IsConnected)
|
|
{
|
|
foreach (Action<PodInfo> rConnectAction in rConnectActions)
|
|
{
|
|
rConnectAction(this);
|
|
}
|
|
rConnectActions.Clear();
|
|
}
|
|
if (this.ConnectCompleted != null)
|
|
{
|
|
this.ConnectCompleted(this, e);
|
|
}
|
|
}
|
|
|
|
private void ConnectCompletionMethod(object completionState)
|
|
{
|
|
AsyncControlState asyncControlState = completionState as AsyncControlState;
|
|
AsyncCompletedEventArgs asyncCompletedEventArgs = new AsyncCompletedEventArgs(asyncControlState.ex, cancelled: false, asyncControlState.asyncOp.UserSuppliedState);
|
|
mLastConnectionException = asyncCompletedEventArgs.Error;
|
|
lock (mConnectSyncRoot)
|
|
{
|
|
if (mConnectAsyncOp != null)
|
|
{
|
|
mConnectAsyncOp.PostOperationCompleted(connectDelegates.OnCompleted, asyncCompletedEventArgs);
|
|
mConnectAsyncOp = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ConnectWorker(AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate)
|
|
{
|
|
Exception ex = null;
|
|
mAllApps.Expire();
|
|
mRunningApps.Expire();
|
|
mVolume = float.NaN;
|
|
try
|
|
{
|
|
if (!mConnection.IsOpen)
|
|
{
|
|
if (mPod.IPAddress == IPAddress.None)
|
|
{
|
|
throw new ArgumentException("Pod IP address cannot be empty.", "IPAddress");
|
|
}
|
|
if (mPod.Key == null || mPod.Key.Length == 0)
|
|
{
|
|
throw new ArgumentException("Pod encryption key cannot be empty.", "Key");
|
|
}
|
|
mConnection.Open(new IPEndPoint(mPod.IPAddress, 53290), mPod.Key);
|
|
}
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
ex = ex2;
|
|
}
|
|
AsyncControlState state = new AsyncControlState(ex, asyncOp);
|
|
completionMethodDelegate(state);
|
|
}
|
|
|
|
public void ConnectAsync(object userState)
|
|
{
|
|
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(userState);
|
|
lock (mConnectSyncRoot)
|
|
{
|
|
if (mConnectAsyncOp != null)
|
|
{
|
|
throw new InvalidOperationException("Already connecting. Cannot connect again.");
|
|
}
|
|
mConnectAsyncOp = asyncOp;
|
|
}
|
|
ThreadPool.QueueUserWorkItem(delegate
|
|
{
|
|
ConnectWorker(asyncOp, connectDelegates.CompletionMethod);
|
|
});
|
|
}
|
|
|
|
public void CancelConnectAsync()
|
|
{
|
|
lock (mConnectSyncRoot)
|
|
{
|
|
if (mConnectAsyncOp != null)
|
|
{
|
|
AsyncCompletedEventArgs arg = new AsyncCompletedEventArgs(null, cancelled: true, mConnectAsyncOp.UserSuppliedState);
|
|
mConnectAsyncOp.PostOperationCompleted(connectDelegates.OnCompleted, arg);
|
|
mConnectAsyncOp = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void LaunchAppCompleted(object operationState)
|
|
{
|
|
ApplicationCompletedEventArgs e = operationState as ApplicationCompletedEventArgs;
|
|
OnLaunchApplicationCompleted(e);
|
|
}
|
|
|
|
protected void OnLaunchApplicationCompleted(ApplicationCompletedEventArgs e)
|
|
{
|
|
if (this.LaunchApplicationCompleted != null)
|
|
{
|
|
this.LaunchApplicationCompleted(this, e);
|
|
}
|
|
}
|
|
|
|
private void LaunchAppCompletionMethod(object completionState)
|
|
{
|
|
AsyncControlState<Guid> asyncControlState = completionState as AsyncControlState<Guid>;
|
|
ApplicationCompletedEventArgs arg = new ApplicationCompletedEventArgs(asyncControlState.state, asyncControlState.ex, canceled: false, asyncControlState.asyncOp.UserSuppliedState);
|
|
asyncControlState.asyncOp.PostOperationCompleted(onLaunchAppCompletedDelegate, arg);
|
|
}
|
|
|
|
private void LaunchAppWorker(Guid appId, AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate)
|
|
{
|
|
Exception ex = null;
|
|
try
|
|
{
|
|
int processId = mConnection.LaunchApp(appId);
|
|
lock (mRunningApps.SyncRoot)
|
|
{
|
|
if (mRunningApps.Item == null)
|
|
{
|
|
mRunningApps.Item = new List<LaunchedAppData>();
|
|
}
|
|
mRunningApps.Item.Add(new LaunchedAppData
|
|
{
|
|
ProcessId = processId,
|
|
LaunchKey = appId
|
|
});
|
|
}
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
ex = ex2;
|
|
}
|
|
completionMethodDelegate(new AsyncControlState<Guid>(appId, ex, asyncOp));
|
|
}
|
|
|
|
public void LaunchApplicationAsync(Guid appId, object userState)
|
|
{
|
|
if (!IsConnected)
|
|
{
|
|
throw new InvalidOperationException("Cannot launch application while disconnected.");
|
|
}
|
|
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(userState);
|
|
ThreadPool.QueueUserWorkItem(delegate
|
|
{
|
|
LaunchAppWorker(appId, asyncOp, launchAppCompletionMethodDelegate);
|
|
});
|
|
}
|
|
|
|
private void KillAppCompleted(object operationState)
|
|
{
|
|
ApplicationCompletedEventArgs e = operationState as ApplicationCompletedEventArgs;
|
|
OnKillApplicationCompleted(e);
|
|
}
|
|
|
|
protected void OnKillApplicationCompleted(ApplicationCompletedEventArgs e)
|
|
{
|
|
if (this.KillApplicationCompleted != null)
|
|
{
|
|
this.KillApplicationCompleted(this, e);
|
|
}
|
|
}
|
|
|
|
private void KillAppCompletionMethod(object completionState)
|
|
{
|
|
AsyncControlState<Guid> asyncControlState = completionState as AsyncControlState<Guid>;
|
|
ApplicationCompletedEventArgs arg = new ApplicationCompletedEventArgs(asyncControlState.state, asyncControlState.ex, canceled: false, asyncControlState.asyncOp.UserSuppliedState);
|
|
asyncControlState.asyncOp.PostOperationCompleted(onKillAppCompletedDelegate, arg);
|
|
}
|
|
|
|
private void KillAppWorker(Guid appId, AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate)
|
|
{
|
|
Exception ex = null;
|
|
try
|
|
{
|
|
mConnection.KillAllOfType(appId);
|
|
lock (mRunningApps.SyncRoot)
|
|
{
|
|
if (mRunningApps.Item != null)
|
|
{
|
|
for (int num = mRunningApps.Item.Count - 1; num >= 0; num--)
|
|
{
|
|
if (mRunningApps.Item[num].LaunchKey == appId)
|
|
{
|
|
mRunningApps.Item.RemoveAt(num);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
ex = ex2;
|
|
}
|
|
AsyncControlState<Guid> state = new AsyncControlState<Guid>(appId, ex, asyncOp);
|
|
completionMethodDelegate(state);
|
|
}
|
|
|
|
public void KillApplicationAsync(Guid appId, object userState)
|
|
{
|
|
if (!IsConnected)
|
|
{
|
|
throw new InvalidOperationException("Cannot kill application while disconnected.");
|
|
}
|
|
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(userState);
|
|
ThreadPool.QueueUserWorkItem(delegate
|
|
{
|
|
KillAppWorker(appId, asyncOp, killAppCompletionMethodDelegate);
|
|
});
|
|
}
|
|
|
|
private void InstallProductInternalCompleted(object operationState)
|
|
{
|
|
InstallProductCompletedEventArgs e = operationState as InstallProductCompletedEventArgs;
|
|
OnInstallProductCompleted(e);
|
|
}
|
|
|
|
private void InstallProductReportProgress(object state)
|
|
{
|
|
InstallProductProgressEventArgs e = state as InstallProductProgressEventArgs;
|
|
OnInstallProductProgress(e);
|
|
}
|
|
|
|
protected void OnInstallProductCompleted(InstallProductCompletedEventArgs e)
|
|
{
|
|
if (this.InstallProductCompleted != null)
|
|
{
|
|
this.InstallProductCompleted(this, e);
|
|
}
|
|
}
|
|
|
|
protected void OnInstallProductProgress(InstallProductProgressEventArgs e)
|
|
{
|
|
if (this.InstallProductProgress != null)
|
|
{
|
|
this.InstallProductProgress(this, e);
|
|
}
|
|
}
|
|
|
|
private void InstallProductCompletionMethod(object completionState)
|
|
{
|
|
AsyncControlState<string> asyncControlState = completionState as AsyncControlState<string>;
|
|
InstallProductCompletedEventArgs arg = new InstallProductCompletedEventArgs(asyncControlState.state, asyncControlState.ex, canceled: false, asyncControlState.asyncOp.UserSuppliedState);
|
|
asyncControlState.asyncOp.PostOperationCompleted(installProductDelegates.OnCompleted, arg);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens the management connection if needed. A connection the launcher has
|
|
/// dropped still reports IsOpen (Connected is stale until an I/O fails), and
|
|
/// the launcher drops sessions idle for ~30s — easily hit while the operator
|
|
/// sits in the install dialogs — so probe an "open" connection with a Ping
|
|
/// and reconnect when it turns out to be dead.
|
|
/// </summary>
|
|
private void EnsureConnectionAlive()
|
|
{
|
|
if (mConnection.IsOpen)
|
|
{
|
|
try
|
|
{
|
|
mConnection.Ping(DateTime.Now);
|
|
return;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
try
|
|
{
|
|
mConnection.Close();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
if (!mConnection.IsOpen)
|
|
{
|
|
mConnection.Open(new IPEndPoint(mPod.IPAddress, ManagePort), mPod.Key);
|
|
}
|
|
}
|
|
|
|
private void InstallProductWorker(string filePath, SendOrPostCallback progressCallback, AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate)
|
|
{
|
|
Exception ex = null;
|
|
try
|
|
{
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
EnsureConnectionAlive();
|
|
Guid guid = mConnection.InstallProduct(filePath);
|
|
if (guid == Guid.Empty)
|
|
{
|
|
throw new Exception($"'{filePath}' is an invalid install file.");
|
|
}
|
|
OutOfBandProgress outOfBandProgress;
|
|
do
|
|
{
|
|
Thread.Sleep(250);
|
|
outOfBandProgress = mConnection.GetOutOfBandProgress(guid);
|
|
InstallProductProgressEventArgs arg = new InstallProductProgressEventArgs(outOfBandProgress.Status, outOfBandProgress.PercentComplete, asyncOp.UserSuppliedState);
|
|
asyncOp.Post(installProductDelegates.OnProgressReport, arg);
|
|
if (progressCallback != null)
|
|
{
|
|
asyncOp.Post(progressCallback, arg);
|
|
}
|
|
}
|
|
while (!outOfBandProgress.IsCompleted);
|
|
if (outOfBandProgress.PercentComplete == 99)
|
|
{
|
|
mAllApps.Expire();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
Program.LogException(ex2, "Installation Failed");
|
|
ex = ex2;
|
|
}
|
|
AsyncControlState<string> state = new AsyncControlState<string>(filePath, ex, asyncOp);
|
|
completionMethodDelegate(state);
|
|
}
|
|
|
|
public void InstallProductAsync(string uncPath, object userState)
|
|
{
|
|
InstallProductAsync(uncPath, userState, null);
|
|
}
|
|
|
|
public void InstallProductAsync(string uncPath, object userState, Action<InstallProductProgressEventArgs> progressCallback)
|
|
{
|
|
if (!IsConnected)
|
|
{
|
|
throw new InvalidOperationException("Cannot install application while disconnected.");
|
|
}
|
|
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(userState);
|
|
ThreadPool.QueueUserWorkItem(delegate
|
|
{
|
|
InstallProductWorker(uncPath, delegate(object e)
|
|
{
|
|
progressCallback((InstallProductProgressEventArgs)e);
|
|
}, asyncOp, installProductDelegates.CompletionMethod);
|
|
});
|
|
}
|
|
|
|
private void UninstallProductInternalCompleted(object operationState)
|
|
{
|
|
AsyncCompletedEventArgs e = operationState as AsyncCompletedEventArgs;
|
|
OnUninstallProductCompleted(e);
|
|
}
|
|
|
|
protected void OnUninstallProductCompleted(AsyncCompletedEventArgs e)
|
|
{
|
|
if (this.UninstallProductCompleted != null)
|
|
{
|
|
this.UninstallProductCompleted(this, e);
|
|
}
|
|
}
|
|
|
|
private void UninstallProductCompletionMethod(object completionState)
|
|
{
|
|
AsyncControlState asyncControlState = completionState as AsyncControlState;
|
|
AsyncCompletedEventArgs arg = new AsyncCompletedEventArgs(asyncControlState.ex, cancelled: false, asyncControlState.asyncOp.UserSuppliedState);
|
|
asyncControlState.asyncOp.PostOperationCompleted(uninstallProductDelegates.OnCompleted, arg);
|
|
}
|
|
|
|
private void UninstallProductWorker(Guid launchKey, AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate)
|
|
{
|
|
Exception ex = null;
|
|
try
|
|
{
|
|
EnsureConnectionAlive();
|
|
mConnection.UninstallApp(launchKey);
|
|
mAllApps.Expire();
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
Program.LogException(ex2, "Uninstallation Failed");
|
|
ex = ex2;
|
|
}
|
|
AsyncControlState state = new AsyncControlState(ex, asyncOp);
|
|
completionMethodDelegate(state);
|
|
}
|
|
|
|
public void UninstallProductAsync(Guid launchKey, object userState)
|
|
{
|
|
if (!IsConnected)
|
|
{
|
|
throw new InvalidOperationException("Cannot uninstall application while disconnected.");
|
|
}
|
|
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(userState);
|
|
ThreadPool.QueueUserWorkItem(delegate
|
|
{
|
|
UninstallProductWorker(launchKey, asyncOp, uninstallProductDelegates.CompletionMethod);
|
|
});
|
|
}
|
|
|
|
private void ServiceStatusGetCompletedInternal(object operationState)
|
|
{
|
|
ServiceStatusEventArgs e = operationState as ServiceStatusEventArgs;
|
|
OnServiceStatusGetCompleted(e);
|
|
}
|
|
|
|
protected void OnServiceStatusGetCompleted(ServiceStatusEventArgs e)
|
|
{
|
|
if (this.ServiceStatusGetCompleted != null)
|
|
{
|
|
this.ServiceStatusGetCompleted(this, e);
|
|
}
|
|
}
|
|
|
|
private void ServiceStatusGetCompletionMethod(object completionState)
|
|
{
|
|
AsyncControlState<ServiceControllerStatus> asyncControlState = completionState as AsyncControlState<ServiceControllerStatus>;
|
|
ServiceStatusEventArgs arg = new ServiceStatusEventArgs(asyncControlState.state, asyncControlState.ex, canceled: false, asyncControlState.asyncOp.UserSuppliedState);
|
|
asyncControlState.asyncOp.PostOperationCompleted(serviceGetDelegates.OnCompleted, arg);
|
|
}
|
|
|
|
private void ServiceStatusWorker(string machineName, AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate)
|
|
{
|
|
Exception ex = null;
|
|
ServiceControllerStatus state = ServiceControllerStatus.Stopped;
|
|
try
|
|
{
|
|
if (IPAddress.TryParse(machineName, out var address))
|
|
{
|
|
IPHostEntry hostEntry = Dns.GetHostEntry(address);
|
|
machineName = hostEntry.HostName;
|
|
}
|
|
using ServiceController serviceController = new ServiceController("TeslaLauncherService", machineName);
|
|
state = serviceController.Status;
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
ex = ex2;
|
|
}
|
|
AsyncControlState<ServiceControllerStatus> state2 = new AsyncControlState<ServiceControllerStatus>(state, ex, asyncOp);
|
|
completionMethodDelegate(state2);
|
|
}
|
|
|
|
public void GetServiceStatusAsync(string machineName, object userState)
|
|
{
|
|
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(userState);
|
|
ThreadPool.QueueUserWorkItem(delegate
|
|
{
|
|
ServiceStatusWorker(machineName, asyncOp, serviceGetDelegates.CompletionMethod);
|
|
});
|
|
}
|
|
|
|
private void ServiceStartCompletedInternal(object operationState)
|
|
{
|
|
ServiceStatusEventArgs e = operationState as ServiceStatusEventArgs;
|
|
OnServiceStartCompleted(e);
|
|
}
|
|
|
|
protected void OnServiceStartCompleted(ServiceStatusEventArgs e)
|
|
{
|
|
if (this.ServiceStartCompleted != null)
|
|
{
|
|
this.ServiceStartCompleted(this, e);
|
|
}
|
|
}
|
|
|
|
private void ServiceStartCompletionMethod(object completionState)
|
|
{
|
|
AsyncControlState<ServiceControllerStatus> asyncControlState = completionState as AsyncControlState<ServiceControllerStatus>;
|
|
ServiceStatusEventArgs arg = new ServiceStatusEventArgs(asyncControlState.state, asyncControlState.ex, canceled: false, asyncControlState.asyncOp.UserSuppliedState);
|
|
asyncControlState.asyncOp.PostOperationCompleted(serviceGetDelegates.OnCompleted, arg);
|
|
}
|
|
|
|
private void ServiceStartWorker(string machineName, AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate)
|
|
{
|
|
Exception ex = null;
|
|
try
|
|
{
|
|
if (IPAddress.TryParse(machineName, out var address))
|
|
{
|
|
IPHostEntry hostEntry = Dns.GetHostEntry(address);
|
|
machineName = hostEntry.HostName;
|
|
}
|
|
using ServiceController serviceController = new ServiceController("TeslaLauncherService", machineName);
|
|
serviceController.Start();
|
|
serviceController.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(30.0));
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
ex = ex2;
|
|
}
|
|
AsyncControlState state = new AsyncControlState(ex, asyncOp);
|
|
completionMethodDelegate(state);
|
|
}
|
|
|
|
public void ServiceStartAsync(string machineName, object userState)
|
|
{
|
|
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(userState);
|
|
ThreadPool.QueueUserWorkItem(delegate
|
|
{
|
|
ServiceStartWorker(machineName, asyncOp, serviceStartDelegates.CompletionMethod);
|
|
});
|
|
}
|
|
|
|
private void ServiceStopCompletedInternal(object operationState)
|
|
{
|
|
ServiceStatusEventArgs e = operationState as ServiceStatusEventArgs;
|
|
OnServiceStopCompleted(e);
|
|
}
|
|
|
|
protected void OnServiceStopCompleted(ServiceStatusEventArgs e)
|
|
{
|
|
if (this.ServiceStopCompleted != null)
|
|
{
|
|
this.ServiceStopCompleted(this, e);
|
|
}
|
|
}
|
|
|
|
private void ServiceStopCompletionMethod(object completionState)
|
|
{
|
|
AsyncControlState<ServiceControllerStatus> asyncControlState = completionState as AsyncControlState<ServiceControllerStatus>;
|
|
ServiceStatusEventArgs arg = new ServiceStatusEventArgs(asyncControlState.state, asyncControlState.ex, canceled: false, asyncControlState.asyncOp.UserSuppliedState);
|
|
asyncControlState.asyncOp.PostOperationCompleted(serviceGetDelegates.OnCompleted, arg);
|
|
}
|
|
|
|
private void ServiceStopWorker(string machineName, AsyncOperation asyncOp, SendOrPostCallback completionMethodDelegate)
|
|
{
|
|
Exception ex = null;
|
|
try
|
|
{
|
|
if (IPAddress.TryParse(machineName, out var address))
|
|
{
|
|
IPHostEntry hostEntry = Dns.GetHostEntry(address);
|
|
machineName = hostEntry.HostName;
|
|
}
|
|
using ServiceController serviceController = new ServiceController("TeslaLauncherService", machineName);
|
|
serviceController.Stop();
|
|
serviceController.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(30.0));
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
ex = ex2;
|
|
}
|
|
AsyncControlState state = new AsyncControlState(ex, asyncOp);
|
|
completionMethodDelegate(state);
|
|
}
|
|
|
|
public void ServiceStopAsync(string machineName, object userState)
|
|
{
|
|
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(userState);
|
|
ThreadPool.QueueUserWorkItem(delegate
|
|
{
|
|
ServiceStopWorker(machineName, asyncOp, serviceStopDelegates.CompletionMethod);
|
|
});
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing && components != null)
|
|
{
|
|
components.Dispose();
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
private void InitializeComponent()
|
|
{
|
|
components = new Container();
|
|
}
|
|
}
|