changeset 95:ebdff34b9e4f

Merge
author Ivo Smits <Ivo@UCIS.nl>
date Thu, 26 Jun 2014 18:45:56 +0200
parents 9b898d8b2541 (current diff) 3c1bba376dca (diff)
children 94df2951d118
files Net/HTTP.cs Util/ArrayUtil.cs
diffstat 8 files changed, 178 insertions(+), 72 deletions(-) [+]
line wrap: on
line diff
--- a/FBGUI/FBGUI.cs	Thu Jun 05 00:46:15 2014 +0200
+++ b/FBGUI/FBGUI.cs	Thu Jun 26 18:45:56 2014 +0200
@@ -8,6 +8,7 @@
 using System.Reflection;
 using System.Threading;
 using System.Windows.Forms;
+using UCIS.Util;
 using UCIS.VNCServer;
 using ThreadingTimer = System.Threading.Timer;
 
@@ -93,7 +94,7 @@
 	}
 	public interface IFBGContainerControl {
 		void AddControl(IFBGControl control);
-		void RemoveControl(IFBGControl control);
+		Boolean RemoveControl(IFBGControl control);
 		void HandleMessage(IFBGControl sender, FBGMessage e);
 		Size ClientSize { get; }
 	}
@@ -212,8 +213,8 @@
 		protected void RaiseEvent(EventHandler eh, EventArgs ea) { if (eh != null) eh(this, ea); }
 		protected void RaiseEvent(EventHandler eh) { if (eh != null) eh(this, new EventArgs()); }
 	}
