locked
Deleting vista registry keys in a driver installer RRS feed

  • Question

  • Hi all,

    My situation is this: I'm currently writing a NSIS driver installer, and I want it to be compatible with Vista. Before installation I do a previous installation check and remove any leftovers from a previous install from the system, also I include uninstall capabilities, which do basically the same thing.

    The problem I ran into was trying to delete keys in the HKLM\SYSTEM\CurrentControlSet\Enum\USB registry directory. By default they are set to read only for "Everyone". I am using this AccessControl plug-in for NSIS, which just makes simple calls to Windows Access Control functions, and can change permissions and ownership. Ownership change uses SetNamedSecurityInfo. So I discovered fairly quickly that in Vista, you cannot simply change permissions, even if you are an administrator. I also found the path you must take in regedit (change ownership to your user, then change permissions), but that leads to a rough situation for my installer.

    The problem is, the AccessControl calls to change ownership require that I first have WRITE_OWNER permissions, but the only way to set those and all the other permissions changes I want to make for that matter is to first have ownership! The SetNamedSecurityInfo page on MSDN also mentioned that if a user has the SE_TAKE_OWNERSHIP_NAME privilege, they could change owners. Well I go a little executable called ntrights.exe that is supposed to do that. I tried adding that to my users privileges, and tried integrating a call to ntrights into my installer, but wouldn't you know it... it still won't let me change owners, returning error code 5 (Access Denied.)

    So, what do I do now, if I can't change permissions unless I'm owner, and can't change owners unless I have permissions?

    Any help is greatly appreciated. Thanks in advance.
    Friday, July 13, 2007 8:03 PM

Answers

  • I managed to find a solution for this problem. Vista simply will not allow you to easily delete certain registry keys, no matter how many administrator privileges you have. You must go through the route of calls to proper device setup API functions, as the system is the only thing allowed to delete them.

    Specifically call SetupDiGetClassDevs first to get the devices. Call it with the GUID parameter = NULL and make Flags = DIGCF_ALLCLASSES, that way non-connected devices will be included in the device list, and finally supply a enumerator value to select only the device group you want to remove.

    Then call SetupDiEnumDeviceInfo with the return from SetupDiGetClassDevs, and increment the value MemberIndex (until it returns false) as a way to enumerate through all of the devices, and for each DeviceInfoData that outputs, send it to SetupDiCallClassInstaller with InstallFunction = DIF_REMOVE.

    This will remove the class/guid and enum registry keys for all instances of the device, including phantom devices. It's basically making the API calls that would be made if you went into the device manager and removed all instances of the device, including phantoms.
    Saturday, July 21, 2007 4:42 AM

