Added bit.ly support for distiributives download; Updated year in copyright text;
This commit is contained in:
parent
8b7b150da7
commit
2dc3ced5cf
12 changed files with 1210 additions and 955 deletions
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011, Outercurve Foundation.
|
||||
// Copyright (c) 2012, Outercurve Foundation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
|
@ -34,43 +34,43 @@ using System.IO;
|
|||
namespace WebsitePanel.Installer.Common
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// File utils.
|
||||
/// </summary>
|
||||
public sealed class FileUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the class.
|
||||
/// </summary>
|
||||
private FileUtils()
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// File utils.
|
||||
/// </summary>
|
||||
public sealed class FileUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the class.
|
||||
/// </summary>
|
||||
private FileUtils()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates drectory with the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The directory path to create.</param>
|
||||
public static void CreateDirectory(string path)
|
||||
{
|
||||
string dir = Path.GetDirectoryName(path);
|
||||
if(!Directory.Exists(dir))
|
||||
{
|
||||
// create directory structure
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates drectory with the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The directory path to create.</param>
|
||||
public static void CreateDirectory(string path)
|
||||
{
|
||||
string dir = Path.GetDirectoryName(path);
|
||||
if(!Directory.Exists(dir))
|
||||
{
|
||||
// create directory structure
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves file content.
|
||||
/// </summary>
|
||||
/// <param name="fileName">File name.</param>
|
||||
/// <param name="content">The array of bytes to write.</param>
|
||||
public static void SaveFileContent(string fileName, byte[] content)
|
||||
{
|
||||
FileStream stream = new FileStream(fileName, FileMode.Create);
|
||||
stream.Write(content, 0, content.Length);
|
||||
stream.Close();
|
||||
}
|
||||
/// <summary>
|
||||
/// Saves file content.
|
||||
/// </summary>
|
||||
/// <param name="fileName">File name.</param>
|
||||
/// <param name="content">The array of bytes to write.</param>
|
||||
public static void SaveFileContent(string fileName, byte[] content)
|
||||
{
|
||||
FileStream stream = new FileStream(fileName, FileMode.Create);
|
||||
stream.Write(content, 0, content.Length);
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves file content.
|
||||
|
@ -84,188 +84,203 @@ namespace WebsitePanel.Installer.Common
|
|||
stream.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified file.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file to be deleted.</param>
|
||||
public static void DeleteFile(string fileName)
|
||||
{
|
||||
int attempts = 0;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteFileInternal(fileName);
|
||||
break;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (attempts > 2)
|
||||
throw;
|
||||
/// <summary>
|
||||
/// Deletes the specified file.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file to be deleted.</param>
|
||||
public static void DeleteFile(string fileName)
|
||||
{
|
||||
int attempts = 0;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteFileInternal(fileName);
|
||||
break;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (attempts > 2)
|
||||
throw;
|
||||
|
||||
attempts++;
|
||||
System.Threading.Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
attempts++;
|
||||
System.Threading.Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified file.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file to be deleted.</param>
|
||||
private static void DeleteReadOnlyFile(string fileName)
|
||||
{
|
||||
FileInfo info = new FileInfo(fileName);
|
||||
info.Attributes = FileAttributes.Normal;
|
||||
info.Delete();
|
||||
}
|
||||
/// <summary>
|
||||
/// Deletes the specified file.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file to be deleted.</param>
|
||||
private static void DeleteReadOnlyFile(string fileName)
|
||||
{
|
||||
FileInfo info = new FileInfo(fileName);
|
||||
info.Attributes = FileAttributes.Normal;
|
||||
info.Delete();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified file.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file to be deleted.</param>
|
||||
private static void DeleteFileInternal(string fileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(fileName);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
DeleteReadOnlyFile(fileName);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Deletes the specified file.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file to be deleted.</param>
|
||||
private static void DeleteFileInternal(string fileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(fileName);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
DeleteReadOnlyFile(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="directory">The name of the directory to be deleted.</param>
|
||||
public static void DeleteDirectory(string directory)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
return;
|
||||
/// <summary>
|
||||
/// Deletes the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="directory">The name of the directory to be deleted.</param>
|
||||
public static void DeleteDirectory(string directory)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
return;
|
||||
|
||||
// iterate through child folders
|
||||
string[] dirs = Directory.GetDirectories(directory);
|
||||
foreach (string dir in dirs)
|
||||
{
|
||||
DeleteDirectory(dir);
|
||||
}
|
||||
// iterate through child folders
|
||||
string[] dirs = Directory.GetDirectories(directory);
|
||||
foreach (string dir in dirs)
|
||||
{
|
||||
DeleteDirectory(dir);
|
||||
}
|
||||
|
||||
// iterate through child files
|
||||
string[] files = Directory.GetFiles(directory);
|
||||
foreach (string file in files)
|
||||
{
|
||||
DeleteFile(file);
|
||||
}
|
||||
// iterate through child files
|
||||
string[] files = Directory.GetFiles(directory);
|
||||
foreach (string file in files)
|
||||
{
|
||||
DeleteFile(file);
|
||||
}
|
||||
|
||||
//try to delete dir for 3 times
|
||||
int attempts = 0;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteDirectoryInternal(directory);
|
||||
break;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (attempts > 2)
|
||||
throw;
|
||||
//try to delete dir for 3 times
|
||||
int attempts = 0;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteDirectoryInternal(directory);
|
||||
break;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (attempts > 2)
|
||||
throw;
|
||||
|
||||
attempts++;
|
||||
System.Threading.Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
attempts++;
|
||||
System.Threading.Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="directory">The name of the directory to be deleted.</param>
|
||||
private static void DeleteDirectoryInternal(string directory)
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(directory);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
DeleteReadOnlyDirectory(directory);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Deletes the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="directory">The name of the directory to be deleted.</param>
|
||||
private static void DeleteDirectoryInternal(string directory)
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(directory);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
DeleteReadOnlyDirectory(directory);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="directory">The name of the directory to be deleted.</param>
|
||||
private static void DeleteReadOnlyDirectory(string directory)
|
||||
{
|
||||
DirectoryInfo info = new DirectoryInfo(directory);
|
||||
info.Attributes = FileAttributes.Normal;
|
||||
info.Delete();
|
||||
}
|
||||
/// <summary>
|
||||
/// Deletes the specified directory.
|
||||
/// </summary>
|
||||
/// <param name="directory">The name of the directory to be deleted.</param>
|
||||
private static void DeleteReadOnlyDirectory(string directory)
|
||||
{
|
||||
DirectoryInfo info = new DirectoryInfo(directory);
|
||||
info.Attributes = FileAttributes.Normal;
|
||||
info.Delete();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified file exists.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The path to check.</param>
|
||||
/// <returns></returns>
|
||||
public static bool FileExists(string fileName)
|
||||
{
|
||||
return File.Exists(fileName);
|
||||
}
|
||||
/// <summary>
|
||||
/// Determines whether the specified file exists.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The path to check.</param>
|
||||
/// <returns></returns>
|
||||
public static bool FileExists(string fileName)
|
||||
{
|
||||
return File.Exists(fileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given path refers to an existing directory on disk.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to test.</param>
|
||||
/// <returns></returns>
|
||||
public static bool DirectoryExists(string path)
|
||||
{
|
||||
return Directory.Exists(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns current application path.
|
||||
/// </summary>
|
||||
/// <returns>Curent application path.</returns>
|
||||
public static string GetCurrentDirectory()
|
||||
{
|
||||
return AppDomain.CurrentDomain.BaseDirectory;
|
||||
}
|
||||
/// <summary>
|
||||
/// Determines whether the given path refers to an existing directory on disk.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to test.</param>
|
||||
/// <returns></returns>
|
||||
public static bool DirectoryExists(string path)
|
||||
{
|
||||
return Directory.Exists(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns current application path.
|
||||
/// </summary>
|
||||
/// <returns>Curent application path.</returns>
|
||||
public static string GetCurrentDirectory()
|
||||
{
|
||||
return AppDomain.CurrentDomain.BaseDirectory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns application temp directory.
|
||||
/// </summary>
|
||||
/// <returns>Application temp directory.</returns>
|
||||
public static string GetTempDirectory()
|
||||
{
|
||||
return Path.Combine(GetCurrentDirectory(), "Tmp");
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns application temp directory.
|
||||
/// </summary>
|
||||
/// <returns>Application temp directory.</returns>
|
||||
public static string GetTempDirectory()
|
||||
{
|
||||
return Path.Combine(GetCurrentDirectory(), "Tmp");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns application data directory.
|
||||
/// </summary>
|
||||
/// <returns>Application data directory.</returns>
|
||||
public static string GetDataDirectory()
|
||||
{
|
||||
return Path.Combine(GetCurrentDirectory(), "Data");
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns application data directory.
|
||||
/// </summary>
|
||||
/// <returns>Application data directory.</returns>
|
||||
public static string GetDataDirectory()
|
||||
{
|
||||
return Path.Combine(GetCurrentDirectory(), "Data");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes application temp directory.
|
||||
/// </summary>
|
||||
public static void DeleteTempDirectory()
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteDirectory(GetTempDirectory());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.WriteError("IO Error", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Deletes application's Data folder.
|
||||
/// </summary>
|
||||
public static void DeleteDataDirectory()
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteDirectory(GetDataDirectory());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.WriteError("IO Error", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes application Tmp directory.
|
||||
/// </summary>
|
||||
public static void DeleteTempDirectory()
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteDirectory(GetTempDirectory());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.WriteError("IO Error", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011, Outercurve Foundation.
|
||||
// Copyright (c) 2012, Outercurve Foundation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
|
@ -45,6 +45,7 @@ using System.Threading;
|
|||
using WebsitePanel.Installer.Core;
|
||||
using WebsitePanel.Installer.Configuration;
|
||||
using System.Xml;
|
||||
using System.Data;
|
||||
|
||||
namespace WebsitePanel.Installer.Common
|
||||
{
|
||||
|
@ -389,5 +390,23 @@ namespace WebsitePanel.Installer.Common
|
|||
mutex = new Mutex(true, "WebsitePanel Installer", out createdNew);
|
||||
return createdNew;
|
||||
}
|
||||
|
||||
public static string GetDistributiveLocationInfo(string ccode, string cversion)
|
||||
{
|
||||
var service = ServiceProviderProxy.GetInstallerWebService();
|
||||
//
|
||||
DataSet ds = service.GetReleaseFileInfo(ccode, cversion);
|
||||
//
|
||||
if (ds == null || ds.Tables.Count == 0 || ds.Tables[0].Rows.Count == 0)
|
||||
{
|
||||
Log.WriteInfo("Component code: {0}; Component version: {1};", ccode, cversion);
|
||||
//
|
||||
throw new ServiceComponentNotFoundException("Seems that the Service has no idea about the component requested.");
|
||||
}
|
||||
//
|
||||
DataRow row = ds.Tables[0].Rows[0];
|
||||
//
|
||||
return row["FullFilePath"].ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011, Outercurve Foundation.
|
||||
// Copyright (c) 2012, Outercurve Foundation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
|
@ -36,380 +36,495 @@ using System.Text;
|
|||
using Ionic.Zip;
|
||||
|
||||
using WebsitePanel.Installer.Common;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Collections;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace WebsitePanel.Installer.Core
|
||||
{
|
||||
public class LoaderEventArgs<T> : EventArgs
|
||||
{
|
||||
public string StatusMessage { get; set; }
|
||||
public T EventData { get; set; }
|
||||
public bool Cancellable { get; set; }
|
||||
}
|
||||
public class LoaderEventArgs<T> : EventArgs
|
||||
{
|
||||
public string StatusMessage { get; set; }
|
||||
public T EventData { get; set; }
|
||||
public bool Cancellable { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loader form.
|
||||
/// </summary>
|
||||
public partial class Loader
|
||||
{
|
||||
public const string ConnectingRemotServiceMessage = "Connecting...";
|
||||
public const string DownloadingSetupFilesMessage = "Downloading setup files...";
|
||||
public const string CopyingSetupFilesMessage = "Copying setup files...";
|
||||
public const string PreparingSetupFilesMessage = "Please wait while Setup prepares the necessary files...";
|
||||
public const string DownloadProgressMessage = "{0} KB of {1} KB";
|
||||
public const string PrepareSetupProgressMessage = "{0}%";
|
||||
public static class LoaderFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates either BitlyLoader or InstallerServiceLoader based on remote file format.
|
||||
/// </summary>
|
||||
/// <param name="remoteFile"></param>
|
||||
/// <returns></returns>
|
||||
public static Loader CreateFileLoader(string remoteFile)
|
||||
{
|
||||
Debug.Assert(!String.IsNullOrEmpty(remoteFile), "Remote file is empty");
|
||||
|
||||
private const int ChunkSize = 262144;
|
||||
private Thread thread;
|
||||
private string localFile;
|
||||
private string remoteFile;
|
||||
private string componentCode;
|
||||
private string version;
|
||||
if (remoteFile.StartsWith("http://bit.ly/"))
|
||||
{
|
||||
return new BitlyLoader(remoteFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Loader(remoteFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<LoaderEventArgs<String>> StatusChanged;
|
||||
public event EventHandler<LoaderEventArgs<Exception>> OperationFailed;
|
||||
public event EventHandler<LoaderEventArgs<Int32>> ProgressChanged;
|
||||
public event EventHandler<EventArgs> OperationCompleted;
|
||||
public class BitlyLoader : Loader
|
||||
{
|
||||
public const string WEB_PI_USER_AGENT_HEADER = "PI-Integrator/3.0.0.0({0})";
|
||||
|
||||
public Loader(string remoteFile)
|
||||
{
|
||||
this.remoteFile = remoteFile;
|
||||
}
|
||||
private WebClient fileLoader;
|
||||
|
||||
public Loader(string localFile, string componentCode, string version)
|
||||
{
|
||||
this.localFile = localFile;
|
||||
this.componentCode = componentCode;
|
||||
this.version = version;
|
||||
}
|
||||
public BitlyLoader(string remoteFile)
|
||||
: base(remoteFile)
|
||||
{
|
||||
InitFileLoader();
|
||||
}
|
||||
|
||||
public void LoadAppDistributive()
|
||||
{
|
||||
thread = new Thread(new ThreadStart(LoadAppDistributiveInternal));
|
||||
thread.Start();
|
||||
}
|
||||
private void InitFileLoader()
|
||||
{
|
||||
fileLoader = new WebClient();
|
||||
// Set HTTP header for Codeplex to allow direct downloads
|
||||
fileLoader.Headers.Add("User-Agent", String.Format(WEB_PI_USER_AGENT_HEADER, Assembly.GetExecutingAssembly().FullName));
|
||||
}
|
||||
|
||||
private void RaiseOnStatusChangedEvent(string statusMessage)
|
||||
{
|
||||
RaiseOnStatusChangedEvent(statusMessage, String.Empty);
|
||||
}
|
||||
protected override Task GetDownloadFileTask(string remoteFile, string tmpFile, CancellationToken ct)
|
||||
{
|
||||
var downloadFileTask = new Task(() =>
|
||||
{
|
||||
if (!File.Exists(tmpFile))
|
||||
{
|
||||
// Mimic synchronous file download operation because we need to track the download progress
|
||||
// and be able to cancel the operation in progress
|
||||
AutoResetEvent autoEvent = new AutoResetEvent(false);
|
||||
|
||||
private void RaiseOnStatusChangedEvent(string statusMessage, string eventData)
|
||||
{
|
||||
RaiseOnStatusChangedEvent(statusMessage, eventData, true);
|
||||
}
|
||||
if (fileLoader.IsBusy.Equals(true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private void RaiseOnStatusChangedEvent(string statusMessage, string eventData, bool cancellable)
|
||||
{
|
||||
if (StatusChanged == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// No event data for status updates
|
||||
StatusChanged(this, new LoaderEventArgs<String> {
|
||||
StatusMessage = statusMessage,
|
||||
EventData = eventData,
|
||||
Cancellable = cancellable
|
||||
});
|
||||
}
|
||||
ct.Register(() =>
|
||||
{
|
||||
fileLoader.CancelAsync();
|
||||
});
|
||||
|
||||
private void RaiseOnProgressChangedEvent(int eventData)
|
||||
{
|
||||
RaiseOnProgressChangedEvent(eventData, true);
|
||||
}
|
||||
Log.WriteStart("Downloading file");
|
||||
Log.WriteInfo("Downloading file \"{0}\" to \"{1}\"", remoteFile, tmpFile);
|
||||
|
||||
// Attach event handlers to track status of the download process
|
||||
fileLoader.DownloadProgressChanged += (obj, e) =>
|
||||
{
|
||||
if (ct.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
private void RaiseOnProgressChangedEvent(int eventData, bool cancellable)
|
||||
{
|
||||
if (ProgressChanged == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//
|
||||
ProgressChanged(this, new LoaderEventArgs<int> {
|
||||
EventData = eventData,
|
||||
Cancellable = cancellable
|
||||
});
|
||||
}
|
||||
RaiseOnProgressChangedEvent(e.ProgressPercentage);
|
||||
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage,
|
||||
String.Format(DownloadProgressMessage, e.BytesReceived / 1024, e.TotalBytesToReceive / 1024));
|
||||
};
|
||||
|
||||
private void RaiseOnOperationFailedEvent(Exception ex)
|
||||
{
|
||||
if (OperationFailed == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//
|
||||
OperationFailed(this, new LoaderEventArgs<Exception> { EventData = ex });
|
||||
}
|
||||
fileLoader.DownloadFileCompleted += (obj, e) =>
|
||||
{
|
||||
if (ct.IsCancellationRequested == false)
|
||||
{
|
||||
RaiseOnProgressChangedEvent(100);
|
||||
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage, "100%");
|
||||
}
|
||||
|
||||
private void RaiseOnOperationCompletedEvent()
|
||||
{
|
||||
if (OperationCompleted == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//
|
||||
OperationCompleted(this, EventArgs.Empty);
|
||||
}
|
||||
if (e.Cancelled)
|
||||
{
|
||||
CancelDownload(tmpFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays process progress.
|
||||
/// </summary>
|
||||
private void LoadAppDistributiveInternal()
|
||||
{
|
||||
//
|
||||
try
|
||||
{
|
||||
var service = ServiceProviderProxy.GetInstallerWebService();
|
||||
//
|
||||
string dataFolder = FileUtils.GetDataDirectory();
|
||||
string tmpFolder = FileUtils.GetTempDirectory();
|
||||
autoEvent.Set();
|
||||
};
|
||||
|
||||
if (!Directory.Exists(dataFolder))
|
||||
{
|
||||
Directory.CreateDirectory(dataFolder);
|
||||
Log.WriteInfo("Data directory created");
|
||||
}
|
||||
fileLoader.DownloadFileAsync(new Uri(remoteFile), tmpFile);
|
||||
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage);
|
||||
|
||||
autoEvent.WaitOne();
|
||||
}
|
||||
}, ct);
|
||||
|
||||
if (Directory.Exists(tmpFolder))
|
||||
{
|
||||
FileUtils.DeleteTempDirectory();
|
||||
}
|
||||
return downloadFileTask;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Directory.Exists(tmpFolder))
|
||||
{
|
||||
Directory.CreateDirectory(tmpFolder);
|
||||
Log.WriteInfo("Tmp directory created");
|
||||
}
|
||||
/// <summary>
|
||||
/// Loader form.
|
||||
/// </summary>
|
||||
public class Loader
|
||||
{
|
||||
public const string ConnectingRemotServiceMessage = "Connecting...";
|
||||
public const string DownloadingSetupFilesMessage = "Downloading setup files...";
|
||||
public const string CopyingSetupFilesMessage = "Copying setup files...";
|
||||
public const string PreparingSetupFilesMessage = "Please wait while Setup prepares the necessary files...";
|
||||
public const string DownloadProgressMessage = "{0} KB of {1} KB";
|
||||
public const string PrepareSetupProgressMessage = "{0}%";
|
||||
|
||||
string fileToDownload = null;
|
||||
if (!string.IsNullOrEmpty(localFile))
|
||||
{
|
||||
fileToDownload = localFile;
|
||||
}
|
||||
else
|
||||
{
|
||||
fileToDownload = Path.GetFileName(remoteFile);
|
||||
}
|
||||
private const int ChunkSize = 262144;
|
||||
private string remoteFile;
|
||||
private CancellationTokenSource cts;
|
||||
|
||||
string destinationFile = Path.Combine(dataFolder, fileToDownload);
|
||||
string tmpFile = Path.Combine(tmpFolder, fileToDownload);
|
||||
public event EventHandler<LoaderEventArgs<String>> StatusChanged;
|
||||
public event EventHandler<LoaderEventArgs<Exception>> OperationFailed;
|
||||
public event EventHandler<LoaderEventArgs<Int32>> ProgressChanged;
|
||||
public event EventHandler<EventArgs> OperationCompleted;
|
||||
|
||||
//check whether file already downloaded
|
||||
if (!File.Exists(destinationFile))
|
||||
{
|
||||
if (string.IsNullOrEmpty(remoteFile))
|
||||
{
|
||||
//need to get remote file name
|
||||
RaiseOnStatusChangedEvent(ConnectingRemotServiceMessage);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(0);
|
||||
//
|
||||
DataSet ds = service.GetReleaseFileInfo(componentCode, version);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(100);
|
||||
//
|
||||
if (ds != null &&
|
||||
ds.Tables.Count > 0 &&
|
||||
ds.Tables[0].Rows.Count > 0)
|
||||
{
|
||||
DataRow row = ds.Tables[0].Rows[0];
|
||||
remoteFile = row["FullFilePath"].ToString();
|
||||
fileToDownload = Path.GetFileName(remoteFile);
|
||||
destinationFile = Path.Combine(dataFolder, fileToDownload);
|
||||
tmpFile = Path.Combine(tmpFolder, fileToDownload);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Installer not found");
|
||||
}
|
||||
}
|
||||
public Loader(string remoteFile)
|
||||
{
|
||||
this.remoteFile = remoteFile;
|
||||
}
|
||||
|
||||
// download file to tmp folder
|
||||
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(0);
|
||||
//
|
||||
DownloadFile(remoteFile, tmpFile);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(100);
|
||||
public void LoadAppDistributive()
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(q => LoadAppDistributiveInternal());
|
||||
}
|
||||
|
||||
// copy downloaded file to data folder
|
||||
RaiseOnStatusChangedEvent(CopyingSetupFilesMessage);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(0);
|
||||
protected void RaiseOnStatusChangedEvent(string statusMessage)
|
||||
{
|
||||
RaiseOnStatusChangedEvent(statusMessage, String.Empty);
|
||||
}
|
||||
|
||||
// Ensure that the target does not exist.
|
||||
if (File.Exists(destinationFile))
|
||||
FileUtils.DeleteFile(destinationFile);
|
||||
File.Move(tmpFile, destinationFile);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(100);
|
||||
}
|
||||
protected void RaiseOnStatusChangedEvent(string statusMessage, string eventData)
|
||||
{
|
||||
RaiseOnStatusChangedEvent(statusMessage, eventData, true);
|
||||
}
|
||||
|
||||
// unzip file
|
||||
RaiseOnStatusChangedEvent(PreparingSetupFilesMessage);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(0);
|
||||
//
|
||||
UnzipFile(destinationFile, tmpFolder);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(100);
|
||||
//
|
||||
RaiseOnOperationCompletedEvent();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (Utils.IsThreadAbortException(ex))
|
||||
return;
|
||||
protected void RaiseOnStatusChangedEvent(string statusMessage, string eventData, bool cancellable)
|
||||
{
|
||||
if (StatusChanged == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// No event data for status updates
|
||||
StatusChanged(this, new LoaderEventArgs<String>
|
||||
{
|
||||
StatusMessage = statusMessage,
|
||||
EventData = eventData,
|
||||
Cancellable = cancellable
|
||||
});
|
||||
}
|
||||
|
||||
Log.WriteError("Loader module error", ex);
|
||||
//
|
||||
RaiseOnOperationFailedEvent(ex);
|
||||
}
|
||||
}
|
||||
protected void RaiseOnProgressChangedEvent(int eventData)
|
||||
{
|
||||
RaiseOnProgressChangedEvent(eventData, true);
|
||||
}
|
||||
|
||||
private void DownloadFile(string sourceFile, string destinationFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
var service = ServiceProviderProxy.GetInstallerWebService();
|
||||
//
|
||||
Log.WriteStart("Downloading file");
|
||||
Log.WriteInfo(string.Format("Downloading file \"{0}\" to \"{1}\"", sourceFile, destinationFile));
|
||||
protected void RaiseOnProgressChangedEvent(int eventData, bool cancellable)
|
||||
{
|
||||
if (ProgressChanged == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//
|
||||
ProgressChanged(this, new LoaderEventArgs<int>
|
||||
{
|
||||
EventData = eventData,
|
||||
Cancellable = cancellable
|
||||
});
|
||||
}
|
||||
|
||||
long downloaded = 0;
|
||||
long fileSize = service.GetFileSize(sourceFile);
|
||||
if (fileSize == 0)
|
||||
{
|
||||
throw new FileNotFoundException("Service returned empty file.", sourceFile);
|
||||
}
|
||||
protected void RaiseOnOperationFailedEvent(Exception ex)
|
||||
{
|
||||
if (OperationFailed == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//
|
||||
OperationFailed(this, new LoaderEventArgs<Exception> { EventData = ex });
|
||||
}
|
||||
|
||||
byte[] content;
|
||||
protected void RaiseOnOperationCompletedEvent()
|
||||
{
|
||||
if (OperationCompleted == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//
|
||||
OperationCompleted(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
while (downloaded < fileSize)
|
||||
{
|
||||
content = service.GetFileChunk(sourceFile, (int)downloaded, ChunkSize);
|
||||
if (content == null)
|
||||
{
|
||||
throw new FileNotFoundException("Service returned NULL file content.", sourceFile);
|
||||
}
|
||||
FileUtils.AppendFileContent(destinationFile, content);
|
||||
downloaded += content.Length;
|
||||
//update progress bar
|
||||
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage,
|
||||
string.Format(DownloadProgressMessage, downloaded / 1024, fileSize / 1024));
|
||||
//
|
||||
RaiseOnProgressChangedEvent(Convert.ToInt32((downloaded * 100) / fileSize));
|
||||
/// <summary>
|
||||
/// Executes a file download request asynchronously
|
||||
/// </summary>
|
||||
private void LoadAppDistributiveInternal()
|
||||
{
|
||||
try
|
||||
{
|
||||
string dataFolder;
|
||||
string tmpFolder;
|
||||
// Retrieve local storage configuration
|
||||
GetLocalStorageInfo(out dataFolder, out tmpFolder);
|
||||
// Initialize storage
|
||||
InitializeLocalStorage(dataFolder, tmpFolder);
|
||||
|
||||
if (content.Length < ChunkSize)
|
||||
break;
|
||||
}
|
||||
//
|
||||
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage, "100%");
|
||||
//
|
||||
Log.WriteEnd(string.Format("Downloaded {0} bytes", downloaded));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (Utils.IsThreadAbortException(ex))
|
||||
return;
|
||||
string fileToDownload = Path.GetFileName(remoteFile);
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
string destinationFile = Path.Combine(dataFolder, fileToDownload);
|
||||
string tmpFile = Path.Combine(tmpFolder, fileToDownload);
|
||||
|
||||
private void UnzipFile(string zipFile, string destFolder)
|
||||
{
|
||||
try
|
||||
{
|
||||
int val = 0;
|
||||
// Negative value means no progress made yet
|
||||
int progress = -1;
|
||||
//
|
||||
Log.WriteStart("Unzipping file");
|
||||
Log.WriteInfo(string.Format("Unzipping file \"{0}\" to the folder \"{1}\"", zipFile, destFolder));
|
||||
cts = new CancellationTokenSource();
|
||||
CancellationToken token = cts.Token;
|
||||
|
||||
long zipSize = 0;
|
||||
var zipInfo = ZipFile.Read(zipFile);
|
||||
try
|
||||
{
|
||||
foreach (ZipEntry entry in zipInfo)
|
||||
{
|
||||
if (!entry.IsDirectory)
|
||||
zipSize += entry.UncompressedSize;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (zipInfo != null)
|
||||
{
|
||||
zipInfo.Dispose();
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
// Download the file requested
|
||||
Task downloadFileTask = GetDownloadFileTask(remoteFile, tmpFile, token);
|
||||
// Move the file downloaded from temporary location to Data folder
|
||||
var moveFileTask = downloadFileTask.ContinueWith((t) =>
|
||||
{
|
||||
if (File.Exists(tmpFile))
|
||||
{
|
||||
// copy downloaded file to data folder
|
||||
RaiseOnStatusChangedEvent(CopyingSetupFilesMessage);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(0);
|
||||
|
||||
long unzipped = 0;
|
||||
//
|
||||
var zip = ZipFile.Read(zipFile);
|
||||
//
|
||||
try
|
||||
{
|
||||
foreach (ZipEntry entry in zip)
|
||||
{
|
||||
//
|
||||
entry.Extract(destFolder, ExtractExistingFileAction.OverwriteSilently);
|
||||
//
|
||||
if (!entry.IsDirectory)
|
||||
unzipped += entry.UncompressedSize;
|
||||
// Ensure that the target does not exist.
|
||||
if (File.Exists(destinationFile))
|
||||
FileUtils.DeleteFile(destinationFile);
|
||||
File.Move(tmpFile, destinationFile);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(100);
|
||||
}
|
||||
}, TaskContinuationOptions.NotOnCanceled);
|
||||
// Unzip file downloaded
|
||||
var unzipFileTask = moveFileTask.ContinueWith((t) =>
|
||||
{
|
||||
if (File.Exists(destinationFile))
|
||||
{
|
||||
RaiseOnStatusChangedEvent(PreparingSetupFilesMessage);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(0);
|
||||
//
|
||||
UnzipFile(destinationFile, tmpFolder);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(100);
|
||||
}
|
||||
}, token);
|
||||
//
|
||||
var notifyCompletionTask = unzipFileTask.ContinueWith((t) =>
|
||||
{
|
||||
RaiseOnOperationCompletedEvent();
|
||||
}, token);
|
||||
|
||||
downloadFileTask.Start();
|
||||
downloadFileTask.Wait();
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
ae.Handle((e) =>
|
||||
{
|
||||
// We handle cancellation requests
|
||||
if (e is OperationCanceledException)
|
||||
{
|
||||
CancelDownload(tmpFile);
|
||||
Log.WriteInfo("Download has been cancelled by the user");
|
||||
return true;
|
||||
}
|
||||
// But other issues just being logged
|
||||
Log.WriteError("Could not download the file", e);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (Utils.IsThreadAbortException(ex))
|
||||
return;
|
||||
|
||||
if (zipSize != 0)
|
||||
{
|
||||
val = Convert.ToInt32(unzipped * 100 / zipSize);
|
||||
// Skip to raise the progress event change when calculated progress
|
||||
// and the current progress value are even
|
||||
if (val == progress)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//
|
||||
RaiseOnStatusChangedEvent(
|
||||
PreparingSetupFilesMessage,
|
||||
String.Format(PrepareSetupProgressMessage, val),
|
||||
false);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(val, false);
|
||||
}
|
||||
}
|
||||
// Notify client the operation can be cancelled at this time
|
||||
RaiseOnProgressChangedEvent(100);
|
||||
//
|
||||
Log.WriteEnd("Unzipped file");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (zip != null)
|
||||
{
|
||||
zip.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (Utils.IsThreadAbortException(ex))
|
||||
return;
|
||||
//
|
||||
RaiseOnOperationFailedEvent(ex);
|
||||
}
|
||||
}
|
||||
Log.WriteError("Loader module error", ex);
|
||||
//
|
||||
RaiseOnOperationFailedEvent(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void AbortOperation()
|
||||
{
|
||||
if (thread != null)
|
||||
{
|
||||
if (thread.IsAlive)
|
||||
{
|
||||
thread.Abort();
|
||||
}
|
||||
thread.Join();
|
||||
}
|
||||
}
|
||||
}
|
||||
protected virtual Task GetDownloadFileTask(string sourceFile, string tmpFile, CancellationToken ct)
|
||||
{
|
||||
var downloadFileTask = new Task(() =>
|
||||
{
|
||||
if (!File.Exists(tmpFile))
|
||||
{
|
||||
var service = ServiceProviderProxy.GetInstallerWebService();
|
||||
|
||||
RaiseOnProgressChangedEvent(0);
|
||||
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage);
|
||||
|
||||
Log.WriteStart("Downloading file");
|
||||
Log.WriteInfo(string.Format("Downloading file \"{0}\" to \"{1}\"", sourceFile, tmpFile));
|
||||
|
||||
long downloaded = 0;
|
||||
long fileSize = service.GetFileSize(sourceFile);
|
||||
if (fileSize == 0)
|
||||
{
|
||||
throw new FileNotFoundException("Service returned empty file.", sourceFile);
|
||||
}
|
||||
|
||||
byte[] content;
|
||||
|
||||
while (downloaded < fileSize)
|
||||
{
|
||||
// Throw OperationCancelledException if there is an incoming cancel request
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
content = service.GetFileChunk(sourceFile, (int)downloaded, ChunkSize);
|
||||
if (content == null)
|
||||
{
|
||||
throw new FileNotFoundException("Service returned NULL file content.", sourceFile);
|
||||
}
|
||||
FileUtils.AppendFileContent(tmpFile, content);
|
||||
downloaded += content.Length;
|
||||
// Update download progress
|
||||
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage,
|
||||
string.Format(DownloadProgressMessage, downloaded / 1024, fileSize / 1024));
|
||||
|
||||
RaiseOnProgressChangedEvent(Convert.ToInt32((downloaded * 100) / fileSize));
|
||||
|
||||
if (content.Length < ChunkSize)
|
||||
break;
|
||||
}
|
||||
|
||||
RaiseOnStatusChangedEvent(DownloadingSetupFilesMessage, "100%");
|
||||
Log.WriteEnd(string.Format("Downloaded {0} bytes", downloaded));
|
||||
}
|
||||
}, ct);
|
||||
|
||||
return downloadFileTask;
|
||||
}
|
||||
|
||||
private static void InitializeLocalStorage(string dataFolder, string tmpFolder)
|
||||
{
|
||||
if (!Directory.Exists(dataFolder))
|
||||
{
|
||||
Directory.CreateDirectory(dataFolder);
|
||||
Log.WriteInfo("Data directory created");
|
||||
}
|
||||
|
||||
if (Directory.Exists(tmpFolder))
|
||||
{
|
||||
Directory.Delete(tmpFolder, true);
|
||||
}
|
||||
|
||||
if (!Directory.Exists(tmpFolder))
|
||||
{
|
||||
Directory.CreateDirectory(tmpFolder);
|
||||
Log.WriteInfo("Tmp directory created");
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetLocalStorageInfo(out string dataFolder, out string tmpFolder)
|
||||
{
|
||||
dataFolder = FileUtils.GetDataDirectory();
|
||||
tmpFolder = FileUtils.GetTempDirectory();
|
||||
}
|
||||
|
||||
private void UnzipFile(string zipFile, string destFolder)
|
||||
{
|
||||
try
|
||||
{
|
||||
int val = 0;
|
||||
// Negative value means no progress made yet
|
||||
int progress = -1;
|
||||
//
|
||||
Log.WriteStart("Unzipping file");
|
||||
Log.WriteInfo(string.Format("Unzipping file \"{0}\" to the folder \"{1}\"", zipFile, destFolder));
|
||||
|
||||
long zipSize = 0;
|
||||
var zipInfo = ZipFile.Read(zipFile);
|
||||
try
|
||||
{
|
||||
foreach (ZipEntry entry in zipInfo)
|
||||
{
|
||||
if (!entry.IsDirectory)
|
||||
zipSize += entry.UncompressedSize;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (zipInfo != null)
|
||||
{
|
||||
zipInfo.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
long unzipped = 0;
|
||||
//
|
||||
var zip = ZipFile.Read(zipFile);
|
||||
//
|
||||
try
|
||||
{
|
||||
foreach (ZipEntry entry in zip)
|
||||
{
|
||||
//
|
||||
entry.Extract(destFolder, ExtractExistingFileAction.OverwriteSilently);
|
||||
//
|
||||
if (!entry.IsDirectory)
|
||||
unzipped += entry.UncompressedSize;
|
||||
|
||||
if (zipSize != 0)
|
||||
{
|
||||
val = Convert.ToInt32(unzipped * 100 / zipSize);
|
||||
// Skip to raise the progress event change when calculated progress
|
||||
// and the current progress value are even
|
||||
if (val == progress)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//
|
||||
RaiseOnStatusChangedEvent(
|
||||
PreparingSetupFilesMessage,
|
||||
String.Format(PrepareSetupProgressMessage, val),
|
||||
false);
|
||||
//
|
||||
RaiseOnProgressChangedEvent(val, false);
|
||||
}
|
||||
}
|
||||
// Notify client the operation can be cancelled at this time
|
||||
RaiseOnProgressChangedEvent(100);
|
||||
//
|
||||
Log.WriteEnd("Unzipped file");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (zip != null)
|
||||
{
|
||||
zip.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (Utils.IsThreadAbortException(ex))
|
||||
return;
|
||||
//
|
||||
RaiseOnOperationFailedEvent(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up temporary file if the download process has been cancelled.
|
||||
/// </summary>
|
||||
/// <param name="tmpFile">Path to the temporary file to cleanup</param>
|
||||
protected virtual void CancelDownload(string tmpFile)
|
||||
{
|
||||
if (File.Exists(tmpFile))
|
||||
{
|
||||
File.Delete(tmpFile);
|
||||
}
|
||||
}
|
||||
|
||||
public void AbortOperation()
|
||||
{
|
||||
// Make sure we are in business
|
||||
if (cts != null)
|
||||
{
|
||||
cts.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace WebsitePanel.Installer.Core
|
||||
{
|
||||
class ServiceComponentNotFoundException : Exception
|
||||
{
|
||||
private string p;
|
||||
|
||||
public ServiceComponentNotFoundException(string p)
|
||||
: base(p)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,6 +35,7 @@
|
|||
<Reference Include="Ionic.Zip.Reduced">
|
||||
<HintPath>..\..\Lib\Ionic.Zip.Reduced.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.configuration" />
|
||||
<Reference Include="System.Data" />
|
||||
|
@ -80,6 +81,7 @@
|
|||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ServiceComponentNotFoundException.cs" />
|
||||
<Compile Include="ServiceProviderProxy.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue