Run a batch program(.bat) through a Visual Basic 6.0 - batch-file

I want to run a batch program(.bat) through a Visual Basic 6.0 application and also want to print the output of the batch program(.bat) in the Visual Basic 6.0 application. I want to execute the dir command in the batch file so that VB6.0 application can print the output in a text box.
VB6.0 code:
Dim com As String
Dim wshThisShell
Dim lngRet As Long
Dim strShellCommand As String
Dim strBatchPath As String
Sub C0ding()
Set wshThisShell = CreateObject("WScript.Shell")
strBatchPath = "C:\first.bat"
strShellCommand = """" & strBatchPath & """"
lngRet = wshThisShell.Run(strShellCommand, vbNormalFocus, vbTrue)
End Sub
Private Sub Command1_Click()
C0ding
End Sub
first.bat:
dir c:\
In the above example 'first.bat' is batch file and containing the 'dir c:\' command. Now VB6.0 app will run the first.bat and show the output of the 'dir c:\' command in a text box.
Please also tell me that I can achieve this requirement means can VB6.0 application regain the control from batch program(.bat)?
Please help me with this.

Following is solution which worked for me:
Private Declare Function CreatePipe Lib "kernel32" (phReadPipe As Long, phWritePipe As Long, lpPipeAttributes As Any, ByVal nSize As Long) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As String, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Any) As Long
Private Declare Function GetNamedPipeInfo Lib "kernel32" (ByVal hNamedPipe As Long, lType As Long, lLenOutBuf As Long, lLenInBuf As Long, lMaxInstances As Long) As Long
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type
Private Type STARTUPINFO
cb As Long
lpReserved As Long
lpDesktop As Long
lpTitle As Long
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 Long
hStdOutput As Long
hStdError As Long
End Type
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long
dwThreadID As Long
End Type
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare 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 Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
'Purpose : Synchronously runs a DOS command line and returns the captured screen output.
'Inputs : sCommandLine The DOS command line to run.
' [bShowWindow] If True displays the DOS output window.
'Outputs : Returns the screen output
'Notes : This routine will work only with those program that send their output to
' the standard output device (stdout).
' Windows NT ONLY.
'Revisions :
Function ShellExecuteCapture(sCommandLine As String, Optional bShowWindow As Boolean = False) As String
Const clReadBytes As Long = 256, INFINITE As Long = &HFFFFFFFF
Const STARTF_USESHOWWINDOW = &H1, STARTF_USESTDHANDLES = &H100&
Const SW_HIDE = 0, SW_NORMAL = 1
Const NORMAL_PRIORITY_CLASS = &H20&
Const PIPE_CLIENT_END = &H0 'The handle refers to the client end of a named pipe instance. This is the default.
Const PIPE_SERVER_END = &H1 'The handle refers to the server end of a named pipe instance. If this value is not specified, the handle refers to the client end of a named pipe instance.
Const PIPE_TYPE_BYTE = &H0 'The named pipe is a byte pipe. This is the default.
Const PIPE_TYPE_MESSAGE = &H4 'The named pipe is a message pipe. If this value is not specified, the pipe is a byte pipe
Dim tProcInfo As PROCESS_INFORMATION, lRetVal As Long, lSuccess As Long
Dim tStartupInf As STARTUPINFO
Dim tSecurAttrib As SECURITY_ATTRIBUTES, lhwndReadPipe As Long, lhwndWritePipe As Long
Dim lBytesRead As Long, sBuffer As String
Dim lPipeOutLen As Long, lPipeInLen As Long, lMaxInst As Long
tSecurAttrib.nLength = Len(tSecurAttrib)
tSecurAttrib.bInheritHandle = 1&
tSecurAttrib.lpSecurityDescriptor = 0&
lRetVal = CreatePipe(lhwndReadPipe, lhwndWritePipe, tSecurAttrib, 0)
If lRetVal = 0 Then
'CreatePipe failed
Exit Function
End If
tStartupInf.cb = Len(tStartupInf)
tStartupInf.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
tStartupInf.hStdOutput = lhwndWritePipe
If bShowWindow Then
'Show the DOS window
tStartupInf.wShowWindow = SW_NORMAL
Else
'Hide the DOS window
tStartupInf.wShowWindow = SW_HIDE
End If
lRetVal = CreateProcessA(0&, sCommandLine, tSecurAttrib, tSecurAttrib, 1&, NORMAL_PRIORITY_CLASS, 0&, 0&, tStartupInf, tProcInfo)
If lRetVal <> 1 Then
'CreateProcess failed
Exit Function
End If
'Process created, wait for completion. Note, this will cause your application
'to hang indefinately until this process completes.
WaitForSingleObject tProcInfo.hProcess, INFINITE
'Determine pipes contents
lSuccess = GetNamedPipeInfo(lhwndReadPipe, PIPE_TYPE_BYTE, lPipeOutLen, lPipeInLen, lMaxInst)
If lSuccess Then
'Got pipe info, create buffer
sBuffer = String(lPipeOutLen, 0)
'Read Output Pipe
lSuccess = ReadFile(lhwndReadPipe, sBuffer, lPipeOutLen, lBytesRead, 0&)
If lSuccess = 1 Then
'Pipe read successfully
ShellExecuteCapture = Left$(sBuffer, lBytesRead)
End If
End If
'Close handles
Call CloseHandle(tProcInfo.hProcess)
Call CloseHandle(tProcInfo.hThread)
Call CloseHandle(lhwndReadPipe)
Call CloseHandle(lhwndWritePipe)
End Function
Sub Test()
'Debug.Print ShellExecuteCapture("C:\first.bat", False)
Text1.Text = ShellExecuteCapture("C:\first.bat", False)
End Sub
Private Sub Command1_Click()
Call Test
End Sub
I got this solution from the following link:
Solution Link

