I need to import a bunch of tables stored in .txt files into an Access database. When they are done importing, I use an ADO connection to communicate between the database and an Excel workbook. I have the Access database set to compact and repair on close.
The problem is, when I close the database after importing the files, I am unable to connect using ADO without waiting for an arbitrary amount of time. The Access window appears to be closed when I try to connect and fail. I have found that the amount of time I have to wait is related to the size of the database after import. After importing the largest sets of files, even a 60 second wait is not enough.
Is there some way I could force the connection to open? Of failing that, how could I check if it was ready to connect?
Here is some of the code I'm using:
MDB_Address = "C:\example.mdb"
Shell "cmd /c " & Chr(34) & MDB_Address & Chr(34), vbHide
'Some code that tests if it has opened happens here
...
Set ObjAccess = GetObject("C:\example.mdb")
' Import tables here
ObjAccess.Quit
Call CloseAccess
Call Wait
mdbPath = "C:\example.mdb"
Set mdbConnection = CreateObject("ADODB.Connection")
' The line below gives a run time error. The description is "Automation error Unspecified Error"
mdbConnection.Open "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & mdbPath
Sub CloseAccess
' I have set up the access database to write a flag to a .txt file when a
userform closes and use this to help check if it has closed.
End Sub
Sub Wait
' Wait 5 seconds. The access window appears to be closed.
Dim nHour As Date, nMinute As Date, nSecond As Date, waitTime As Date
nHour = Hour(Now())
nMinute = Minute(Now())
nSecond = Second(Now()) + 5
waitTime = TimeSerial(nHour, nMinute, nSecond)
Application.Wait waitTime
End Sub
Here is what I have ended up doing to test if the database is closed. I use Windows API functions to get the process handle for the Access database and then get its exit status.
This seems to work pretty well. There are surely other ways of accomplishing this - I think there is an ldb file that is created in the directory and it would probably work to check for its existence.
Private Declare Function OpenProcess Lib "kernel32" _
(ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function GetExitCodeProcess Lib "kernel32" _
(ByVal hProcess As Long, lpExitCode As Long) As Long
'Open the data base
TaskID = Shell("cmd /c " & Chr(34) & MDB_Address & Chr(34), vbHide)
ACCESS_TYPE = &H400
hProc = OpenProcess(ACCESS_TYPE, False, TaskID)
'Some code that tests if it has opened happens here
...
Set ObjAccess = GetObject("C:\example.mdb")
' Import tables here
ObjAccess.Quit
Call CloseAccess
Sub CloseAccess()
Dim test As Long
'Test if the database has closed
Do Until lExitCode <> 259 And test <> 0
test = GetExitCodeProcess(hProc, lExitCode)
DoEvents
Loop
End Sub
Related
I have a master_macro that calls 2 other macros: (sub)calc and (sub)transfer. The calc macro doesn't need any variables passed to it, but the other one does and when the master_macro calls the transfer macro, there are several passed variables that are being modified even though they are passed using the byval method. these declarations are nested in a for loop because they are workbook names.
dpath = ws.cells(2,9).value
fname = ws.cells(i,10).value
set wkbk = workbooks.open(dpath & fname & ".xlsx")
call calc
tempname = ws.cells(4,9).value
fpath = ws.cells(3,9).value
temppath = ws.cells(5,9).value
set wkbk2 = workbooks.open(temppath & tempname) 'the tempname should have the extension
call transfer(fname, dpath, fpath, tempname, wkbk, wkbk2)
Here is how I start the transfer subroutine.
sub transfer(byval fname, byval dpath, byval fpath, byval tempname, byval wkbk, byval wkbk2)
I have breakpoints at the call point to step through it, to see what the variables are. Somehow the fpath and the tempname varialbes get switched. I was under the impression that if use byval they can't be changed. I noticed the error when once the transfer ran and broke at the 'next i' in the loop, it didn't save the tempname in the fpath folder.
I am trying to automate a process which involves using VBA in Excel to open a file on a remote desktop through RDP. I have successfully managed to log into RDP but am now struggling to open the file consistently. I wrote some code relying on SendKeys that maybe works 10% of the time but am looking for something more robust.
Sub RunRDP()
Dim RetVal As Variant
Dim Target As String
Dim Sheet As Variant
'Log-in info
Target = "AAAA.com"
UserName = "BBBBBB\CCC"
Pwd = "DDDDD"
'Connect to Remote Desktop
RetVal = Shell("cmdkey /generic:""" & Target & """ /user:""" & UserName & """ /pass:""" & Pwd & """", 3)
RetVal = Shell("c:\Windows\System32\mstsc.exe /v:" & Target, 3)
'Press yes through cert errors
Do
If InStr(ActiveWinTitle, "Remote Desktop Connection") > 0 Then
Application.SendKeys "y", True
End If
Loop Until InStr(ActiveWinTitle, "AAAA") > 0
Application.Wait (Now + TimeValue("00:00:03"))
If InStr(ActiveWinTitle, "Remote Desktop Connection") > 0 Then
AppActivate "AAAAA.com - Remote Desktop Connection"
Else
AppActivate "AAAAA.com"
End If
Application.Wait (Now + TimeValue("00:00:07"))
The above code works as expected. ActiveWinTitle is a function to grab the current window's caption, see below:
Public Declare Function GetForegroundWindow Lib "user32" _
() As Long
Public Declare Function GetWindowText Lib "user32" _
Alias "GetWindowTextA" (ByVal HWnd As Long, _
ByVal lpString As String, ByVal cch As Long) As Long
Public Function ActiveWinTitle() As String
Dim WinText As String
Dim HWnd As Long
Dim L As Long
HWnd = GetForegroundWindow()
WinText = String(255, vbNullChar)
L = GetWindowText(HWnd, WinText, 255)
ActiveWinTitle = Left(WinText, InStr(1, WinText, vbNullChar) - 1)
End Function
The below code is what I've tried to make work for opening the file. Its explanation in English follows:
Application.SendKeys "RE", True
Application.SendKeys "~", True
Application.Wait (Now + TimeValue("00:00:01"))
Application.SendKeys "{F4}", True
Application.Wait (Now + TimeValue("00:00:01"))
Application.SendKeys "{BS}{BS}{BS}{BS}{BS}{BS}{BS}{BS}{BS}{BS}{BS}F:\[**FILEPATH HERE**]~", True
Type RE on the desktop to highlight the Recycle Bin
Press Enter to open the Recycle Bin (to get to a file explorer window)
Wait one second
Press F4 to move cursor to address bar
Wait one second
Delete "Recycle Bin" from the address bar, write in the correct filepath, and press Enter
Obviously this is extremely unreliable and is the reason I'm looking for something better.
This code is something I'm using for work and am looking to share with my colleagues - I am not able to download any programs to use instead of VBA because of this.
I have looked at these questions without much avail:
Script to Open a batch file on a remote computer
I am not familiar with WMI and am not sure if I would have to completely replace using RDP. I tried looking at the documentation for it and it's quite above my head.
Run a batch file on a remote desktop from VBA
This is an earlier thread from the same user. It has some dead links that I was unable to follow.
I've looked at a lot of threads that had the same unanswered question as mine. It may be a futile effort, but I'd like to know definitively if this is manageable or not. [EDIT: Some of the unanswered forum posts I've found in my research below]
https://www.office-forums.com/threads/vba-remote-desktop-connection-mstscax.2170171/
https://www.tek-tips.com/viewthread.cfm?qid=1582592
https://www.ozgrid.com/forum/forum/help-forums/excel-vba-macros/1210417-controlling-remote-desktop-from-vba
Thanks in advance for all of your help.
Would a non-programming approach help you, too?
On the remote computer, create a scheduled task which launches when someone connects to the user session.
...and simply run anything from there.
Of course, maybe you still want to reach only for advanced techniques, but sometimes they can be easily avoided only by using existing tools.
When automating other MS-Office applications with excel, I frequently get ok-only prompts saying that Microsoft Excel is waiting for another application to complete an OLE action.
This only happens when automating lengthy tasks.
How can I deal with this in an appropriate fashion?
Two recent examples (I recon the code is less important):
creating an accdb-Database from Excel with an Access.Application and populating it by running rather complex SQL-queries on large amount of data.
Public Function createDB(pathDB As String, pathSQL As String) As String
Dim dbs As DAO.Database
Dim sql As String
Dim statement As Variant, file As Variant
Dim sErr As String, iErr As Integer
With New Access.Application
With .DBEngine.CreateDatabase(pathDB, dbLangGeneral)
For Each file In Split(pathSQL, ";")
sql = fetchSQL(file)
For Each statement In Split(sql, ";" & vbNewLine)
If Len(statement) < 5 Then GoTo skpStatement
Debug.Print statement
On Error Resume Next
.Execute statement, dbFailOnError
With Err
If .Number <> 0 Then
iErr = iErr + 1
sErr = sErr & vbCrLf & "Error " & .Number & " | " & Replace(.Description, vbCrLf, vbNullString)
.Clear
End If
End With
On Error GoTo 0
skpStatement:
Next statement
Next file
End With
.Quit acQuitSaveAll
End With
dTime = Now() - starttime
' Returnwert
If sErr = vbNullString Then sErr = "Keine Fehler"
createDB = "Zeit: " & Now & " | Dauer: " & Format(dTime, "hh:mm:ss") & " | Anzahl Fehler: " & iErr & vbCrLf & sErr
' Log
With ThisWorkbook
'...
.Saved = True
.Save
End With
End Function
create mail merges from Excel in a Word.Application, using existing and rather large .docm-templates and dynamic SQL-queries that returns the receipents
Set rst = GetRecordset(ThisWorkbook.Sheets("Parameter").Range("A1:S100"))
With New Word.Application
.Visible = False
While Not rst.EOF
If rst!Verarbeiten And Not IsNull(rst!Verarbeiten) Then
Debug.Print rst!Sql
.Documents.Open rst!inpath & Application.PathSeparator & rst!infile
stroutfile = fCheckPath(rst!outpath, True) & Application.PathSeparator & rst!outfile
.Run "quelle_aendern", rst!DataSource, rst!Sql
.Run MacroName:="TemplateProject.AutoExec.SeriendruckInDokument"
Application.DisplayAlerts = False
.ActiveDocument.ExportAsFixedFormat _
OutputFileName:=stroutfile _
, ExportFormat:=wdExportFormatPDF _
, OpenAfterExport:=False _
, OptimizeFor:=wdExportOptimizeForPrint _
, Range:=wdExportAllDocument _
, From:=1, To:=1 _
, Item:=wdExportDocumentContent _
, IncludeDocProps:=False _
, KeepIRM:=True _
, CreateBookmarks:=wdExportCreateNoBookmarks _
, DocStructureTags:=False _
, BitmapMissingFonts:=True _
, UseISO19005_1:=False
Application.DisplayAlerts = True
For Each doc In .Documents
With doc
.Saved = True
.Close SaveChanges:=wdDoNotSaveChanges
End With
Next doc
End If
rst.MoveNext
Wend
.Quit
End With
notes:
When run on a smaller scale (for example, when querying less records or using less complex templates), both codes do run smoothly.
In both cases, when I OK through all the reappearing prompts, the code will eventually finish with the desired results.
Therefore, I guess I'm not encountering an error (also it doesn't trigger the error handlers), but rather something like a timeout.
As suggested on other sources, I do wrap my code into Application.DisplayAlerts = False. This, however, seems like a horrible idea, since there might actually be cases where I do need to be alerted.
I'll add the code that #Tehscript linked to in the comments.
You can solve this by using the COM API to remove VBA's message filter.
This will prevent COM from telling VBA to displaying a message box when it
thinks the process you're calling has blocked. Note that if the process
really has blocked for some reason this will prevent you from receiving any
notification of that. [source]
I think this is the code I used back in 2006 for the same problem (it worked).
Private Declare Function _
CoRegisterMessageFilter Lib "OLE32.DLL" _
(ByVal lFilterIn As Long, _
ByRef lPreviousFilter) As Long
Sub KillMessageFilter()
'''Original script Rob Bovey
'''https://groups.google.com/forum/?hl=en#!msg/microsoft.public.excel.programming/ct8NRT-o7rs/jawi42S8Ci0J
'''http://www.appspro.com/
Dim lMsgFilter As Long
''' Remove the message filter before calling Reflections.
CoRegisterMessageFilter 0&, lMsgFilter
''' Call your code here....
''' Restore the message filter after calling Reflections.
CoRegisterMessageFilter lMsgFilter, lMsgFilter
End Sub
I tried the COM API code as well, which works. But it is only useful in so far as you don't see the error - the 30-sec delay every time the error is triggered still happens which makes this unworkable for me.
The better change I have made is to turn off "Real time presence in Microsoft Office" in Drive File Stream (the google product). This has (so far!) resolved the issue for me. I'm guessing there is some sort of clash between this and another excel addin.
I am trying to run an ssh command, and capture the output to a variable in VBA (using OpenSSH). I can get this to work fine in the command line:
ssh user#ip python C:\Temp\Remote.py
The results are a list of values returned to the command line window. I would like to read this into a VBA variable.
I found this, this and this. The first two do not seem to send or recieve the command correctly, as the code gets hung up on oShell.Exec(...). The shell looks like it is executing, but it just gets hung. If I close the hanging command window, the results in VBA are blank.
Dim sCmd As String
sCmd = "ssh user#ip python C:\Temp\Remote.py"
Dim oShell As Object
Set oShell = CreateObject("WScript.Shell")
Dim oExec As Object
Dim oOutput As Object
Set oExec = oShell.Exec(sCmd)
Set oOutput = oExec.StdOut
Dim s As String
Dim sLine As String
While Not oOutput.AtEndOfStream
sLine = oOutput.ReadLine
If sLine <> "" Then s = s & sLine & vbCrLf
Wend
The third seems to partially work (Using 'retVal = Shell("ssh user#ip python C:\Temp\Remote.py", vbNormalFocus)' ), but I cannot get the value out. I can see the command window open, the values come back, but I get some integer back in retVal.
Any help?
The WScript.Shell object's .Exec() method is the method that is designed to (a) run a console command / application, and (b) capture its output via properties of the WshScriptExec instance it returns.
At this point it is a mystery as to why your ssh-based command doesn't work - your code generally does work with console commands.
That said, there is another reason not to use .Exec(), when run from a GUI application, such as a VBA-hosting application, the console window is invariably made visible during execution, which can be visually disruptive.
The following alternatives (optionally) allow running a command hidden (without a visible window), but note that neither directly supports capturing output from the invoked command:
VBA's own Shell() function, which, however, invariably executes asynchronously, so it's cumbersome to determine when the command finishes.
The WScriptShell object's .Run() method, which optionally allows waiting for the command to finish.
To get both invisible execution and output capturing, you can:
combine the WScriptShell object's .Run() method
with sending the command's output to a temporary file,
and, upon command completion, read the temporary file into memory.
Dim cmd as String
Dim exitCode As Integer
Dim tempFile As String
Dim capturedOutput As String
' Construct the name of a temporary file to capture the command's stdout output in.
tempFile = Environ$("TEMP") & "\" & CreateObject("Scripting.FileSystemObject").GetTempName()
' Define the command to invoke.
cmd = "ssh user#ip python C:\Temp\Remote.py"
' Use .Run() to invoke the command.
' - In order to use output redirection, the command must be prefixed with 'cmd /c '.
' - Setting the last argument to `True` makes the invocation synchronous.
' - Replace vbNormalFocus with vbHidden to run the command *invisibly*.
exitCode = CreateObject("WScript.Shell").Run("cmd /c " & cmd & " >" & tempFile, vbNormalFocus, True)
If exitCode = 0 Then ' Command succeeded.
' Read the output file, then delete the temporary file.
capturedOutput = CreateObject("Scripting.FileSystemObject").OpenTextFile(tempFile).ReadAll()
CreateObject("Scripting.FileSystemObject").DeleteFile(tempFile)
' Display the output.
MsgBox "Captured Output:" & vbNewLine & capturedOutput
Else ' Command indicated failure.
MsgBox "An unexpected error occurred.", vbExclamation
End If
Here's an alternative based on Shell() - inspired by this implementation.
As you can see, making Shell() synchronous requires a lot more effort and requires use of the Windows API:
Private Declare Function OpenProcess Lib "kernel32.dll" (ByVal dwDesiredAccessas As Long, ByVal bInheritHandle As Long, ByVal dwProcId As Long) As Long
Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32.dll" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function GetExitCodeProcess Lib "kernel32.dll" (ByVal hProcess As Long, ByRef lpExitCodeOut As Long) As Integer
Sub Main()
Dim cmd As String
Dim taskId As Integer
Dim exitCode As Integer
Dim tempFile As String
Dim capturedOutput As String
' Construct the name of a temporary file to capture the command's stdout output in.
tempFile = Environ$("TEMP") & "\" & CreateObject("Scripting.FileSystemObject").GetTempName()
' Define the command to run.
cmd = "ssh user#ip python C:\Temp\Remote.py"
' Use the SyncShell() helper function defined below to invoke the command
' synchronously and to obtain its exit code.
' - In order to use output redirection, the command must be prefixed with 'cmd /c '.
' - Add a 3rd argument with a timeout value in seconds if you don't want to wait
' indefinitely for the process to complete.
' - Replace vbNormalFocus with vbHidden to run the command *invisibly*.
exitCode = SyncShell("cmd /c " & cmd & " >" & tempFile, vbNormalFocus)
If exitCode = 0 Then ' Command succeeded.
' Read the output file and delete the temporary file.
capturedOutput = CreateObject("Scripting.FileSystemObject").OpenTextFile(tempFile).ReadAll()
CreateObject("Scripting.FileSystemObject").DeleteFile (tempFile)
' Display the output.
MsgBox "Captured Output:" & vbNewLine & capturedOutput
Else ' Command indicated failure.
MsgBox "An unexpected error occurred.", vbExclamation
End If
End Sub
' Helper function
Private Function SyncShell(ByVal cmd As String, Optional ByVal windowStyle As VbAppWinStyle = vbMinimizedFocus, Optional ByVal timeoutInSecs As Double = -1) As Long
Dim pid As Long ' PID (Process ID) as returned by Shell().
Dim h As Long ' Process handle
Dim sts As Long ' WinAPI return value
Dim timeoutMs As Long ' WINAPI timeout value
Dim exitCode As Long
' Invoke the command (invariably asynchronously) and store the PID returned.
' Note that the invocation may fail.
pid = Shell(cmd, windowStyle)
' Translate the PIP into a process *handle* with the SYNCHRONIZE and PROCESS_QUERY_LIMITED_INFORMATION access rights,
' so we can wait for the process to terminate and query its exit code.
h = OpenProcess(&H100000 Or &H1000, 0, pid) ' &H100000 == SYNCHRONIZE, &H1000 == PROCESS_QUERY_LIMITED_INFORMATION
If h = 0 Then Err.Raise vbObjectError + 1024, , "Failed to obtain process handle for process with ID " & pid & "."
' Now wait for the process to terminate.
If timeoutInSecs = -1 Then
timeoutMs = &HFFFF ' INFINITE
Else
timeoutMs = timeoutInSecs * 1000
End If
sts = WaitForSingleObject(h, timeoutMs)
If sts <> 0 Then Err.Raise vbObjectError + 1025, , "Waiting for process with ID " & pid & " to terminate timed out, or an unexpected error occurred."
' Obtain the process's exit code.
sts = GetExitCodeProcess(h, exitCode) ' Return value is a BOOL: 1 for true, 0 for false
If sts <> 1 Then Err.Raise vbObjectError + 1026, , "Failed to obtain exit code for process ID " & pid & "."
CloseHandle h
' Return the exit code.
SyncShell = exitCode
End Function
I think you should try using the "command spec" at the start of the command. On your machine this can be obtained in VBA with VBA.Environ$("comspec") which on my my machine returns C:\WINDOWS\system32\cmd.exe
So try
sCmd = VBA.Environ$("comspec") & " /C " & " ssh user#ip python C:\Temp\Remote.py"
The command spec needs switches in some instances cmd switches
I do not have Python installed so cannot test.
I have a folder with 30 or so .gz zipped files and 1 .zip files. I can ue code to utilise Windows Explorer to unzip the 1 .zip file, but unfortunately, Windows explorer does not unzip .gz files. I have created code which utilises Winzip to open all these files, but unfortunately this opens up the path folder, every time it unzips, I end up with 30+ open folders, which I then close, one by one with further code - unnecessary. A process that takes near 10 minutes.
Scouring the net, I've found and adapted a Ron De Bruin code that utilises '7-zip' software , open source and freely available online, to unzip without opening up a new folder each time. It unzips all files effortlessly in about a minute, far better. The code is below (mainly comments so not as long as it first looks!). My only problem is that sometimes this unzips files, and sometimes this runs without unzipping any files. When it runs perfectly, it toggles the 'GetExitCodePorcess hProcess, ExitCode' line longer, there I'm assuming it is processes to get an ExitCode which allows it to unzip the file. When it isn't working, it only toggles once or twice and moves onto the next stage, therefore, I assume that it generated the wrong exit code.
Is the problem the PtrSafe Function? Or is it in my ShellStr, or anywhere else? Please help, as I want to avoid using the Winzip method. If anyone has any other alternatives, please suggest!
#If VBA7 Then
Private Declare PtrSafe Function OpenProcess Lib "kernel32" _
(ByVal dwDesiredAccess As Long, _
ByVal bInheritHandle As Long, _
ByVal dwProcessId As Long) As Long
Private Declare PtrSafe Function GetExitCodeProcess Lib "kernel32" _
(ByVal hProcess As Long, _
lpExitCode As Long) As Long
#Else
Private Declare Function OpenProcess Lib "kernel32" _
(ByVal dwDesiredAccess As Long, _
ByVal bInheritHandle As Long, _
ByVal dwProcessId As Long) As Long
Private Declare Function GetExitCodeProcess Lib "kernel32" _
(ByVal hProcess As Long, _
lpExitCode As Long) As Long
#End If
Public Const PROCESS_QUERY_INFORMATION = &H400
Public Const STILL_ACTIVE = &H103
Public Sub ShellAndWait(ByVal PathName As String, Optional WindowState)
Dim hProg As Long
Dim hProcess As Long, ExitCode As Long
'fill in the missing parameter and execute the program
If IsMissing(WindowState) Then WindowState = 1
hProg = Shell(PathName, WindowState)
'hProg is a process ID under Win32. To get the process handle:
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, hProg)
Do
'populate Exitcode variable
GetExitCodeProcess hProcess, ExitCode
DoEvents
Loop While ExitCode = STILL_ACTIVE
End Sub
'With this example you unzip a fixed zip file: FileNameZip = "C:\Users\Ron\Test.zip"
'Note this file must exist, this is the only thing that you must change before you test it
'The zip file will be unzipped in a new folder in: Application.DefaultFilePath
'Normal if you have not change it this will be your Documents folder
'The name of the folder that the code create in this folder is the Date/Time
'You can change this folder to this if you want to use a fixed folder:
'NameUnZipFolder = "C:\Users\Ron\TestFolder\"
'Read the comments in the code about the commands/Switches in the ShellStr
Public Sub B_UnZip_Zip_File_Fixed()
Dim PathZipProgram As String, FolderPath As String
Dim UnzipFile As Variant, ShellStr As String
FolderPath = _
ThisWorkbook.Path
If Right(FolderPath, 1) <> "\" Then
FolderPath = FolderPath & "\"
End If
'Path of the Zip program
PathZipProgram = "C:\program files\7-Zip\"
If Right(PathZipProgram, 1) <> "\" Then
PathZipProgram = PathZipProgram & "\"
End If
'Check if this is the path where 7z is installed.
If Dir(PathZipProgram & "7z.exe") = "" Then
MsgBox "Please find your copy of 7z.exe and try again"
Exit Sub
End If
UnzipFile = _
Dir(FolderPath & "*.gz")
While UnzipFile <> _
""
If InStr(1, UnzipFile, ".gz") > _
0 Then
ShellStr = PathZipProgram & "7z.exe e -aoa -r" _
& " " & Chr(34) & UnzipFile & Chr(34) _
& " -o" & Chr(34) & FolderPath & Chr(34) & " " & "*.*"
ShellAndWait ShellStr, vbHide
End If
UnzipFile = _
Dir
Wend
'Create path and name of the normal folder to unzip the files in
'In this example we use: Application.DefaultFilePath
'Normal if you have not change it this will be your Documents folder
'The name of the folder that the code create in this folder is the Date/Time
'NameUnZipFolder = Application.DefaultFilePath & "\" & Format(Now, "yyyy-mm-dd h-mm-ss")
'You can also use a fixed path like
'NameUnZipFolder = "C:\Users\Ron\TestFolder\"
'Name of the zip file that you want to unzip (.zip or .7z files)
'FileNameZip = "C:\Users\Ron\Test.zip"
'There are a few commands/Switches that you can change in the ShellStr
'We use x command now to keep the folder stucture, replace it with e if you want only the files
'-aoa Overwrite All existing files without prompt.
'-aos Skip extracting of existing files.
'-aou aUto rename extracting file (for example, name.txt will be renamed to name_1.txt).
'-aot auto rename existing file (for example, name.txt will be renamed to name_1.txt).
'Use -r if you also want to unzip the subfolders from the zip file
'You can add -ppassword if you want to unzip a zip file with password (only .7z files)
'Change "*.*" to for example "*.txt" if you only want to unzip the txt files
'Use "*.xl*" for all Excel files: xls, xlsx, xlsm, xlsb
'MsgBox "Look in " & NameUnZipFolder & " for extracted files"
End Sub
No, the exit code tells you the result of the external process that you spawned. For Windows 0 indicates success, non-zero indicates failure (or something else that meant the process wasn't successful)
So basically for some of .gz files 7zip can't complete successfully. You as the coder need to deal with this likely eventuality.
So your best bet is to print/log the 7zip command that it ran ShellStr and run that yourself manually in a command prompt/dos window to see the reason why.