diff --git a/MediaBrowser.Installer/App.config b/MediaBrowser.Installer/App.config index eb69b68d0d..558196382b 100644 --- a/MediaBrowser.Installer/App.config +++ b/MediaBrowser.Installer/App.config @@ -2,7 +2,7 @@ - + diff --git a/MediaBrowser.Installer/Code/ShellLinkNative.cs b/MediaBrowser.Installer/Code/ShellLinkNative.cs new file mode 100644 index 0000000000..20e0bc6c3d --- /dev/null +++ b/MediaBrowser.Installer/Code/ShellLinkNative.cs @@ -0,0 +1,276 @@ +/************************************************************************** +* +* Filename: ShellLinkNative.cs +* Author: Mattias Sjögren (mattias@mvps.org) +* http://www.msjogren.net/dotnet/ +* +* Description: Defines the native types used to manipulate shell shortcuts. +* +* Public types: enum SLR_FLAGS +* enum SLGP_FLAGS +* struct WIN32_FIND_DATA[A|W] +* interface IPersistFile +* interface IShellLink[A|W] +* class ShellLink +* +* +* Copyright ©2001-2002, Mattias Sjögren +* +**************************************************************************/ + +using System; +using System.Text; +using System.Runtime.InteropServices; + +namespace MediaBrowser.Installer.Code +{ + // IShellLink.Resolve fFlags + [Flags()] + public enum SLR_FLAGS + { + SLR_NO_UI = 0x1, + SLR_ANY_MATCH = 0x2, + SLR_UPDATE = 0x4, + SLR_NOUPDATE = 0x8, + SLR_NOSEARCH = 0x10, + SLR_NOTRACK = 0x20, + SLR_NOLINKINFO = 0x40, + SLR_INVOKE_MSI = 0x80 + } + + // IShellLink.GetPath fFlags + [Flags()] + public enum SLGP_FLAGS + { + SLGP_SHORTPATH = 0x1, + SLGP_UNCPRIORITY = 0x2, + SLGP_RAWPATH = 0x4 + } + + [StructLayoutAttribute(LayoutKind.Sequential, CharSet=CharSet.Ansi)] + public struct WIN32_FIND_DATAA + { + public int dwFileAttributes; + public FILETIME ftCreationTime; + public FILETIME ftLastAccessTime; + public FILETIME ftLastWriteTime; + public int nFileSizeHigh; + public int nFileSizeLow; + public int dwReserved0; + public int dwReserved1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_PATH)] + public string cFileName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=14)] + public string cAlternateFileName; + private const int MAX_PATH = 260; + } + + [StructLayoutAttribute(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public struct WIN32_FIND_DATAW + { + public int dwFileAttributes; + public FILETIME ftCreationTime; + public FILETIME ftLastAccessTime; + public FILETIME ftLastWriteTime; + public int nFileSizeHigh; + public int nFileSizeLow; + public int dwReserved0; + public int dwReserved1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_PATH)] + public string cFileName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=14)] + public string cAlternateFileName; + private const int MAX_PATH = 260; + } + + [ + ComImport(), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("0000010B-0000-0000-C000-000000000046") + ] + public interface IPersistFile + { + #region Methods inherited from IPersist + + void GetClassID( + out Guid pClassID); + + #endregion + + [PreserveSig()] + int IsDirty(); + + void Load( + [MarshalAs(UnmanagedType.LPWStr)] string pszFileName, + int dwMode); + + void Save( + [MarshalAs(UnmanagedType.LPWStr)] string pszFileName, + [MarshalAs(UnmanagedType.Bool)] bool fRemember); + + void SaveCompleted( + [MarshalAs(UnmanagedType.LPWStr)] string pszFileName); + + void GetCurFile( + out IntPtr ppszFileName); + + } + + [ + ComImport(), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214EE-0000-0000-C000-000000000046") + ] + public interface IShellLinkA + { + void GetPath( + [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszFile, + int cchMaxPath, + out WIN32_FIND_DATAA pfd, + SLGP_FLAGS fFlags); + + void GetIDList( + out IntPtr ppidl); + + void SetIDList( + IntPtr pidl); + + void GetDescription( + [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszName, + int cchMaxName); + + void SetDescription( + [MarshalAs(UnmanagedType.LPStr)] string pszName); + + void GetWorkingDirectory( + [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszDir, + int cchMaxPath); + + void SetWorkingDirectory( + [MarshalAs(UnmanagedType.LPStr)] string pszDir); + + void GetArguments( + [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszArgs, + int cchMaxPath); + + void SetArguments( + [MarshalAs(UnmanagedType.LPStr)] string pszArgs); + + void GetHotkey( + out short pwHotkey); + + void SetHotkey( + short wHotkey); + + void GetShowCmd( + out int piShowCmd); + + void SetShowCmd( + int iShowCmd); + + void GetIconLocation( + [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszIconPath, + int cchIconPath, + out int piIcon); + + void SetIconLocation( + [MarshalAs(UnmanagedType.LPStr)] string pszIconPath, + int iIcon); + + void SetRelativePath( + [MarshalAs(UnmanagedType.LPStr)] string pszPathRel, + int dwReserved); + + void Resolve( + IntPtr hwnd, + SLR_FLAGS fFlags); + + void SetPath( + [MarshalAs(UnmanagedType.LPStr)] string pszFile); + + } + + [ + ComImport(), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046") + ] + public interface IShellLinkW + { + void GetPath( + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, + out WIN32_FIND_DATAW pfd, + SLGP_FLAGS fFlags); + + void GetIDList( + out IntPtr ppidl); + + void SetIDList( + IntPtr pidl); + + void GetDescription( + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, + int cchMaxName); + + void SetDescription( + [MarshalAs(UnmanagedType.LPWStr)] string pszName); + + void GetWorkingDirectory( + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + + void SetWorkingDirectory( + [MarshalAs(UnmanagedType.LPWStr)] string pszDir); + + void GetArguments( + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + + void SetArguments( + [MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + + void GetHotkey( + out short pwHotkey); + + void SetHotkey( + short wHotkey); + + void GetShowCmd( + out int piShowCmd); + + void SetShowCmd( + int iShowCmd); + + void GetIconLocation( + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, + out int piIcon); + + void SetIconLocation( + [MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, + int iIcon); + + void SetRelativePath( + [MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + int dwReserved); + + void Resolve( + IntPtr hwnd, + SLR_FLAGS fFlags); + + void SetPath( + [MarshalAs(UnmanagedType.LPWStr)] string pszFile); + + } + + + [ + ComImport(), + Guid("00021401-0000-0000-C000-000000000046") + ] + public class ShellLink // : IPersistFile, IShellLinkA, IShellLinkW + { + } + +} diff --git a/MediaBrowser.Installer/Code/ShellShortcut.cs b/MediaBrowser.Installer/Code/ShellShortcut.cs new file mode 100644 index 0000000000..33b60732c3 --- /dev/null +++ b/MediaBrowser.Installer/Code/ShellShortcut.cs @@ -0,0 +1,348 @@ +/************************************************************************** +* +* Filename: ShellShortcut.cs +* Author: Mattias Sjögren (mattias@mvps.org) +* http://www.msjogren.net/dotnet/ +* +* Description: Defines a .NET friendly class, ShellShortcut, for reading +* and writing shortcuts. +* Define the conditional compilation symbol UNICODE to use +* IShellLinkW internally. +* +* Public types: class ShellShortcut +* +* +* Dependencies: ShellLinkNative.cs +* +* +* Copyright ©2001-2002, Mattias Sjögren +* +**************************************************************************/ + +using System; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; + + +namespace MediaBrowser.Installer.Code +{ + /// + /// .NET friendly wrapper for the ShellLink class + /// + public class ShellShortcut : IDisposable + { + private const int INFOTIPSIZE = 1024; + private const int MAX_PATH = 260; + + private const int SW_SHOWNORMAL = 1; + private const int SW_SHOWMINIMIZED = 2; + private const int SW_SHOWMAXIMIZED = 3; + private const int SW_SHOWMINNOACTIVE = 7; + + + #if UNICODE + private IShellLinkW m_Link; + #else + private IShellLinkA m_Link; + #endif + private string m_sPath; + + /// + /// + /// Path to new or existing shortcut file (.lnk). + /// + /// + public ShellShortcut(string linkPath) + { + IPersistFile pf; + + m_sPath = linkPath; + + #if UNICODE + m_Link = (IShellLinkW) new ShellLink(); + #else + m_Link = (IShellLinkA) new ShellLink(); + #endif + + if ( File.Exists( linkPath ) ) { + pf = (IPersistFile)m_Link; + pf.Load( linkPath, 0 ); + } + + } + + // + // IDisplosable implementation + // + public void Dispose() + { + if ( m_Link != null ) { + Marshal.ReleaseComObject( m_Link ); + m_Link = null; + } + } + + /// + /// Gets or sets the argument list of the shortcut. + /// + public string Arguments + { + get + { + StringBuilder sb = new StringBuilder( INFOTIPSIZE ); + m_Link.GetArguments( sb, sb.Capacity ); + return sb.ToString(); + } + set { m_Link.SetArguments( value ); } + } + + /// + /// Gets or sets a description of the shortcut. + /// + public string Description + { + get + { + StringBuilder sb = new StringBuilder( INFOTIPSIZE ); + m_Link.GetDescription( sb, sb.Capacity ); + return sb.ToString(); + } + set { m_Link.SetDescription( value ); } + } + + /// + /// Gets or sets the working directory (aka start in directory) of the shortcut. + /// + public string WorkingDirectory + { + get + { + StringBuilder sb = new StringBuilder( MAX_PATH ); + m_Link.GetWorkingDirectory( sb, sb.Capacity ); + return sb.ToString(); + } + set { m_Link.SetWorkingDirectory( value ); } + } + + // + // If Path returns an empty string, the shortcut is associated with + // a PIDL instead, which can be retrieved with IShellLink.GetIDList(). + // This is beyond the scope of this wrapper class. + // + /// + /// Gets or sets the target path of the shortcut. + /// + public string Path + { + get + { + #if UNICODE + WIN32_FIND_DATAW wfd = new WIN32_FIND_DATAW(); + #else + WIN32_FIND_DATAA wfd = new WIN32_FIND_DATAA(); + #endif + StringBuilder sb = new StringBuilder( MAX_PATH ); + + m_Link.GetPath( sb, sb.Capacity, out wfd, SLGP_FLAGS.SLGP_UNCPRIORITY ); + return sb.ToString(); + } + set { m_Link.SetPath( value ); } + } + + /// + /// Gets or sets the path of the assigned to the shortcut. + /// + /// + /// + /// + public string IconPath + { + get + { + StringBuilder sb = new StringBuilder( MAX_PATH ); + int nIconIdx; + m_Link.GetIconLocation( sb, sb.Capacity, out nIconIdx ); + return sb.ToString(); + } + set { m_Link.SetIconLocation( value, IconIndex ); } + } + + /// + /// Gets or sets the index of the assigned to the shortcut. + /// Set to zero when the property specifies a .ICO file. + /// + /// + /// + /// + public int IconIndex + { + get + { + StringBuilder sb = new StringBuilder( MAX_PATH ); + int nIconIdx; + m_Link.GetIconLocation( sb, sb.Capacity, out nIconIdx ); + return nIconIdx; + } + set { m_Link.SetIconLocation( IconPath, value ); } + } + + /// + /// Retrieves the Icon of the shortcut as it will appear in Explorer. + /// Use the and + /// properties to change it. + /// + public Icon Icon + { + get + { + StringBuilder sb = new StringBuilder( MAX_PATH ); + int nIconIdx; + IntPtr hIcon, hInst; + Icon ico, clone; + + + m_Link.GetIconLocation( sb, sb.Capacity, out nIconIdx ); + hInst = Marshal.GetHINSTANCE( this.GetType().Module ); + hIcon = Native.ExtractIcon( hInst, sb.ToString(), nIconIdx ); + if ( hIcon == IntPtr.Zero ) + return null; + + // Return a cloned Icon, because we have to free the original ourselves. + ico = Icon.FromHandle( hIcon ); + clone = (Icon)ico.Clone(); + ico.Dispose(); + Native.DestroyIcon( hIcon ); + return clone; + } + } + + /// + /// Gets or sets the System.Diagnostics.ProcessWindowStyle value + /// that decides the initial show state of the shortcut target. Note that + /// ProcessWindowStyle.Hidden is not a valid property value. + /// + public ProcessWindowStyle WindowStyle + { + get + { + int nWS; + m_Link.GetShowCmd( out nWS ); + + switch ( nWS ) { + case SW_SHOWMINIMIZED: + case SW_SHOWMINNOACTIVE: + return ProcessWindowStyle.Minimized; + + case SW_SHOWMAXIMIZED: + return ProcessWindowStyle.Maximized; + + default: + return ProcessWindowStyle.Normal; + } + } + set + { + int nWS; + + switch ( value ) { + case ProcessWindowStyle.Normal: + nWS = SW_SHOWNORMAL; + break; + + case ProcessWindowStyle.Minimized: + nWS = SW_SHOWMINNOACTIVE; + break; + + case ProcessWindowStyle.Maximized: + nWS = SW_SHOWMAXIMIZED; + break; + + default: // ProcessWindowStyle.Hidden + throw new ArgumentException("Unsupported ProcessWindowStyle value."); + } + + m_Link.SetShowCmd( nWS ); + + } + } + + /// + /// Gets or sets the hotkey for the shortcut. + /// + public Keys Hotkey + { + get + { + short wHotkey; + int dwHotkey; + + m_Link.GetHotkey( out wHotkey ); + + // + // Convert from IShellLink 16-bit format to Keys enumeration 32-bit value + // IShellLink: 0xMMVK + // Keys: 0x00MM00VK + // MM = Modifier (Alt, Control, Shift) + // VK = Virtual key code + // + dwHotkey = ((wHotkey & 0xFF00) << 8) | (wHotkey & 0xFF); + return (Keys) dwHotkey; + } + set + { + short wHotkey; + + if ( (value & Keys.Modifiers) == 0 ) + throw new ArgumentException("Hotkey must include a modifier key."); + + // + // Convert from Keys enumeration 32-bit value to IShellLink 16-bit format + // IShellLink: 0xMMVK + // Keys: 0x00MM00VK + // MM = Modifier (Alt, Control, Shift) + // VK = Virtual key code + // + wHotkey = unchecked((short) ( ((int) (value & Keys.Modifiers) >> 8) | (int) (value & Keys.KeyCode) )); + m_Link.SetHotkey( wHotkey ); + + } + } + + /// + /// Saves the shortcut to disk. + /// + public void Save() + { + IPersistFile pf = (IPersistFile) m_Link; + pf.Save( m_sPath, true ); + } + + /// + /// Returns a reference to the internal ShellLink object, + /// which can be used to perform more advanced operations + /// not supported by this wrapper class, by using the + /// IShellLink interface directly. + /// + public object ShellLink + { + get { return m_Link; } + } + + + #region Native Win32 API functions + private class Native + { + [DllImport("shell32.dll", CharSet=CharSet.Auto)] + public static extern IntPtr ExtractIcon(IntPtr hInst, string lpszExeFileName, int nIconIndex); + + [DllImport("user32.dll")] + public static extern bool DestroyIcon(IntPtr hIcon); + } + #endregion + + } +} diff --git a/MediaBrowser.Installer/MainWindow.xaml.cs b/MediaBrowser.Installer/MainWindow.xaml.cs index 01e15dfd8c..f711736910 100644 --- a/MediaBrowser.Installer/MainWindow.xaml.cs +++ b/MediaBrowser.Installer/MainWindow.xaml.cs @@ -10,7 +10,6 @@ using System.Linq; using Ionic.Zip; using MediaBrowser.Installer.Code; using ServiceStack.Text; -using IWshRuntimeLibrary; namespace MediaBrowser.Installer { @@ -124,7 +123,7 @@ namespace MediaBrowser.Installer var fullPath = Path.Combine(RootPath, "System", TargetExe); try { - CreateShortcut(fullPath); + CreateShortcuts(fullPath); } catch (Exception e) { @@ -155,7 +154,7 @@ namespace MediaBrowser.Installer var json = await client.DownloadStringTaskAsync("http://www.mb3admin.com/admin/service/package/retrieveAll?name=" + PackageName); var packages = JsonSerializer.DeserializeFromString>(json); - var version = packages[0].versions.Where(v => v.classification == PackageClass).OrderByDescending(v => v.version).FirstOrDefault(v => v.version <= PackageVersion); + var version = packages[0].versions.Where(v => v.classification <= PackageClass).OrderByDescending(v => v.version).FirstOrDefault(v => v.version <= PackageVersion); if (version == null) { SystemClose("Could not locate download package. Aborting."); @@ -225,21 +224,24 @@ namespace MediaBrowser.Installer /// Only do current user to avoid need for admin elevation /// /// - protected void CreateShortcut(string targetExe) + protected void CreateShortcuts(string targetExe) { // get path to all users start menu - var shell = new WshShell(); - var startMenu = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu),"Media Browser"); + var startMenu = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu),"Media Browser 3"); if (!Directory.Exists(startMenu)) Directory.CreateDirectory(startMenu); - var product = (IWshShortcut)shell.CreateShortcut(Path.Combine(startMenu, FriendlyName+".lnk")); - product.TargetPath = targetExe; - product.Description = "Run " + FriendlyName; + var product = new ShellShortcut(Path.Combine(startMenu, FriendlyName+".lnk")) {Path = targetExe, Description = "Run " + FriendlyName}; product.Save(); - var uninstall = (IWshShortcut)shell.CreateShortcut(Path.Combine(startMenu, "Uninstall " + FriendlyName + ".lnk")); - uninstall.TargetPath = Path.Combine(Path.GetDirectoryName(targetExe),"MediaBrowser.Uninstaller.exe"); - uninstall.Arguments = (PackageName == "MBServer" ? "server" : "mbt"); - uninstall.Description = "Uninstall " + FriendlyName; + if (PackageName == "MBServer") + { + var path = Path.Combine(startMenu, "MB Dashboard.lnk"); + var dashboard = new ShellShortcut(path) + {Path = @"http://localhost:8096/mediabrowser/dashboard/dashboard.html", Description = "Open the Media Browser Server Dashboard (configuration)"}; + dashboard.Save(); + + } + var uninstall = new ShellShortcut(Path.Combine(startMenu, "Uninstall " + FriendlyName + ".lnk")) + {Path = Path.Combine(Path.GetDirectoryName(targetExe), "MediaBrowser.Uninstaller.exe"), Arguments = (PackageName == "MBServer" ? "server" : "mbt"), Description = "Uninstall " + FriendlyName}; uninstall.Save(); } diff --git a/MediaBrowser.Installer/MediaBrowser.Installer.csproj b/MediaBrowser.Installer/MediaBrowser.Installer.csproj index 30267e76ec..3193a2d867 100644 --- a/MediaBrowser.Installer/MediaBrowser.Installer.csproj +++ b/MediaBrowser.Installer/MediaBrowser.Installer.csproj @@ -14,7 +14,7 @@ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 true - http://www.mb3admin.com/downloads/beta/server/ + http://www.mb3admin.com/downloads/dev/server/ false Web false @@ -29,7 +29,7 @@ Media Browser Team Media Browser false - 14 + 16 0.1.1.%2a false true @@ -85,7 +85,9 @@ + + @@ -126,6 +128,8 @@ + + MainWindow.xaml Code @@ -149,7 +153,9 @@ ResXFileCodeGenerator Resources.Designer.cs - + + Designer + @@ -159,7 +165,9 @@ - + + Designer + @@ -190,17 +198,6 @@ - - - {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B} - 1 - 0 - 0 - tlbimp - False - True - - diff --git a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs index 61b177df97..474d985f2f 100644 --- a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs +++ b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs @@ -1,4 +1,5 @@ -using ProtoBuf; +using MediaBrowser.Model.Updates; +using ProtoBuf; namespace MediaBrowser.Model.Configuration { @@ -38,6 +39,12 @@ namespace MediaBrowser.Model.Configuration [ProtoMember(3)] public bool EnableAutoUpdate { get; set; } + /// + /// Gets of sets a value indicating the level of system updates (Release, Beta, Dev) + /// + [ProtoMember(60)] + public PackageVersionClass SystemUpdateLevel { get; set; } + /// /// The number of days we should retain log files /// diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 56bc434250..71f368397b 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Weather; +using MediaBrowser.Model.Updates; +using MediaBrowser.Model.Weather; using ProtoBuf; using System; @@ -298,8 +299,8 @@ namespace MediaBrowser.Model.Configuration /// true if [enable developer tools]; otherwise, false. [ProtoMember(57)] public bool EnableDeveloperTools { get; set; } - - // Next Proto number ====> 59 + + // Next Proto number ====> 61 /// /// Initializes a new instance of the class. diff --git a/MediaBrowser.Uninstaller.Execute/MainWindow.xaml b/MediaBrowser.Uninstaller.Execute/MainWindow.xaml index d2a093ec42..8039171095 100644 --- a/MediaBrowser.Uninstaller.Execute/MainWindow.xaml +++ b/MediaBrowser.Uninstaller.Execute/MainWindow.xaml @@ -7,10 +7,14 @@