Your example is not a batch file, but if all you want to do is display the results of a command prompt's dir c:\ command in a textbox, then the following should work:
Disclaimer: The following is "Air Code" and not tested for syntax
Private Sub Command1_Click()
Dim sCommand As String
sCommand = "dir c:\ > C:\tempFile.txt"
Shell "%COMSPEC% /c " & sCommand
Dim inCh As Integer
inCh = Freefile
Open "C:\tempFile.txt" For Input As inCh
Text1.Text = Input$(Lof(inCh), inCh)
Close inCh
End Sub
There are several variations and alternative ways to accomplish this, this is just a quick-and-dirty solution example.

Lots of simple ways to skin this cat, for example:
Option Explicit
'Reference to: Windows Script Host Object Model
Private WshExec As IWshRuntimeLibrary.WshExec
Private Sub Form_Load()
With New IWshRuntimeLibrary.WshShell
Set WshExec = .Exec("cmd.exe /c dir c:\")
End With
Timer1.Interval = 100
End Sub
Private Sub Form_Resize()
If WindowState <> vbMinimized Then
Text1.Move 0, 0, ScaleWidth, ScaleHeight
End If
End Sub
Private Sub Timer1_Timer()
With WshExec
Select Case .Status
Case WshFinished, WshFailed
Text1.Text = .StdOut.ReadAll()
Timer1.Interval = 0
End Select
End With
End Sub

Related

How to fix RUNDLL32.EXE user32.dll,UpdatePerUserSystemParameters 1, True not updating every time

I have had a look at this StackOverflow article and the same thing applies to me. Why is it that RUNDLL32.EXE user32.dll,UpdatePerUserSystemParameters 1, True does not work everytime? Is there some other way to make it work rather than repeating that until it works or is there some way to code it so that it works? .cmd , .bat and .ps1 is fine) Or is the best/only way to run it alot of times so that it works
Right now my solution is to just run that multiple time until it works. Is there any other way to refresh the desktop wallpaper without running RUNDLL32.EXE user32.dll,UpdatePerUserSystemParameters 1, True alot of times?
From Help
https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-systemparametersinfow
Although this is from the 2001 documentation and has been removed from current.
Setting pvParam to "" removes the wallpaper. Setting pvParam to VBNULL
reverts to the default wallpaper.
REM ChangeWallpaper.bat
REM Compiles ChangeWallpaper.vb to ChangeWallpaper.exe
C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc "%~dp0\ChangeWallpaper.vb" /out:"%~dp0\ChangeWallpaper.exe" /target:winexe
pause
;ChangeWallpaper.vb
Imports System.Runtime.InteropServices
Public Module ChangeWallpaper
Public Declare Unicode Function SystemParametersInfoW Lib "user32" (ByVal uAction As Integer, ByVal uParam As Integer, ByVal lpvParam As String, ByVal fuWinIni As Integer) As Integer
Public Const SPI_SETDESKWALLPAPER = 20
Public Const SPIF_SENDWININICHANGE = &H2
Public Const SPIF_UPDATEINIFILE = &H1
Public Sub Main()
Dim Ret as Integer
Dim FName As String
Fname = "C:\Windows\Web\Wallpaper\Theme1\img1.jpg"
'This below line which is commented out takes a filename on the command line
'FName = Replace(Command(), """", "")
Ret = SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, FName, SPIF_SENDWININICHANGE + SPIF_UPDATEINIFILE)
If Ret = 0 Then Msgbox(err.lastdllerror)
End Sub
End Module
The code is from here https://winsourcecode.blogspot.com/2019/06/changewallpaper.html
Update
This is the problem with using it
Declare Function UpdatePerUserSystemParameters Lib "User32.dll" (ByVal i As Long, ByVal b As Boolean) As long
As you can see from the article Rundll32 is passing a hwnd (probably 0 to say Desktop is the parent) for j and RunDll32's HInst as a Boolean for b, and as this will be non zero it will be treated as true.

