Launch process under another user account - vb6

Below is the sequence of Process calling;
Service calling Process A (this causes Process A to run under local system account).
Process A launching Process B under different user account (non admin user) using CreateProcessWithLogonW API (as logon credentials of different user is available).
Process B performing some activity which involves invoking another process.
Up to step 2 everything works fine. Process B launched under given user account but Process B is not able to perform a task like executing batch file or launching one more process using CreateProcess API.
Below is the code for invoking Process B;
Dim si As STARTUPINFO
Dim pi As PROCESS_INFORMATION
Dim wUser As String
Dim wDomain As String
Dim wPassword As String
Dim wCommandLine As String
Dim wCurrentDir As String
Dim wApplicaiotnName
Dim Result As Long
si.cb = Len(si)
si.lpDesktop = "WinSta0\Default"
Result = CreateProcessWithLogonW(wUser, wDomain, wPassword, _LOGON_WITH_PROFILE,
wApplicaiotnName, "", _CREATE_UNICODE_ENVIRONMENT, 0&, wCurrentDir, si, pi)
And below code used in Process B to execute batch file;
Dim proc As PROCESS_INFORMATION
Dim start As STARTUPINFO
Dim ret As Long
Dim lpId As Long
Dim llReturn As Long
Dim RetVal As Long
With start
.cb = Len(start)
.lpDesktop = "WinSta0\Default"
If Not IsMissing(WindowStyle) Then
.dwFlags = STARTF_USESHOWWINDOW
.wShowWindow = WindowStyle
End If
End With
ret& = CreateProcessA(0&, pathName, 0&, 0&, 1&, _
NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)

Related

Issue creating new process cmd.exe, opens and then closes immediately VB6/Windows 10

When i run the following code to create a cmd.exe process in windows 10, the console application opens and then closes immediately. In windows 7 this is working fine.
If i run calc.exe, this process does not closes immediately, this just happens with cmd.exe.
Const NORMAL_PRIORITY_CLASS = &H20&
Dim lSuccess As Long
Dim sApplicationName As String
Dim sCommandLine As String
Dim wCurrentDirectory As String
Dim pInfo As PROCESS_INFORMATION
Dim sInfo As STARTUPINFO
sApplicationName = "C:\Windows\system32\cmd.exe"
sCommandLine = ""
wCurrentDirectory = "C:\Windows\system32"
lSuccess = CreateProcess(sApplicationName, _
sCommandLine, _
ByVal 0&, _
ByVal 0&, _
0&, _
NORMAL_PRIORITY_CLASS, _
ByVal 0&, _
wCurrentDirectory, _
sInfo, _
pInfo)
EDIT:
After some debug i have found the problem, when its invoked SetParent function (after Create Process) the cmd.exe closes, but not calc.exe.
This SetParent function is declared in this way
Public Declare Function SetParent Lib "user32" (ByVal hWndChild As Long, ByVal hWndNewParent As Long) As Long
hWndChild is the window handle ID of cmd.exe or calc.exe, and hWndNewParent is the window handle ID of the parent in this case the application that (indirectly) runs this code.

Bring, to foreground, a specific program 'path\app.exe' if it is running using api in vb6

