VBA shell() does NOT return the exepected PID & ^Function CreateProcessA^ crashes Access

louis1-6497 0 Reputation points
2024-09-09T11:56:48.89+00:00

I am trying to start and control processes from and started by MsAccess. For that purpose I use the shell function.

https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/shell-function

That function is supposed to return the PID of the started process. Since the function is quite simple there was no reason to doubt ..... Never the less I tried this morning

PID = Shell("C:\WINDOWS\NOTEPAD.EXE", 1)

stop PID is e.g. ^12345^

In windows taskmanager details , I searched notepad and I searched for a PID ^12345^

Up to my surprise:

  • notepad was running with a completely different PID and
  • PID ^12345^ was not present

So I decide to search for an alternative and I thought I had found that in the form of a function "Function CreateProcessA" as described here

https://learn.microsoft.com/en-us/office/vba/access/concepts/windows-api/determine-when-a-shelled-process-ends

So I did copy the code from that website and did test with

Sub TestIt()

Dim PID As Double

Call StartProcess(PID, "NotePad.exe")    'StartProcess copied below

Stop

End Sub

Tja and as expected NotePad is started. ...... AND MsAccess is crashing

So both options:

  1. PID = Shell("C:\WINDOWS\NOTEPAD.EXE", 1)
  2. Call StartProcess(PID, "NotePad.exe")

Are drama .......

Note that the basic info is given on the given microsoft web links, where you have to add PtrSafe to the given functions

Private Declare PtrSafe Function WaitForSingleObject Lib "kernel32"

Private Declare PtrSafe Function CreateProcessA Lib "kernel32"

Private Declare PtrSafe Function CloseHandle Lib "kernel32"

Of course it is (more than) time that those functions are replaced by 64bit versions

Sub StartProcess(ByRef PID As Double, ByVal CmdLine As String)

Dim proc As PROCESS_INFORMATION

Dim start As STARTUPINFO

Dim ReturnValue As Integer

' Initialize the STARTUPINFO structure:

start.cb = Len(start)    

'(ByVal lpApplicationName As Long,      '0&

'ByVal lpCommandLine As String,         'CmdLine$

'ByVal lpProcessAttributes As Long,     '0&

'ByVal lpThreadAttributes As Long,      '0&

'ByVal bInheritHandles As Long,         '1&

'ByVal dwCreationFlags As Long,         'NORMAL_PRIORITY_CLASS ^= &H20&^

'ByVal lpEnvironment As Long,           '0&

'ByVal lpCurrentDirectory As Long,      '0&

'lpStartupInfo As STARTUPINFO,          'start

'lpProcessInformation As PROCESS_INFORMATION    'proc

') As Long



' Start the shelled application:

ReturnValue = CreateProcessA(0&, CmdLine$, 0&, 0&, 1&, _

NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)

'PID = proc.dwProcessID

Stop

End Sub

Microsoft 365
Microsoft 365
Formerly Office 365, is a line of subscription services offered by Microsoft which adds to and includes the Microsoft Office product line.
4,770 questions
Access
Access
A family of Microsoft relational database management systems designed for ease of use.
382 questions
Windows 11
Windows 11
A Microsoft operating system designed for productivity, creativity, and ease of use.
9,478 questions
Microsoft Q&A
Microsoft Q&A
Use this tag to share suggestions, feature requests, and bugs with the Microsoft Q&A team. The Microsoft Q&A team will evaluate your feedback on a regular basis and provide updates along the way.
806 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Viorel 116.7K Reputation points
    2024-09-09T13:01:01.2366667+00:00

    I think that Shell and CreateProcess return the correct values, but some processes start other processes or instances, then exit. Therefore, you probably see the PID of intermediate processes, but the started applications still run using other processes.

    To use CreateProcess, try the adjusted definitions:

    Private Type STARTUPINFO
        cb As Long
        lpReserved As String
        lpDesktop As String
        lpTitle As String
        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 Long
        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 WaitForSingleObject Lib "kernel32" (ByVal hHandle As LongPtr, ByVal dwMilliseconds As Long) As Long
    
    Private Declare PtrSafe Function CreateProcessA Lib "kernel32" ( _
        ByVal lpApplicationName As LongPtr, _
        ByVal lpCommandLine As String, _
        ByVal lpProcessAttributes As LongPtr, _
        ByVal lpThreadAttributes As LongPtr, _
        ByVal bInheritHandles As Long, _
        ByVal dwCreationFlags As Long, _
        ByVal lpEnvironment As LongPtr, _
        ByVal lpCurrentDirectory As LongPtr, _
        ByRef lpStartupInfo As STARTUPINFO, _
        ByRef lpProcessInformation As PROCESS_INFORMATION) As Long
    
    Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As LongPtr) As Long
    
    Private Const NORMAL_PRIORITY_CLASS = &H20&
    Private Const INFINITE = -1&
    
    Sub Test1()
    
        Dim proc As PROCESS_INFORMATION
        Dim start As STARTUPINFO
        Dim ReturnValue As Integer
        
        start.cb = Len(start)
        
        ReturnValue = CreateProcessA(0&, "Notepad.exe", 0&, 0&, 1&, NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)
        
        If ReturnValue = 0 Then
            MsgBox "Error"
        Else
            MsgBox proc.dwProcessID
            
            WaitForSingleObject proc.hProcess, -1
            
            MsgBox "Process exited"
            
        End If
        
    End Sub
    

    To deal with special behaviour of some applications, probably a more complex approach is required, based on "jobs". (Maybe you can start a new question with new details),


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.