2009-09-17 31 views
39

Tôi đang viết một ứng dụng C# nhỏ để vô hiệu hóa thiết bị (bàn di chuột của tôi) bất cứ khi nào một thiết bị chuột khác được phát hiện và bật lại bàn di chuột nếu không phát hiện thấy chuột. Tôi thậm chí không thể vô hiệu hóa touchpad trong trình quản lý thiết bị (nó đang chạy trên trình điều khiển lớp chuột mặc định).Chức năng API Win32 để bật/tắt thiết bị theo cách lập trình

Tôi bắt đầu phát triển trình điều khiển thiết bị vì vậy tôi có thể viết một trình điều khiển lọc nhỏ chỉ chấp nhận IOCTL để bật và tắt thông báo sự kiện chuột lên ngăn xếp thiết bị và nhận tin nhắn từ chế độ người dùng qua PDO thô . Tuy nhiên, I asked that question và ai đó đã gợi ý rằng tôi có thể làm điều này trong usermode thông qua các chức năng SetupDi... Điều đó sẽ thực sự tốt, bởi vì phương pháp giao tiếp PDO thô này là một PITA để làm việc cùng.

Trước đây, tôi chỉ sử dụng Thiết lậpDiGetClassDevs và có rất nhiều người trong số họ có thể trải nghiệm nhiều hơn với phần này của API Win32 chỉ cho tôi biết một cách nhanh chóng giao diện của nó hoặc nếu có cái gì đó ở đâu đó trong các góc tối của khuôn khổ sẽ làm điều này (có thể trong WMI?).

Cập nhật (24/9/09) Tôi đã tìm ra cách thực hiện việc này với trình điều khiển bộ lọc và đăng cách thực hiện trên my original question. Tôi vẫn muốn biết nếu nó có thể kích hoạt hoặc vô hiệu hóa các thiết bị trực tiếp từ Win32 và nếu như vậy, làm thế nào - vì vậy tôi sẽ để lại câu hỏi này mở.

Trả lời

53