I am trying to use api functions inside vb6 that will allow me to bring a program to the foreground if it is running. At which point I will use sendkeys to send key strokes to the program in question.
The kicker is that the only thing I know about the program is its path and .exe name. For instance, 'c:\anyfolder\anyprog.exe'.
I can find all kinds of info on how to do this if I know other things about the program but not if I only know the above (not even what the title bar says when it is in the foreground, which the program itself changes regularly).
Is there a way to do this?
So far, with Remy's help, I have this vb6 code where I try to convert C code from Taking a Snapshot and Viewing Processes to vb6. But it is not quite working, any ideas?
Private Sub FillLists_Click()
PathList.Clear
FileNameList.Clear
Dim p As Long
Dim m As Long
Dim ml As Long
Dim hProcessSnapshot As Long
Dim h As Long
Dim hl As Long
Dim uProcess As PROCESSENTRY32
Dim uModule As MODULEENTRY32
hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0&)
If hProcessSnapshot = 0 Then Exit Sub
uProcess.dwSize = Len(uProcess)
p = ProcessFirst(hProcessSnapshot, uProcess)
Do While p 'as long as p is not 0
h = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, uProcess.th32ProcessID)
hl = GetLastError()
uModule.dwSize = Len(uModule)
m = Module32First(h, uModule)
ml = GetLastError()
PathList.AddItem "h=" & h & " hl=" & hl & " m=" & m & " ml=" & ml & uModule.szModule
FileNameList.AddItem uProcess.szExeFile
Call CloseHandle(h)
p = ProcessNext(hProcessSnapshot, uProcess)
Loop
Call CloseHandle(hProcessSnapshot)
End Sub
AND the output from that:
So, the above did not work, likely because vb6 is 32bit and my computer is Win7 64 bit. I found this function on a Google search for 'vb6 QueryFullProcessImageName' from a Russian forum thread, couldn't read the comments but the code was golden!
Function GetProcessNameByPID(pid As Long) As String
Const PROCESS_QUERY_LIMITED_INFORMATION As Long = &H1000
Const PROCESS_QUERY_INFORMATION As Long = &H400
Const MAX_PATH As Long = 260
Dim hProc As Long
Dim Path As String
Dim lStr As Long
Dim inf(68) As Long
Dim IsVistaAndLater As Boolean
inf(0) = 276: GetVersionEx inf(0): IsVistaAndLater = inf(1) >= 6
If Not IsVistaAndLater Then Exit Function
hProc = OpenProcess(IIf(IsVistaAndLater, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_QUERY_INFORMATION), False, pid)
If hProc <> 0 Then 'INVALID_HANDLE_VALUE Then
lStr = MAX_PATH
Path = Space(lStr)
' minimum Windows Vista !!!!!!!
If QueryFullProcessImageName(hProc, 0, StrPtr(Path), lStr) Then
GetProcessNameByPID = Left$(Path, lStr)
End If
CloseHandle hProc
End If
End Function
Now my code is:
Private Sub FillLists_Click()
PathList.Clear
FileNameList.Clear
Dim p As Long
Dim hProcessSnapshot As Long
Dim uProcess As PROCESSENTRY32
hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0&)
If hProcessSnapshot = 0 Then Exit Sub
uProcess.dwSize = Len(uProcess)
p = ProcessFirst(hProcessSnapshot, uProcess)
Do While p 'as long as p is not 0
PathList.AddItem GetProcessNameByPID(uProcess.th32ProcessID)
FileNameList.AddItem uProcess.szExeFile
p = ProcessNext(hProcessSnapshot, uProcess)
Loop
Call CloseHandle(hProcessSnapshot)
End Sub
AND the output from that is (and the rest is just do a AppActivate uProcess.th32ProcessID when you find the one you want):
THANKS REMY!
EDIT: Be careful, it turns out that you can't bring another application to the foreground if the application that is causing another app to come to foreground in minimized. I also needed to enumerate only the apps in the Alt-Tab group of programs and use api's in a way that forced a window to the foreground, not AppActivate or the SetForeGroundWindow() by itself.
You would have to:
enumerate all running processes, looking at their full paths and filenames until you find the one you are interested in. Use EnumProcesses() or CreateToolhelp32Snapshot() for that. See Enumerating All Processes and Taking a Snapshot and Viewing Processes for examples. Once you find the desired filename, you will know its Process ID.
use EnumWindows() and GetWindowThreadProcessId() to enumerate all top-level windows looking for the one(s) that belong to the same Process ID. Then you can restore those window(s) as needed (if you have permission to do so, that is - see all of the restrictions mentioned in the documentation for SetForegroundWindow()). If a window is minimized, you can try sending it a WM_SYCOMMAND/SC_RESTORE message. But if the window is already non-minimized but just not focused, you might run into resistence trying to focus it programmably.

Shell process' standard output reading in Visual Basic 6

