1    	//  =========================================================================
2    	//  
3    	//                             INTEL CONFIDENTIAL
4    	//                            Copyright 2005 - 2015
5    	//                    Intel Corporation All Rights Reserved. 
6    	//  
7    	//  =========================================================================
8    	//  The source code contained or described herein and all documents 
9    	//  related to the source code ("Material") are owned by Intel Corporation 
10   	//  or its suppliers or licensors. Title to the Material remains with 
11   	//  Intel Corporation or its suppliers and licensors. The Material contains 
12   	//  trade secrets and proprietary and confidential information of Intel or 
13   	//  its suppliers and licensors. The Material is protected by worldwide 
14   	//  copyright and trade secret laws and treaty provisions. No part of the 
15   	//  Material may be used, copied, reproduced, modified, published, uploaded, 
16   	//  posted, transmitted, distributed, or disclosed in any way without Intel’s 
17   	//  prior express written permission.
18   	//  
19   	//  No license under any patent, copyright, trade secret or other intellectual 
20   	//  property right is granted to or conferred upon you by disclosure or 
21   	//  delivery of the Materials, either expressly, by implication, inducement, 
22   	//  estoppel or otherwise. Any license under such intellectual property rights 
23   	//  must be express and approved by Intel in writing.
24   	//  ==========================================================================
25   	
26   	using System;
27   	using System.Collections.Generic;
28   	using System.ComponentModel;
29   	using System.IO;
30   	using System.Linq;
31   	using System.Runtime.InteropServices;
32   	using System.Text;
33   	using System.Text.RegularExpressions;
34   	using Microsoft.Win32;
35   	
36   	namespace Intel.Tools.DeviceManager
37   	{
38   		internal static class DeviceQuery
39   		{
40   			#region GetAllDevices
41   			internal static DeviceList GetAllDevices()
42   			{
43   				int memberIndex = 0;
44   				SP_DEVINFO_DATA devInfoData;
(1) Event new_resource: Created a new object of type "Intel.Tools.DeviceManager.DeviceList", which implements "System.IDisposable". [details]
(2) Event var_assign: Assigning: "deviceList" = resource returned from "new Intel.Tools.DeviceManager.DeviceList()".
Also see events: [noescape][leaked_resource]
45   				var deviceList = new DeviceList();
46   	
(3) Event cond_true: Condition "true", taking true branch.
47   				while (true)
48   				{
49   					devInfoData = SP_DEVINFO_DATA.Empty();
50   	
(4) Event noescape: Resource "deviceList" is not closed or saved in "DeviceInfoSet.get". [details]
(5) Event throw_uncaught: Throwing "System.Exception" from call to "DeviceInfoSet.get"; exiting method with uncaught exception. [details]
(6) Event leaked_resource: Variable "deviceList" going out of scope leaks the resource it refers to.
Also see events: [new_resource][var_assign]
51   					if (!Win32Interop.SetupDiEnumDeviceInfo(deviceList.DeviceInfoSet, memberIndex, ref devInfoData))
52   					{
53   						var error = Marshal.GetLastWin32Error();
54   	
55   						if (error == Win32Error.NoMoreItems)
56   							break;
57   						else
58   						{
59   							deviceList.Dispose();
60   							throw new Win32Exception(error, "An error occurred when searching for drivers.");
61   						}
62   					}
63   					else
64   					{
65   						deviceList.Add(new Device(deviceList, devInfoData));
66   					}
67   	
68   					memberIndex++;
69   				}
70   	
71   				return deviceList;
72   			}
73   			#endregion
74   	
75   			#region Get Properties
76   			#region Extensions
77   			internal static object GetProperty(this Device device, DeviceRegistryProperty flags)
78   			{
79   				var devInfoData = device.DevInfoData;
80   	
81   				object obj = GetProperty(device.DeviceInfoSet, ref devInfoData, (uint)flags);
82   	
83   				device.DevInfoData = devInfoData;
84   	
85   				return obj;
86   			}
87   	
88   			internal static int GetPropertyInt(this Device device, DeviceRegistryProperty flags)
89   			{
90   				var devInfoData = device.DevInfoData;
91   	
92   				int value = GetPropertyInt(device.DeviceInfoSet, ref devInfoData, (uint)flags);
93   	
94   				device.DevInfoData = devInfoData;
95   	
96   				return value;
97   			}
98   	
99   			internal static string GetPropertyString(this Device device, DeviceRegistryProperty flags)
100  			{
101  				var devInfoData = device.DevInfoData;
102  	
103  				string value = GetPropertyString(device.DeviceInfoSet, ref devInfoData, (uint)flags);
104  	
105  				device.DevInfoData = devInfoData;
106  	
107  				return value;
108  			}
109  	
110  			/// <summary>
111  			/// Gets the RegistryKey to the driver information for the given device.
112  			/// </summary>
113  			private static RegistryKey GetDriverKey(this Device device, bool writable = false)
114  			{
115  				if (!_string.IsNullOrWhiteSpace(device.Driver))
116  				{
117  					using (var hklm = Registry.LocalMachine)
118  					{
119  						return hklm.OpenSubKey(@"System\CurrentControlSet\Control\Class\" + device.Driver, writable);
120  					}
121  				}
122  				else return null;
123  			}
124  	
125  			internal static string GetProviderName(this Device device)
126  			{
127  				using (var key = device.GetDriverKey())
128  				{
129  					return key == null ? string.Empty : key.GetValue("ProviderName", string.Empty) as string;
130  				}
131  			}
132  	
133  			internal static string GetInfPath(this Device device)
134  			{
135  				using (var key = device.GetDriverKey())
136  				{
137  					if (key == null)
138  					{
139  						return string.Empty;
140  					}
141  					else
142  					{
143  						string infPath = key.GetValue("InfPath", string.Empty) as string;
144  	
145  						if (!_string.IsNullOrWhiteSpace(infPath))
146  						{
147  							return _Path.Combine(_Path.GetWindowsDirectory(), "INF", infPath);
148  							
149  						}
150  						else return string.Empty;
151  					}
152  				}
153  			}
154  	
155  			internal static string GetPackageInfoName(this Device device)
156  			{
157  				string infPath = device.GetInfPath();
158  	
159  				if (File.Exists(infPath))
160  				{
161  					return GetPackageInfoName(infPath);
162  				}
163  				else
164  				{
165  					return string.Empty;
166  				}
167  			}
168  	
169  			private static string GetPackageInfoName(string infPath)
170  			{
171  				string packageInfoName = string.Empty;
172  	
173  				using (var reader = new StreamReader(infPath))
174  				{
175  					string line = reader.ReadLine();;
176  					string sectionPattern = @"^\s*\[\s*(.+)\s*\]\s*$";
177  					string packageInfoSectionPattern = @"^\s*\[\s*PackageInfo\s*\]\s*$";
178  					string nameFieldPattern = @"^\s*Name\s*=\s*(.+)\s*$";
179  	
180  					//Search the file until we find the PackageInfo section.
181  					while (!reader.EndOfStream)
182  					{
183  						line = reader.ReadLine();
184  	
185  						//If we found the PackageInfo section...
186  						if (Regex.IsMatch(line, packageInfoSectionPattern, RegexOptions.IgnoreCase))
187  						{
188  							//...then break.
189  							break;
190  						}
191  					}
192  	
193  					//Continue searching until we find the next section or the Name field.
194  					while (!reader.EndOfStream)
195  					{
196  						line = reader.ReadLine();
197  	
198  						//If we found the Name field...
199  						if (Regex.IsMatch(line, nameFieldPattern, RegexOptions.IgnoreCase))
200  						{
201  							//...then get the value of that field so we can return it.
202  							packageInfoName = Regex.Match(line, nameFieldPattern, RegexOptions.IgnoreCase).Groups[1].Value;
203  							break;
204  						}
205  						//...else if we found the next section...
206  						else if (Regex.IsMatch(line, sectionPattern))
207  						{
208  							//...then break.
209  							break;
210  						}
211  						//...otherwise, continue searching.
212  					}
213  	
214  					reader.Close();
215  				}
216  	
217  				return packageInfoName;
218  			}
219  			#endregion
220  	
221  			internal static object GetProperty(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA devInfoData, uint flags)
222  			{
223  				IntPtr buffer = IntPtr.Zero;
224  				int bufferSize = 0;
225  				uint regType = 0, requiredSize = 0;
226  	
227  				//Find the correct amount of memory required for the buffer.
228  				if (!Win32Interop.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, flags, out regType, buffer, bufferSize, out requiredSize))
229  				{
230  					switch ((RegistryValueKind)regType)
231  					{
232  						case RegistryValueKind.DWord:
233  							return GetPropertyInt(deviceInfoSet, ref devInfoData, flags);
234  	
235  						case RegistryValueKind.Binary: //break;
236  						case RegistryValueKind.ExpandString: //break;
237  						case RegistryValueKind.MultiString: //break;
238  						//case RegistryValueKind.None: //break; // Not available in .Net 3.5
239  						case RegistryValueKind.QWord: //break;
240  						case RegistryValueKind.Unknown: //break;
241  						case RegistryValueKind.String:
242  							return GetPropertyString(deviceInfoSet, ref devInfoData, flags);
243  	
244  						default:
245  							break;
246  					}
247  				}
248  	
249  				return string.Empty;
250  			}
251  	
252  			internal static int GetPropertyInt(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA devInfoData, uint flags)
253  			{
254  				byte[] buffer = null;// new byte[0];
255  				int bufferSize = 0;
256  				uint regType = 0, requiredSize = 0;
257  	
258  				//Find the correct amount of memory required for the buffer.
259  	            unsafe { fixed (byte* bufferPtr = buffer) {
260  	                IntPtr bufferIntPtr = new IntPtr(bufferPtr);
261  	                if (!Win32Interop.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, flags, out regType, bufferIntPtr, bufferSize, out requiredSize))
262  	                {
263  	                    int error = Marshal.GetLastWin32Error();
264  	
265  	                    switch (error)
266  	                    {
267  	                        case Win32Error.InsufficientBuffer:
268  	                            //Do nothing.
269  	                            break;
270  	                        case Win32Error.InvalidData:
271  	                            //Return 0
272  	                            return 0;
273  	                        default:
274  	                            //Throw an exception.
275  	                            throw new Win32Exception(error);
276  	                    }
277  	                }
278  	            }}
279  	
280  				//Allocate the correct amount of memory for the buffer.
281  				bufferSize = (int)requiredSize;
282  				buffer = new byte[bufferSize];
283  	            unsafe { fixed (byte* bufferPtr = buffer) {
284  	                IntPtr bufferIntPtr = new IntPtr(bufferPtr);
285  				    try
286  				    {
287  					    //Populate the buffer.
288  					    if (Win32Interop.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, flags, out regType, bufferIntPtr, bufferSize, out requiredSize))
289  					    {
290  						    return BitConverter.ToInt32(buffer, 0);
291  					    }
292  					    else { throw new Win32Exception(Marshal.GetLastWin32Error()); }
293  				    }
294  				    catch { throw; }
295  	            }}
296  			}
297  	
298  			internal static string GetPropertyString(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA devInfoData, uint flags)
299  			{
300  				IntPtr buffer = IntPtr.Zero;
301  				int bufferSize = 0;
302  				uint regType = 0, requiredSize = 0;
303  	
304  				//Find the correct amount of memory required for the buffer.
305  				if (!Win32Interop.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, flags, out regType, buffer, bufferSize, out requiredSize))
306  				{
307  					if (regType == (int)RegistryValueKind.Unknown)
308  					{
309  						return null;
310  					}
311  					else if (regType != (int)RegistryValueKind.String)
312  					{
313  						return string.Format("   ~~~~~ {0} ~~~~~", (RegistryValueKind)regType);
314  					}
315  	
316  					int error = Marshal.GetLastWin32Error();
317  	
318  					switch (error)
319  					{
320  						case Win32Error.InsufficientBuffer:
321  							//Do nothing.
322  							break;
323  						case Win32Error.InvalidData:
324  							//Return an empty string.
325  							return string.Empty;
326  						default:
327  							//Throw an exception.
328  							throw new Win32Exception(error);
329  					}
330  				}
331  	
332  				//Allocate the correct amount of memory for the buffer.
333  				bufferSize = (int)requiredSize;
334  				buffer = Marshal.AllocHGlobal(bufferSize);
335  	
336  				try
337  				{
338  					//Populate the buffer.
339  					if (Win32Interop.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, flags, out regType, buffer, bufferSize, out requiredSize))
340  					{
341  						return Marshal.PtrToStringAuto(buffer);
342  					}
343  					else { throw new Win32Exception(Marshal.GetLastWin32Error()); }
344  				}
345  				catch { throw; }
346  				finally
347  				{
348  					Marshal.FreeHGlobal(buffer);
349  				}
350  			}
351  	
352  			internal static Dictionary<string, string> GetAllRegistryProperties(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA devInfoData)
353  			{
354  				var allRegistryProperties = new Dictionary<string, string>();
355  				Type type = typeof(DeviceRegistryProperty);
356  				var values = (int[])Enum.GetValues(type);
357  				string propertyValue;
358  	
359  				foreach (var value in values)
360  				{
361  					try
362  					{
363  						propertyValue = GetProperty(deviceInfoSet, ref devInfoData, (uint)value)?.ToString();
364  					    if (propertyValue == null) propertyValue = string.Empty;
365  					}
366  					catch
367  					{ propertyValue = " ~~~~~ Error! ~~~~~ "; } //TODO: Should I provide a different value here?
368  	
369  				    allRegistryProperties.Add(
370  				        Enum.GetName(type, value),
371  				        propertyValue);
372  				}
373  	
374  				return allRegistryProperties;
375  			}
376  	
377  			#endregion
378  	
379  			#region Set Properties
380  			internal static bool SetProperty(this Device device, int value, DeviceRegistryProperty flags)
381  			{
382  				if (device.IsDisposed)
383  					throw new ObjectDisposedException("Cannot change config flags on the disposed object.", (Exception)null);
384  	
385  				SP_DEVINFO_DATA devInfoData = device.DevInfoData;
386  				byte[] buffer = BitConverter.GetBytes(value);
387  	            unsafe { fixed (byte* bufferPtr = buffer) {
388  	                IntPtr bufferIntPtr = new IntPtr(bufferPtr);
389  				    if (Win32Interop.SetupDiSetDeviceRegistryProperty(device.DeviceInfoSet, ref devInfoData, (uint)flags, bufferIntPtr, buffer.Length))
390  				    {
391  					    device.DevInfoData = devInfoData;
392  					    return true;
393  				    }
394  				    else throw new Win32Exception(Marshal.GetLastWin32Error());
395  	            }}
396  			}
397  	
398  			internal static bool SetProperty(this Device device, string value, DeviceRegistryProperty flags)
399  			{
400  				if (device.IsDisposed)
401  					throw new ObjectDisposedException("Cannot change property string on the disposed object.", (Exception)null);
402  	
403  				SP_DEVINFO_DATA devInfoData = device.DevInfoData;
404  				IntPtr buffer = Marshal.StringToHGlobalAuto(value);
405  				int size = value.Length * Marshal.SystemDefaultCharSize;
406  	
407  				if (Win32Interop.SetupDiSetDeviceRegistryProperty(device.DeviceInfoSet, ref devInfoData, (uint)flags, buffer, size))
408  				{
409  					device.DevInfoData = devInfoData;
410  					return true;
411  				}
412  				else throw new Win32Exception(Marshal.GetLastWin32Error());
413  			}
414  	
415  	        #region Commented Code
416  	        //TODO: Why did I delete this method? Does it not work? Does Description cover it well enough?
417  	        //public void SetFriendlyName(string value)
418  	        //{
419  	        //	if (this.SetDevicePropertyString(value, DeviceRegistryProperty.FriendlyName))
420  	        //	{
421  	        //		this.FriendlyName = value;
422  	        //	}
423  	        //}
424  	        #endregion
425  	        #endregion
426  	
427  	        #region Get device instance id
428  	
429  	        internal static string GetDevicePathString(this Device device)
430  	        {
431  	            var devInfoData = device.DevInfoData;
432  	
433  	            string value = GetDeviceInstancePath(device.DeviceInfoSet, ref devInfoData);
434  	
435  	            device.DevInfoData = devInfoData;
436  	
437  	            return value;
438  	        }
439  	
440  	        private static string GetDeviceInstancePath(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA devInfoData)
441  	        {
442  	            //Allocate the correct amount of memory for the buffer.           
443  	            int bufferSize = 256;
444  	            int requiredSize = 0;
445  	            StringBuilder buffer = new StringBuilder(bufferSize);
446  	            //Populate the buffer.
447  	            if (Win32Interop.SetupDiGetDeviceInstanceId(deviceInfoSet, ref devInfoData, buffer, bufferSize, out requiredSize))
448  	            {
449  	                return buffer.ToString();
450  	            }
451  	            else { throw new Win32Exception(Marshal.GetLastWin32Error()); }
452  	        }
453  	        #endregion
454  	
455  	        #region Get Hardware IDs
456  	        internal static string[] GetHardwareIDs(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA devInfoData)
457  			{
458  				IntPtr buffer = IntPtr.Zero;
459  				int bufferSize = 0;
460  				uint regType = 7, requiredSize = 0;
461  	
462  				uint flags = (uint)DeviceRegistryProperty.HardwareID;
463  	
464  				//Find the correct amount of memory required for the buffer.
465  				if (!Win32Interop.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, flags, out regType, buffer, bufferSize, out requiredSize))
466  				{
467  					int error = Marshal.GetLastWin32Error();
468  	
469  					switch (error)
470  					{
471  						case Win32Error.InsufficientBuffer:
472  							//Do nothing.
473  							break;
474  						case Win32Error.InvalidData:
475  							//Return an empty array.
476  							return new string[0];
477  						default:
478  							//Throw an exception.
479  							throw new Win32Exception(error);
480  					}
481  				}
482  	
483  				//Allocate the correct amount of memory for the buffer.
484  				bufferSize = (int)requiredSize;
485  				buffer = Marshal.AllocHGlobal(bufferSize);
486  	
487  				try
488  				{
489  					//Populate the buffer.
490  					if (Win32Interop.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, flags, out regType, buffer, bufferSize, out requiredSize))
491  					{
492  						return PtrToStringArrayAuto(buffer);
493  					}
494  					else { throw new Win32Exception(Marshal.GetLastWin32Error()); }
495  				}
496  				catch { throw; }
497  				finally
498  				{
499  					Marshal.FreeHGlobal(buffer);
500  				}
501  			}
502  	
503  			/// <summary>
504  			/// Converts a pointer to an unmanaged string buffer of the
505  			/// REG_MULTI_SZ format to an array of strings, excluding
506  			/// the null terminating character '\0'.
507  			/// </summary>
508  			/// <param name="buffer">A pointer to the unmanaged REG_MULTI_SZ buffer.</param>
509  			/// <returns>An array containing the strings of the REG_MULTI_SZ value.</returns>
510  			private static string[] PtrToStringArrayAuto(IntPtr buffer)
511  			{
512  				//Notes:
513  				// REG_MULTI_SZ is formatted as such: "String1\0String2\0String3\0LastString\0\0"
514  	
515  				int offset = 0;
516  				string currentString = string.Empty;
517  				List<string> multiStringList = new List<string>();
518  	
519  				while (!string.IsNullOrEmpty(currentString = Marshal.PtrToStringAuto(_IntPtr.Offset(buffer, offset))))
520  				{
521  					multiStringList.Add(currentString);
522  					offset += (currentString.Length + 1) * Marshal.SystemDefaultCharSize; //Note: "+1" for the '\0' terminating character, and "x1" or "x2" for Unicode (two bytes per character).
523  				}
524  	
525  				return multiStringList.ToArray();
526  			}
527  	        #endregion
528  	
529  	        #region Get Parent of Device
530  	        internal static int GetDeviceParent(SP_DEVINFO_DATA DeviceInfo)
531  	        {
532  	            UInt32 CR_SUCCESS = 0x00000000;
533  	            int parentDeviceInst = 0;
534  	            if(Win32Interop.CM_Get_Parent(ref parentDeviceInst, DeviceInfo.DevInst, 0) == CR_SUCCESS)
535  	            {
536  	                return parentDeviceInst;
537  	            }
538  	            throw new Win32Exception(Marshal.GetLastWin32Error());
539  	        }
540  	        #endregion
541  	    }
542  	}
543