Thema Datum  Von Nutzer Rating
Antwort
Rot Shell-Prozess starten, stderr auslesen in 64bit
18.04.2024 12:50:55 Max
NotSolved
18.04.2024 21:27:08 ralf_b
NotSolved
20.04.2024 11:25:18 Max
NotSolved
20.04.2024 13:14:10 ralf_b
NotSolved
19.04.2024 09:40:20 ralf_b
Solved

Ansicht des Beitrags:
Von:
Max
Datum:
18.04.2024 12:50:55
Views:
434
Rating: Antwort:
  Ja
Thema:
Shell-Prozess starten, stderr auslesen in 64bit

Hallo Alle,

 

ich verwende seit längerem ein Makro unter Excel, um ein Programm in einer DOSBox zu starten und den output aus stderr auszulesen. Dazu benutze ich die Windows-API. Nun musste ich das auf Excel365 portieren, was erstmal etwas knifflig war. Nachdem ich nun (hoffentlich) alle Deklarationen in ihre 64bit-Versionen korrigiert habe, läuft das Ding auch durch.

Problem: ich kann zwar ohne weiteres stdout über eine pipee auslesen, eine identisch erzeugte pipe für stderr gibt aber nichts zurück. Ich habe mir beholfen, indem ich beim Programmaufruf stderr auf stdout umleite ("cmd.exe meinprogramm 2>&1"). Das funktioniert zwar, trotzdem wüsste ich gerne was ich falsch mache.

Jemand irgendeine Idee?

Code:

Option VBASupport 1
Option Explicit

    Private Declare PtrSafe Function CreatePipe Lib "kernel32" ( _
        phReadPipe As LongPtr, _
        phWritePipe As LongPtr, _
        lpPipeAttributes As Any, _
        ByVal nSize As Long) As Long

    Private Declare PtrSafe Function ReadFile Lib "kernel32" ( _
        ByVal hFile As LongPtr, _
        ByVal lpBuffer As String, _
        ByVal nNumberOfBytesToRead As Long, _
        lpNumberOfBytesRead As Long, _
        ByVal lpOverlapped As Any) As Long

    Private Declare PtrSafe Function PeekNamedPipe Lib "kernel32" ( _
		ByVal hNamedPipe As LongPtr, _
        lpBuffer As Any, _
        ByVal nBufferSize As Long, _
        lpBytesRead As Long, _
        lpTotalBytesAvail As Long, _
        lpBytesLeftThisMessage As Long _
        ) As Long

    Private Type SECURITY_ATTRIBUTES
        nLength As Long
        lpSecurityDescriptor As LongPtr
		bInheritHandle As Long
    End Type

    Private Type STARTUPINFO
         cb As Long
         lpReserved As LongPtr
         lpDesktop As LongPtr
         lpTitle As LongPtr
         dwX As Long
         dwY As Long
         dwXSize As Long
         dwYSize As Long
         dwXCountChars As Long
         dwYCountChars As Long
         dwFillAttribute As Long
         dwFlags As Long
         wShowWindow As Integer
         cbReserved2 As Integer
         lpReserved2 As LongPtr
         hStdInput As LongPtr
         hStdOutput As LongPtr
         hStdError As LongPtr
    End Type

    Private Type PROCESS_INFORMATION
         hProcess As LongPtr
         hThread As LongPtr
         dwProcessID As Long
         dwThreadID As Long
    End Type

    Private Declare PtrSafe Function CreateProcessA Lib "kernel32" (ByVal _
         lpApplicationName As Long, ByVal lpCommandLine As String, _
         lpProcessAttributes As Any, lpThreadAttributes As Any, _
         ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
         ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, _
         lpStartupInfo As Any, lpProcessInformation As Any) As Long

    Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal _
         hObject As LongPtr) As Long
         
        
    Private Declare PtrSafe Function GetExitCodeProcess Lib _
        "kernel32" (ByVal hProcess As LongPtr, lpExitCode _
        As Long) As Long
      
    Private Const SW_SHOWNORMAL = 1
    Private Const SW_HIDE = 0
      
    Private Const STILL_ACTIVE = 259
      
    Private Const NORMAL_PRIORITY_CLASS = &H20&
      
    Private Const STARTF_USESHOWWINDOW = &H1&
    Private Const STARTF_USESTDHANDLES = &H100&
    Public Function ExecCmd(cmdline$) As String
      
        Dim proc As PROCESS_INFORMATION, ret As Long, bSuccess As Long
        Dim start As STARTUPINFO
        Dim sa As SECURITY_ATTRIBUTES, hReadPipe As LongPtr, hWritePipe _
        As LongPtr, hReadPipe2 As LongPtr, hWritePipe2 As LongPtr, ExitCode As Long, _
        tBytesr As Long, tBytesa As Long, tMsg As Long, Result As Long
        
        Dim bytesread As Long, mybuff As String
        Dim i As Integer

        mybuff = String(1024, "A")
        sa.nLength = Len(sa)
        sa.bInheritHandle = 1&
        sa.lpSecurityDescriptor = 0&

        ret = CreatePipe(hReadPipe, hWritePipe, sa, 0)
        If ret = 0 Then
            ExecCmd = "Error CreatePipe 1: " & Err.LastDllError
            Exit Function
        End If
        start.hStdOutput = hWritePipe

        ret = CreatePipe(hReadPipe2, hWritePipe2, sa, 0)
        If ret = 0 Then
            ExecCmd = "Error CreatePipe 2: " & Err.LastDllError
            Exit Function
        End If
        start.hStdError = hWritePipe2
        
        start.cb = Len(start)
        start.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
        start.wShowWindow = SW_SHOWNORMAL
       

        ret& = CreateProcessA(0&, cmdline$, sa, sa, 1&, _
                NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)
        If ret <> 1 Then
              ExecCmd = "Error CreateProcessA: " & Err.LastDllError
              Exit Function
        End If
        Do
            GetExitCodeProcess proc.hProcess, ExitCode
            
            '**This call returns 0
			'Result = PeekNamedPipe(hReadPipe2, ByVal 0&, 0, ByVal 0&, tBytesa, ByVal 0&)
            
			'**This call works as expected
			Result = PeekNamedPipe(hReadPipe, ByVal 0&, 0&, ByVal 0&, tBytesa, ByVal 0&)

            If Result <> 0 And tBytesa > 0 Then
                bSuccess = ReadFile(hReadPipe, mybuff, 1024, bytesread, 0&)
                If bSuccess = 1 Then
                    ExecCmd = ExecCmd & Left(mybuff, bytesread)
                End If
            End If
            DoEvents
            'Don't quit looping until the app has closed
        Loop While ExitCode = STILL_ACTIVE

        ret& = CloseHandle(proc.hProcess)
        ret& = CloseHandle(proc.hThread)
        ret& = CloseHandle(hReadPipe)
        ret& = CloseHandle(hWritePipe)
        ret& = CloseHandle(hReadPipe2)
        ret& = CloseHandle(hWritePipe2)
    End Function
	