First, let me say that I'm not a Visual Basic 6 expert...
My need is to:
launch from a VB6 client code an exeternal .exe file
wait for the process to finish and - during its execution - read the messages coming from its standard output "on the fly" (so that I can print it on a text-filed widget or similars).
I'm wondering if it is even possible to do that in VB6...after a long search on the Internet I didn't come up with anything. Found a lot of examples of how to use the Shell function, but it seems to force me to read the stdout all at once when the process' execution is over, but I want to poll the process for "fresh" messages as they become available.
Any code snippets/suggestions/references are really appreciated.
Thanks in advance!
Use CreatePipe() to create an anonymous pipe that you can pass to CreateProcess().
You can then read from this pipe as required (either using polling or overlapped/async I/O.
This should give you enough info to find a good example.
You can always use Exec method of WshShell to do the job.
I prefer to use a home-grown API based solution cExec.cls much simpler than Bob Riemersma's user control (but not as versatile).
You can also create a batch file that has all the commands that you need to run, and then from VB6 call the batch file by executing
Shell "C:\YourPath\BatchFileName.bat > OutputFileName.txt" 'Overwrites OutputFilename.txt everytime
once you execute that, then open OutputFileName.txt and you will find all of the messages and output that was generated during the batch process. You can then read it in VB6 in a simple open "filename" for input as #1
You should also notice that if you use double GreaterThan symbols, the the output file will not be overwritten every time the batch runs. Instead, it will get appended with the new lines of output.
Shell "C:\YourPath\BatchFileName.bat >> OutputFileName.txt" 'This will append to OutputFileName.txt
Here is the function you want. The exercise of declaring the API (CreatePipe, CreateProcessA, CloseHandle, etc), the types (PROCESS_INFORMATION, STARTUPINFO, SECURITY_ATTRIBUTES) the constants (STARTF_USESTDHANDLES, STARF_USESHOWWINDOW, etc) are left to the reader.
Public Function ExecuteCommand(ByVal CommandLine As String, Optional bShowWindow As Boolean = False, Optional sCurrentDir As String) As String
Dim proc As PROCESS_INFORMATION 'Process info filled by CreateProcessA
Dim ret As Long 'long variable for get the return value of the
'API functions
Dim start As STARTUPINFO 'StartUp Info passed to the CreateProceeeA
'function
Dim sa As SECURITY_ATTRIBUTES 'Security Attributes passeed to the
'CreateProcessA function
Dim hReadPipe As Long 'Read Pipe handle created by CreatePipe
Dim hWritePipe As Long 'Write Pite handle created by CreatePipe
Dim lngBytesRead As Long 'Amount of byte read from the Read Pipe handle
Dim strBuff As String * 256 'String buffer reading the Pipe
'if the parameter is not empty update the CommandLine property
If Len(CommandLine) > 0 Then
mCommand = CommandLine
End If
'if the command line is empty then exit whit a error message
If Len(mCommand) = 0 Then
ApplicationEventLogError "Command Line empty in procedure ExecuteCommand of module modPipedOutput."
Exit Function
End If
'Create the Pipe
sa.nLength = Len(sa)
sa.bInheritHandle = 1&
sa.lpSecurityDescriptor = 0&
ret = CreatePipe(hReadPipe, hWritePipe, sa, 0)
If ret = 0 Then
'If an error occur during the Pipe creation exit
Debug.Print "CreatePipe failed. Error: " & Err.LastDllError & " (" & ReturnError(Err.LastDllError)
Exit Function
End If
'Launch the command line application
start.cb = Len(start)
start.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
'set the StdOutput and the StdError output to the same Write Pipe handle
start.hStdOutput = hWritePipe
start.hStdError = hWritePipe
' start.hStdInput = hInReadPipe
If bShowWindow Then
start.wShowWindow = SW_SHOWNORMAL
Else
start.wShowWindow = SW_HIDE
End If
'Execute the command
If Len(sCurrentDir) = 0 Then
ret& = CreateProcessA(0&, mCommand, sa, sa, 1&, _
NORMAL_PRIORITY_CLASS, 0&, vbNullString, start, proc)
Else
ret& = CreateProcessA(0&, mCommand, sa, sa, 1&, _
NORMAL_PRIORITY_CLASS, 0&, sCurrentDir, start, proc)
End If
If ret <> 1 Then
'if the command is not found ....
Debug.Print "File or command not found in procedure ExecuteCommand"
Exit Function
End If
'Now We can ... must close the hWritePipe
ret = CloseHandle(hWritePipe)
' ret = CloseHandle(hInReadPipe)
mOutputs = vbNullString
'Read the ReadPipe handle
Do
ret = ReadFile(hReadPipe, strBuff, 256, lngBytesRead, 0&)
mOutputs = mOutputs & Left$(strBuff, lngBytesRead)
'Send data to the object via ReceiveOutputs event
Loop While ret <> 0
'Close the opened handles
Call CloseHandle(proc.hProcess)
Call CloseHandle(proc.hThread)
Call CloseHandle(hReadPipe)
'Return the Outputs property with the entire DOS output
ExecuteCommand = mOutputs
End Function

Process monitor / dispatcher in VB6

I need to write a little application in VB6 to run instances of another VB6 application and keep an eye on the running processes, but I don't have any idea how to get process information in VB6. I can see some of what I need with the tasklist utility but I don't really know how create processes (specifying the process or application name if possible) and fetching information about processes from the operating system.
This application is to run on a Windows XP machine.
Does anyone know of a get-you-started tutorial or helpful web page for this sort of thing?
There are numerous Windows API functions you can use to do this. I'd start with looking at EnumProcesses (VB6 example and declaration here) which can be used to gather information about all running processes. You can also use OpenProcess
to start interrogating Windows about a particular process (another VB6 example).
There is also a fairly nice example on MSDN.
And of course, there is CreateProcess (AllApi link) or ShellExecute (AllApi) for spawning processes - the former gives you more control over the creation of the process, while the latter is a much simpler call.
There was another question posted about this a while back with some example code.
Another possible approach would be to use WMI (some useful snippets to adapt).
Finally, here are some tutorials that show you how to do it (I'd recommend trying it yourself first though :):
Getting Process Information using PSAPI
Another EnumProcesses/OpenProcess implementation
WMI-based demonstration
Here are some related questions although you probably already saw them when you searched this site before posting:
Monitoring processes to see if they've crashed in vb6
How can I execute a .bat file but wait until its done running before moving on?
How To Enumerate Processes From VB 6 on Win 2003?
Since you say the other application is ** also VB6**, it would be easier to make the other application into an ActiveX exe. Then you can get references to objects in the other application direct from your first application. COM solves it all for you.
Here's Microsoft's tutorial on the subject - you can download the code too.
Or here's another answer where I've written about this
You don't need to go spelunking for processes just to get a handle to child processes that you spawn. The VB6 Shell() function returns a Process ID you can use to call OpenProcess with. CreateProcess gives you the handle directly.
Ok, here is a super-stripped-down example of a program in VB6 to spawn and monitor programs. The example is coded to start and repeatedly restart 3 copies of the command shell (trivial sample child program). It is also written to kill any running children when it is terminated, and there are better alternatives to use in most cases. See A Safer Alternative to TerminateProcess().
This demo also reports back the exit code of each process that quits. You could enter exit 1234 or somesuch to see this in action.
To create the demo open a new VB6 Project with a Form. Add a multiline TextBox Text1 and a Timer Timer1 (which is used to poll the children for completion). Paste this code into the Form:
Option Explicit
Private Const SYNCHRONIZE = &H100000
Private Const PROCESS_QUERY_INFORMATION = &H400&
Private Const PROCESS_TERMINATE = &H1&
Private Const WAIT_OBJECT_0 = 0
Private Const INVALID_HANDLE = -1
Private Const DEAD_HANDLE = -2
Private Declare Function CloseHandle Lib "kernel32" ( _
ByVal hObject As Long) As Long
Private Declare Function GetExitCodeProcess Lib "kernel32" ( _
ByVal hProcess As Long, _
ByRef lpExitCode As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" ( _
ByVal dwDesiredAccess As Long, _
ByVal bInheritHandle As Long, _
ByVal dwProcessId As Long) As Long
Private Declare Function TerminateProcess Lib "kernel32" ( _
ByVal hProcess As Long, _
ByVal uExitCode As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" ( _
ByVal hHandle As Long, _
ByVal dwMilliseconds As Long) As Long
Private Tasks() As String
Private Handles() As Long
Private Sub Form_Load()
Dim I As Integer
'We'll run 3 copies of the command shell as an example.
ReDim Tasks(2)
ReDim Handles(2)
For I = 0 To 2
Tasks(I) = Environ$("COMSPEC") & " /k ""#ECHO I am #" & CStr(I) & """"
Handles(I) = INVALID_HANDLE
Next
Timer1.Interval = 100
Timer1.Enabled = True
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
Dim I As Integer
Timer1.Enabled = False
DoEvents
For I = 0 To UBound(Tasks)
If Handles(I) <> INVALID_HANDLE And Handles(I) <> DEAD_HANDLE Then
TerminateProcess Handles(I), 666
CloseHandle Handles(I)
Handles(I) = DEAD_HANDLE
End If
Next
End Sub
Private Sub Timer1_Timer()
Dim I As Integer
Dim ExitCode As Long
Dim Pid As Long
Timer1.Enabled = False
For I = 0 To UBound(Tasks)
If Handles(I) <> INVALID_HANDLE Then
If WaitForSingleObject(Handles(I), 0) = WAIT_OBJECT_0 Then
If GetExitCodeProcess(Handles(I), ExitCode) <> 0 Then
Text1.SelText = "Task " & CStr(I) & " terminated, " _
& "exit code: " & CStr(ExitCode) _
& ", restarting task." _
& vbNewLine
Else
Text1.SelText = "Task " & CStr(I) & " terminated, " _
& "failed to retrieve exit code, error " _
& CStr(Err.LastDllError) _
& ", restarting task." _
& vbNewLine
End If
CloseHandle Handles(I)
Handles(I) = INVALID_HANDLE
End If
End If
If Handles(I) = INVALID_HANDLE Then
Pid = Shell(Tasks(I), vbNormalFocus)
If Pid <> 0 Then
Handles(I) = OpenProcess(SYNCHRONIZE _
Or PROCESS_QUERY_INFORMATION _
Or PROCESS_TERMINATE, 0, Pid)
If Handles(I) <> 0 Then
Text1.SelText = "Task " & CStr(I) & " started." _
& vbNewLine
Else
Text1.SelText = "Task " & CStr(I) _
& ", failed to open child process." _
& vbNewLine
Handles(I) = DEAD_HANDLE
End If
Else
Text1.SelText = "Task " & CStr(I) _
& ", failed to Shell child process." _
& vbNewLine
Handles(I) = DEAD_HANDLE
End If
End If
Next
Timer1.Enabled = True
End Sub
Hopefully this helps answer the question.
something more simple will use sockets.
Launch you app server and on your client implements the communication against your server. With that you will provide intercommunication.
well i say. because i dont be what you try do
Sorry it only apply if your clients are done in house i you have the option of added changes

CreateProcessWithLogon Error requires elevation

all
I am using CreateProcessWithLogon method to installing softwares for non autherized users to installing software, it was working fine for windows XP users, but same application shows the error as follows in windows 7 machine.
System.ComponentModel.Win32Exception(0x80004005): the requested operation requires elevation
please any one suggest me any idea to fix it.
i am using the code as follows
Private Const LOGON_NETCREDENTIALS_ONLY As Integer = &H2
Private Const NORMAL_PRIORITY_CLASS As Integer = &H20
Private Const CREATE_DEFAULT_ERROR_MODE As Integer = &H4000000
Private Const CREATE_NEW_CONSOLE As Integer = &H10
Private Const CREATE_NEW_PROCESS_GROUP As Integer = &H200
Private Const LOGON_WITH_PROFILE As Integer = &H1
Private Const LOGON_WITH_PROFILE1 As Integer = 0
Dim siStartup As STARTUPINFO
Dim piProcess As PROCESS_INFORMATION
Dim intReturn As Integer
Dim bResult As Boolean = False
Dim result As Integer
Dim sFile, sArg As String
IMP_USER_NAME = AppSettings("UserName")
IMP_PASS_WORD = AppSettings("Password")
IMP_DOMAIN_NAME = AppSettings("Domain")
Try
If sApplication.EndsWith(".msi") Then
sApplication = sApplication & " " & """ALLUSERS=1"""
sArg = "msiexec.exe /i """ & sApplication & """"
sFile = vbNullString
Else
If bToExecute = False Then
sArg = vbNullString
sFile = sApplication
Else
sArg = "cmd /c """ & sApplication & """"
sFile = vbNullString
End If
End If
siStartup.cb = Marshal.SizeOf(siStartup)
siStartup.dwFlags = 0
intReturn = CreateProcessWithLogon(IMP_USER_NAME, IMP_DOMAIN_NAME, IMP_PASS_WORD, LOGON_WITH_PROFILE, sFile, sArg, _
NORMAL_PRIORITY_CLASS Or CREATE_DEFAULT_ERROR_MODE Or CREATE_NEW_CONSOLE Or CREATE_NEW_PROCESS_GROUP, _
IntPtr.Zero, IntPtr.Zero, siStartup, piProcess)
Thanks,
Senthil
See http://blogs.msdn.com/b/cjacks/archive/2010/02/01/why-can-t-i-elevate-my-application-to-run-as-administrator-while-using-createprocesswithlogonw.aspx : you need an intermediate process that execute ShellExec() to elevate the priveleges of an application called with CreateProcessWithLogon
Alternativeley, you can call cmd /c cd <targetDir>&<targetDrive>:&<execName> with CreateProcessWithLogon to avoid creating that intermediate process.
Probably the reason is that you use LOGON_WITH_PROFILE flag. In the "Remark" part of the CreateProcessWithLogonW documentation you can read following
By default, CreateProcessWithLogonW
does not load the specified user
profile into the HKEY_USERS registry
key. This means that access to
information in the HKEY_CURRENT_USER
registry key may not produce results
that are consistent with a normal
interactive logon. It is your
responsibility to load the user
registry hive into HKEY_USERS before
calling CreateProcessWithLogonW, by
using LOGON_WITH_PROFILE, or by
calling the LoadUserProfile function.
So I recommend you to try the same code without LOGON_WITH_PROFILE flag. If you will see that it was the problem and you do need to use the flag you should use LoadUserProfile function and UnloadUserProfile in your code.

Resources