VBA merge files in current dir using cmd.exe

I have an Excel sheet in one folder and try to merge certain files within this folder to one file using the following code:
Private Sub CommandButton1_Click()
Dim RET As Variant
RET = Shell("cmd.exe copy files1.txt + file2.txt out.txt", 0)
End Sub
As a return value for RET i get 1560. No error while debugging, but no "out.txt" either.
What's wrong with my code? Thanks
I think you miss the /C in the cmd arguments and the path.
Private Sub CommandButton1_Click()
Dim RET As Variant
RET = Shell("cmd.exe /C copy C:\Data\files1.txt + C:\Data\file2.txt C:\Data\out.txt", 0)
End Sub
The return value not equal 0 indicates the process is started (it is the actual process id)
The VBA way;
Function readFile(path) As String
On Error GoTo ERR_IO
Dim hF As Integer: hF = FreeFile
Open path For Input As #hF
readFile = Input$(LOF(hF), hF)
ERR_IO:
Close #hF
End Function
Function writeFile(path, buffer) As Boolean
On Error GoTo ERR_IO
Dim hF As Integer: hF = FreeFile
Open path For Output As #hF
Print #hF, buffer
writeFile = True
ERR_IO:
Close #hF
End Function
Sub merge()
Dim buffer As String
buffer = readFile("C:\xxx\files1.txt")
buffer = buffer & readFile("C:\xxx\files2.txt")
writeFile "c:\xxx\out.txt", buffer
End Sub

MS Access: Can't change the file type in the Save As dialog box