Sub test()
    Dim result As String
    
	'**This call returns nothing
	'result = ExecCmd("cmd.exe /c dir X*")
	
	'**This call returns the expected error
	result = ExecCmd("cmd.exe /c dir X* 2>&1")
    MsgBox (result)
End Sub

 


Ihre Antwort
  • Bitte beschreiben Sie Ihr Problem möglichst ausführlich. (Wichtige Info z.B.: Office Version, Betriebssystem, Wo genau kommen Sie nicht weiter)
  • Bitte helfen Sie ebenfalls wenn Ihnen geholfen werden konnte und markieren Sie Ihre Anfrage als erledigt (Klick auf Häckchen)
  • Bei Crossposting, entsprechende Links auf andere Forenbeiträge beifügen / nachtragen
  • Codeschnipsel am besten über den Code-Button im Text-Editor einfügen
  • Die Angabe der Emailadresse ist freiwillig und wird nur verwendet, um Sie bei Antworten auf Ihren Beitrag zu benachrichtigen
Thema: Name: Email:

 
 

  • Bitte beschreiben Sie Ihr Problem möglichst ausführlich. (Wichtige Info z.B.: Office Version, Betriebssystem, Wo genau kommen Sie nicht weiter)
  • Bitte helfen Sie ebenfalls wenn Ihnen geholfen werden konnte und markieren Sie Ihre Anfrage als erledigt (Klick auf Häckchen)
  • Bei Crossposting, entsprechende Links auf andere Forenbeiträge beifügen / nachtragen
  • Codeschnipsel am besten über den Code-Button im Text-Editor einfügen
  • Die Angabe der Emailadresse ist freiwillig und wird nur verwendet, um Sie bei Antworten auf Ihren Beitrag zu benachrichtigen

Thema Datum  Von Nutzer Rating
Antwort
Rot Shell-Prozess starten, stderr auslesen in 64bit
18.04.2024 12:50:55 Max
NotSolved
18.04.2024 21:27:08 ralf_b
NotSolved
20.04.2024 11:25:18 Max
NotSolved
20.04.2024 13:14:10 ralf_b
NotSolved
19.04.2024 09:40:20 ralf_b
Solved