Optical drive eject action now functional.
1.1 --- a/Server/FormEditObject.cs Thu Aug 18 20:14:30 2016 +0200
1.2 +++ b/Server/FormEditObject.cs Fri Aug 19 17:12:54 2016 +0200
1.3 @@ -104,22 +104,25 @@
1.4 private void FetchPropertiesValue(T aObject)
1.5 {
1.6 int ctrlIndex = 0;
1.7 + //For each of our properties
1.8 foreach (PropertyInfo pi in aObject.GetType().GetProperties())
1.9 {
1.10 - AttributeObjectProperty[] attributes =
1.11 - ((AttributeObjectProperty[]) pi.GetCustomAttributes(typeof(AttributeObjectProperty), true));
1.12 + //Get our property attribute
1.13 + AttributeObjectProperty[] attributes = ((AttributeObjectProperty[]) pi.GetCustomAttributes(typeof(AttributeObjectProperty), true));
1.14 if (attributes.Length != 1)
1.15 {
1.16 + //No attribute, skip this property then.
1.17 continue;
1.18 }
1.19 -
1.20 AttributeObjectProperty attribute = attributes[0];
1.21
1.22 + //Check that we support this type of property
1.23 if (!IsPropertyTypeSupported(pi))
1.24 {
1.25 continue;
1.26 }
1.27
1.28 + //Now fetch our property value
1.29 GetPropertyValueFromControl(iTableLayoutPanel.Controls[ctrlIndex+1], pi, aObject); //+1 otherwise we get the label
1.30
1.31 ctrlIndex+=2; //Jump over the label too
1.32 @@ -163,6 +166,14 @@
1.33 PropertyFile value = new PropertyFile {FullPath=ctrl.Text};
1.34 aInfo.SetValue(aObject, value);
1.35 }
1.36 + else if (aInfo.PropertyType == typeof(PropertyComboBox))
1.37 + {
1.38 + ComboBox ctrl = (ComboBox)aControl;
1.39 + string currentItem = ctrl.SelectedItem.ToString();
1.40 + PropertyComboBox pcb = (PropertyComboBox)aInfo.GetValue(aObject);
1.41 + pcb.CurrentItem = currentItem;
1.42 + }
1.43 +
1.44 //TODO: add support for other types here
1.45 }
1.46
1.47 @@ -191,21 +202,21 @@
1.48 {
1.49 //Enum properties are using combo box
1.50 ComboBox ctrl = new ComboBox();
1.51 - ctrl.AutoSize = true;
1.52 - ctrl.Sorted = true;
1.53 + ctrl.AutoSize = true;
1.54 + ctrl.Sorted = true;
1.55 ctrl.DropDownStyle = ComboBoxStyle.DropDownList;
1.56 //Data source is fine but it gives us duplicate entries for duplicated enum values
1.57 //ctrl.DataSource = Enum.GetValues(aInfo.PropertyType);
1.58
1.59 //Therefore we need to explicitly create our items
1.60 - Size cbSize = new Size(0,0);
1.61 + Size cbSize = new Size(0, 0);
1.62 foreach (string name in aInfo.PropertyType.GetEnumNames())
1.63 {
1.64 ctrl.Items.Add(name.ToString());
1.65 Graphics g = this.CreateGraphics();
1.66 //Since combobox autosize would not work we need to get measure text ourselves
1.67 - SizeF size=g.MeasureString(name.ToString(), ctrl.Font);
1.68 - cbSize.Width = Math.Max(cbSize.Width,(int)size.Width);
1.69 + SizeF size = g.MeasureString(name.ToString(), ctrl.Font);
1.70 + cbSize.Width = Math.Max(cbSize.Width, (int)size.Width);
1.71 cbSize.Height = Math.Max(cbSize.Height, (int)size.Height);
1.72 }
1.73
1.74 @@ -225,7 +236,7 @@
1.75 CheckBox ctrl = new CheckBox();
1.76 ctrl.AutoSize = true;
1.77 ctrl.Text = aAttribute.Description;
1.78 - ctrl.Checked = (bool)aInfo.GetValue(aObject);
1.79 + ctrl.Checked = (bool)aInfo.GetValue(aObject);
1.80 return ctrl;
1.81 }
1.82 else if (aInfo.PropertyType == typeof(string))
1.83 @@ -263,8 +274,27 @@
1.84
1.85 return ctrl;
1.86 }
1.87 + else if (aInfo.PropertyType == typeof(PropertyComboBox))
1.88 + {
1.89 + //ComboBox property
1.90 + ComboBox ctrl = new ComboBox();
1.91 + ctrl.AutoSize = true;
1.92 + ctrl.Sorted = true;
1.93 + ctrl.DropDownStyle = ComboBoxStyle.DropDownList;
1.94 + //Data source is such a pain to set the current item
1.95 + //ctrl.DataSource = ((PropertyComboBox)aInfo.GetValue(aObject)).Items;
1.96 +
1.97 + PropertyComboBox pcb = ((PropertyComboBox)aInfo.GetValue(aObject));
1.98 + foreach (string item in pcb.Items)
1.99 + {
1.100 + ctrl.Items.Add(item);
1.101 + }
1.102 +
1.103 + ctrl.SelectedItem = ((PropertyComboBox)aInfo.GetValue(aObject)).CurrentItem;
1.104 + //
1.105 + return ctrl;
1.106 + }
1.107 //TODO: add support for other control type here
1.108 -
1.109 return null;
1.110 }
1.111
1.112 @@ -294,6 +324,11 @@
1.113 {
1.114 return true;
1.115 }
1.116 + else if (aInfo.PropertyType == typeof(PropertyComboBox))
1.117 + {
1.118 + return true;
1.119 + }
1.120 +
1.121 //TODO: add support for other type here
1.122
1.123 return false;
2.1 --- a/Server/FormMain.Designer.cs Thu Aug 18 20:14:30 2016 +0200
2.2 +++ b/Server/FormMain.Designer.cs Fri Aug 19 17:12:54 2016 +0200
2.3 @@ -247,7 +247,7 @@
2.4 this.toolStripStatusLabelSpring,
2.5 this.toolStripStatusLabelPower,
2.6 this.toolStripStatusLabelFps});
2.7 - this.statusStrip.Location = new System.Drawing.Point(0, 539);
2.8 + this.statusStrip.Location = new System.Drawing.Point(0, 540);
2.9 this.statusStrip.Name = "statusStrip";
2.10 this.statusStrip.RenderMode = System.Windows.Forms.ToolStripRenderMode.Professional;
2.11 this.statusStrip.Size = new System.Drawing.Size(784, 22);
2.12 @@ -1124,7 +1124,7 @@
2.13 // buttonEventEdit
2.14 //
2.15 this.buttonEventEdit.Enabled = false;
2.16 - this.buttonEventEdit.Location = new System.Drawing.Point(6, 35);
2.17 + this.buttonEventEdit.Location = new System.Drawing.Point(6, 187);
2.18 this.buttonEventEdit.Name = "buttonEventEdit";
2.19 this.buttonEventEdit.Size = new System.Drawing.Size(96, 23);
2.20 this.buttonEventEdit.TabIndex = 29;
2.21 @@ -1135,7 +1135,7 @@
2.22 // buttonEventDelete
2.23 //
2.24 this.buttonEventDelete.Enabled = false;
2.25 - this.buttonEventDelete.Location = new System.Drawing.Point(6, 64);
2.26 + this.buttonEventDelete.Location = new System.Drawing.Point(6, 216);
2.27 this.buttonEventDelete.Name = "buttonEventDelete";
2.28 this.buttonEventDelete.Size = new System.Drawing.Size(96, 23);
2.29 this.buttonEventDelete.TabIndex = 28;
2.30 @@ -1145,7 +1145,7 @@
2.31 //
2.32 // buttonEventAdd
2.33 //
2.34 - this.buttonEventAdd.Location = new System.Drawing.Point(6, 6);
2.35 + this.buttonEventAdd.Location = new System.Drawing.Point(6, 158);
2.36 this.buttonEventAdd.Name = "buttonEventAdd";
2.37 this.buttonEventAdd.Size = new System.Drawing.Size(96, 23);
2.38 this.buttonEventAdd.TabIndex = 27;
2.39 @@ -1156,7 +1156,7 @@
2.40 // buttonEventTest
2.41 //
2.42 this.buttonEventTest.Enabled = false;
2.43 - this.buttonEventTest.Location = new System.Drawing.Point(6, 93);
2.44 + this.buttonEventTest.Location = new System.Drawing.Point(6, 245);
2.45 this.buttonEventTest.Name = "buttonEventTest";
2.46 this.buttonEventTest.Size = new System.Drawing.Size(96, 23);
2.47 this.buttonEventTest.TabIndex = 26;
2.48 @@ -1167,7 +1167,7 @@
2.49 // buttonActionEdit
2.50 //
2.51 this.buttonActionEdit.Enabled = false;
2.52 - this.buttonActionEdit.Location = new System.Drawing.Point(6, 190);
2.53 + this.buttonActionEdit.Location = new System.Drawing.Point(6, 35);
2.54 this.buttonActionEdit.Name = "buttonActionEdit";
2.55 this.buttonActionEdit.Size = new System.Drawing.Size(96, 23);
2.56 this.buttonActionEdit.TabIndex = 25;
2.57 @@ -1200,7 +1200,7 @@
2.58 // buttonActionTest
2.59 //
2.60 this.buttonActionTest.Enabled = false;
2.61 - this.buttonActionTest.Location = new System.Drawing.Point(6, 248);
2.62 + this.buttonActionTest.Location = new System.Drawing.Point(6, 93);
2.63 this.buttonActionTest.Name = "buttonActionTest";
2.64 this.buttonActionTest.Size = new System.Drawing.Size(96, 23);
2.65 this.buttonActionTest.TabIndex = 22;
2.66 @@ -1211,7 +1211,7 @@
2.67 // buttonActionDelete
2.68 //
2.69 this.buttonActionDelete.Enabled = false;
2.70 - this.buttonActionDelete.Location = new System.Drawing.Point(6, 219);
2.71 + this.buttonActionDelete.Location = new System.Drawing.Point(6, 64);
2.72 this.buttonActionDelete.Name = "buttonActionDelete";
2.73 this.buttonActionDelete.Size = new System.Drawing.Size(96, 23);
2.74 this.buttonActionDelete.TabIndex = 21;
2.75 @@ -1222,9 +1222,9 @@
2.76 // buttonActionAdd
2.77 //
2.78 this.buttonActionAdd.Enabled = false;
2.79 - this.buttonActionAdd.Location = new System.Drawing.Point(6, 157);
2.80 + this.buttonActionAdd.Location = new System.Drawing.Point(6, 6);
2.81 this.buttonActionAdd.Name = "buttonActionAdd";
2.82 - this.buttonActionAdd.Size = new System.Drawing.Size(96, 27);
2.83 + this.buttonActionAdd.Size = new System.Drawing.Size(96, 23);
2.84 this.buttonActionAdd.TabIndex = 20;
2.85 this.buttonActionAdd.Text = "Add Action";
2.86 this.buttonActionAdd.UseVisualStyleBackColor = true;
2.87 @@ -1370,7 +1370,7 @@
2.88 //
2.89 this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
2.90 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
2.91 - this.ClientSize = new System.Drawing.Size(784, 561);
2.92 + this.ClientSize = new System.Drawing.Size(784, 562);
2.93 this.Controls.Add(this.labelFontHeight);
2.94 this.Controls.Add(this.labelFontWidth);
2.95 this.Controls.Add(this.labelWarning);
3.1 --- a/Server/FormMain.cs Thu Aug 18 20:14:30 2016 +0200
3.2 +++ b/Server/FormMain.cs Fri Aug 19 17:12:54 2016 +0200
3.3 @@ -151,7 +151,7 @@
3.4 // We loaded events and actions from our settings
3.5 // Internalizer apparently skips constructor so we need to initialize it here
3.6 // Though I reckon that should only be needed when loading an empty EAR manager I guess.
3.7 - Properties.Settings.Default.EarManager.Init();
3.8 + Properties.Settings.Default.EarManager.Construct();
3.9 }
3.10 iSkipFrameRendering = false;
3.11 iClosing = false;
4.1 --- a/SharpLibEar/ActionOpticalDriveEject.cs Thu Aug 18 20:14:30 2016 +0200
4.2 +++ b/SharpLibEar/ActionOpticalDriveEject.cs Fri Aug 19 17:12:54 2016 +0200
4.3 @@ -1,12 +1,308 @@
4.4 -using System;
4.5 +using Microsoft.Win32.SafeHandles;
4.6 +using System;
4.7 using System.Collections.Generic;
4.8 +using System.Diagnostics;
4.9 +using System.IO;
4.10 using System.Linq;
4.11 +using System.Runtime.Serialization;
4.12 using System.Text;
4.13 using System.Threading.Tasks;
4.14 +using SharpLib.Win32;
4.15 +using System.ComponentModel;
4.16 +using System.Runtime.InteropServices;
4.17
4.18 namespace SharpLib.Ear
4.19 {
4.20 - class ActionOpticalDriveEject
4.21 + [DataContract]
4.22 + [AttributeObject(Id = "Action.OpticalDrive.Eject", Name = "Eject", Description = "Eject media from an optical drive.")]
4.23 + public class ActionOpticalDriveEject : Action
4.24 {
4.25 + [DataMember]
4.26 + [AttributeObjectProperty
4.27 + (
4.28 + Id = "Action.OpticalDrive.Eject.Drive",
4.29 + Name = "Drive to eject",
4.30 + Description = "Select the drive you want to eject."
4.31 + )
4.32 + ]
4.33 + public PropertyComboBox Drive { get; set; } = new PropertyComboBox();
4.34 +
4.35 +
4.36 + protected override void DoConstruct()
4.37 + {
4.38 + base.DoConstruct();
4.39 + PopulateOpticalDrives();
4.40 + CheckCurrentItem();
4.41 + }
4.42 +
4.43 +
4.44 + public override string Brief()
4.45 + {
4.46 + return Name + " " + Drive.CurrentItem ;
4.47 + }
4.48 +
4.49 + public override bool IsValid()
4.50 + {
4.51 + //This object is valid if our current item is contained in our drive list
4.52 + return Drive.Items.Contains(Drive.CurrentItem);
4.53 + }
4.54 +
4.55 + protected override void DoExecute()
4.56 + {
4.57 + DriveEject(Drive.CurrentItem);
4.58 + }
4.59 +
4.60 +
4.61 + private void CheckCurrentItem()
4.62 + {
4.63 + if (!Drive.Items.Contains(Drive.CurrentItem) && Drive.Items.Count>0)
4.64 + {
4.65 + //Current item unknown, reset it then
4.66 + Drive.CurrentItem = Drive.Items[0];
4.67 + }
4.68 + }
4.69 +
4.70 + /// <summary>
4.71 + ///
4.72 + /// </summary>
4.73 + private void PopulateOpticalDrives()
4.74 + {
4.75 + //Reset our list of drives
4.76 + Drive.Items = new List<string>();
4.77 + //Go through each drives on our system and collected the optical ones in our list
4.78 + DriveInfo[] allDrives = DriveInfo.GetDrives();
4.79 + foreach (DriveInfo d in allDrives)
4.80 + {
4.81 + Debug.WriteLine("Drive " + d.Name);
4.82 + Debug.WriteLine(" Drive type: {0}", d.DriveType);
4.83 +
4.84 + if (d.DriveType == DriveType.CDRom)
4.85 + {
4.86 + //This is an optical drive, add it now
4.87 + Drive.Items.Add(d.Name.Substring(0, 2));
4.88 + }
4.89 + }
4.90 + }
4.91 +
4.92 +
4.93 + /// <summary>
4.94 + ///
4.95 + /// </summary>
4.96 + /// <param name="aPrefix"></param>
4.97 + static private void CheckLastError(string aPrefix)
4.98 + {
4.99 + string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
4.100 + Debug.WriteLine(aPrefix + Marshal.GetLastWin32Error().ToString() + ": " + errorMessage);
4.101 + }
4.102 +
4.103 + /// <summary>
4.104 + ///
4.105 + /// </summary>
4.106 + /// <param name="data"></param>
4.107 + /// <returns></returns>
4.108 + static private IntPtr MarshalToPointer(object data)
4.109 + {
4.110 + IntPtr buf = Marshal.AllocHGlobal(
4.111 + Marshal.SizeOf(data));
4.112 + Marshal.StructureToPtr(data,
4.113 + buf, false);
4.114 + return buf;
4.115 + }
4.116 +
4.117 + /// <summary>
4.118 + ///
4.119 + /// </summary>
4.120 + /// <returns></returns>
4.121 + static private SafeFileHandle OpenVolume(string aDriveName)
4.122 + {
4.123 + return Function.CreateFile("\\\\.\\" + aDriveName,
4.124 + SharpLib.Win32.FileAccess.GENERIC_READ,
4.125 + SharpLib.Win32.FileShare.FILE_SHARE_READ | SharpLib.Win32.FileShare.FILE_SHARE_WRITE,
4.126 + IntPtr.Zero,
4.127 + CreationDisposition.OPEN_EXISTING,
4.128 + 0,
4.129 + IntPtr.Zero);
4.130 + }
4.131 +
4.132 + /// <summary>
4.133 + ///
4.134 + /// </summary>
4.135 + /// <param name="aVolume"></param>
4.136 + /// <returns></returns>
4.137 + static private bool LockVolume(SafeFileHandle aVolume)
4.138 + {
4.139 + //Hope that's doing what I think it does
4.140 + IntPtr dwBytesReturned = new IntPtr();
4.141 + //Should not be needed but I'm not sure how to pass NULL in there.
4.142 + OVERLAPPED overlapped = new OVERLAPPED();
4.143 +
4.144 + int tries = 0;
4.145 + const int KMaxTries = 100;
4.146 + const int KSleepTime = 10;
4.147 + bool success = false;
4.148 +
4.149 + while (!success && tries < KMaxTries)
4.150 + {
4.151 + success = Function.DeviceIoControl(aVolume, Const.FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, dwBytesReturned, ref overlapped);
4.152 + System.Threading.Thread.Sleep(KSleepTime);
4.153 + tries++;
4.154 + }
4.155 +
4.156 + CheckLastError("Lock volume: ");
4.157 +
4.158 + return success;
4.159 + }
4.160 +
4.161 + /// <summary>
4.162 + ///
4.163 + /// </summary>
4.164 + /// <param name="aVolume"></param>
4.165 + /// <returns></returns>
4.166 + static private bool DismountVolume(SafeFileHandle aVolume)
4.167 + {
4.168 + //Hope that's doing what I think it does
4.169 + IntPtr dwBytesReturned = new IntPtr();
4.170 + //Should not be needed but I'm not sure how to pass NULL in there.
4.171 + OVERLAPPED overlapped = new OVERLAPPED();
4.172 +
4.173 + bool res = Function.DeviceIoControl(aVolume, Const.FSCTL_DISMOUNT_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, dwBytesReturned, ref overlapped);
4.174 + CheckLastError("Dismount volume: ");
4.175 + return res;
4.176 + }
4.177 +
4.178 +
4.179 +
4.180 + /// <summary>
4.181 + ///
4.182 + /// </summary>
4.183 + /// <param name="aVolume"></param>
4.184 + /// <param name="aPreventRemoval"></param>
4.185 + /// <returns></returns>
4.186 + static private bool PreventRemovalOfVolume(SafeFileHandle aVolume, bool aPreventRemoval)
4.187 + {
4.188 + //Hope that's doing what I think it does
4.189 + IntPtr dwBytesReturned = new IntPtr();
4.190 + //Should not be needed but I'm not sure how to pass NULL in there.
4.191 + OVERLAPPED overlapped = new OVERLAPPED();
4.192 + //
4.193 + PREVENT_MEDIA_REMOVAL preventMediaRemoval = new PREVENT_MEDIA_REMOVAL();
4.194 + preventMediaRemoval.PreventMediaRemoval = Convert.ToByte(aPreventRemoval);
4.195 + IntPtr preventMediaRemovalParam = MarshalToPointer(preventMediaRemoval);
4.196 +
4.197 + bool result = Function.DeviceIoControl(aVolume, Const.IOCTL_STORAGE_MEDIA_REMOVAL, preventMediaRemovalParam, Convert.ToUInt32(Marshal.SizeOf(preventMediaRemoval)), IntPtr.Zero, 0, dwBytesReturned, ref overlapped);
4.198 + CheckLastError("Media removal: ");
4.199 + Marshal.FreeHGlobal(preventMediaRemovalParam);
4.200 +
4.201 + return result;
4.202 + }
4.203 +
4.204 + /// <summary>
4.205 + /// Eject optical drive media opening the tray if any.
4.206 + /// </summary>
4.207 + /// <param name="aVolume"></param>
4.208 + /// <returns></returns>
4.209 + static private bool MediaEject(SafeFileHandle aVolume)
4.210 + {
4.211 + //Hope that's doing what I think it does
4.212 + IntPtr dwBytesReturned = new IntPtr();
4.213 + //Should not be needed but I'm not sure how to pass NULL in there.
4.214 + OVERLAPPED overlapped = new OVERLAPPED();
4.215 +
4.216 + bool res = Function.DeviceIoControl(aVolume, Const.IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, dwBytesReturned, ref overlapped);
4.217 + CheckLastError("Media eject: ");
4.218 + return res;
4.219 + }
4.220 +
4.221 + /// <summary>
4.222 + /// Close an optical drive tray.
4.223 + /// </summary>
4.224 + /// <param name="aVolume"></param>
4.225 + /// <returns></returns>
4.226 + static private bool MediaLoad(SafeFileHandle aVolume)
4.227 + {
4.228 + //Hope that's doing what I think it does
4.229 + IntPtr dwBytesReturned = new IntPtr();
4.230 + //Should not be needed but I'm not sure how to pass NULL in there.
4.231 + OVERLAPPED overlapped = new OVERLAPPED();
4.232 +
4.233 + bool res = Function.DeviceIoControl(aVolume, Const.IOCTL_STORAGE_LOAD_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, dwBytesReturned, ref overlapped);
4.234 + CheckLastError("Media load: ");
4.235 + return res;
4.236 + }
4.237 +
4.238 + /// <summary>
4.239 + ///
4.240 + /// </summary>
4.241 + /// <param name="aVolume"></param>
4.242 + /// <returns></returns>
4.243 + static private bool StorageCheckVerify(SafeFileHandle aVolume)
4.244 + {
4.245 + //Hope that's doing what I think it does
4.246 + IntPtr dwBytesReturned = new IntPtr();
4.247 + //Should not be needed but I'm not sure how to pass NULL in there.
4.248 + OVERLAPPED overlapped = new OVERLAPPED();
4.249 +
4.250 + bool res = Function.DeviceIoControl(aVolume, Const.IOCTL_STORAGE_CHECK_VERIFY2, IntPtr.Zero, 0, IntPtr.Zero, 0, dwBytesReturned, ref overlapped);
4.251 +
4.252 + CheckLastError("Check verify: ");
4.253 +
4.254 + return res;
4.255 + }
4.256 +
4.257 +
4.258 + /// <summary>
4.259 + /// Perform media ejection.
4.260 + /// </summary>
4.261 + static private void DriveEject(string aDrive)
4.262 + {
4.263 + string drive = aDrive;
4.264 + if (drive.Length != 2)
4.265 + {
4.266 + //Not a proper drive spec.
4.267 + //Probably 'None' selected.
4.268 + return;
4.269 + }
4.270 +
4.271 + SafeFileHandle handle = OpenVolume(drive);
4.272 + if (handle.IsInvalid)
4.273 + {
4.274 + CheckLastError("ERROR: Failed to open volume: ");
4.275 + return;
4.276 + }
4.277 +
4.278 + if (LockVolume(handle) && DismountVolume(handle))
4.279 + {
4.280 + Debug.WriteLine("Volume was dismounted.");
4.281 +
4.282 + if (PreventRemovalOfVolume(handle, false))
4.283 + {
4.284 + //StorageCheckVerify(handle);
4.285 +
4.286 + DateTime before;
4.287 + before = DateTime.Now;
4.288 + bool ejectSuccess = MediaEject(handle);
4.289 + double ms = (DateTime.Now - before).TotalMilliseconds;
4.290 +
4.291 + //We assume that if it take more than a certain time to for eject to execute it means we actually ejected.
4.292 + //If our eject completes too rapidly we assume the tray is already open and we will try to close it.
4.293 + if (ejectSuccess && ms > 100)
4.294 + {
4.295 + Debug.WriteLine("Media was ejected");
4.296 + }
4.297 + else if (MediaLoad(handle))
4.298 + {
4.299 + Debug.WriteLine("Media was loaded");
4.300 + }
4.301 + }
4.302 + }
4.303 + else
4.304 + {
4.305 + Debug.WriteLine("Volume lock or dismount failed.");
4.306 + }
4.307 +
4.308 + //This is needed to make sure we can open the volume next time around
4.309 + handle.Dispose();
4.310 + }
4.311 +
4.312 }
4.313 }
5.1 --- a/SharpLibEar/Event.cs Thu Aug 18 20:14:30 2016 +0200
5.2 +++ b/SharpLibEar/Event.cs Fri Aug 19 17:12:54 2016 +0200
5.3 @@ -18,16 +18,22 @@
5.4 Description = "When enabled an event instance can be triggered."
5.5 )
5.6 ]
5.7 - public bool Enabled { get; set; }
5.8 + public bool Enabled { get; set; } = true;
5.9
5.10 [DataMember]
5.11 public List<Action> Actions = new List<Action>();
5.12
5.13
5.14 + protected override void DoConstruct()
5.15 + {
5.16 + base.DoConstruct();
5.17
5.18 - protected Event()
5.19 - {
5.20 - Enabled = true;
5.21 + // TODO: Construct properties too
5.22 + foreach (Action a in Actions)
5.23 + {
5.24 + a.Construct();
5.25 + }
5.26 +
5.27 }
5.28
5.29
6.1 --- a/SharpLibEar/Manager.cs Thu Aug 18 20:14:30 2016 +0200
6.2 +++ b/SharpLibEar/Manager.cs Fri Aug 19 17:12:54 2016 +0200
6.3 @@ -15,8 +15,7 @@
6.4 /// Users can implement their own events and actions.
6.5 /// </summary>
6.6 [DataContract]
6.7 - [KnownType("DerivedTypes")]
6.8 - public class Manager
6.9 + public class Manager: Object
6.10 {
6.11 /// <summary>
6.12 /// Our events instances.
6.13 @@ -24,23 +23,24 @@
6.14 [DataMember]
6.15 public List<Event> Events;
6.16
6.17 - /// <summary>
6.18 - /// Constructor
6.19 - /// </summary>
6.20 - public Manager()
6.21 - {
6.22 - Init();
6.23 - }
6.24
6.25 /// <summary>
6.26 /// Executes after internalization took place.
6.27 /// </summary>
6.28 - public void Init()
6.29 + protected override void DoConstruct()
6.30 {
6.31 + base.DoConstruct();
6.32 +
6.33 if (Events == null)
6.34 {
6.35 Events = new List<Event>();
6.36 }
6.37 +
6.38 + // TODO: Object properties should be constructed too
6.39 + foreach (Event e in Events)
6.40 + {
6.41 + e.Construct();
6.42 + }
6.43
6.44 }
6.45
6.46 @@ -86,15 +86,5 @@
6.47 }
6.48 }
6.49 }
6.50 -
6.51 - /// <summary>
6.52 - /// Allow extending our data contract.
6.53 - /// See KnownType above.
6.54 - /// </summary>
6.55 - /// <returns></returns>
6.56 - private static IEnumerable<Type> DerivedTypes()
6.57 - {
6.58 - return SharpLib.Utils.Reflection.GetDerivedTypes<Manager>();
6.59 - }
6.60 }
6.61 }
6.62 \ No newline at end of file
7.1 --- a/SharpLibEar/Object.cs Thu Aug 18 20:14:30 2016 +0200
7.2 +++ b/SharpLibEar/Object.cs Fri Aug 19 17:12:54 2016 +0200
7.3 @@ -21,6 +21,33 @@
7.4 [KnownType("DerivedTypes")]
7.5 public abstract class Object: IComparable
7.6 {
7.7 + private bool iConstructed = false;
7.8 +
7.9 + public Object()
7.10 + {
7.11 + Construct();
7.12 + }
7.13 +
7.14 + /// <summary>
7.15 + /// Needed as our constructor is not called following internalization.
7.16 + /// </summary>
7.17 + public void Construct()
7.18 + {
7.19 + if (!iConstructed)
7.20 + {
7.21 + DoConstruct();
7.22 + iConstructed = true;
7.23 + }
7.24 + }
7.25 +
7.26 + /// <summary>
7.27 + ///
7.28 + /// </summary>
7.29 + protected virtual void DoConstruct()
7.30 + {
7.31 +
7.32 + }
7.33 +
7.34 /// <summary>
7.35 /// Static object name.
7.36 /// </summary>
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/SharpLibEar/PropertyComboBox.cs Fri Aug 19 17:12:54 2016 +0200
8.3 @@ -0,0 +1,22 @@
8.4 +using System;
8.5 +using System.Collections.Generic;
8.6 +using System.Linq;
8.7 +using System.Runtime.Serialization;
8.8 +using System.Text;
8.9 +using System.Threading.Tasks;
8.10 +
8.11 +namespace SharpLib.Ear
8.12 +{
8.13 + /// <summary>
8.14 + /// ComboBox property
8.15 + /// </summary>
8.16 + [DataContract]
8.17 + [AttributeObject(Id = "Property.ComboBox", Name = "ComboBox", Description = "ComboBox property.")]
8.18 + public class PropertyComboBox : Object
8.19 + {
8.20 + public IList<string> Items { get; set; } = new List<string>();
8.21 +
8.22 + [DataMember]
8.23 + public string CurrentItem { get; set; }
8.24 + }
8.25 +}
9.1 --- a/SharpLibEar/SharpLibEar.csproj Thu Aug 18 20:14:30 2016 +0200
9.2 +++ b/SharpLibEar/SharpLibEar.csproj Fri Aug 19 17:12:54 2016 +0200
9.3 @@ -48,6 +48,10 @@
9.4 <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
9.5 </PropertyGroup>
9.6 <ItemGroup>
9.7 + <Reference Include="SharpLibWin32, Version=1.0.0.0, Culture=neutral, processorArchitecture=x86">
9.8 + <HintPath>..\packages\SharpLibWin32.0.0.9\lib\net20\SharpLibWin32.dll</HintPath>
9.9 + <Private>True</Private>
9.10 + </Reference>
9.11 <Reference Include="System" />
9.12 <Reference Include="System.Core" />
9.13 <Reference Include="System.Runtime.Serialization" />
9.14 @@ -73,6 +77,7 @@
9.15 <Compile Include="Manager.cs" />
9.16 <Compile Include="Object.cs" />
9.17 <Compile Include="Properties\AssemblyInfo.cs" />
9.18 + <Compile Include="PropertyComboBox.cs" />
9.19 <Compile Include="PropertyFile.cs" />
9.20 </ItemGroup>
9.21 <ItemGroup>
9.22 @@ -81,6 +86,9 @@
9.23 <Name>SharpLibUtils</Name>
9.24 </ProjectReference>
9.25 </ItemGroup>
9.26 + <ItemGroup>
9.27 + <None Include="packages.config" />
9.28 + </ItemGroup>
9.29 <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
9.30 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
9.31 Other similar extension points exist, see Microsoft.Common.targets.
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/SharpLibEar/packages.config Fri Aug 19 17:12:54 2016 +0200
10.3 @@ -0,0 +1,4 @@
10.4 +<?xml version="1.0" encoding="utf-8"?>
10.5 +<packages>
10.6 + <package id="SharpLibWin32" version="0.0.9" targetFramework="net46" />
10.7 +</packages>
10.8 \ No newline at end of file