I have an .mdb file created in access 2003 and I'd like to convert it to access 2007 .accdb
but when I go to the Save as dialog it lets me save the file only as it's current format(.mdb) and there isn't any additional file formats in the drop down box.
same thing happens when I create a new .accdb and try to save it as an 2003 .mdb file. I can't see anything but .accdb in the Save As file format drop down box.
I could probably run a VBA code to save as my wanted file format but it isn't optimal.
does any one knows what the solution might be?
Thank you,
Jake
'PUT THIS IN A STANDARD CLASS MODULE FIRST
Option Compare Database
Private mstrFileName As String
Private mblnStatus As Boolean
'Declare needed functions
Private Declare Function GetOpenFileName Lib "comdlg32.dll" Alias "GetOpenFileNameA" _
(pOpenfilename As OPENFILENAME) As Long
Private Declare Function GetSaveFileName Lib "comdlg32.dll" Alias "GetSaveFileNameA" _
(pOpenfilename As OPENFILENAME) As Long
'Declare OPENFILENAME custom Type
Private Type OPENFILENAME
lStructSize As Long
hwndOwner As Long
hInstance As Long
lpstrFilter As String
lpstrCustomFilter As String
nMaxCustFilter As Long
nFilterIndex As Long
lpstrFile As String
nMaxFile As Long
lpstrFileTitle As String
nMaxFileTitle As Long
lpstrInitialDir As String
lpstrTitle As String
Flags As Long
nFileOffset As Integer
nFileExtension As Integer
lpstrDefExt As String
lCustData As Long
lpfnHook As Long
lpTemplateName As String
End Type
'Function needed to call the "Save As" dialog
Public Function SaveFileDialog(lngFormHwnd As Long, _
lngAppInstance As Long, strInitDir As String, _
strFileFilter As String) As Long
Dim SaveFile As OPENFILENAME
Dim X As Long
If IsMissing(strFileName) Then strFileName = ""
With SaveFile
.lStructSize = Len(SaveFile)
.hwndOwner = lngFormHwnd
.hInstance = lngAppInstance
.lpstrFilter = strFileFilter
.nFilterIndex = 1
.lpstrFile = String(257, 0)
'Use for a Default File SaveAs Name - [UD]
'.lpstrFile = "testfile.txt" & String(257 - Len("testfile.txt"), 0)
.nMaxFile = Len(SaveFile.lpstrFile) - 1
.lpstrFileTitle = SaveFile.lpstrFile
.nMaxFileTitle = SaveFile.nMaxFile
.lpstrInitialDir = strInitDir
.lpstrTitle = "Enter a Filename to Save As" '[UD]
.Flags = 0
.lpstrDefExt = ".xls" 'Sets default file extension to Excel,
'in case user does not type it - [UD]
End With
X = GetSaveFileName(SaveFile)
If X = 0 Then
mstrFileName = "none"
mblnStatus = False
Else
mstrFileName = Trim(SaveFile.lpstrFile)
mblnStatus = True
End If
End Function
Public Property Let GetName(strName As String)
mstrFileName = strName
End Property
Public Property Get GetName() As String
GetName = mstrFileName
End Property
Public Property Let GetStatus(blnStatus As Boolean)
mblnStatus = blnStatus
End Property
Public Property Get GetStatus() As Boolean
GetStatus = mblnStatus
End Property
'THEN WE'LL CALL IT LIKE
Private Sub cmdTest_Click()
On Error GoTo Err_cmdTest_Click
Dim cDlg As New CommonDialogAPI 'Instantiate CommonDialog
Dim lngFormHwnd As Long
Dim lngAppInstance As Long
Dim strInitDir As String
Dim strFileFilter As String
Dim lngResult As Long
lngFormHwnd = Me.Hwnd 'Form Handle
lngAppInstance = Application.hWndAccessApp 'Application Handle
strInitDir = "C:\" 'Initial Directory - [UD]
'Create any Filters here - [UD]
strFileFilter = "Excel Files (*.xls)" & Chr(0) & "*.xls" & Chr(0) & _
"ALL Files (*.*)" & Chr(0) & "*.* & Chr(0)"
'"Text Files (*.csv, *.txt)" & _
'Chr(0) & "*.csv; *.txt" & Chr(0)
lngResult = cDlg.SaveFileDialog(lngFormHwnd, _
lngAppInstance, strInitDir, strFileFilter)
If cDlg.GetStatus = True Then
MsgBox "You chose the Filename of: " & cDlg.GetName 'Retrieve Filename - [UD]
Else
MsgBox "No file chosen." '[UD]
End If
Exit_cmdTest_Click:
Exit Sub
Err_cmdTest_Click:
MsgBox Err.Description, vbExclamation, "Error in cmdTest_Click()"
Resume Exit_cmdTest_Click
End Sub

Read entire ini section and put into array

