# HG changeset patch
# User Stephane Lenclud
# Date 1471619574 -7200
# Node ID cc2251d065db413d4792614d059deb84ad9b006e
# Parent 0a956121c273aab09d1b25b368b5495947f8da9d
Optical drive eject action now functional.
diff -r 0a956121c273 -r cc2251d065db Server/FormEditObject.cs
--- a/Server/FormEditObject.cs Thu Aug 18 20:14:30 2016 +0200
+++ b/Server/FormEditObject.cs Fri Aug 19 17:12:54 2016 +0200
@@ -104,22 +104,25 @@
private void FetchPropertiesValue(T aObject)
{
int ctrlIndex = 0;
+ //For each of our properties
foreach (PropertyInfo pi in aObject.GetType().GetProperties())
{
- AttributeObjectProperty[] attributes =
- ((AttributeObjectProperty[]) pi.GetCustomAttributes(typeof(AttributeObjectProperty), true));
+ //Get our property attribute
+ AttributeObjectProperty[] attributes = ((AttributeObjectProperty[]) pi.GetCustomAttributes(typeof(AttributeObjectProperty), true));
if (attributes.Length != 1)
{
+ //No attribute, skip this property then.
continue;
}
-
AttributeObjectProperty attribute = attributes[0];
+ //Check that we support this type of property
if (!IsPropertyTypeSupported(pi))
{
continue;
}
+ //Now fetch our property value
GetPropertyValueFromControl(iTableLayoutPanel.Controls[ctrlIndex+1], pi, aObject); //+1 otherwise we get the label
ctrlIndex+=2; //Jump over the label too
@@ -163,6 +166,14 @@
PropertyFile value = new PropertyFile {FullPath=ctrl.Text};
aInfo.SetValue(aObject, value);
}
+ else if (aInfo.PropertyType == typeof(PropertyComboBox))
+ {
+ ComboBox ctrl = (ComboBox)aControl;
+ string currentItem = ctrl.SelectedItem.ToString();
+ PropertyComboBox pcb = (PropertyComboBox)aInfo.GetValue(aObject);
+ pcb.CurrentItem = currentItem;
+ }
+
//TODO: add support for other types here
}
@@ -191,21 +202,21 @@
{
//Enum properties are using combo box
ComboBox ctrl = new ComboBox();
- ctrl.AutoSize = true;
- ctrl.Sorted = true;
+ ctrl.AutoSize = true;
+ ctrl.Sorted = true;
ctrl.DropDownStyle = ComboBoxStyle.DropDownList;
//Data source is fine but it gives us duplicate entries for duplicated enum values
//ctrl.DataSource = Enum.GetValues(aInfo.PropertyType);
//Therefore we need to explicitly create our items
- Size cbSize = new Size(0,0);
+ Size cbSize = new Size(0, 0);
foreach (string name in aInfo.PropertyType.GetEnumNames())
{
ctrl.Items.Add(name.ToString());
Graphics g = this.CreateGraphics();
//Since combobox autosize would not work we need to get measure text ourselves
- SizeF size=g.MeasureString(name.ToString(), ctrl.Font);
- cbSize.Width = Math.Max(cbSize.Width,(int)size.Width);
+ SizeF size = g.MeasureString(name.ToString(), ctrl.Font);
+ cbSize.Width = Math.Max(cbSize.Width, (int)size.Width);
cbSize.Height = Math.Max(cbSize.Height, (int)size.Height);
}
@@ -225,7 +236,7 @@
CheckBox ctrl = new CheckBox();
ctrl.AutoSize = true;
ctrl.Text = aAttribute.Description;
- ctrl.Checked = (bool)aInfo.GetValue(aObject);
+ ctrl.Checked = (bool)aInfo.GetValue(aObject);
return ctrl;
}
else if (aInfo.PropertyType == typeof(string))
@@ -263,8 +274,27 @@
return ctrl;
}
+ else if (aInfo.PropertyType == typeof(PropertyComboBox))
+ {
+ //ComboBox property
+ ComboBox ctrl = new ComboBox();
+ ctrl.AutoSize = true;
+ ctrl.Sorted = true;
+ ctrl.DropDownStyle = ComboBoxStyle.DropDownList;
+ //Data source is such a pain to set the current item
+ //ctrl.DataSource = ((PropertyComboBox)aInfo.GetValue(aObject)).Items;
+
+ PropertyComboBox pcb = ((PropertyComboBox)aInfo.GetValue(aObject));
+ foreach (string item in pcb.Items)
+ {
+ ctrl.Items.Add(item);
+ }
+
+ ctrl.SelectedItem = ((PropertyComboBox)aInfo.GetValue(aObject)).CurrentItem;
+ //
+ return ctrl;
+ }
//TODO: add support for other control type here
-
return null;
}
@@ -294,6 +324,11 @@
{
return true;
}
+ else if (aInfo.PropertyType == typeof(PropertyComboBox))
+ {
+ return true;
+ }
+
//TODO: add support for other type here
return false;
diff -r 0a956121c273 -r cc2251d065db Server/FormMain.Designer.cs
--- a/Server/FormMain.Designer.cs Thu Aug 18 20:14:30 2016 +0200
+++ b/Server/FormMain.Designer.cs Fri Aug 19 17:12:54 2016 +0200
@@ -247,7 +247,7 @@
this.toolStripStatusLabelSpring,
this.toolStripStatusLabelPower,
this.toolStripStatusLabelFps});
- this.statusStrip.Location = new System.Drawing.Point(0, 539);
+ this.statusStrip.Location = new System.Drawing.Point(0, 540);
this.statusStrip.Name = "statusStrip";
this.statusStrip.RenderMode = System.Windows.Forms.ToolStripRenderMode.Professional;
this.statusStrip.Size = new System.Drawing.Size(784, 22);
@@ -1124,7 +1124,7 @@
// buttonEventEdit
//
this.buttonEventEdit.Enabled = false;
- this.buttonEventEdit.Location = new System.Drawing.Point(6, 35);
+ this.buttonEventEdit.Location = new System.Drawing.Point(6, 187);
this.buttonEventEdit.Name = "buttonEventEdit";
this.buttonEventEdit.Size = new System.Drawing.Size(96, 23);
this.buttonEventEdit.TabIndex = 29;
@@ -1135,7 +1135,7 @@
// buttonEventDelete
//
this.buttonEventDelete.Enabled = false;
- this.buttonEventDelete.Location = new System.Drawing.Point(6, 64);
+ this.buttonEventDelete.Location = new System.Drawing.Point(6, 216);
this.buttonEventDelete.Name = "buttonEventDelete";
this.buttonEventDelete.Size = new System.Drawing.Size(96, 23);
this.buttonEventDelete.TabIndex = 28;
@@ -1145,7 +1145,7 @@
//
// buttonEventAdd
//
- this.buttonEventAdd.Location = new System.Drawing.Point(6, 6);
+ this.buttonEventAdd.Location = new System.Drawing.Point(6, 158);
this.buttonEventAdd.Name = "buttonEventAdd";
this.buttonEventAdd.Size = new System.Drawing.Size(96, 23);
this.buttonEventAdd.TabIndex = 27;
@@ -1156,7 +1156,7 @@
// buttonEventTest
//
this.buttonEventTest.Enabled = false;
- this.buttonEventTest.Location = new System.Drawing.Point(6, 93);
+ this.buttonEventTest.Location = new System.Drawing.Point(6, 245);
this.buttonEventTest.Name = "buttonEventTest";
this.buttonEventTest.Size = new System.Drawing.Size(96, 23);
this.buttonEventTest.TabIndex = 26;
@@ -1167,7 +1167,7 @@
// buttonActionEdit
//
this.buttonActionEdit.Enabled = false;
- this.buttonActionEdit.Location = new System.Drawing.Point(6, 190);
+ this.buttonActionEdit.Location = new System.Drawing.Point(6, 35);
this.buttonActionEdit.Name = "buttonActionEdit";
this.buttonActionEdit.Size = new System.Drawing.Size(96, 23);
this.buttonActionEdit.TabIndex = 25;
@@ -1200,7 +1200,7 @@
// buttonActionTest
//
this.buttonActionTest.Enabled = false;
- this.buttonActionTest.Location = new System.Drawing.Point(6, 248);
+ this.buttonActionTest.Location = new System.Drawing.Point(6, 93);
this.buttonActionTest.Name = "buttonActionTest";
this.buttonActionTest.Size = new System.Drawing.Size(96, 23);
this.buttonActionTest.TabIndex = 22;
@@ -1211,7 +1211,7 @@
// buttonActionDelete
//
this.buttonActionDelete.Enabled = false;
- this.buttonActionDelete.Location = new System.Drawing.Point(6, 219);
+ this.buttonActionDelete.Location = new System.Drawing.Point(6, 64);
this.buttonActionDelete.Name = "buttonActionDelete";
this.buttonActionDelete.Size = new System.Drawing.Size(96, 23);
this.buttonActionDelete.TabIndex = 21;
@@ -1222,9 +1222,9 @@
// buttonActionAdd
//
this.buttonActionAdd.Enabled = false;
- this.buttonActionAdd.Location = new System.Drawing.Point(6, 157);
+ this.buttonActionAdd.Location = new System.Drawing.Point(6, 6);
this.buttonActionAdd.Name = "buttonActionAdd";
- this.buttonActionAdd.Size = new System.Drawing.Size(96, 27);
+ this.buttonActionAdd.Size = new System.Drawing.Size(96, 23);
this.buttonActionAdd.TabIndex = 20;
this.buttonActionAdd.Text = "Add Action";
this.buttonActionAdd.UseVisualStyleBackColor = true;
@@ -1370,7 +1370,7 @@
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(784, 561);
+ this.ClientSize = new System.Drawing.Size(784, 562);
this.Controls.Add(this.labelFontHeight);
this.Controls.Add(this.labelFontWidth);
this.Controls.Add(this.labelWarning);
diff -r 0a956121c273 -r cc2251d065db Server/FormMain.cs
--- a/Server/FormMain.cs Thu Aug 18 20:14:30 2016 +0200
+++ b/Server/FormMain.cs Fri Aug 19 17:12:54 2016 +0200
@@ -151,7 +151,7 @@
// We loaded events and actions from our settings
// Internalizer apparently skips constructor so we need to initialize it here
// Though I reckon that should only be needed when loading an empty EAR manager I guess.
- Properties.Settings.Default.EarManager.Init();
+ Properties.Settings.Default.EarManager.Construct();
}
iSkipFrameRendering = false;
iClosing = false;
diff -r 0a956121c273 -r cc2251d065db SharpLibEar/ActionOpticalDriveEject.cs
--- a/SharpLibEar/ActionOpticalDriveEject.cs Thu Aug 18 20:14:30 2016 +0200
+++ b/SharpLibEar/ActionOpticalDriveEject.cs Fri Aug 19 17:12:54 2016 +0200
@@ -1,12 +1,308 @@
-using System;
+using Microsoft.Win32.SafeHandles;
+using System;
using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
using System.Linq;
+using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
+using SharpLib.Win32;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
namespace SharpLib.Ear
{
- class ActionOpticalDriveEject
+ [DataContract]
+ [AttributeObject(Id = "Action.OpticalDrive.Eject", Name = "Eject", Description = "Eject media from an optical drive.")]
+ public class ActionOpticalDriveEject : Action
{
+ [DataMember]
+ [AttributeObjectProperty
+ (
+ Id = "Action.OpticalDrive.Eject.Drive",
+ Name = "Drive to eject",
+ Description = "Select the drive you want to eject."
+ )
+ ]
+ public PropertyComboBox Drive { get; set; } = new PropertyComboBox();
+
+
+ protected override void DoConstruct()
+ {
+ base.DoConstruct();
+ PopulateOpticalDrives();
+ CheckCurrentItem();
+ }
+
+
+ public override string Brief()
+ {
+ return Name + " " + Drive.CurrentItem ;
+ }
+
+ public override bool IsValid()
+ {
+ //This object is valid if our current item is contained in our drive list
+ return Drive.Items.Contains(Drive.CurrentItem);
+ }
+
+ protected override void DoExecute()
+ {
+ DriveEject(Drive.CurrentItem);
+ }
+
+
+ private void CheckCurrentItem()
+ {
+ if (!Drive.Items.Contains(Drive.CurrentItem) && Drive.Items.Count>0)
+ {
+ //Current item unknown, reset it then
+ Drive.CurrentItem = Drive.Items[0];
+ }
+ }
+
+ ///
+ ///
+ ///
+ private void PopulateOpticalDrives()
+ {
+ //Reset our list of drives
+ Drive.Items = new List();
+ //Go through each drives on our system and collected the optical ones in our list
+ DriveInfo[] allDrives = DriveInfo.GetDrives();
+ foreach (DriveInfo d in allDrives)
+ {
+ Debug.WriteLine("Drive " + d.Name);
+ Debug.WriteLine(" Drive type: {0}", d.DriveType);
+
+ if (d.DriveType == DriveType.CDRom)
+ {
+ //This is an optical drive, add it now
+ Drive.Items.Add(d.Name.Substring(0, 2));
+ }
+ }
+ }
+
+
+ ///
+ ///
+ ///
+ ///
+ static private void CheckLastError(string aPrefix)
+ {
+ string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
+ Debug.WriteLine(aPrefix + Marshal.GetLastWin32Error().ToString() + ": " + errorMessage);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ static private IntPtr MarshalToPointer(object data)
+ {
+ IntPtr buf = Marshal.AllocHGlobal(
+ Marshal.SizeOf(data));
+ Marshal.StructureToPtr(data,
+ buf, false);
+ return buf;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ static private SafeFileHandle OpenVolume(string aDriveName)
+ {
+ return Function.CreateFile("\\\\.\\" + aDriveName,
+ SharpLib.Win32.FileAccess.GENERIC_READ,
+ SharpLib.Win32.FileShare.FILE_SHARE_READ | SharpLib.Win32.FileShare.FILE_SHARE_WRITE,
+ IntPtr.Zero,
+ CreationDisposition.OPEN_EXISTING,
+ 0,
+ IntPtr.Zero);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ static private bool LockVolume(SafeFileHandle aVolume)
+ {
+ //Hope that's doing what I think it does
+ IntPtr dwBytesReturned = new IntPtr();
+ //Should not be needed but I'm not sure how to pass NULL in there.
+ OVERLAPPED overlapped = new OVERLAPPED();
+
+ int tries = 0;
+ const int KMaxTries = 100;
+ const int KSleepTime = 10;
+ bool success = false;
+
+ while (!success && tries < KMaxTries)
+ {
+ success = Function.DeviceIoControl(aVolume, Const.FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, dwBytesReturned, ref overlapped);
+ System.Threading.Thread.Sleep(KSleepTime);
+ tries++;
+ }
+
+ CheckLastError("Lock volume: ");
+
+ return success;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ static private bool DismountVolume(SafeFileHandle aVolume)
+ {
+ //Hope that's doing what I think it does
+ IntPtr dwBytesReturned = new IntPtr();
+ //Should not be needed but I'm not sure how to pass NULL in there.
+ OVERLAPPED overlapped = new OVERLAPPED();
+
+ bool res = Function.DeviceIoControl(aVolume, Const.FSCTL_DISMOUNT_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, dwBytesReturned, ref overlapped);
+ CheckLastError("Dismount volume: ");
+ return res;
+ }
+
+
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ static private bool PreventRemovalOfVolume(SafeFileHandle aVolume, bool aPreventRemoval)
+ {
+ //Hope that's doing what I think it does
+ IntPtr dwBytesReturned = new IntPtr();
+ //Should not be needed but I'm not sure how to pass NULL in there.
+ OVERLAPPED overlapped = new OVERLAPPED();
+ //
+ PREVENT_MEDIA_REMOVAL preventMediaRemoval = new PREVENT_MEDIA_REMOVAL();
+ preventMediaRemoval.PreventMediaRemoval = Convert.ToByte(aPreventRemoval);
+ IntPtr preventMediaRemovalParam = MarshalToPointer(preventMediaRemoval);
+
+ bool result = Function.DeviceIoControl(aVolume, Const.IOCTL_STORAGE_MEDIA_REMOVAL, preventMediaRemovalParam, Convert.ToUInt32(Marshal.SizeOf(preventMediaRemoval)), IntPtr.Zero, 0, dwBytesReturned, ref overlapped);
+ CheckLastError("Media removal: ");
+ Marshal.FreeHGlobal(preventMediaRemovalParam);
+
+ return result;
+ }
+
+ ///
+ /// Eject optical drive media opening the tray if any.
+ ///
+ ///
+ ///
+ static private bool MediaEject(SafeFileHandle aVolume)
+ {
+ //Hope that's doing what I think it does
+ IntPtr dwBytesReturned = new IntPtr();
+ //Should not be needed but I'm not sure how to pass NULL in there.
+ OVERLAPPED overlapped = new OVERLAPPED();
+
+ bool res = Function.DeviceIoControl(aVolume, Const.IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, dwBytesReturned, ref overlapped);
+ CheckLastError("Media eject: ");
+ return res;
+ }
+
+ ///
+ /// Close an optical drive tray.
+ ///
+ ///
+ ///
+ static private bool MediaLoad(SafeFileHandle aVolume)
+ {
+ //Hope that's doing what I think it does
+ IntPtr dwBytesReturned = new IntPtr();
+ //Should not be needed but I'm not sure how to pass NULL in there.
+ OVERLAPPED overlapped = new OVERLAPPED();
+
+ bool res = Function.DeviceIoControl(aVolume, Const.IOCTL_STORAGE_LOAD_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, dwBytesReturned, ref overlapped);
+ CheckLastError("Media load: ");
+ return res;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ static private bool StorageCheckVerify(SafeFileHandle aVolume)
+ {
+ //Hope that's doing what I think it does
+ IntPtr dwBytesReturned = new IntPtr();
+ //Should not be needed but I'm not sure how to pass NULL in there.
+ OVERLAPPED overlapped = new OVERLAPPED();
+
+ bool res = Function.DeviceIoControl(aVolume, Const.IOCTL_STORAGE_CHECK_VERIFY2, IntPtr.Zero, 0, IntPtr.Zero, 0, dwBytesReturned, ref overlapped);
+
+ CheckLastError("Check verify: ");
+
+ return res;
+ }
+
+
+ ///
+ /// Perform media ejection.
+ ///
+ static private void DriveEject(string aDrive)
+ {
+ string drive = aDrive;
+ if (drive.Length != 2)
+ {
+ //Not a proper drive spec.
+ //Probably 'None' selected.
+ return;
+ }
+
+ SafeFileHandle handle = OpenVolume(drive);
+ if (handle.IsInvalid)
+ {
+ CheckLastError("ERROR: Failed to open volume: ");
+ return;
+ }
+
+ if (LockVolume(handle) && DismountVolume(handle))
+ {
+ Debug.WriteLine("Volume was dismounted.");
+
+ if (PreventRemovalOfVolume(handle, false))
+ {
+ //StorageCheckVerify(handle);
+
+ DateTime before;
+ before = DateTime.Now;
+ bool ejectSuccess = MediaEject(handle);
+ double ms = (DateTime.Now - before).TotalMilliseconds;
+
+ //We assume that if it take more than a certain time to for eject to execute it means we actually ejected.
+ //If our eject completes too rapidly we assume the tray is already open and we will try to close it.
+ if (ejectSuccess && ms > 100)
+ {
+ Debug.WriteLine("Media was ejected");
+ }
+ else if (MediaLoad(handle))
+ {
+ Debug.WriteLine("Media was loaded");
+ }
+ }
+ }
+ else
+ {
+ Debug.WriteLine("Volume lock or dismount failed.");
+ }
+
+ //This is needed to make sure we can open the volume next time around
+ handle.Dispose();
+ }
+
}
}
diff -r 0a956121c273 -r cc2251d065db SharpLibEar/Event.cs
--- a/SharpLibEar/Event.cs Thu Aug 18 20:14:30 2016 +0200
+++ b/SharpLibEar/Event.cs Fri Aug 19 17:12:54 2016 +0200
@@ -18,16 +18,22 @@
Description = "When enabled an event instance can be triggered."
)
]
- public bool Enabled { get; set; }
+ public bool Enabled { get; set; } = true;
[DataMember]
public List Actions = new List();
+ protected override void DoConstruct()
+ {
+ base.DoConstruct();
- protected Event()
- {
- Enabled = true;
+ // TODO: Construct properties too
+ foreach (Action a in Actions)
+ {
+ a.Construct();
+ }
+
}
diff -r 0a956121c273 -r cc2251d065db SharpLibEar/Manager.cs
--- a/SharpLibEar/Manager.cs Thu Aug 18 20:14:30 2016 +0200
+++ b/SharpLibEar/Manager.cs Fri Aug 19 17:12:54 2016 +0200
@@ -15,8 +15,7 @@
/// Users can implement their own events and actions.
///
[DataContract]
- [KnownType("DerivedTypes")]
- public class Manager
+ public class Manager: Object
{
///
/// Our events instances.
@@ -24,23 +23,24 @@
[DataMember]
public List Events;
- ///
- /// Constructor
- ///
- public Manager()
- {
- Init();
- }
///
/// Executes after internalization took place.
///
- public void Init()
+ protected override void DoConstruct()
{
+ base.DoConstruct();
+
if (Events == null)
{
Events = new List();
}
+
+ // TODO: Object properties should be constructed too
+ foreach (Event e in Events)
+ {
+ e.Construct();
+ }
}
@@ -86,15 +86,5 @@
}
}
}
-
- ///
- /// Allow extending our data contract.
- /// See KnownType above.
- ///
- ///
- private static IEnumerable DerivedTypes()
- {
- return SharpLib.Utils.Reflection.GetDerivedTypes();
- }
}
}
\ No newline at end of file
diff -r 0a956121c273 -r cc2251d065db SharpLibEar/Object.cs
--- a/SharpLibEar/Object.cs Thu Aug 18 20:14:30 2016 +0200
+++ b/SharpLibEar/Object.cs Fri Aug 19 17:12:54 2016 +0200
@@ -21,6 +21,33 @@
[KnownType("DerivedTypes")]
public abstract class Object: IComparable
{
+ private bool iConstructed = false;
+
+ public Object()
+ {
+ Construct();
+ }
+
+ ///
+ /// Needed as our constructor is not called following internalization.
+ ///
+ public void Construct()
+ {
+ if (!iConstructed)
+ {
+ DoConstruct();
+ iConstructed = true;
+ }
+ }
+
+ ///
+ ///
+ ///
+ protected virtual void DoConstruct()
+ {
+
+ }
+
///
/// Static object name.
///
diff -r 0a956121c273 -r cc2251d065db SharpLibEar/PropertyComboBox.cs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SharpLibEar/PropertyComboBox.cs Fri Aug 19 17:12:54 2016 +0200
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SharpLib.Ear
+{
+ ///
+ /// ComboBox property
+ ///
+ [DataContract]
+ [AttributeObject(Id = "Property.ComboBox", Name = "ComboBox", Description = "ComboBox property.")]
+ public class PropertyComboBox : Object
+ {
+ public IList Items { get; set; } = new List();
+
+ [DataMember]
+ public string CurrentItem { get; set; }
+ }
+}
diff -r 0a956121c273 -r cc2251d065db SharpLibEar/SharpLibEar.csproj
--- a/SharpLibEar/SharpLibEar.csproj Thu Aug 18 20:14:30 2016 +0200
+++ b/SharpLibEar/SharpLibEar.csproj Fri Aug 19 17:12:54 2016 +0200
@@ -48,6 +48,10 @@
MinimumRecommendedRules.ruleset
+
+ ..\packages\SharpLibWin32.0.0.9\lib\net20\SharpLibWin32.dll
+ True
+
@@ -73,6 +77,7 @@
+
@@ -81,6 +86,9 @@
SharpLibUtils
+
+
+