Announcement

Collapse
No announcement yet.

Redirect to a Variable

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Redirect to a Variable

    I thought there was a way to redirect the output of a CMD directly to a variable, but I can't seem to remember it.

    This works, only by redirecting the output to a file and then reading the file.

    Code:
    Function GetDriveSerialNumber() As String
       Local temp$
       Shell ("cmd /C wmic diskdrive get serialnumber > temp\serialnumber.txt",0)
       Open "temp\serialnumber.txt" For Binary As #1 : Get$ #1, Lof(1), temp$ : Close #1
       Function = temp$
    End Function

  • #2
    The only variables available to Cmd.exe are environment variables (and any you create disappear at the end of the Cmd.exe session)

    Comment


    • #3
      Gary,

      That is the normal way to do it, once you have the redirected data, its normal to clean up and delete the temp file.
      hutch at movsd dot com
      The MASM Forum - SLL Modules and PB Libraries

      http://www.masm32.com/board/index.php?board=69.0

      Comment


      • #4
        not a direct answer to your question, and I know you like minimal code, but if you use WMI why not call it direct, instead of using the wmic (WMI command tool)

        '
        Code:
        ' ########################################################################################
        ' WMI Template
        ' Namespace = root\CIMV2
        ' WMI Class = Win32_DiskDrive
        ' ########################################################################################
        
        #COMPILE EXE
        #DIM ALL
        
        ' ========================================================================================
        ' Retrieves the values of all of the properties of the Win32_DiskDrive class
        ' ========================================================================================
        #INCLUDE  ONCE "WMI.INC"
        FUNCTION WMI_Win32_DiskDrive (OPTIONAL BYVAL bstrComputer AS WSTRING) AS WSTRING
        
           LOCAL hr AS LONG                                  ' // HRESULT
           LOCAL pService AS ISWbemServices                  ' // Services object
           LOCAL pObjectSet AS ISWbemObjectSet               ' // ISWbemObjectSet interface
           LOCAL pEnum AS IEnumVariant                       ' // Generic collection's enumerator reference
           LOCAL bstrDisplayName AS WSTRING                  ' // Display name
           LOCAL bstrQuery AS WSTRING                        ' // Query string
           LOCAL oItem AS DISPATCH                           ' // Generic object variable
           LOCAL vItem AS VARIANT                            ' // Generic object variant
           LOCAL vRes AS VARIANT                             ' // General purpose variant
           LOCAL sBuffer AS WSTRING                          ' // General purpose string buffer
        
           ' // Variants to store the property values
            LOCAL vSerialNumber AS VARIANT                    ' // String
        
           ' // Connect to WMI using a moniker
           IF LEN(bstrComputer) = 0 THEN bstrComputer = "."
           bstrDisplayName = "winmgmts:{impersonationLevel=impersonate}!\\" & bstrComputer & "\root\CIMV2"
           pService = WmiGetObject(bstrDisplayName)
           IF ISNOTHING(pService) THEN EXIT FUNCTION
        
           ' // Execute a query to get a reference to the collection of objects
           bstrQuery = "SELECT * FROM Win32_DiskDrive"
           pObjectSet = pService.ExecQuery(bstrQuery, "WQL", %wbemFlagReturnImmediately)
           IF ISNOTHING(pObjectSet) THEN EXIT FUNCTION
           ' // Retrieve a reference to the collection's enumerator
           pEnum = pObjectSet.NewEnum_
           IF ISNOTHING(pEnum) THEN EXIT FUNCTION
           ' // Iterate through the collection of objects
           DO
              ' // Retrieve a reference to the next object in the collection
              hr = pEnum.Next(1, vItem, BYVAL %NULL)
              IF hr <> %S_OK THEN EXIT DO
              ' // Assign the VT_DISPATCH variant to the object variable
              oItem = vItem : vItem = EMPTY
              IF ISNOTHING(oItem) THEN EXIT DO
              ' // Retrieve the values of the properties
        
              OBJECT GET oItem.SerialNumber TO vSerialNumber
              ' // Release the object
              oItem = NOTHING
        
         '// sBuffer
        sBuffer += "SerialNumber = " & VARIANT$(vSerialNumber)  & $CRLF
           LOOP
        
           ' // Release the enumerator
           pEnum = NOTHING
           ' // Release the collection
           pObjectSet = NOTHING
           ' // Release the service
           pService = NOTHING
        FUNCTION = sBuffer
        END FUNCTION
        
        ' ========================================================================================
        ' Main
        ' ========================================================================================
        FUNCTION PBMAIN
        
         ?  WMI_Win32_DiskDrive
        
        END FUNCTION
        ' ========================================================================================
        
        '

        Comment


        • #5
          Originally posted by Gary Beene View Post
          I thought there was a way to redirect the output of a CMD directly to a variable, but I can't seem to remember it.

          This works, only by redirecting the output to a file and then reading the file.

          Code:
          Function GetDriveSerialNumber() As String
          Local temp$
          Shell ("cmd /C wmic diskdrive get serialnumber > temp\serialnumber.txt",0)
          Open "temp\serialnumber.txt" For Binary As #1 : Get$ #1, Lof(1), temp$ : Close #1
          Function = temp$
          End Function
          That doesn't work for my laptop!
          I have a permanently inserted SDHC card (as Drive A: ) which I use for ReadyBoost. That returns a blank SerialNumber and it is the device returned by your function.
          Rod's Class iterates through and gives me my Fixed Disk's SerialNumber on the second iteration.

          Edit: The problem wan't what I thought it was. The actual issue is that WMIC is creating a WSTRING file with a LE BOM marker and PB was choking on the initial bytes ( even if I use StrTemp AS WSTRING).
          Last edited by Stuart McLachlan; 30 Aug 2022, 02:05 AM.

          Comment


          • #6
            Another alternative for you:
            '
            Code:
            FUNCTION PBMAIN() AS LONG
                LOCAL strClip AS STRING
                SHELL ("cmd /C wmic /Output:clipboard diskdrive get serialnumber",0)
                CLIPBOARD GET TEXT strClip
                ? strClip
            END FUNCTION
            '

            Comment


            • #7
              Originally posted by Gary Beene View Post
              I thought there was a way to redirect the output of a CMD directly to a variable, but I can't seem to remember it.
              ...
              Alternatively?
              Code:
              Function GetDriveSerialNumber() As String
                 Local temp$
                 Clipboard Reset
                 Shell ("cmd /C wmic diskdrive get serialnumber | clip",0)
                 Clipboard Get Text To temp$
                 Function = temp$
              End Function
              Otherwise, use a pipe to capture stdout ?
              Rgds, Dave

              Comment


              • #8
                IIRC, "CMD" is not a cooperating application.

                Go with Mr. Macia's suggestion.. or just send to a file and read the file.

                Or maybe even better if you are solving this specific challenge ... just call the GetVolumeInformation() WinAPI function. That returns disk serial number directly.

                Michael Mattias
                Tal Systems (retired)
                Port Washington WI USA
                [email protected]
                http://www.talsystems.com

                Comment


                • #9
                  Originally posted by Michael Mattias View Post
                  IIRC, "CMD" is not a cooperating application.

                  Go with Mr. Macia's suggestion.. or just send to a file and read the file.

                  Or maybe even better if you are solving this specific challenge ... just call the GetVolumeInformation() WinAPI function. That returns disk serial number directly.
                  Volume <> Disk ! Especially when there are multiple volumes on a physical disk.

                  "This function returns the volume serial number that the operating system assigns when a hard disk is formatted. To programmatically obtain the hard disk's serial number that the manufacturer assigns, use the Windows Management Instrumentation (WMI) Win32_PhysicalMedia property SerialNumber."

                  Comment


                  • #10
                    Here is some modified PBWin code that redirects the console output to a variable. It was published in several messages about 20-22 years ago, and originated from Semen Matusovski. I have used it to capture larger streams of output to a string variable, for subsequent parsing.


                    Code:
                    ' using Jose Roca Includes, original code furnished by Semen Matusovski about 22 years ago
                    ' modifications to increase console buffer size, and drop custom GetConsoleWindow function
                    ' tested on a Win 7 platform only
                    #COMPILE EXE
                    #DIM ALL
                    #REGISTER NONE
                    #INCLUDE "WIN32API.INC"
                    ' = = = = = = = = = = = = = = = = = = = = = = = = = =
                    FUNCTION PBMAIN
                       DIM bbuf AS STRING
                       bbuf= GET_ConsoleInfo$("wmic diskdrive get serialnumber")
                       MSGBOX LEFT$(bbuf, 400)
                    END FUNCTION
                    ' = = = = = = = = = = = = = = = = = = = = = = = = = =
                    FUNCTION GET_ConsoleInfo$(CmdLine AS STRING)
                       DIM hFile AS LONG, i AS LONG, cxy AS COORD, sBuf AS STRING, Buf AS STRING
                       DIM sb AS CONSOLE_SCREEN_BUFFER_INFO
                    
                       AllocConsole
                       ' next few code lines not needed in this example, but need to increase default
                       ' 80x300 buffer size when there is a larger stream of output to capture
                       cxy.x = 80 : cxy.y = 800
                       SetConsoleScreenBufferSize(GetStdHandle(-11&), cxy)
                       ' minimizing the console helps, but there is an annoying flash as it shrinks
                       ' ShowWindow(GetConsoleWindow(), %SW_MINIMIZE)
                       SHELL(CmdLine, 0) 'unfortunately, the 0 is not hiding the console window
                       ' should instead use CreateProcess with CREATE_NO_WINDOW flag, but have had issues with
                       ' it seizing or staying in memory with a larger output stream - need help on how to do
                       ' it right, and still ensure console buffer is large enough
                    
                       hFile = CreateFile("CONOUT$", %GENERIC_READ OR %GENERIC_WRITE, %FILE_SHARE_READ OR _
                       %FILE_SHARE_WRITE, BYVAL 0&, %OPEN_ALWAYS, %FILE_ATTRIBUTE_NORMAL, BYVAL 0&)
                       IF hFile = %INVALID_HANDLE_VALUE THEN EXIT FUNCTION
                    
                       GetConsoleScreenBufferInfo hFile, sb : Buf = SPACE$(sb.dwsize.x * sb.dwsize.y)
                       cxy.x = 0 : cxy.y = 0
                       ReadConsoleOutputCharacter hFile, BYVAL STRPTR(Buf), LEN(Buf), BYVAL cxy, i
                       CloseHandle hFile
                       OemToCharBuff BYVAL STRPTR(Buf), BYVAL STRPTR(Buf), LEN(Buf)
                       FOR i = 0 TO sb.dwsize.y - 1
                          sBuf += MID$(Buf, i * sb.dwsize.x + 1, sb.dwsize.x) + $CRLF
                       NEXT
                       FreeConsole
                       FUNCTION= sBuf
                    END FUNCTION
                    '
                    Just another possible solution...

                    This is my first post, so I hope it formats correctly...

                    Comment


                    • #11
                      Volume <> Disk ! Especially when there are multiple volumes on a physical disk.

                      See Post #1, the member's question.

                      See my limiting statement .. " .... if you are solving this specific challenge ".

                      Context matters.
                      Michael Mattias
                      Tal Systems (retired)
                      Port Washington WI USA
                      [email protected]
                      http://www.talsystems.com

                      Comment


                      • #12
                        Originally posted by Michael Mattias View Post


                        See Post #1, the member's question.

                        See my limiting statement .. " .... if you are solving this specific challenge ".

                        Context matters.
                        You mean this part of his post?
                        Shell ("cmd /C wmic diskdrive get serialnumber ...

                        In which case, "if you are solving this specific challenge" must therefore refer to "disk"

                        And your incorrect statement:
                        call the GetVolumeInformation() WinAPI function. That returns disk serial number directly.

                        I repeat Volume <> disk.

                        compare and contrast the result from:
                        wmic diskdrive get serialnumber
                        and
                        wmic volume get serialnumber

                        '
                        Code:
                        FUNCTION PBMAIN() AS LONG
                        LOCAL strClip, strClip2 AS STRING
                        
                        SHELL ("cmd /C wmic /Output:clipboard diskdrive get serialnumber",0)
                        CLIPBOARD GET TEXT strClip
                        strClip = "DISK:" & $LF & strClip
                        
                        SHELL ("cmd /C wmic /OUtput:clipboard volume get serialnumber > serialnumber.txt",0)
                        CLIPBOARD GET TEXT strClip2
                        ? strCLip & $LF & "Volumes:" & $LF & strClip2
                        END FUNCTION
                        '

                        Comment


                        • #13

                          See also https://forum.powerbasic.com/forum/u...ting-to-a-file. Citing more code from Semen Matusovski.
                          I made a few minor adjustments to liberalize Semen's code, the most important was to significantly increase the size of the pipe buffer. It was set to zero, which should result in automatic buffering, but it cut off some of my data stream near 1048 bytes (not the WMIC output, which is small).

                          Code:
                          ' tested on Win 7
                          #COMPILE EXE '#Win 8.04# (or later)
                          #DIM ALL
                          #INCLUDE "Win32Api.inc"
                          %ButtonRun    = 101
                          %EditCaptured = 201
                          GLOBAL ExeToCapture AS STRING, ExeFolder AS STRING
                          '______________________________________________________________________________
                          FUNCTION ShellConsoleApp(sCmdLine AS STRING, sFolder AS STRING, sResult AS STRING, _
                                BYVAL dwMillisecondsWait AS DWORD) AS LONG
                            'Thanks to Semen Matusovski: http://www.powerbasic.com/support/pb...d.php?p=138870
                            LOCAL SecurityAttr AS SECURITY_ATTRIBUTES, ExitCode AS LONG
                            LOCAL StartUpInfos AS STARTUPINFO, ProcessInfos AS PROCESS_INFORMATION
                            LOCAL zChBuf AS ASCIIZ * 1048 * 16, zChEof AS ASCIIZ * 23
                            LOCAL hReadPipe AS DWORD, hWritePipe AS DWORD, BytesRead AS DWORD, BytesWritten AS DWORD
                          
                            'Set security attributes
                            SecurityAttr.nLength        = SIZEOF(SECURITY_ATTRIBUTES)
                            SecurityAttr.bInheritHandle = 1 ' TRUE
                            'Create a new pipe, needs high buffer value to catch all output - 0 does not always work properly
                            IF CreatePipe(hReadPipe, hWritePipe, SecurityAttr, BYVAL 140000)=0 THEN _
                                FUNCTION = -2 : EXIT FUNCTION
                          
                            'Start program
                            StartUpInfos.cb           = SIZEOF(STARTUPINFO)
                            StartUpInfos.dwFlags      = %STARTF_USESHOWWINDOW OR %STARTF_USESTDHANDLES
                            StartUpInfos.wShowWindow  = %SW_HIDE
                            StartUpInfos.hStdOutput   = hWritePipe 'Handle to be used as standard output handle of the process
                            StartUpInfos.hStdError    = hWritePipe
                            ' flag %CREATE_NO_WINDOW v. CREATE_NEW_CONSOLE seems to make little difference, as wShowWindow rules!
                            IF CreateProcess("", BYVAL STRPTR(sCmdLine), BYVAL 0, BYVAL 0, 1, _
                                 %CREATE_NEW_CONSOLE, _ 'Creation & priority flags
                                 0, "", StartUpInfos, ProcessInfos) = 0 THEN _
                                   FUNCTION = -1 : EXIT FUNCTION
                          
                            WaitForSingleObject(ProcessInfos.hProcess, dwMillisecondsWait)
                            GetExitCodeProcess(ProcessInfos.hProcess, ExitCode)
                            IF ExitCode = %STILL_ACTIVE THEN
                               TerminateProcess(ProcessInfos.hProcess, 0)
                               FUNCTION = -3
                            ELSE
                               FUNCTION = ExitCode
                            END IF
                          
                            'Read the pipe anyway
                            zChEof  = "End of pipe 2016-04-15" 'Something unique
                            sResult = ""
                            DO
                             IF WriteFile(hWritePipe, BYREF zChEof, LEN(zChEof), BytesWritten, BYVAL 0) = 0 THEN _
                                FUNCTION = -4 : EXIT DO
                             IF BytesWritten <> LEN(zChEof) THEN FUNCTION = -4 : EXIT DO
                             DO
                               IF ReadFile(hReadPipe, BYREF zChBuf, SIZEOF(zChBuf), BytesRead, BYVAL 0) = 0 THEN _
                                  FUNCTION = -4 : EXIT DO
                               IF BytesRead = 0 THEN FUNCTION = -4: EXIT DO
                               sResult += LEFT$(zChBuf, BytesRead)
                               IF RIGHT$(sResult, LEN(zChEof)) = zChEof THEN _
                                  sResult = LEFT$(sResult, LEN(sResult) - LEN(zChEof)) : EXIT DO
                             LOOP
                             EXIT DO
                            LOOP
                            IF LEN(sResult) THEN OemToCharBuff BYVAL STRPTR(sResult), BYVAL STRPTR(sResult), LEN(sResult)
                            CloseHandle(hReadPipe)  : CloseHandle(hWritePipe)   'Close pipe and process handles
                            CloseHandle(ProcessInfos.hThread)  : CloseHandle(ProcessInfos.hProcess)
                          END FUNCTION
                          '______________________________________________________________________________
                          #IF ((%DEF(%PB_WIN32)) AND (%PB_REVISION > &H0805))
                          THREAD FUNCTION pbCompilerThread(BYVAL hWnd AS LONG) AS LONG
                          #ELSE
                          FUNCTION pbCompilerThread(BYVAL hWnd AS LONG) AS LONG
                          #ENDIF
                           LOCAL sCmdLine AS STRING, sResult AS STRING, sExitCode AS STRING, ExitCode AS LONG
                          
                           sCmdLine = ExeFolder & ExeToCapture
                                                                                  'Timeout in miliseconds v
                           ExitCode = ShellConsoleApp(ExeFolder  & ExeToCapture , ExeFolder, sResult, 5000)
                           SELECT CASE ExitCode
                             CASE -1: sExitCode = "Can't start the program:"      & $CRLF & sCmdLine & $CRLF
                             CASE -2: sExitCode = "Can't read the console Output" & $CRLF & sCmdLine & $CRLF
                             CASE -3: sExitCode = "Execution canceled (time out)" & $CRLF & sCmdLine & $CRLF
                             CASE ELSE: sExitCode = FORMAT$(LEN(sResult)) & " Chars recovered by " & $CRLF & sCmdLine & $CRLF
                           END SELECT
                          
                           IF sResult = "" THEN sResult = sExitCode
                           SendMessage(GetDlgItem(hWnd, %EditCaptured), %WM_SETTEXT, 0, STRPTR(sResult)) 'Send to textbox
                           MSGBOX sResult  ' this variable contains the results, the rest was display eye candy
                          END FUNCTION
                          '______________________________________________________________________________
                          CALLBACK FUNCTION DlgProc()
                           STATIC hThread AS DWORD
                           SELECT CASE AS LONG CBMSG
                             CASE %WM_COMMAND
                               SELECT CASE AS LONG CBCTL
                                 CASE %ButtonRun
                                   IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                                     THREAD CREATE pbCompilerThread(CBHNDL) TO hThread
                                     THREAD CLOSE hThread TO hThread : hThread = 0
                                   END IF
                               END SELECT
                           END SELECT
                          END FUNCTION
                          '______________________________________________________________________________
                          FUNCTION PBMAIN
                           LOCAL hDlg AS DWORD
                           ExeFolder  = ""   ' ".\"
                           'ExeToCapture = "cmd.exe /c mem /c"
                           ExeToCapture = "wmic diskdrive get serialnumber"
                          
                           DIALOG NEW PIXELS, %HWND_DESKTOP, "STDOUT text capture", , , _
                           400, 200, %WS_CAPTION OR %WS_SYSMENU, TO hDlg
                           CONTROL ADD BUTTON,  hDlg, %ButtonRun, "Run " & ExeToCapture, 75, 5, 250, 20
                           CONTROL ADD TEXTBOX, hDlg, %EditCaptured, "Capture the output text of " & ExeToCapture, _
                           5, 30, 390, 165, %WS_CHILD OR %WS_VISIBLE OR _
                            %WS_TABSTOP OR %WS_HSCROLL OR %WS_VSCROLL OR %ES_LEFT OR %ES_MULTILINE OR _
                           %ES_NOHIDESEL OR %ES_AUTOHSCROLL OR %ES_AUTOVSCROLL OR %ES_WANTRETURN, _
                           %WS_EX_CLIENTEDGE OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR
                           DIALOG SHOW MODAL hDlg, CALL DlgProc
                          END FUNCTION
                          '______________________________________________________________________________​
                          Not exactly minimal, but there is no flash of a disappearing console, and it captures the CMD output to a string variable.

                          Comment


                          • #14
                            Just a reminder, both PBCC and PBWIn can write and read the Clipboard.
                            There's no need for any compicated code these days.
                            If it's your own application, just use CLIPBOARD SET TEXT in the originating application.
                            If it's not, just pipe the console output to CLIP.EXE, as in Post 7

                            https://docs.microsoft.com/en-us/win...-commands/clip
                            Although the reference is under "Windows Server", it's available with all current WIndows versions.

                            Comment


                            • #15
                              Just a reminder, both PBCC and PBWIn can write and read the Clipboard.
                              And, just a reminder, the clipboard is a shared resource, meanaing some other process might overwrite what you have stored there before your cooperating process gets a chance to read it.

                              Piping is a much more secure - "secure" in the sense of data integrity - method of passing data from one process to another than is using the clipboard. A little more work, perhaps, than a couple of CLIPBOARD statements, but ya gets what ya pays for.
                              Michael Mattias
                              Tal Systems (retired)
                              Port Washington WI USA
                              [email protected]
                              http://www.talsystems.com

                              Comment


                              • #16
                                Howdy, Stuart!

                                Edit: The problem wan't what I thought it was. The actual issue is that WMIC is creating a WSTRING file with a LE BOM marker and PB was choking on the initial bytes ( even if I use StrTemp AS WSTRING).
                                Yes, I saw that too. For all the 00, 0D, 20 and 0A characters it's hard to know what actually constitutes the serial number as intended by the manufacturer.

                                Howdy, Anne!
                                Don't I recall seeing that you also retrieve drive serial numbers? If you get them as a result of using wmic, do you do any manipulation of the returned value, such drop anything before the first 0A, replace all $NUL with a ".", Trim$ 0A, 0D, 00 off both ends - just to simplify the string?

                                Comment


                                • #17
                                  Howdy, Russel!

                                  Is that right, you've only posted a couple of times since 2017? Well, then how rude of us not to post some celebratory comments!
                                  and and and and yippee!

                                  We love active members here. Even if your per-year post count is low, you still have time to get that count up there with the rest of us!

                                  You can see that even those of us who post a lot still find plenty of questions to ask, so join the fun with with more questions and more solutions.

                                  Here, silence is not golden!

                                  Comment


                                  • #18
                                    FYI - if I do this to the string temp$ as returned by wmic ...

                                    Code:
                                       temp$ = Remain$(temp$,$Lf)
                                       Replace $Nul With "" In temp$
                                       temp$ = Trim$(temp$, Any $CrLf + ". ")    ​
                                    ... then the resulting drive serial number is modified and returned as this ... visually easier to look at and without all those extra (unimportant?) characters

                                    Code:
                                    0000_0000_0100_0000_E4D2_5C00_6CDF_5101

                                    Comment

                                    Working...
                                    X