Ok, so I have these functions I'm tring to use via my vba code.
It's probably the as it would have been with vbs as well.
Here's the function(s)
'declarations for working with Ini files
Private Declare Function GetPrivateProfileSection Lib "kernel32" Alias _
"GetPrivateProfileSectionA" (ByVal lpAppName As String, ByVal lpReturnedString As String, _
ByVal nSize As Long, ByVal lpFileName As String) As Long
Private Declare Function GetPrivateProfileString Lib "kernel32" Alias _
"GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, _
ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, _
ByVal lpFileName As String) As Long
'// INI CONTROLLING PROCEDURES
'reads an Ini string
Public Function ReadIni(Filename As String, Section As String, Key As String) As String
Dim RetVal As String * 255, v As Long
v = GetPrivateProfileString(Section, Key, "", RetVal, 255, Filename)
ReadIni = Left(RetVal, v + 0)
End Function
'reads an Ini section
Public Function ReadIniSection(Filename As String, Section As String) As String
Dim RetVal As String * 255, v As Long
v = GetPrivateProfileSection(Section, RetVal, 255, Filename)
ReadIniSection = Left(RetVal, v + 0)
End Function
How can I use this to create a function that basically allows me to specify only the section I want to look in, and then find each ini string within that section and put it into an array and return that Array so I can do a loop with it?
Edit: I see that ReadIniSection returns all of the keys in a huge string.
Meaning, I need to split it up.
ReadIniSection returns something that looks like this:
"Fornavn=FORNAVN[]Etternavn=ETTERNAVN" etc etc. The[] in the middle there isn't brackets, it's a square. Probably some character it doesn't recognize. So I guess I should run it through a split command that takes the value between a = and the square.
See if this helps - splitting on nullchar \0:
Private Sub ListIniSectionLines()
Dim S As String: S = ReadIniSection("c:\windows\win.ini", "MAIL")
Dim vLines As Variant: vLines = Split(S, Chr$(0))
Dim vLine As Variant
For Each vLine In vLines
Debug.Print vLine
Next vLine
End Sub

VBA: Read file from clipboard

I'm trying to load a file in a VBA macro that has been copied from, say, an Explorer window.
I can easily get the data from the clipboard using DataObject::GetFromClipboard, but the VBA interface to DataObject doesn't seem to have methods for working with any other formats than plain text. There are only GetText and SetText methods.
If I can't get a file stream directly from the DataObject, the filename(s) would also do, so maybe GetText could be forced to return the name of a file placed on the clipboard?
There is very little documentation to be found for VBA anywhere. :(
Maybe someone could point me to an API wrapper class for VBA that has this sort of functionality?
This works for me (in a module);
Private Declare Function IsClipboardFormatAvailable Lib "user32" (ByVal uFormat As Long) As Long
Private Declare Function OpenClipboard Lib "user32" (ByVal Hwnd As Long) As Long
Private Declare Function GetClipboardData Lib "user32" (ByVal uFormat As Long) As Long
Private Declare Function CloseClipboard Lib "user32" () As Long
Private Declare Function DragQueryFile Lib "shell32.dll" Alias "DragQueryFileA" (ByVal drop_handle As Long, ByVal UINT As Long, ByVal lpStr As String, ByVal ch As Long) As Long
Private Const CF_HDROP As Long = 15
Public Function GetFiles(ByRef fileCount As Long) As String()
Dim hDrop As Long, i As Long
Dim aFiles() As String, sFileName As String * 1024
fileCount = 0
If Not CBool(IsClipboardFormatAvailable(CF_HDROP)) Then Exit Function
If Not CBool(OpenClipboard(0&)) Then Exit Function
hDrop = GetClipboardData(CF_HDROP)
If Not CBool(hDrop) Then GoTo done
fileCount = DragQueryFile(hDrop, -1, vbNullString, 0)
ReDim aFiles(fileCount - 1)
For i = 0 To fileCount - 1
DragQueryFile hDrop, i, sFileName, Len(sFileName)
aFiles(i) = Left$(sFileName, InStr(sFileName, vbNullChar) - 1)
Next
GetFiles = aFiles
done:
CloseClipboard
End Function
Use:
Sub wibble()
Dim a() As String, fileCount As Long, i As Long
a = GetFiles(fileCount)
If (fileCount = 0) Then
MsgBox "no files"
Else
For i = 0 To fileCount - 1
MsgBox "found " & a(i)
Next
End If
End Sub
Save the files if they are in the clipboard to the destination folder.
Public Declare PtrSafe Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Long) As Long
Public Const CF_HDROP As Long = 15
Public Function SaveFilesFromClipboard(DestinationFolder As String) As Boolean
SaveFilesFromClipboard = False
If Not CBool(IsClipboardFormatAvailable(CF_HDROP)) Then Exit Function
CreateObject("Shell.Application").Namespace(CVar(DestinationFolder)).self.InvokeVerb "Paste"
SaveFilesFromClipboard = True
End Function
Seems like a strange way to try to get at the textfile. The DataObject class is only for working with text strings to and from the clipboard.
Here is a very good resource of that:
http://www.cpearson.com/excel/Clipboard.aspx
If your wanting to get a file stream of a file you can look into the FileSystemObject and TextStream Classes.

Resources