All replies

  • Update:

    So I'm using the small program SetACL, and it works pretty well. I am able to set the owner and permissions of HKLM\...\USB's VID/PID keys and the device keys corresponding to each of those, but here's what I'm encountering issues now: because of some way inheritance or the keys are setup in Vista, where I could normally easily set the top key and delete it (presumably it's passing permissions down), in Vista that doesn't seem to happen.

    Exporting one of the registry keys and reading it in notepad, I see that there are sub-keys that do not even show up there, so SetACL can't even see them. They are basically hidden from view, even in regedit, and unless I go through this absurd process of going to permissions over and over until it lets me alter permissions manually, I have no way of changing the permissions of the deep sub keys, thus I cannot delete the top VID/PID keys.

    This is absurd to say the least.

    Any ideas?
    Tuesday, July 17, 2007 6:49 PM
  • Drivers for Vista should follow the Device Installation Rules, including using the correct API calls rather than enumerating/modifying Registry keys directly. This ensures that they work properly for Standard Users and install themselves correctly in the Vista Driver Store so they can be repaired/reinstalled without access to the original media if necessary.

     

    Complete information on driver installation can be found here.

    Wednesday, July 18, 2007 11:54 AM
  • Thank you for your reply. My driver installer code already makes install and uninstall calls to DIFxAPI exported functions as needed. The installation is a breeze, because the API calls take care of most of the work, and the uninstall calls simplify things somewhat. The only trouble spot for me is this: I've made an attempt to clean the system of all leftovers from my drivers when the user selects uninstall or when the user selects install and the pre-install system check finds leftovers from previous installations.

    DIFxAPI's uninstaller is fairly good at getting rid of registry keys as long as the devices are connected to the system during uninstall (the \Enum\USB keys disappear). I'd prefer it though, if the user could be told to disconnect all devices before uninstall. Do you know of any way to get rid of all devices, including phantom one's (disconnected, but installed) just by using DIFx? In addition, some keys are left in Services, but I can get rid of those fairly easily. Is it considered OK to just leave these sorts of keys in the registry?

    Thanks.
    Wednesday, July 18, 2007 5:55 PM
  • From the uninstall guide:

     

    "

    When the PnP manager uninstalls a device, it simply removes a subset of the system states that were created during installation. For example, it removes the association of the driver files with the device that the SCM controls. This process does not undo some install actions. For example, the driver package and binaries remain where they were and some registry keys that are created by the class or co‑installer and some other registry operations are not changed. However, this uninstall action is sufficient to prevent Windows from loading the driver for all common user scenarios.

    "

     

    So I'd assume its ok just to leave them as is.

    Thursday, July 19, 2007 1:53 AM
  • Just wanted to let you know you are not the only one.  I have been cleaning up the registry on my uninstall which was working just fine on XP, and now with Vista I am unable to do the same thing.
    Thursday, July 19, 2007 3:44 PM
  • I managed to find a solution for this problem. Vista simply will not allow you to easily delete certain registry keys, no matter how many administrator privileges you have. You must go through the route of calls to proper device setup API functions, as the system is the only thing allowed to delete them.

    Specifically call SetupDiGetClassDevs first to get the devices. Call it with the GUID parameter = NULL and make Flags = DIGCF_ALLCLASSES, that way non-connected devices will be included in the device list, and finally supply a enumerator value to select only the device group you want to remove.

    Then call SetupDiEnumDeviceInfo with the return from SetupDiGetClassDevs, and increment the value MemberIndex (until it returns false) as a way to enumerate through all of the devices, and for each DeviceInfoData that outputs, send it to SetupDiCallClassInstaller with InstallFunction = DIF_REMOVE.

    This will remove the class/guid and enum registry keys for all instances of the device, including phantom devices. It's basically making the API calls that would be made if you went into the device manager and removed all instances of the device, including phantoms.
    Saturday, July 21, 2007 4:42 AM
  • Thank you for the hints!  I ended up doing something similar, but since I know what Vid/Pid to look for, I ended up calling SetupDiGetDeviceRegistryProperty( ... SPDRP_HARDWAREID ... ).  My enumeration starts the same with SetupDiGetClassDevs, then SetupDiEnumDeviceInfo, and then SetupDiGetDeviceRegistryProperty.  I compare the hardwareID that is returned with the one that I know belongs to my device ( USB\Vid_xxxx&Pid_yyyy ), and if there is a match, then call SetupDiRemoveDevice.
    Saturday, July 21, 2007 6:34 PM
  • I'm glad to hear you got yours working too! From the sounds of it that way definitely makes sense and should work. But I thought I'd just let you know, at first, I wasn't sure if you could only pass values like "USB" to SetupDiGetClassDevs for Enumerator, but I tried it out and it seems to accept full enumerations like "USB\Vid_xxxx&Pid_yyyy". On each of the machines I tested it on, there were several entries in ...\USB\, and as I had hoped, it only got rid of the devices I explicitly specified in calls to SetupDiGetClassDevs.

    So if you want, you can probably eliminate that last check with SetupDiGetDeviceRegistryProperty, by making a call to SetupDiGetClassDevs with Enumerator = "USB\Vid_xxxx&Pid_yyyy". Let me know if something doesn't work right, cause if there's something improper about the way I did it, I had better fix it!
    Saturday, July 21, 2007 6:48 PM
  • Yes, that seems to be working ok for me.  But I think I am going to do the SetupDiGetDeviceRegistryProperty call just to double check that I am deleting what I want.

     

    Saturday, July 21, 2007 7:53 PM
  • Any chance you can post the sample code of this for a nsis installer as I am also trying to do the same thing.

    Thanks.
    Tuesday, January 29, 2008 6:55 PM
  • Use an alias name, and give as least personal information as possible. Remember, The public can view your post. Then delete your history and your cookies. Hopefully, this will cure your problem. Good luck. 

     

    Thursday, January 31, 2008 12:45 AM
  • // Sample code to remove a VID/PID

    // lpszVidPid in the format "USB\\Vid_AAAA&Pid_BBBB"

     

    void RemoveAVidPid( LPSTR lpszVidPid )
    {
        HDEVINFO hdev;

        hdev = SetupDiGetClassDevs( NULL,
                                    lpszVidPid,
                                    NULL,
                                    DIGCF_ALLCLASSES );  // this should also get stuff that is not plugged in

     

        if ( hdev != INVALID_HANDLE_VALUE )
        {
            SP_DEVINFO_DATA          devInfo;
            DWORD                    dwIdx = 0;
            DWORD                    dataT;
            LPTSTR                   buffer;
            DWORD                    bufferSize;

            for ( ;; )
            {
                memset( &devInfo, 0, sizeof(SP_DEVINFO_DATA) );
                devInfo.cbSize = sizeof( SP_DEVINFO_DATA );

                if ( !SetupDiEnumDeviceInfo(hdev, dwIdx++, &devInfo) )
                {
                    if ( GetLastError() == ERROR_NO_MORE_ITEMS )
                        break;
                }

                bufferSize = 0;

                SetupDiGetDeviceRegistryProperty(hdev,
                                                 &devInfo,
                                                 SPDRP_HARDWAREID,
                                                 &dataT,
                                                 NULL,
                                                 0,
                                                 &bufferSize );

                if ( bufferSize )
                {
                    buffer = (LPTSTR)WMalloc( bufferSize );

                    if ( SetupDiGetDeviceRegistryProperty(hdev,
                                                          &devInfo,
                                                          SPDRP_HARDWAREID,
                                                          &dataT,
                                                          (PBYTE)buffer,
                                                          bufferSize,
                                                          &bufferSize) )
                    {
                        if ( strlen(lpszVidPid) && !strnicmp(lpszVidPid, buffer, strlen(lpszVidPid)) )
                        {
                            if ( SetupDiRemoveDevice( hdev, &devInfo ) )
                                WriteHist( "Removed from registry", lpszVidPid, NULL );
                            else
                                WriteHist( "NOT Removed from registry", lpszVidPid, NULL );
                        }

                    }
                }
            }

            SetupDiDestroyDeviceInfoList( hdev );

        }
    }

     

     

    Thursday, January 31, 2008 4:35 AM