Jump to content

Locate Windows Drive using WMI (for WinPE)


doestergaard
 Share

Recommended Posts

In one of my scripts, I needed to know, where Windows was installed at. I know there is several ways to this already, but I was thinking that I would share how I do it using WMI.

If anyone has any suggestions or comment on the code, feel free to do so.

 

Code:

#include <File.au3>

Local $STR_DRIVE = GetWindowsDrive()
If @error Then
   MsgBox(@extended, "Result", "Unable to locate Windows", 0)
Else
   MsgBox(@extended, "Result", "Windows is located at: " & $STR_DRIVE, 0)
EndIf
Exit


Func GetWindowsDrive()
   Local $WMI_GET = ObjGet("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
   If Not IsObj($WMI_GET) Then Return SetError(1, 16, 0)

   Local $WMI_COL_DRIVES = $WMI_GET.ExecQuery("SELECT DeviceID FROM Win32_DiskDrive WHERE MediaType='Fixed hard disk media'")

   For $WMI_DRIVE In $WMI_COL_DRIVES
      Local $WMI_COL_PARTITIONS = $WMI_GET.ExecQuery("ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" & $WMI_DRIVE.DeviceID & "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition")

      For $WMI_PARTITION In $WMI_COL_PARTITIONS
         Local $WMI_COL_LOGICALDISKS = $WMI_GET.ExecQuery("ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" & $WMI_PARTITION.DeviceID & "'} WHERE AssocClass = Win32_LogicalDiskToPartition")

         For $WMI_LOGICALDISK In $WMI_COL_LOGICALDISKS
            If FileExists($WMI_LOGICALDISK.DeviceID & "\Windows\explorer.exe") Then
               Local $STR_RESULT = $WMI_LOGICALDISK.DeviceID
            EndIf
         Next

      Next

   Next

   $WMI_GET = 0
   $WMI_COL_DRIVES = 0
   $WMI_DRIVE = 0
   $WMI_COL_PARTITIONS = 0
   $WMI_PARTITION = 0
   $WMI_LOGICALDISKS = 0
   $WMI_LOGICALDISK = 0

   Return SetError(0, 64, $STR_RESULT)
EndFunc ; ==>GetWindowsDrive()

 

Edited by doestergaard
Link to comment
Share on other sites

It's a good idea to declare your vars first, especially with WMI and loops --

or else you wind up having AutoIt errors like:

"Error: Variable used without being declared"

when a property is not found.

 

And you are right, there are several ways...

Local $rtn = Example()
MsgBox(0, '', $rtn)
Exit

Func Example()
    Local $objWMI = ObjGet('winmgmts:\\.\root\CIMV2')
    Local $objClass = $objWMI.InstancesOf('Win32_OperatingSystem')
    If Not IsObj($objClass) Then Return -1; invalid object
    If Not $objClass.Count Then Return -2; nothing found

    For $objItem In $objClass
       Return $objItem.WindowsDirectory
    Next
EndFunc

 

I personally would use the @WindowsDir macro.

 

Edited by ripdad

"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Link to comment
Share on other sites

It's a good idea to declare your vars first, especially with WMI and loops --

or else you wind up having AutoIt errors like:

"Error: Variable used without being declared"

when a property is not found.

 

And you are right, there are several ways...

Local $rtn = Example()
MsgBox(0, '', $rtn)
Exit

Func Example()
    Local $objWMI = ObjGet('winmgmts:\\.\root\CIMV2')
    Local $objClass = $objWMI.InstancesOf('Win32_OperatingSystem')
    If Not IsObj($objClass) Then Return -1; invalid object
    If Not $objClass.Count Then Return -2; nothing found

    For $objItem In $objClass
       Return $objItem.WindowsDirectory
    Next
EndFunc

 

I personally would use the @WindowsDir macro.

 

Forgot to mention that the script was intended for use in Windows PE. The Win32_OperatingSystem class will only work on the current running OS. I haven't tried running use the macro you are suggesting, but my guess is that the same thing will happen.

Link to comment
Share on other sites

Yes, that would change things a bit.

Guess I would do it something like this, providing there is only

one Windows installation and the folder name is Windows...

Local $rtn = Example()
If @error Then
    MsgBox(0, '', 'An Error Occurred')
Else
    MsgBox(0, '', $rtn)
EndIf

Func Example()
    Local $array = DriveGetDrive('FIXED')
    If IsArray($array) Then
        For $i = 1 To $array[0]
            If FileExists($array[$i] & '\Windows\Explorer.exe') Then
                Return StringUpper($array[$i]) & '\Windows'
            EndIf
        Next
    EndIf
    Return SetError(1)
EndFunc

 

"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Link to comment
Share on other sites

Thats exactly the problem i am trying to solve, what if there is more than one? Maybe you can throw in a counter that does increment by 1, and if the counter is above 1 then display a window where the user must select what drive to use to proceed, at least thats what im aiming at.

I've just used this method, because it proved to be working so far. But if there is a proper way to do it im all ears. I ended up using WMI because i think it is cool, and that way i can even filter out, what disk and partition Windows was found on.

 

Thanks Again for you contribution, it is always cool to learn new stuff :)

 