Bạn có thể bật/thiết bị vô hiệu hóa từ Win32 (và do đó từ C# thông qua P/Invoke) sử dụng các API SetupDi nhưng không phải tất cả các thiết bị là "vô hiệu hóa-thể" theo cách này.

Sự cố bạn gặp phải khi cố gắng vô hiệu hóa bàn di chuột từ Win32 (hoặc WMI hoặc bất kỳ API nào khác gọi vào nhóm chức năng SetupDi *) là trình điều khiển chuột mặc định trong hầu hết các máy tính xách tay có bàn di chuột ("Chuột tương thích PS/2") không hỗ trợ bị tắt bằng cách sử dụng API SetupDi. Tôi nghi ngờ điều này có thể là do những con chuột già thực sự sử dụng đầu nối PS/2 không thể bị tháo rời mà không cần phải lắp phần cứng.

Để xác minh rằng bạn không thể tắt, hãy truy cập Trình quản lý thiết bị và nhấp chuột phải vào trình điều khiển chuột của bạn. Nếu bạn thấy một tùy chọn vô hiệu hóa, bạn có thể sử dụng SetupDi để tắt nó. Nếu không có tùy chọn vô hiệu hóa, bạn đã hết may mắn ... chào mừng bạn đến với IOCTL-land!

Nếu bạn làm hãy xem tùy chọn tắt, sau đó mã bên dưới (được chuyển đến C# từ mẫu VB tôi tìm thấy here) sẽ cho phép bạn tắt và bật lại thiết bị.

Dưới đây là đoạn code để gọi thư viện:

public static void EnableMouse(bool enable) 
    { 
     // every type of device has a hard-coded GUID, this is the one for mice 
     Guid mouseGuid = new Guid("{4d36e96f-e325-11ce-bfc1-08002be10318}"); 

     // get this from the properties dialog box of this device in Device Manager 
     string instancePath = @"ACPI\PNP0F03\4&3688D3F&0"; 

     DeviceHelper.SetDeviceEnabled(mouseGuid, instancePath, enable); 
    } 

Đây là thư viện riêng của mình, chuyển thể từ here.

using System; 
using System.Text; 
using System.Collections.Generic; 
using DisableDevice; 
using System.Runtime.InteropServices; 
using System.ComponentModel; 
using Microsoft.Win32.SafeHandles; 
using System.Security; 
using System.Runtime.ConstrainedExecution; 
using System.Management; 

namespace DisableDevice 
{ 

    [Flags()] 
    internal enum SetupDiGetClassDevsFlags 
    { 
     Default = 1, 
     Present = 2, 
     AllClasses = 4, 
     Profile = 8, 
     DeviceInterface = (int)0x10 
    } 

    internal enum DiFunction 
    { 
     SelectDevice = 1, 
     InstallDevice = 2, 
     AssignResources = 3, 
     Properties = 4, 
     Remove = 5, 
     FirstTimeSetup = 6, 
     FoundDevice = 7, 
     SelectClassDrivers = 8, 
     ValidateClassDrivers = 9, 
     InstallClassDrivers = (int)0xa, 
     CalcDiskSpace = (int)0xb, 
     DestroyPrivateData = (int)0xc, 
     ValidateDriver = (int)0xd, 
     Detect = (int)0xf, 
     InstallWizard = (int)0x10, 
     DestroyWizardData = (int)0x11, 
     PropertyChange = (int)0x12, 
     EnableClass = (int)0x13, 
     DetectVerify = (int)0x14, 
     InstallDeviceFiles = (int)0x15, 
     UnRemove = (int)0x16, 
     SelectBestCompatDrv = (int)0x17, 
     AllowInstall = (int)0x18, 
     RegisterDevice = (int)0x19, 
     NewDeviceWizardPreSelect = (int)0x1a, 
     NewDeviceWizardSelect = (int)0x1b, 
     NewDeviceWizardPreAnalyze = (int)0x1c, 
     NewDeviceWizardPostAnalyze = (int)0x1d, 
     NewDeviceWizardFinishInstall = (int)0x1e, 
     Unused1 = (int)0x1f, 
     InstallInterfaces = (int)0x20, 
     DetectCancel = (int)0x21, 
     RegisterCoInstallers = (int)0x22, 
     AddPropertyPageAdvanced = (int)0x23, 
     AddPropertyPageBasic = (int)0x24, 
     Reserved1 = (int)0x25, 
     Troubleshooter = (int)0x26, 
     PowerMessageWake = (int)0x27, 
     AddRemotePropertyPageAdvanced = (int)0x28, 
     UpdateDriverUI = (int)0x29, 
     Reserved2 = (int)0x30 
    } 

    internal enum StateChangeAction 
    { 
     Enable = 1, 
     Disable = 2, 
     PropChange = 3, 
     Start = 4, 
     Stop = 5 
    } 

    [Flags()] 
    internal enum Scopes 
    { 
     Global = 1, 
     ConfigSpecific = 2, 
     ConfigGeneral = 4 
    } 

    internal enum SetupApiError 
    { 
     NoAssociatedClass = unchecked((int)0xe0000200), 
     ClassMismatch = unchecked((int)0xe0000201), 
     DuplicateFound = unchecked((int)0xe0000202), 
     NoDriverSelected = unchecked((int)0xe0000203), 
     KeyDoesNotExist = unchecked((int)0xe0000204), 
     InvalidDevinstName = unchecked((int)0xe0000205), 
     InvalidClass = unchecked((int)0xe0000206), 
     DevinstAlreadyExists = unchecked((int)0xe0000207), 
     DevinfoNotRegistered = unchecked((int)0xe0000208), 
     InvalidRegProperty = unchecked((int)0xe0000209), 
     NoInf = unchecked((int)0xe000020a), 
     NoSuchHDevinst = unchecked((int)0xe000020b), 
     CantLoadClassIcon = unchecked((int)0xe000020c), 
     InvalidClassInstaller = unchecked((int)0xe000020d), 
     DiDoDefault = unchecked((int)0xe000020e), 
     DiNoFileCopy = unchecked((int)0xe000020f), 
     InvalidHwProfile = unchecked((int)0xe0000210), 
     NoDeviceSelected = unchecked((int)0xe0000211), 
     DevinfolistLocked = unchecked((int)0xe0000212), 
     DevinfodataLocked = unchecked((int)0xe0000213), 
     DiBadPath = unchecked((int)0xe0000214), 
     NoClassInstallParams = unchecked((int)0xe0000215), 
     FileQueueLocked = unchecked((int)0xe0000216), 
     BadServiceInstallSect = unchecked((int)0xe0000217), 
     NoClassDriverList = unchecked((int)0xe0000218), 
     NoAssociatedService = unchecked((int)0xe0000219), 
     NoDefaultDeviceInterface = unchecked((int)0xe000021a), 
     DeviceInterfaceActive = unchecked((int)0xe000021b), 
     DeviceInterfaceRemoved = unchecked((int)0xe000021c), 
     BadInterfaceInstallSect = unchecked((int)0xe000021d), 
     NoSuchInterfaceClass = unchecked((int)0xe000021e), 
     InvalidReferenceString = unchecked((int)0xe000021f), 
     InvalidMachineName = unchecked((int)0xe0000220), 
     RemoteCommFailure = unchecked((int)0xe0000221), 
     MachineUnavailable = unchecked((int)0xe0000222), 
     NoConfigMgrServices = unchecked((int)0xe0000223), 
     InvalidPropPageProvider = unchecked((int)0xe0000224), 
     NoSuchDeviceInterface = unchecked((int)0xe0000225), 
     DiPostProcessingRequired = unchecked((int)0xe0000226), 
     InvalidCOInstaller = unchecked((int)0xe0000227), 
     NoCompatDrivers = unchecked((int)0xe0000228), 
     NoDeviceIcon = unchecked((int)0xe0000229), 
     InvalidInfLogConfig = unchecked((int)0xe000022a), 
     DiDontInstall = unchecked((int)0xe000022b), 
     InvalidFilterDriver = unchecked((int)0xe000022c), 
     NonWindowsNTDriver = unchecked((int)0xe000022d), 
     NonWindowsDriver = unchecked((int)0xe000022e), 
     NoCatalogForOemInf = unchecked((int)0xe000022f), 
     DevInstallQueueNonNative = unchecked((int)0xe0000230), 
     NotDisableable = unchecked((int)0xe0000231), 
     CantRemoveDevinst = unchecked((int)0xe0000232), 
     InvalidTarget = unchecked((int)0xe0000233), 
     DriverNonNative = unchecked((int)0xe0000234), 
     InWow64 = unchecked((int)0xe0000235), 
     SetSystemRestorePoint = unchecked((int)0xe0000236), 
     IncorrectlyCopiedInf = unchecked((int)0xe0000237), 
     SceDisabled = unchecked((int)0xe0000238), 
     UnknownException = unchecked((int)0xe0000239), 
     PnpRegistryError = unchecked((int)0xe000023a), 
     RemoteRequestUnsupported = unchecked((int)0xe000023b), 
     NotAnInstalledOemInf = unchecked((int)0xe000023c), 
     InfInUseByDevices = unchecked((int)0xe000023d), 
     DiFunctionObsolete = unchecked((int)0xe000023e), 
     NoAuthenticodeCatalog = unchecked((int)0xe000023f), 
     AuthenticodeDisallowed = unchecked((int)0xe0000240), 
     AuthenticodeTrustedPublisher = unchecked((int)0xe0000241), 
     AuthenticodeTrustNotEstablished = unchecked((int)0xe0000242), 
     AuthenticodePublisherNotTrusted = unchecked((int)0xe0000243), 
     SignatureOSAttributeMismatch = unchecked((int)0xe0000244), 
     OnlyValidateViaAuthenticode = unchecked((int)0xe0000245) 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    internal struct DeviceInfoData 
    { 
     public int Size; 
     public Guid ClassGuid; 
     public int DevInst; 
     public IntPtr Reserved; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    internal struct PropertyChangeParameters 
    { 
     public int Size; 
     // part of header. It's flattened out into 1 structure. 
     public DiFunction DiFunction; 
     public StateChangeAction StateChange; 
     public Scopes Scope; 
     public int HwProfile; 
    } 

    internal class NativeMethods 
    { 

     private const string setupapi = "setupapi.dll"; 

     private NativeMethods() 
     { 
     } 

     [DllImport(setupapi, CallingConvention = CallingConvention.Winapi, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool SetupDiCallClassInstaller(DiFunction installFunction, SafeDeviceInfoSetHandle deviceInfoSet, [In()] 
ref DeviceInfoData deviceInfoData); 

     [DllImport(setupapi, CallingConvention = CallingConvention.Winapi, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool SetupDiEnumDeviceInfo(SafeDeviceInfoSetHandle deviceInfoSet, int memberIndex, ref DeviceInfoData deviceInfoData); 

     [DllImport(setupapi, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, SetLastError = true)] 
     public static extern SafeDeviceInfoSetHandle SetupDiGetClassDevs([In()] 
ref Guid classGuid, [MarshalAs(UnmanagedType.LPWStr)] 
string enumerator, IntPtr hwndParent, SetupDiGetClassDevsFlags flags); 

     /* 
     [DllImport(setupapi, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool SetupDiGetDeviceInstanceId(SafeDeviceInfoSetHandle deviceInfoSet, [In()] 
ref DeviceInfoData did, [MarshalAs(UnmanagedType.LPTStr)] 
StringBuilder deviceInstanceId, int deviceInstanceIdSize, [Out()] 
ref int requiredSize); 
     */ 
     [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool SetupDiGetDeviceInstanceId(
      IntPtr DeviceInfoSet, 
      ref DeviceInfoData did, 
      [MarshalAs(UnmanagedType.LPTStr)] StringBuilder DeviceInstanceId, 
      int DeviceInstanceIdSize, 
      out int RequiredSize 
     ); 

     [SuppressUnmanagedCodeSecurity()] 
     [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
     [DllImport(setupapi, CallingConvention = CallingConvention.Winapi, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet); 

     [DllImport(setupapi, CallingConvention = CallingConvention.Winapi, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool SetupDiSetClassInstallParams(SafeDeviceInfoSetHandle deviceInfoSet, [In()] 
ref DeviceInfoData deviceInfoData, [In()] 
ref PropertyChangeParameters classInstallParams, int classInstallParamsSize); 

    } 

    internal class SafeDeviceInfoSetHandle : SafeHandleZeroOrMinusOneIsInvalid 
    { 

     public SafeDeviceInfoSetHandle() 
      : base(true) 
     { 
     } 

     protected override bool ReleaseHandle() 
     { 
      return NativeMethods.SetupDiDestroyDeviceInfoList(this.handle); 
     } 

    } 

    public sealed class DeviceHelper 
    { 

     private DeviceHelper() 
     { 
     } 

     /// <summary> 
     /// Enable or disable a device. 
     /// </summary> 
     /// <param name="classGuid">The class guid of the device. Available in the device manager.</param> 
     /// <param name="instanceId">The device instance id of the device. Available in the device manager.</param> 
     /// <param name="enable">True to enable, False to disable.</param> 
     /// <remarks>Will throw an exception if the device is not Disableable.</remarks> 
     public static void SetDeviceEnabled(Guid classGuid, string instanceId, bool enable) 
     { 
      SafeDeviceInfoSetHandle diSetHandle = null; 
      try 
      { 
       // Get the handle to a device information set for all devices matching classGuid that are present on the 
       // system. 
       diSetHandle = NativeMethods.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, SetupDiGetClassDevsFlags.Present); 
       // Get the device information data for each matching device. 
       DeviceInfoData[] diData = GetDeviceInfoData(diSetHandle); 
       // Find the index of our instance. i.e. the touchpad mouse - I have 3 mice attached... 
       int index = GetIndexOfInstance(diSetHandle, diData, instanceId); 
       // Disable... 
       EnableDevice(diSetHandle, diData[index], enable); 
      } 
      finally 
      { 
       if (diSetHandle != null) 
       { 
        if (diSetHandle.IsClosed == false) 
        { 
         diSetHandle.Close(); 
        } 
        diSetHandle.Dispose(); 
       } 
      } 
     } 

     private static DeviceInfoData[] GetDeviceInfoData(SafeDeviceInfoSetHandle handle) 
     { 
      List<DeviceInfoData> data = new List<DeviceInfoData>(); 
      DeviceInfoData did = new DeviceInfoData(); 
      int didSize = Marshal.SizeOf(did); 
      did.Size = didSize; 
      int index = 0; 
      while (NativeMethods.SetupDiEnumDeviceInfo(handle, index, ref did)) 
      { 
       data.Add(did); 
       index += 1; 
       did = new DeviceInfoData(); 
       did.Size = didSize; 
      } 
      return data.ToArray(); 
     } 

     // Find the index of the particular DeviceInfoData for the instanceId. 
     private static int GetIndexOfInstance(SafeDeviceInfoSetHandle handle, DeviceInfoData[] diData, string instanceId) 
     { 
      const int ERROR_INSUFFICIENT_BUFFER = 122; 
      for (int index = 0; index <= diData.Length - 1; index++) 
      { 
       StringBuilder sb = new StringBuilder(1); 
       int requiredSize = 0; 
       bool result = NativeMethods.SetupDiGetDeviceInstanceId(handle.DangerousGetHandle(), ref diData[index], sb, sb.Capacity, out requiredSize); 
       if (result == false && Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER) 
       { 
        sb.Capacity = requiredSize; 
        result = NativeMethods.SetupDiGetDeviceInstanceId(handle.DangerousGetHandle(), ref diData[index], sb, sb.Capacity, out requiredSize); 
       } 
       if (result == false) 
        throw new Win32Exception(); 
       if (instanceId.Equals(sb.ToString())) 
       { 
        return index; 
       } 
      } 
      // not found 
      return -1; 
     } 

     // enable/disable... 
     private static void EnableDevice(SafeDeviceInfoSetHandle handle, DeviceInfoData diData, bool enable) 
     { 
      PropertyChangeParameters @params = new PropertyChangeParameters(); 
      // The size is just the size of the header, but we've flattened the structure. 
      // The header comprises the first two fields, both integer. 
      @params.Size = 8; 
      @params.DiFunction = DiFunction.PropertyChange; 
      @params.Scope = Scopes.Global; 
      if (enable) 
      { 
       @params.StateChange = StateChangeAction.Enable; 
      } 
      else 
      { 
       @params.StateChange = StateChangeAction.Disable; 
      } 

      bool result = NativeMethods.SetupDiSetClassInstallParams(handle, ref diData, ref @params, Marshal.SizeOf(@params)); 
      if (result == false) throw new Win32Exception(); 
      result = NativeMethods.SetupDiCallClassInstaller(DiFunction.PropertyChange, handle, ref diData); 
      if (result == false) 
      { 
       int err = Marshal.GetLastWin32Error(); 
       if (err == (int)SetupApiError.NotDisableable) 
        throw new ArgumentException("Device can't be disabled (programmatically or in Device Manager)."); 
       else if (err >= (int)SetupApiError.NoAssociatedClass && err <= (int)SetupApiError.OnlyValidateViaAuthenticode) 
        throw new Win32Exception("SetupAPI error: " + ((SetupApiError)err).ToString()); 
       else 
        throw new Win32Exception(); 
      } 
     } 
    } 
} 

Lưu ý rằng khi bạn nhận được một ngoại lệ Index-Out-Of-Bounds trên dòng int index = GetIndexOfInstance(diSetHandle, diData, instanceId);, bạn có thể đã sử dụng classGuid sai cho thiết bị hoặc các instanceId sai.

Cũng lưu ý rằng khi bạn chạy mã này trên nền tảng Windows 64 bit, bạn nên nhắm mục tiêu nền tảng 64 bit khi bạn tạo ứng dụng của mình. Nếu không - tức là khi chạy ứng dụng của bạn dưới dạng quy trình 32 bit trên nền tảng Windows 64 bit - bạn sẽ gặp lỗi SetupAPI InWow64 (ERROR_IN_WOW64).

Khi nhắm mục tiêu nền tảng Windows 64 bit, bạn cũng có thể phải thực hiện thay đổi đối với các phần khác của ứng dụng, ví dụ: khi làm số học con trỏ, để ngăn chặn tràn.

+0

Cảm ơn, đây là những gì tôi cần biết. –

+1

if (err <= (int) SetupApiError.NoAssociatedClass && err> = (int) SetupApiError.OnlyValidateViaAuthenticode) nên được lật (> = ... && ... <=) –

+2

@Edwin Evans - đẹp bắt! Tôi đã cập nhật mã ở trên với bản sửa lỗi của bạn. –

0

Bàn di chuột, theo như tôi biết, là thiết bị không chuẩn (có con chuột std làm tập hợp con). Trên máy tính xách tay của tôi, tôi không thể tắt nó bằng phím Fn cho đến khi tôi cài đặt trình điều khiển ATK100 phù hợp. Bạn nên tìm interop đến thứ ATK100.DLL.

+0

Đối với tôi, nó chỉ xuất hiện như một con chuột ps/2 chung. –

+0

Nó sẽ xuất hiện dưới dạng Chuột. Bạn đang thiếu phần ATK. –

6

Đây có phải là những gì bạn đang tìm kiếm không?

Hardware Helper Library for C#

+1

Cảm ơn, tôi đã tìm thấy điều này, nhưng nó không làm việc cho tôi vì một lý do nào đó. –

Các vấn đề liên quan