-	public class FBGContainerControl : FBGControl, IFBGContainerControl {
-		protected List<IFBGControl> controls = new List<IFBGControl>();
+	public class FBGContainerControl : FBGControl, IFBGContainerControl, IList<IFBGControl> {
+		protected IFBGControl[] controls = new IFBGControl[0];
 		protected IFBGControl mouseCaptureControl = null;
 		protected IFBGControl keyboardCaptureControl = null;
 		public virtual Rectangle ClientRectangle { get { return new Rectangle(Point.Empty, Bounds.Size); } }
@@ -221,18 +222,27 @@
 		public FBGContainerControl(IFBGContainerControl parent) : base(parent) { }
 		void IFBGContainerControl.AddControl(IFBGControl control) { AddControl(control); }
 		protected virtual void AddControl(IFBGControl control) {
-			controls.Add(control);
+			ArrayUtil.Add(ref controls, control);
 			if (control.Visible) Invalidate(control);
 		}
-		public virtual void RemoveControl(IFBGControl control) {
-			if (controls.Remove(control)) {
-				if (control.Visible) Invalidate(control);
+		public virtual Boolean RemoveControl(IFBGControl control) {
+			if (!ArrayUtil.Remove(ref controls, control)) return false;
+			if (control.Visible) Invalidate(control);
+			HandleMessage(control, new FBGPointingCaptureMessage(control, false));
+			HandleMessage(control, new FBGKeyboardCaptureMessage(control, false));
+			control.Orphaned();
+			return true;
+		}
+		public virtual void RemoveAllControls() {
+			IFBGControl[] c = Interlocked.Exchange(ref controls, new IFBGControl[0]);
+			foreach (IFBGControl control in c) {
 				HandleMessage(control, new FBGPointingCaptureMessage(control, false));
 				HandleMessage(control, new FBGKeyboardCaptureMessage(control, false));
 				control.Orphaned();
 			}
+			Invalidate();
 		}
-		public IList<IFBGControl> Controls { get { return controls.AsReadOnly(); } }
+		public IList<IFBGControl> Controls { get { return this; } }
 		public virtual Point PointToChild(IFBGControl child, Point point) {
 			return point - (Size)child.Bounds.Location - (Size)ClientRectangle.Location;
 		}
@@ -240,10 +250,11 @@
 			return point + (Size)child.Bounds.Location + (Size)ClientRectangle.Location;
 		}
 		public virtual void BringControlToFront(IFBGControl control) {
-			if (controls.Count == 0) return;
-			if (ReferenceEquals(controls[controls.Count - 1], control)) return;
-			if (!controls.Remove(control)) return;
-			controls.Add(control);
+			if (controls.Length == 0) return;
+			int oldindex = Array.IndexOf(controls, control);
+			if (oldindex == -1 || oldindex == controls.Length - 1) return;
+			for (int i = oldindex; i < controls.Length - 1; i++) controls[i] = controls[i + 1];
+			controls[controls.Length - 1] = control;
 			if (control.Visible) Invalidate(control);
 		}
 		public virtual void Invalidate(IFBGControl control) {
@@ -278,7 +289,7 @@
 			Rectangle childarea = ClientRectangle;
 			if (!childarea.Contains(p)) return null;
 			p.Offset(-childarea.X, -childarea.Y);
-			return ((List<IFBGControl>)controls).FindLast(delegate(IFBGControl control) { return control.Visible && control.Bounds.Contains(p); });
+			return Array.FindLast(controls, delegate(IFBGControl control) { return control.Visible && control.Bounds.Contains(p); });
 		}
 		protected override void HandlePointingEvent(FBGPointingEvent e) {
 			IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : FindControlAtPosition(e.Position);
@@ -328,13 +339,31 @@
 			base.HandleKeyboardCaptureEvent(e);
 		}
 		protected override void Orphaned() {
-			base.Orphaned();
-			IFBGControl[] c = controls.ToArray();
-			controls.Clear();
+			IFBGControl[] c = Interlocked.Exchange(ref controls, new IFBGControl[0]);
 			foreach (IFBGControl control in c) control.Orphaned();
 			mouseCaptureControl = null;
 			keyboardCaptureControl = null;
+			base.Orphaned();
 		}
+
+		#region IList<IFBGControl> Members
+		int IList<IFBGControl>.IndexOf(IFBGControl item) { return Array.IndexOf(controls, item); }
+		void IList<IFBGControl>.Insert(int index, IFBGControl item) { throw new NotSupportedException(); }
+		void IList<IFBGControl>.RemoveAt(int index) { RemoveControl(controls[index]); }
+		IFBGControl IList<IFBGControl>.this[int index] {
+			get { return controls[index]; }
+			set { throw new NotSupportedException(); }
+		}
+		void ICollection<IFBGControl>.Add(IFBGControl item) { throw new NotSupportedException(); }
+		void ICollection<IFBGControl>.Clear() { RemoveAllControls(); }
+		bool ICollection<IFBGControl>.Contains(IFBGControl item) { return Array.IndexOf(controls, item) != -1; }
+		void ICollection<IFBGControl>.CopyTo(IFBGControl[] array, int arrayIndex) { controls.CopyTo(array, arrayIndex); }
+		int ICollection<IFBGControl>.Count { get { return controls.Length; } }
+		bool ICollection<IFBGControl>.IsReadOnly { get { return false; } }
+		bool ICollection<IFBGControl>.Remove(IFBGControl item) { return RemoveControl(item); }
+		IEnumerator<IFBGControl> IEnumerable<IFBGControl>.GetEnumerator() { return ((IEnumerable<IFBGControl>)controls).GetEnumerator(); }
+		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return controls.GetEnumerator(); }
+		#endregion
 	}
 	public class FBGDockContainer : FBGContainerControl {
 		private Dictionary<IFBGControl, DockStyle> dockStyles = new Dictionary<IFBGControl, DockStyle>();
@@ -351,9 +380,14 @@
 			base.BringControlToFront(control);
 			if (dockStyles.ContainsKey(control)) DoLayout();
 		}
-		public override void RemoveControl(IFBGControl control) {
-			base.RemoveControl(control);
+		public override Boolean RemoveControl(IFBGControl control) {
+			if (!base.RemoveControl(control)) return false;
 			if (dockStyles.Remove(control)) DoLayout();
+			return true;
+		}
+		public override void RemoveAllControls() {
+			base.RemoveAllControls();
+			dockStyles.Clear();
 		}
 		public override Rectangle Bounds {
 			get { return base.Bounds; }
@@ -384,7 +418,7 @@
 		public void SetDockStyle(IFBGControl control, DockStyle style) {
 			if (style == DockStyle.None) {
 				if (dockStyles.Remove(control)) DoLayout();
-			} else if (controls.Contains(control)) {
+			} else if (Array.IndexOf(controls, control) != -1) {
 				anchorStyles.Remove(control);
 				dockStyles[control] = style;
 				DoLayout();
@@ -398,13 +432,13 @@
 		public void SetAnchorStyle(IFBGControl control, AnchorStyles style) {
 			if (style == (AnchorStyles.Left | AnchorStyles.Top)) {
 				anchorStyles.Remove(control);
-			} else if (controls.Contains(control)) {
+			} else if (Array.IndexOf(controls, control) != -1) {
 				dockStyles.Remove(control);
 				anchorStyles[control] = style;
 			}
 		}
 		public void SetAnchor(IFBGControl control, AnchorStyles style, int value) {
-			if (controls.Contains(control)) {
+			if (Array.IndexOf(controls, control) != -1) {
 				AnchorStyles oldstyle;
 				if (!anchorStyles.TryGetValue(control, out oldstyle)) oldstyle = AnchorStyles.Left | AnchorStyles.Top;
 				Rectangle b = control.Bounds;
@@ -496,13 +530,14 @@
 			control.Bounds = new Rectangle(Point.Empty, ClientSize);
 			Invalidate(control.Bounds);
 		}
-		void IFBGContainerControl.RemoveControl(IFBGControl control) {
-			if (!ReferenceEquals(childControl, control)) return;
+		Boolean IFBGContainerControl.RemoveControl(IFBGControl control) {
+			if (!ReferenceEquals(childControl, control)) return false;
 			childControl = null;
 			Invalidate(control.Bounds);
 			if (mouseCaptureControl == control) mouseCaptureControl = null;
 			if (keyboardCaptureControl == control) control = null;
 			control.Orphaned();
+			return true;
 		}
 		void IFBGContainerControl.HandleMessage(IFBGControl sender, FBGMessage e) {
 			if (e is FBGInvalidateMessage) {
@@ -623,6 +658,7 @@
 		public static readonly FBGCursor SizeBottomLeft = SizeTopLeft.RotateFlip(RotateFlipType.RotateNoneFlipY);
 		public static readonly FBGCursor SizeBottomRight = SizeTopLeft.RotateFlip(RotateFlipType.RotateNoneFlipXY);
 		public static readonly FBGCursor Hand = LoadFromResource("cursor_hand", 5, 0);
+		public static readonly FBGCursor IBeam = LoadFromResource("cursor_ibeam", 3, 7);
 		public static FBGCursor ArrowCursor { get { return Arrow; } }
 	}
 	public class FBGRenderer : FBGContainerControl, IDisposable {
@@ -1039,10 +1075,19 @@
 				}
 			};
 		}
-		public override void RemoveControl(IFBGControl control) {
-			windowcontainer.RemoveControl(control);
+		public override Boolean RemoveControl(IFBGControl control) {
+			if (!windowcontainer.RemoveControl(control)) return false;
+			FBGWindowState ws = windowstates[control];
+			menucontainer.RemoveControl(ws.MenuButton);
 			windowstates.Remove(control);
 			ScaleMenuButtons();
+			return true;
+		}
+		public override void RemoveAllControls() {
+			windowcontainer.RemoveAllControls();
+			menucontainer.RemoveAllControls();
+			windowstates.Clear();
+			ScaleMenuButtons();
 		}
 		private void ScaleMenuButtons() {
 			int bcount = windowstates.Count;
@@ -1105,14 +1150,16 @@
 		public FBGTextBox(IFBGContainerControl parent) : base(parent) { 
 			BackColor = Color.White;
 			Size = new Size(200, 20);
+			Cursor = FBGCursor.IBeam;
 		}
 		private String text = String.Empty;
 		private Char passwordChar = (Char)0;
 		private Font font = new Font(FontFamily.GenericMonospace, 10);
 		private Brush brush = SystemBrushes.ControlText;
 		private Boolean hasKeyboardFocus = false;
-		public String Text { get { return text; } set { text = value; if (CaretPosition > text.Length) CaretPosition = text.Length; Invalidate(); RaiseEvent(TextChanged); } }
-		public Font Font { get { return font; } set { font = value; Invalidate(); } }
+		private float[] characterPositions = null;
+		public String Text { get { return text; } set { text = value; if (CaretPosition > text.Length) CaretPosition = text.Length; characterPositions = null; Invalidate(); RaiseEvent(TextChanged); } }
+		public Font Font { get { return font; } set { font = value; characterPositions = null; Invalidate(); } }
 		public Brush Brush { get { return brush; } set { brush = value; Invalidate(); } }
 		public Color Color { set { Brush = new SolidBrush(value); } }
 		public Int32 CaretPosition { get; private set; }
@@ -1122,6 +1169,10 @@
 		protected override void Paint(Graphics g) {
 			base.Paint(g);
 			g.DrawRectangle(Pens.Gray, 0, 0, Bounds.Width - 1, Bounds.Height - 1);
+			float[] positions = characterPositions;
+			String textbuffer = text;
+			int textlength = textbuffer == null ? 0 : textbuffer.Length;
+			if (positions == null || positions.Length != textlength) characterPositions = positions = new float[text.Length];
 			using (StringFormat sf_nonprinting = new StringFormat(StringFormat.GenericTypographic)) {
 				sf_nonprinting.Trimming = StringTrimming.None;
 				sf_nonprinting.FormatFlags = StringFormatFlags.DisplayFormatControl | StringFormatFlags.MeasureTrailingSpaces;
@@ -1129,43 +1180,28 @@
 
 				float x = 1;
 				float y = 1;
-				float w = Width - 2;
-				if (hasKeyboardFocus && CaretPosition == 0) {
-					g.DrawLine(Pens.Black, x + 2, 2, x + 2, Height - 4);
-				}
+				if (hasKeyboardFocus && CaretPosition == 0) g.DrawLine(Pens.Black, x + 2, 2, x + 2, Height - 4);
 				String c = passwordChar == 0 ? null : new String(passwordChar, 1);
-				for (int i = 0; i < text.Length; i++) {
-					if (passwordChar == 0) c = text.Substring(i, 1);
-					SizeF s = g.MeasureString(c, font, (int)Math.Ceiling(w), sf_nonprinting);
+				for (int i = 0; i < textlength; i++) {
+					if (passwordChar == 0) c = textbuffer.Substring(i, 1);
 					g.DrawString(c, font, brush, x, y);
+					SizeF s = g.MeasureString(c, font, int.MaxValue, sf_nonprinting);
 					x += (float)Math.Ceiling(s.Width);
-					w -= (float)Math.Ceiling(s.Width);
+					if (positions.Length > i) positions[i] = x;
 
-					if (hasKeyboardFocus && i == CaretPosition - 1) {
-						g.DrawLine(Pens.Black, x + 2, 2, x + 2, Height - 4);
-					}
+					if (hasKeyboardFocus && i == CaretPosition - 1) g.DrawLine(Pens.Black, x + 2, 2, x + 2, Height - 4);
 				}
 			}
 		}
+		int GetCharacterIndexAtPosition(float x) {
+			float[] positions = characterPositions;
+			if (positions == null) return -1;
+			for (int i = 0; i < positions.Length; i++) if (x < characterPositions[i]) return i;
+			return characterPositions.Length;
+		}
 		protected override void MouseDown(Point position, MouseButtons buttons) {
 			hasKeyboardFocus = CaptureKeyboard(true);
-			CaretPosition = text.Length;
-			float x = 1;
-			String c = passwordChar == 0 ? null : new String(passwordChar, 1);
-			for (int i = 0; i < text.Length; i++) {
-				if (passwordChar == 0) c = text.Substring(i, 1);
-				Size s;
-				try {
-					s = TextRenderer.MeasureText(c, font, Size.Empty, TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix);
-				} catch (Exception) {
-					break;
-				}
-				x += s.Width;
-				if (position.X < x) {
-					CaretPosition = i;
-					break;
-				}
-			}
+			CaretPosition = GetCharacterIndexAtPosition(position.X);
 			Invalidate();
 		}
 		protected override void KeyDown(Keys key) {
@@ -1211,6 +1247,7 @@
 		}
 		public void Focus() {
 			hasKeyboardFocus = CaptureKeyboard(true);
+			Invalidate();
 		}
 		protected override void LostKeyboardCapture() {
 			base.LostKeyboardCapture();
@@ -1606,7 +1643,7 @@
 			base.Paint(g);
 			g.DrawRectangle(Pens.DarkBlue, 0, 0, Bounds.Width - 1, Bounds.Height - 1);
 			int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight());
-			String text = SelectedText;
+			String text = Text;
 			if (text == null) {
 				g.FillRectangle(Brushes.DarkGray, 2, 2, Bounds.Width - 4 - 16, Bounds.Height - 4);
 			} else {
@@ -1621,7 +1658,7 @@
 			ControlPaint.DrawScrollButton(g, xoff, 1, 16, he, ScrollButton.Up, buttonUpState);
 			ControlPaint.DrawScrollButton(g, xoff, Bounds.Height - he - 1, 16, he, ScrollButton.Down, buttonDownState);
 		}
-		protected abstract String SelectedText { get; }
+		protected abstract String Text { get; }
 		protected override void MouseDown(Point position, MouseButtons buttons) {
 			CaptureKeyboard(true);
 			if ((buttons & MouseButtons.Left) != 0) {
@@ -1698,7 +1735,7 @@
 				}
 			}
 		}
-		protected override string SelectedText {
+		protected override string Text {
 			get {
 				if (selectedIndex == -1) return null;
 				Object item = items[selectedIndex];
@@ -1741,7 +1778,7 @@
 			set { maximum = value; if (this.value > maximum) this.Value = maximum; }
 		}
 		public int Step { get; set; }
-		protected override string SelectedText {
+		protected override string Text {
 			get { return value.ToString(); }
 		}
 		protected override void ButtonPressDown() {
Binary file FBGUI/cursor_ibeam.png has changed
--- a/Net/HTTP.cs	Thu Jun 05 00:46:15 2014 +0200
+++ b/Net/HTTP.cs	Thu Jun 26 18:45:56 2014 +0200
@@ -96,7 +96,7 @@
 		private PrebufferingStream Reader;
 		private List<HTTPHeader> RequestHeaders;
 		private HTTPConnectionState State = HTTPConnectionState.Starting;
-		private KeyValuePair<String, String>[] QueryParameters = null, PostParameters = null;
+		private KeyValuePair<String, String>[] QueryParameters = null, PostParameters = null, Cookies = null;
 		private HTTPOutputStream ResponseStream = null;
 		private HTTPInputStream RequestStream = null;
 
@@ -539,6 +539,7 @@
 			return list.ToArray();
 		}
 		public KeyValuePair<String, String>[] GetQueryParameters() {
+			if (RequestQuery == null) return new KeyValuePair<String, String>[0];
 			if (QueryParameters == null) QueryParameters = DecodeUrlEncodedFields(RequestQuery);
 			return QueryParameters;
 		}
@@ -565,6 +566,51 @@
 			return PostParameters;
 		}
 
+		public String GetCookie(String name) {
+			foreach (KeyValuePair<String, String> kvp in GetCookies()) if (kvp.Key == name) return kvp.Value;
+			return null;
+		}
+		public String[] GetCookies(String name) {
+			List<String> list = new List<string>();
+			foreach (KeyValuePair<String, String> kvp in GetCookies()) if (kvp.Key == name) list.Add(kvp.Value);
+			return list.ToArray();
+		}
+		public KeyValuePair<String, String>[] GetCookies() {
+			if (Cookies == null) {
+				String cookie = GetRequestHeader("Cookie");
+				List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
+				if (cookie != null) {
+					foreach (String part in cookie.Split(';', ',')) {
+						String[] subparts = part.Split('=');
+						String key = subparts[0].Trim(' ', '\t', '"');
+						String value = (subparts.Length < 2) ? null : subparts[1].Trim(' ', '\t', '"');
+						list.Add(new KeyValuePair<string, string>(key, value));
+					}
+				}
+				Cookies = list.ToArray();
+			}
+			return Cookies;
+		}
+
+		public void SetCookie(String name, String value) {
+			SendHeader("Set-Cookie", String.Format("{0}={1}", name, value));
+		}
+		public void SetCookie(String name, String value, DateTime expire) {
+			SendHeader("Set-Cookie", String.Format("{0}={1}; Expires={2:R}", name, value, expire));
+		}
+		public void SetCookie(String name, String value, DateTime? expire, String path, String domain, Boolean secure, Boolean httponly) {
+			StringBuilder sb = new StringBuilder();
+			sb.Append(name);
+			sb.Append("=");
+			sb.Append(value);
+			if (expire != null) sb.AppendFormat("; Expires={0:R}", expire.Value.ToUniversalTime());
+			if (path != null) sb.AppendFormat("; Path={0}", path);
+			if (domain != null) sb.AppendFormat("; Domain={0}", domain);
+			if (secure) sb.Append("; Secure");
+			if (httponly) sb.Append("; HttpOnly");
+			SendHeader("Set-Cookie", sb.ToString());
+		}
+
 		public Stream OpenRequestStream() {
 			if (RequestStream == null) RequestStream = new HTTPInputStream(this);
 			return RequestStream;
@@ -708,6 +754,9 @@
 		}
 		public void AddPrefix(String prefix, IHTTPContentProvider contentProvider) {
 			Prefixes.Add(new KeyValuePair<string, IHTTPContentProvider>(prefix, contentProvider));
+			Prefixes.Sort(delegate(KeyValuePair<String, IHTTPContentProvider> a, KeyValuePair<String, IHTTPContentProvider> b) {
+				return -String.CompareOrdinal(a.Key, b.Key);
+			});
 		}
 		public void DeletePrefix(String prefix) {
 			Prefixes.RemoveAll(delegate(KeyValuePair<string, IHTTPContentProvider> item) { return prefix.Equals(item.Key, PrefixComparison); });
--- a/Pml/PmlConnection.cs	Thu Jun 05 00:46:15 2014 +0200
+++ b/Pml/PmlConnection.cs	Thu Jun 26 18:45:56 2014 +0200
@@ -307,7 +307,7 @@
 					}
 				}
 			} catch (System.Threading.ThreadAbortException ex) {
-				throw ex;
+				throw;
 			} catch (Exception ex) {
 				Console.WriteLine(ex.ToString());
 			} finally {
--- a/UCIS.Core.csproj	Thu Jun 05 00:46:15 2014 +0200
+++ b/UCIS.Core.csproj	Thu Jun 26 18:45:56 2014 +0200
@@ -194,6 +194,9 @@
   <ItemGroup>
     <EmbeddedResource Include="FBGUI\cursor_hand.png" />
   </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="FBGUI\cursor_ibeam.png" />
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
--- a/USBLib/Communication/LibUsb0/LibUsbDevice.cs	Thu Jun 05 00:46:15 2014 +0200
+++ b/USBLib/Communication/LibUsb0/LibUsbDevice.cs	Thu Jun 26 18:45:56 2014 +0200
@@ -204,7 +204,7 @@
 					int transfered = 0;
 					while (length > 0) {
 						int ret;
-						DeviceIoControl(DeviceHandle, cltCode, ref req, LibUsbRequest.Size, (IntPtr)(b + offset), Math.Min(Int16.MaxValue, length), out ret);
+						DeviceIoControl(DeviceHandle, cltCode, ref req, LibUsbRequest.Size, (IntPtr)(b + offset), length, out ret);
 						if (ret <= 0) throw new System.IO.EndOfStreamException();
 						length -= ret;
 						offset += ret;
@@ -214,7 +214,7 @@
 				} else {
 					int cltCode = isochronous ? LibUsbIoCtl.ISOCHRONOUS_READ : LibUsbIoCtl.INTERRUPT_OR_BULK_READ;
 					int ret;
-					DeviceIoControl(DeviceHandle, cltCode, ref req, LibUsbRequest.Size, (IntPtr)(b + offset), Math.Min(UInt16.MaxValue, length), out ret);
+					DeviceIoControl(DeviceHandle, cltCode, ref req, LibUsbRequest.Size, (IntPtr)(b + offset), length, out ret);
 					return ret;
 				}
 			}
--- a/USBLib/Internal/Windows/SetupApi.cs	Thu Jun 05 00:46:15 2014 +0200
+++ b/USBLib/Internal/Windows/SetupApi.cs	Thu Jun 26 18:45:56 2014 +0200
@@ -94,6 +94,9 @@
 		[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
 		public static extern CR CM_Reenumerate_DevNode(UInt32 dnDevInst, UInt32 ulFlags);
 
+		[DllImport("newdev.dll", SetLastError = true, CharSet = CharSet.Auto)]
+		public static extern bool DiUninstallDevice(IntPtr hwndParent, SafeDeviceInfoSetHandle DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, UInt32 Flags, out Boolean NeedReboot);
+
 		//public const int DIGCF_DEFAULT = 0x00000001;  // only valid with DIGCF_DEVICEINTERFACE
 		public const int DIGCF_PRESENT = 0x00000002;
 		public const int DIGCF_ALLCLASSES = 0x00000004;
--- a/USBLib/Windows/Devices/DeviceNode.cs	Thu Jun 05 00:46:15 2014 +0200
+++ b/USBLib/Windows/Devices/DeviceNode.cs	Thu Jun 26 18:45:56 2014 +0200
@@ -29,7 +29,8 @@
 		}
 		public static DeviceNode GetDevice(String deviceID) {
 			UInt32 node;
-			CR ret = SetupApi.CM_Locate_DevNode(out node, deviceID, 0);
+			CR ret = SetupApi.CM_Locate_DevNode(out node, deviceID, 4);
+			if (ret == CR.NO_SUCH_DEVNODE) return null;
 			CMException.Throw(ret, "CM_Locate_DevNode");
 			return new DeviceNode(node);
 		}
@@ -58,7 +59,6 @@
 		}
 		public static IList<DeviceNode> GetDevices(String enumerator, Boolean present) {
 			using (SafeDeviceInfoSetHandle dis = SetupApi.SetupDiGetClassDevsA(IntPtr.Zero, enumerator, IntPtr.Zero, (present ? DICFG.PRESENT : 0) | DICFG.ALLCLASSES)) {
-			//using (SafeDeviceInfoSetHandle dis = SetupApi.SetupDiGetClassDevsA(IntPtr.Zero, enumerator, IntPtr.Zero, DICFG.ALLCLASSES | DICFG.DEVICEINTERFACE)) {
 				return GetDevicesInSet(dis);
 			}
 		}
@@ -88,7 +88,6 @@
 				SP_DEVINFO_DATA dd = new SP_DEVINFO_DATA(true);
 				if (!SetupApi.SetupDiEnumDeviceInfo(dis, 0, ref dd))
 					return null;
-				//throw new Win32Exception(Marshal.GetLastWin32Error());
 				RegistryValueKind propertyType;
 				byte[] propBuffer = new byte[256];
 				int requiredSize;
@@ -148,7 +147,6 @@
 				SP_DEVINFO_DATA dd = new SP_DEVINFO_DATA(true);
 				if (!SetupApi.SetupDiEnumDeviceInfo(dis, 0, ref dd))
 					return null;
-					//throw new Win32Exception(Marshal.GetLastWin32Error());
 				RegistryValueKind propertyType;
 				byte[] propBuffer = new byte[256];
 				int requiredSize;
@@ -290,14 +288,17 @@
 			return new DeviceNode(node);
 		}
 
+		public Boolean GetStatus(out UInt32 status, out UInt32 problem) {
+			CR ret = SetupApi.CM_Get_DevNode_Status(out status, out problem, DevInst, 0);
+			if (ret == CR.NO_SUCH_DEVNODE) return false;
+			CMException.Throw(ret, "CM_Get_DevNode_Status");
+			return true;
+		}
+
 		public Boolean Present {
 			get {
 				UInt32 status, problem;
-				CR ret = SetupApi.CM_Get_DevNode_Status(out status, out problem, DevInst, 0);
-				if (ret == CR.NO_SUCH_DEVNODE) return false;
-				CMException.Throw(ret, "CM_Get_DevNode_Status");
-				if (status == 25174016) return false;
-				return true;
+				return GetStatus(out status, out problem) && status != 25174016;
 			}
 		}
 
@@ -337,5 +338,18 @@
 					throw new Win32Exception(Marshal.GetLastWin32Error());
 			}
 		}
+
+		public Boolean Uninstall() {
+			using (SafeDeviceInfoSetHandle dis = SetupApi.SetupDiGetClassDevsA(IntPtr.Zero, DeviceID, IntPtr.Zero, DICFG.DEVICEINTERFACE | DICFG.ALLCLASSES)) {
+				if (dis.IsInvalid) throw new Win32Exception(Marshal.GetLastWin32Error());
+				SP_DEVINFO_DATA dd = new SP_DEVINFO_DATA(true);
+				if (!SetupApi.SetupDiEnumDeviceInfo(dis, 0, ref dd))
+					throw new Win32Exception(Marshal.GetLastWin32Error());
+				Boolean needsReboot;
+				if (!SetupApi.DiUninstallDevice(IntPtr.Zero, dis, ref dd, 0, out needsReboot))
+					throw new Win32Exception(Marshal.GetLastWin32Error());
+				return needsReboot;
+			}
+		}
 	}
 }