Link to comment
Share on other sites

You could also use DISM (or imagex)  with the windir parameter or get-wiminfo if you are going in blind.   Because from what i am understanding, you know a wim has been deployed, you just dont know the location of windir..

Edited by boththose

,-. .--. ________ .-. .-. ,---. ,-. .-. .-. .-.
|(| / /\ \ |\ /| |__ __||| | | || .-' | |/ / \ \_/ )/
(_) / /__\ \ |(\ / | )| | | `-' | | `-. | | / __ \ (_)
| | | __ | (_)\/ | (_) | | .-. | | .-' | | \ |__| ) (
| | | | |)| | \ / | | | | | |)| | `--. | |) \ | |
`-' |_| (_) | |\/| | `-' /( (_)/( __.' |((_)-' /(_|
'-' '-' (__) (__) (_) (__)

Link to comment
Share on other sites

@boththose

Thanks for your reply. Image has not been deployed yet. If a user / administrator choose to leave the hard drive(s) untouched, the script should look for and detect where Windows is installed.

So far, I've been using this, which I created myself:

Example:

Opt("MustDeclareVars", 1)

Local $STR_RETURN = GetDiskAndPartition()

If $STR_RETURN Then

   Local $ARR_RESULT = StringSplit($STR_RETURN, ",")

   Local Const $STR_DRIVE = $ARR_RESULT[1]
   Local Const $INT_DISK = StringRegExpReplace($ARR_RESULT[2], "\D", "")
   Local Const $INT_PARTITION = StringRegExpReplace($ARR_RESULT[3], "\D", "")

   MsgBox(64, "Result", "Windows was found on drive '" & $STR_DRIVE & "' and is located at: Disk #" & $INT_DISK & ", Partition #" & $INT_PARTITION, 0)
   Exit
Else
   MsgBox(16, "Result", "Error: Could not detect", 0)
EndIf

Exit


Func GetDiskAndPartition()
   Local $WMI_GET = ObjGet("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
   If Not IsObj($WMI_GET) Then Return SetError(1, 16, "Invalid object")

   Local $WMI_COL_DRIVES = $WMI_GET.ExecQuery("SELECT DeviceID FROM Win32_DiskDrive WHERE MediaType='Fixed hard disk media'")

   For $WMI_DRIVE In $WMI_COL_DRIVES
      Local $WMI_COL_PARTITIONS = $WMI_GET.ExecQuery("ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" & $WMI_DRIVE.DeviceID & "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition")

      For $WMI_PARTITION In $WMI_COL_PARTITIONS
         Local $WMI_COL_LOGICALDISKS = $WMI_GET.ExecQuery("ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" & $WMI_PARTITION.DeviceID & "'} WHERE AssocClass = Win32_LogicalDiskToPartition")

         For $WMI_LOGICALDISK In $WMI_COL_LOGICALDISKS
            If FileExists($WMI_LOGICALDISK.DeviceID & "\Windows\explorer.exe") Then

               Local $STR_RESULT = $WMI_LOGICALDISK.DeviceID & "\," & $WMI_PARTITION.Caption

            EndIf

         Next



      Next



   Next



   $WMI_GET = 0
   $WMI_COL_DRIVES = 0
   $WMI_DRIVE = 0
   $WMI_COL_PARTITIONS = 0
   $WMI_PARTITION = 0
   $WMI_COL_LOGICALDISKS = 0
   $WMI_LOGICALDISK = 0

   If $STR_RESULT = "\" Then Return False
   Return $STR_RESULT
EndFunc ; ==>GetWindowsDrive()

I am filtering out the disk and partition number as the results found is used to create a disk part txt file.

Edited by doestergaard
Link to comment
Share on other sites

 the script should look for and detect where Windows is installed.

but 

 Image has not been deployed yet

Does it hurt when you read that?

If its vista or higher, whatever is there can be interrogated with imagex

Edited by boththose

,-. .--. ________ .-. .-. ,---. ,-. .-. .-. .-.
|(| / /\ \ |\ /| |__ __||| | | || .-' | |/ / \ \_/ )/
(_) / /__\ \ |(\ / | )| | | `-' | | `-. | | / __ \ (_)
| | | __ | (_)\/ | (_) | | .-. | | .-' | | \ |__| ) (
| | | | |)| | \ / | | | | | |)| | `--. | |) \ | |
`-' |_| (_) | |\/| | `-' /( (_)/( __.' |((_)-' /(_|
'-' '-' (__) (__) (_) (__)

Link to comment
Share on other sites

I dont know what you mean by "Does it hurt when i read that?" :P

A lot of Pc's comes with Windows preinstalled, so I think it makes sense that you should have the option choose whether or not you want to format the entire drive.

Another scenario would be that you have more than one physical drive installed, for ex. backup purposes. IMHO me, its a critical step that my script selects the right disk and partition to avoid deleting the wrong files :)

Edited by doestergaard
Link to comment
Share on other sites

This should be more robust and do what you expect.

#include 'Array.au3'

Local $a = Example()
If @error Or Not IsArray($a) Then
    MsgBox(0, '', 'An Error Occurred')
Else
    _ArrayDisplay($a)
    ;-----------------------------------------------------
    If $a[0] > 1 Then; <-- test
        MsgBox(0, '', 'Found more than one installation!')
        ; <-- additional code here to handle this event
    EndIf
    ;-----------------------------------------------------
EndIf

Func Example()
    Local $sDrive, $hFile, $sFolder, $str = ''
    Local $array = DriveGetDrive('FIXED')
    ;
    If IsArray($array) Then
        For $i = 1 To $array[0]
            $sDrive = StringUpper($array[$i])
            $hFile = FileFindFirstFile($sDrive & '\*')
            If $hFile = -1 Then ContinueLoop
            ;
            While 1
                $sFolder = FileFindNextFile($hFile); enum all root folders on drive
                If @error Then
                    ExitLoop
                ElseIf @extended Then
                    If FileExists($sDrive & '\' & $sFolder & '\System32\Config') Then
                        $str &= '|' & $sDrive & '\' & $sFolder
                    EndIf
                EndIf
            WEnd
            FileClose($hFile)
        Next
    EndIf
    ;
    If StringLen($str) Then
        $str = StringTrimLeft($str, 1)
        Return StringSplit($str, '|')
    Else
        Return SetError(1)
    EndIf
EndFunc

 

"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Link to comment
Share on other sites

DISM would fail when there is no target, just as FileExists fails when there is no file, expectedly. 

You could also loop C: to Z: running just the If fileexists line and it may be faster?  If it is a brand new OEM box you could also read the panther logs. 

,-. .--. ________ .-. .-. ,---. ,-. .-. .-. .-.
|(| / /\ \ |\ /| |__ __||| | | || .-' | |/ / \ \_/ )/
(_) / /__\ \ |(\ / | )| | | `-' | | `-. | | / __ \ (_)
| | | __ | (_)\/ | (_) | | .-. | | .-' | | \ |__| ) (
| | | | |)| | \ / | | | | | |)| | `--. | |) \ | |
`-' |_| (_) | |\/| | `-' /( (_)/( __.' |((_)-' /(_|
'-' '-' (__) (__) (_) (__)

Link to comment
Share on other sites

MsgBox(0,"","@WindowsDir"&@CR&@WindowsDir)
MsgBox(0,"",'EnvGet("windir")'&@CR&EnvGet("windir"))

unfortunately ScITE would not run the code within my WinPE but running this worked just fine from the command prompt. 

Edited by argumentum
corrected the code

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...