diff options
author | sr55 <[email protected]> | 2012-08-19 16:43:13 +0000 |
---|---|---|
committer | sr55 <[email protected]> | 2012-08-19 16:43:13 +0000 |
commit | 4934b148e573ceffad62cbb2e8c09c95a0fe61b3 (patch) | |
tree | 5b07f3e8fa74b7f83573746cfe77a9f6c6c2b5ee /win/CS/HandBrakeWPF | |
parent | 5ea4bd4112519332f1f420d4fd751bc8829e0509 (diff) |
WinGui: Prototype of process isolation support (to be used for libhb when this is fixed up). Uses WCF for process communication.
Initially for the scan service only, encode service proxy coming soon.
No changes required for the UI application. Two new implementations of IScan and IEncode will act as a proxy between the UI and the Server Service Layer.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@4911 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'win/CS/HandBrakeWPF')
-rw-r--r-- | win/CS/HandBrakeWPF/HandBrakeWPF.csproj | 9 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Isolation/BackgroundServiceConnector.cs | 225 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Isolation/Interfaces/IIsolatedScanService.cs | 24 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Isolation/IsolatedScanService.cs | 220 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs | 38 | ||||
-rw-r--r-- | win/CS/HandBrakeWPF/Views/MainView.xaml | 1 |
6 files changed, 512 insertions, 5 deletions
diff --git a/win/CS/HandBrakeWPF/HandBrakeWPF.csproj b/win/CS/HandBrakeWPF/HandBrakeWPF.csproj index b9b5a69da..d560cc961 100644 --- a/win/CS/HandBrakeWPF/HandBrakeWPF.csproj +++ b/win/CS/HandBrakeWPF/HandBrakeWPF.csproj @@ -95,6 +95,8 @@ <Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Drawing" />
<Reference Include="System.Management" />
+ <Reference Include="System.Runtime.Serialization" />
+ <Reference Include="System.ServiceModel" />
<Reference Include="System.Windows.Interactivity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\libraries\caliburn\System.Windows.Interactivity.dll</HintPath>
</Reference>
@@ -127,6 +129,9 @@ <Compile Include="Converters\Options\OptionsTabConverter.cs" />
<Compile Include="Converters\Subtitles\SubtitlesQueueDisplayConverter.cs" />
<Compile Include="Converters\Video\VideoEncoderConverter.cs" />
+ <Compile Include="Isolation\BackgroundServiceConnector.cs" />
+ <Compile Include="Isolation\Interfaces\IIsolatedScanService.cs" />
+ <Compile Include="Isolation\IsolatedScanService.cs" />
<Compile Include="Services\DriveDetectService.cs" />
<Compile Include="Services\Interfaces\IDriveDetectService.cs" />
<Compile Include="Model\ShellWindow.cs" />
@@ -420,6 +425,10 @@ <Project>{F0A61F62-2C3B-4A87-AFF4-0C4256253DA1}</Project>
<Name>HandBrakeInterop</Name>
</ProjectReference>
+ <ProjectReference Include="..\HandBrake.Server\HandBrake.Server.csproj">
+ <Project>{36847BA0-6814-41E1-B1C3-1D9D874418E9}</Project>
+ <Name>HandBrake.Server</Name>
+ </ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
diff --git a/win/CS/HandBrakeWPF/Isolation/BackgroundServiceConnector.cs b/win/CS/HandBrakeWPF/Isolation/BackgroundServiceConnector.cs new file mode 100644 index 000000000..b6198da46 --- /dev/null +++ b/win/CS/HandBrakeWPF/Isolation/BackgroundServiceConnector.cs @@ -0,0 +1,225 @@ +// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="BackgroundServiceConnector.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// Background Service Connector.
+// HandBrake has the ability to connect to a service app that will control HandBrakeCLI or Libhb.
+// This acts as process isolation.
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Isolation
+{
+ using System;
+ using System.Diagnostics;
+ using System.ServiceModel;
+ using System.Threading;
+
+ using HandBrake.ApplicationServices.EventArgs;
+ using HandBrake.ApplicationServices.Services.Interfaces;
+
+ using HandBrakeWPF.Services.Interfaces;
+
+ /// <summary>
+ /// Background Service Connector.
+ /// HandBrake has the ability to connect to a service app that will control HandBrakeCLI or Libhb.
+ /// This acts as process isolation.
+ /// </summary>
+ public class BackgroundServiceConnector : IHbServiceCallback, IDisposable
+ {
+ /// <summary>
+ /// The error service.
+ /// </summary>
+ private readonly IErrorService errorService;
+
+ #region Constants and Fields
+
+ /// <summary>
+ /// Gets or sets the pipe factory.
+ /// DuplexChannelFactory is necessary for Callbacks.
+ /// </summary>
+ private DuplexChannelFactory<IServerService> pipeFactory;
+
+ /// <summary>
+ /// The background process.
+ /// </summary>
+ private Process backgroundProcess;
+
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BackgroundServiceConnector"/> class.
+ /// </summary>
+ /// <param name="errorService">
+ /// The error service.
+ /// </param>
+ public BackgroundServiceConnector(IErrorService errorService)
+ {
+ this.errorService = errorService;
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether is connected.
+ /// </summary>
+ public bool IsConnected { get; set; }
+
+ /// <summary>
+ /// Gets or sets the service.
+ /// </summary>
+ public IServerService Service { get; set; }
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// The can connect.
+ /// </summary>
+ /// <returns>
+ /// The System.Boolean.
+ /// </returns>
+ public bool CanConnect()
+ {
+ return true;
+ }
+
+ /// <summary>
+ /// The connect.
+ /// </summary>
+ public void Connect()
+ {
+ ThreadPool.QueueUserWorkItem(delegate
+ {
+ try
+ {
+ this.pipeFactory = new DuplexChannelFactory<IServerService>(
+ new InstanceContext(this),
+ new NetTcpBinding(),
+ new EndpointAddress("net.tcp://127.0.0.1:8000/IHbService"));
+
+ // Connect and Subscribe to the Server
+ this.Service = this.pipeFactory.CreateChannel();
+ this.Service.Subscribe();
+ this.IsConnected = true;
+ }
+ catch (Exception exc)
+ {
+ Caliburn.Micro.Execute.OnUIThread(() => this.errorService.ShowError("Unable to connect to background worker service", "Please restart HandBrake", exc));
+ }
+ });
+ }
+
+ /// <summary>
+ /// The disconnect.
+ /// </summary>
+ public void Disconnect()
+ {
+ if (backgroundProcess != null && !backgroundProcess.HasExited)
+ {
+ try
+ {
+ this.Service.Unsubscribe();
+ }
+ catch (Exception exc)
+ {
+ this.errorService.ShowError("Unable to disconnect from service", "It may have already close. Check for any left over HandBrake.Server.exe processes", exc);
+ }
+ }
+ }
+
+ /// <summary>
+ /// The scan source.
+ /// </summary>
+ /// <param name="path">
+ /// The path.
+ /// </param>
+ /// <param name="title">
+ /// The title.
+ /// </param>
+ /// <param name="previewCount">
+ /// The preview count.
+ /// </param>
+ public void ScanSource(string path, int title, int previewCount)
+ {
+ ThreadPool.QueueUserWorkItem(delegate { this.Service.ScanSource(path, title, previewCount); });
+ }
+
+ /// <summary>
+ /// The start server.
+ /// </summary>
+ public void StartServer()
+ {
+ if (this.backgroundProcess == null)
+ {
+ this.backgroundProcess = Process.Start("HandBrake.Server.exe");
+ }
+ }
+
+ #endregion
+
+ #region Implemented Interfaces
+
+ #region IDisposable
+
+ /// <summary>
+ /// The dispose.
+ /// </summary>
+ public void Dispose()
+ {
+ this.Service.Unsubscribe();
+ }
+
+ #endregion
+
+ #region IHbServiceCallback
+
+ /// <summary>
+ /// The scan completed.
+ /// </summary>
+ /// <param name="eventArgs">
+ /// The event args.
+ /// </param>
+ public virtual void ScanCompletedCallback(ScanCompletedEventArgs eventArgs)
+ {
+ }
+
+ /// <summary>
+ /// The scan progress.
+ /// </summary>
+ /// <param name="eventArgs">
+ /// The event args.
+ /// </param>
+ public virtual void ScanProgressCallback(ScanProgressEventArgs eventArgs)
+ {
+ }
+
+ /// <summary>
+ /// The scan started callback.
+ /// </summary>
+ public virtual void ScanStartedCallback()
+ {
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Implementation of IHbServiceCallback
+
+ /// <summary>
+ /// The test.
+ /// </summary>
+ /// <param name="message">
+ /// The message.
+ /// </param>
+ public void Test(string message)
+ {
+ Console.WriteLine(message);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Isolation/Interfaces/IIsolatedScanService.cs b/win/CS/HandBrakeWPF/Isolation/Interfaces/IIsolatedScanService.cs new file mode 100644 index 000000000..7b94deb58 --- /dev/null +++ b/win/CS/HandBrakeWPF/Isolation/Interfaces/IIsolatedScanService.cs @@ -0,0 +1,24 @@ +// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="IIsolatedScanService.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// The Isolated Scan Service interface.
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Isolation.Interfaces
+{
+ using HandBrake.ApplicationServices.Services.Interfaces;
+
+ /// <summary>
+ /// The Isolated Scan Service interface.
+ /// </summary>
+ public interface IIsolatedScanService : IScan
+ {
+ /// <summary>
+ /// The disconnect.
+ /// </summary>
+ void Disconnect();
+ }
+}
\ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Isolation/IsolatedScanService.cs b/win/CS/HandBrakeWPF/Isolation/IsolatedScanService.cs new file mode 100644 index 000000000..01153d2c6 --- /dev/null +++ b/win/CS/HandBrakeWPF/Isolation/IsolatedScanService.cs @@ -0,0 +1,220 @@ +// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="IsolatedScanService.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// Isolated Scan Service
+// This is an implementation of the IScan implementation that runs scans on a seperate process
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrakeWPF.Isolation
+{
+ using System;
+ using System.Threading;
+
+ using HandBrake.ApplicationServices.EventArgs;
+ using HandBrake.ApplicationServices.Parsing;
+ using HandBrake.ApplicationServices.Services.Interfaces;
+
+ using HandBrakeWPF.Isolation.Interfaces;
+ using HandBrakeWPF.Services.Interfaces;
+
+ /// <summary>
+ /// Isolated Scan Service.
+ /// This is an implementation of the IScan implementation that runs scans on a seperate process
+ /// </summary>
+ public class IsolatedScanService : BackgroundServiceConnector, IIsolatedScanService
+ {
+ #region Constants and Fields
+
+ /// <summary>
+ /// The post action.
+ /// </summary>
+ private Action<bool> postScanAction;
+
+ #endregion
+
+ #region Events
+
+ /// <summary>
+ /// The scan completed.
+ /// </summary>
+ public event ScanCompletedStatus ScanCompleted;
+
+ /// <summary>
+ /// The scan stared.
+ /// </summary>
+ public event EventHandler ScanStared;
+
+ /// <summary>
+ /// The scan status changed.
+ /// </summary>
+ public event ScanProgessStatus ScanStatusChanged;
+
+ #endregion
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IsolatedScanService"/> class.
+ /// </summary>
+ /// <param name="errorService">
+ /// The error Service.
+ /// </param>
+ public IsolatedScanService(IErrorService errorService) : base(errorService)
+ {
+ try
+ {
+ if (this.CanConnect())
+ {
+ this.StartServer();
+ this.Connect();
+ }
+ }
+ catch (Exception exception)
+ {
+ errorService.ShowError(
+ "Unable to connect to scan worker process.", "Try restarting HandBrake", exception);
+ }
+ }
+
+ #region Properties
+
+ /// <summary>
+ /// Gets ActivityLog.
+ /// </summary>
+ public string ActivityLog
+ {
+ get
+ {
+ return this.Service.ActivityLog;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether IsScanning.
+ /// </summary>
+ public bool IsScanning
+ {
+ get
+ {
+ return this.Service.IsScanning;
+ }
+ }
+
+ /// <summary>
+ /// Gets the Souce Data.
+ /// </summary>
+ public Source SouceData
+ {
+ get
+ {
+ return this.Service.SouceData;
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// The scan completed callback.
+ /// </summary>
+ /// <param name="eventArgs">
+ /// The event args.
+ /// </param>
+ public override void ScanCompletedCallback(ScanCompletedEventArgs eventArgs)
+ {
+ if (this.postScanAction != null)
+ {
+ this.postScanAction(true);
+ }
+
+ if (this.ScanCompleted != null)
+ {
+ ThreadPool.QueueUserWorkItem(delegate { this.ScanCompleted(this, eventArgs); });
+ }
+
+ base.ScanCompletedCallback(eventArgs);
+ }
+
+ /// <summary>
+ /// The scan progress callback.
+ /// </summary>
+ /// <param name="eventArgs">
+ /// The event args.
+ /// </param>
+ public override void ScanProgressCallback(ScanProgressEventArgs eventArgs)
+ {
+ if (this.ScanStatusChanged != null)
+ {
+ ThreadPool.QueueUserWorkItem(delegate { this.ScanStatusChanged(this, eventArgs); });
+ }
+
+ base.ScanProgressCallback(eventArgs);
+ }
+
+ /// <summary>
+ /// The scan started callback.
+ /// </summary>
+ public override void ScanStartedCallback()
+ {
+ if (this.ScanStared != null)
+ {
+ ThreadPool.QueueUserWorkItem(delegate { this.ScanStared(this, EventArgs.Empty); });
+ }
+
+ base.ScanStartedCallback();
+ }
+
+ #endregion
+
+ #region Implemented Interfaces
+
+ #region IScan
+
+ /// <summary>
+ /// Take a Scan Log file, and process it as if it were from the CLI.
+ /// </summary>
+ /// <param name="path">
+ /// The path to the log file.
+ /// </param>
+ public void DebugScanLog(string path)
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Scan a Source Path.
+ /// Title 0: scan all
+ /// </summary>
+ /// <param name="sourcePath">
+ /// Path to the file to scan
+ /// </param>
+ /// <param name="title">
+ /// int title number. 0 for scan all
+ /// </param>
+ /// <param name="previewCount">
+ /// The preview Count.
+ /// </param>
+ /// <param name="postAction">
+ /// The post Action.
+ /// </param>
+ public void Scan(string sourcePath, int title, int previewCount, Action<bool> postAction)
+ {
+ this.postScanAction = postAction;
+ this.Service.ScanSource(sourcePath, title, previewCount);
+ }
+
+ /// <summary>
+ /// Kill the scan
+ /// </summary>
+ public void Stop()
+ {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
\ No newline at end of file diff --git a/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs index 606d87a96..3fc83ddac 100644 --- a/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs +++ b/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs @@ -29,6 +29,8 @@ namespace HandBrakeWPF.ViewModels using HandBrakeWPF.Commands;
using HandBrakeWPF.Helpers;
+ using HandBrakeWPF.Isolation;
+ using HandBrakeWPF.Isolation.Interfaces;
using HandBrakeWPF.Model;
using HandBrakeWPF.Services.Interfaces;
using HandBrakeWPF.ViewModels.Interfaces;
@@ -48,7 +50,7 @@ namespace HandBrakeWPF.ViewModels /// <summary>
/// The Source Scan Service.
/// </summary>
- private readonly IScan scanService;
+ private IScan scanService;
/// <summary>
/// The Encode Service
@@ -198,7 +200,6 @@ namespace HandBrakeWPF.ViewModels {
GeneralUtilities.SetInstanceId();
-
this.scanService = scanService;
this.encodeService = encodeService;
this.presetService = presetService;
@@ -819,6 +820,13 @@ namespace HandBrakeWPF.ViewModels // Shutdown Service
this.driveDetectService.Close();
+ IIsolatedScanService isolatedScanService = this.scanService as IsolatedScanService;
+ if (isolatedScanService != null)
+ {
+ // Kill any background services for this instance of HandBrake.
+ isolatedScanService.Disconnect();
+ }
+
// Unsubscribe from Events.
this.scanService.ScanStared -= this.ScanStared;
this.scanService.ScanCompleted -= this.ScanCompleted;
@@ -1133,6 +1141,26 @@ namespace HandBrakeWPF.ViewModels }
}
+ /// <summary>
+ /// The test isolation services.
+ /// Swaps out the implementation of IScan to the IsolatedScanService version.
+ /// </summary>
+ public void TestIsolationServices()
+ {
+ // Unhook the old service
+ this.scanService.ScanStared -= this.ScanStared;
+ this.scanService.ScanCompleted -= this.ScanCompleted;
+ this.scanService.ScanStatusChanged -= this.ScanStatusChanged;
+
+ // Replace the Service
+ this.scanService = new IsolatedScanService(this.errorService);
+
+ // Add Event Hooks
+ this.scanService.ScanStared += this.ScanStared;
+ this.scanService.ScanCompleted += this.ScanCompleted;
+ this.scanService.ScanStatusChanged += this.ScanStatusChanged;
+ }
+
#endregion
#region Main Window Public Methods
@@ -1562,19 +1590,19 @@ namespace HandBrakeWPF.ViewModels /// </param>
private void ScanCompleted(object sender, HandBrake.ApplicationServices.EventArgs.ScanCompletedEventArgs e)
{
+ this.scanService.SouceData.CopyTo(this.ScannedSource);
Execute.OnUIThread(() =>
{
if (e.Successful)
{
- this.scanService.SouceData.CopyTo(this.ScannedSource);
this.NotifyOfPropertyChange("ScannedSource");
this.NotifyOfPropertyChange("ScannedSource.Titles");
this.SelectedTitle = this.ScannedSource.Titles.FirstOrDefault(t => t.MainTitle)
?? this.ScannedSource.Titles.FirstOrDefault();
- this.SetupTabs();
- this.ShowStatusWindow = false;
+ this.SetupTabs();
}
+ this.ShowStatusWindow = false;
this.SourceLabel = "Scan Completed";
this.StatusLabel = "Scan Completed";
});
diff --git a/win/CS/HandBrakeWPF/Views/MainView.xaml b/win/CS/HandBrakeWPF/Views/MainView.xaml index 57e7beee4..c7f6c14c4 100644 --- a/win/CS/HandBrakeWPF/Views/MainView.xaml +++ b/win/CS/HandBrakeWPF/Views/MainView.xaml @@ -134,6 +134,7 @@ <MenuItem Header="Debug" Foreground="Transparent" >
<MenuItem Header="Show CLI Equiv" Micro:Message.Attach="[Event Click] = [Action ShowCliQuery]" />
<MenuItem Header="Debug Scan Log" Micro:Message.Attach="[Event Click] = [Action DebugScanLog]" />
+ <MenuItem Header="Test Isolation Service" Micro:Message.Attach="[Event Click] = [Action TestIsolationServices]" />
</MenuItem>
</Menu>
|