Overwrite file when copying into zip file using Microsoft Shell COM Library - excel

I'm using Microsoft Shell Controls And Automation COM library from VBA. I have a zip file and I am copying a file into the zip file. The problem is that the file already exists and it throws a dialog box which says "There is already a file with the same name in this location do you want to a) Copy And Replace or b) Don't copy". I would like to programmatically say Copy And Replace (overwrite!).
I think the equivalent on the .NET framework has a documentation page at http://msdn.microsoft.com/en-us/library/windows/desktop/ms723207%28v=vs.85%29.aspx
but I have tried a variety of these.
Also, I will accept removing the original item so that the addition succeeds with no problem; sadly I could not find a delete item method.
I am using Windows 8, 64 bit edition. I'm in Excel (14) VBA (7) and the library I'm using is at C:\Windows\SysWOW64\shell32.dll
Here is head of IDL of the type library I'm talking about
// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: shell32.dll
[
uuid(50A7E9B0-70EF-11D1-B75A-00A0C90564FE),
version(1.0),
helpstring("Microsoft Shell Controls And Automation")
]
library Shell32
{
// TLib : // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}

My preferred answer is the use the MoveHere method to move the old one out the way before adding. That way no dialog box is thrown. Thanks all.

Here's how you can delete a file from a zip using Shell:
Sub DeleteFromZip()
Dim fldr, zpath, itm, vrbs, vrb
zpath = "C:\_stuff\test\test.zip"
Set fldr = CreateObject("Shell.Application").Namespace(zpath)
For Each itm In fldr.items
If itm.Name = "IMG_0000322_2014.03.11.jpg" Then
itm.invokeverb "Delete"
Exit For
End If
Next itm
End Sub
This does raise a confirmation dialog though - see this link for how to avoid it:
FolderItem.InvokeVerb("Delete") without confirmation

Related

How to stop from deleting a file/workbook in MS excel when it is already open?

We have designed Microsoft Addins for MS Excel and Word 2019 written in VB.net.
There we have designed a tab, on clicking this tab, we open a Task Pane.
On loading this task pane, we execute a code to launch another Excel File/Word file.
So when I delete a file in MS Word that is already open it shows an exception The file 'Filename' already exists.
Given below is the code snippet I am using to delete an already existing open file named processFile
My.Computer.FileSystem.DeleteFile(processFile)
Now when I run the same code snippet in MS Excel it does not show this exception and deletes the file.
I am not able to understand this behavior.
Kindly suggest if anyone has understanding on it
Here is some more information about which environment I'm working in:
Operating System : Microsoft Windows 10 Pro
Code Editor : Visual Studio 2019
Technology : Vb.net(.Net Framework 4.8)
MS Office Version : 2019(32 bit) : Microsoft Windows 10 Pro
The IOException should be thrown if the file is in use. You need to close any editors first and then delete files.
Check if the file is open. I have a code below that may help.
As import sfile use the fullpath with filename. (eg. "C:\Windows\test.txt")
returns True when file it's open and false when it isn't.
Yu will need "Imports System.IO"
Public Function IsFileInUse(sFile As String) As Boolean
Try
If File.Exists(sFile) Then
Using f As New IO.FileStream(sFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None)
End Using
End If
Catch Ex As Exception
Return True
End Try
Return False
End Function
As per the information in the comment section, the file is opened in "protected mode" and you are saying that it is deleting while it is still open to the user.
So yes, if the file is engaged and deleted by some program, it should throw an exception and not delete. But if no exception is coming, i.e. some specific handling has been done by Windows/MS Excel so that file has been deleted while it is still kept on buffer and open. Its answer can be given by Microsoft only.
One solution, I can suggest is to close the "protected object" and then delete the file so that no file will be opened to the user and it will be safely deleted.
Snippet to open the file for Add-ins(VSTO):
Dim objProtectedMode As String objProtectedMode = Globals.Connect.Application.ProtectedViewWindows.Open(fileLocation, , False, False)
Snippet to delete the file safely:
If Not (objProtectedMode Is Nothing) Then
objProtectedMode.Close()
End If
My.Computer.FileSystem.DeleteFile(objProtectedMode)
Note: - Solution may vary for other add-ins. Code snippet provided for VSTO Add-ins.

Changing the location of a referenced library

I have a C# Library which I use in an Excel VBA project. I wish now wish to restructure my project and want to move the library to a different folder. However after removing the reference to the library and deleting all instances of the library from my computer, the References Available box for the project still shows the reference to the old library location even though it no longer exists on the computer and nothing I do seems to be able to remove that reference.
I don't know if this makes any difference, but the project is an Excel AddIn.
I had this problem back in 2019, and received a response to a question with the same title in April 2019. On that occasion the the issue was resolved by following the advise given. However this time the system stubbornly refuses to play ball.
The procedure that I was advised to follow in 2019 was:
Remove the reference
Save the File and close it
Delete the dll (in my case the .tbl) file from your computer. Do not save it anywhere.
ReOpen the file
Check if there is any reference still there. If not copy the dll to a new folder and then set a reference again. Save and close the file.
ReOpen to check if everything is OK
I was going to try code to remove the reference, but the following code did not find the reference
Sub delRef()
deleteReference ("FiskDLLlib")
End Sub
Sub deleteReference(s As String)
Dim oFs As Object, oReferences As Object, oReference As Object
Dim sFileName As String, sRefName As String, sRefFileName As String
Dim toBeDeletedRef As String
Set oReferences = Application.Workbooks("fiskAIWkBook.xlam").VBProject.References
For Each oReference In oReferences
sRefFileName = oReference.FullPath
sRefName = oReference.name
If sRefName = s Then
toBeDeletedRef = s
Exit For
End If
Next
If toBeDeletedRef <> "" Then
Debug.Print oReference.FullPath
Else
Debug.Print "No Reference found for " & s
End If
End Sub
Similarly the Watches panel didn't show the library.
I have subsequently discovered a registry key
\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib{B22F6C9D-53E0-4D1B-9596-56AA1EA4BDBA}\1.0\0\win32
the value of which was set to the location of the reference I am trying to remove. I changed this to a reference to the new location in which I want to store my dll (.tlb) file. This at least looked like it would allow me to create a new reference in the References box but on clicking OK I got a message saying "Error in loading DLL"
I resolved my problem in the end by removing all references to the library in the Registry with a registry Cleanup tool (ReImage) before copying the library to its new directory and then reregistering it with regASM

Do the DSOFile functions only apply to the non-binary Excel Document types?

Using Windows 10 (Build 1903 if that's relevant?) and 64bit Office 365 (probably relevant?) I've implemented a system that allows me to version control Excel VBA code.
I'm using the Workbook_BeforeSave method to check whether the current file is saved or not, and if it is saved, where it is saved to.
This works fine and will prompt the user as to whether they want to update the code contained within. I then thought that maybe I should in fact check if the code "needs" to be updated prior to prompting the user.
First off, I found the following question/solution: Using VBA to read the metadata or file properties of files in a SharePoint doc library
which I couldn't use without DSOFile.dll that I was able to install from here:
https://www.microsoft.com/en-us/download/details.aspx?id=8422
Here follows the code I have which doesn't work:
Private Function CheckTemplateIsNewerThanCurrentFile(ByVal templatePath As String) As Boolean
Dim templateName As String
Dim fso As New FileSystemObject
templateName = ActiveWorkbook.CustomDocumentProperties("TemplateName").Value
If fso.FileExists(templatePath & "\" & LocalTemplateName) Then
Dim objDSO As New DSOFile.OleDocumentProperties
objDSO.Open templatePath & "\" & LocalTemplateName, True, dsoOptionDefault
If Not objDSO.CustomProperties("LastCommitDate") = ActiveDocument.CustomDocumentProperties("LastCommitDate").Value Then
CheckTemplateIsNewerThanCurrentFile = False
Else
CheckTemplateIsNewerThanCurrentFile = True
TemplateLastCommitDate = objDSO.CustomProperties.Item("LastCommitDate")
End If
End If
End Function
And here (highlighted) is the error I receive trying to run the method above on an .xlsb file:
(FWIW: the reason for use of the .xlsb format is because we're working with 500K+ rows of data in the process we're carrying out. Yes, I know Excel is ABSOLUTELY NOT the tool for this but we're lumbered with it now)
I know I could have already tried changing the file format to .xlsm but because this file is version controlled that is a pain to do if the method is still likely to fail.
Thanks in advance,
Alex.

How to discover Oleobject ClassType of files?

I'm stuck how to determine files' Classtypes needed to use in code to embed these files into a Word document:
Selection.InlineShapes.AddOLEObject ClassType:="AcroExch.Document.11", _
FileName:="C:\Work\Dashbaord & ".pdf", LinkToFile:=False, _
DisplayAsIcon:=False
I need to embed csv, pdf, xlsx and txt files. How I can automatically loop all files in folders and automatically determine the ClassType of each?
In order to insert a file as an OLE Object the file type needs to have an available OLE Server installed on the machine, or it needs to be in a format that the Windows Packager mechanism can "wrap up" into an OLE type. Before you go this route you need to ensure that anyone who tries to work with such a document has corresponding OLE Server software installed on the machine on which the document is opened. Just because the machine that creates an embedded OLE object can do so doesn't mean another machine can work with the result, later on.
OLE Server software will be noted in the Registry. The Microsoft Office applications (Word, Excel, PowerPoint, etc.) are able to function as OLE Servers. In the Registry you'll find corresponding entries such as Word.Document and Excel.Workbook... or AcroExch.Document for PDF files if Microsoft Office and the Adobe Acrobat Reader are installed.
One way to figure out which ClassTypes to use would be to manually insert each file type and inspect the resulting Embed field code.
To look the ClassTypes up in the Registry, something like the following code sample can be used in Word. Word has the function System.PrivateProfileString that wraps up a Windows API call to the Registry. It can be used to retrieve and to write information. (This code does not loop the files in a directory as the question was about how to determine the ClassType. For the sake of simplicity a file extension is hard-coded.)
A file type that does not have an OLE Server won't have a . in the default value of the Registry key. A .txt file, for example, is listed as txtfile. You may have to watch out for some file types; for example on my installation a csv file is listed as Excel.CSV, which may not be what you want...
Sub RetrieveOLEInfo()
Dim fileExt As String
Dim regKey As String
Dim oleServer As String
fileExt = "docx"
regKey = "HKEY_Classes_Root\."
oleServer = System.PrivateProfileString("", regKey & fileExt, "")
'Debug.Print oleServer
If InStr(oleServer, ".") = 0 Then
Debug.Print "Insert as a Package"
Else
Debug.Print "Insert as: " & oleServer
End If
End Sub

Change .xla File with MSBuild

I'm trying to create a build script for my current project, which includes an Excel Add-in. The Add-in contains a VBProject with a file modGlobal with a variable version_Number. This number needs to be changed for every build. The exact steps:
Open XLA document with Excel.
Switch to VBEditor mode. (Alt+F11)
Open VBProject, entering a password.
Open modGlobal file.
Change variable's default value to the current date.
Close & save the project.
I'm at a loss for how to automate the process. The best I can come up with is an excel macro or Auto-IT script. I could also write a custom MSBuild task, but that might get... tricky. Does anyone else have any other suggestions?
An alternative way of handling versioning of an XLA file is to use a custom property in Document Properties. You can access and manipulate using COM as described here: http://support.microsoft.com/?kbid=224351.
Advantages of this are:
You can examine the version number without opening the XLA file
You don't need Excel on your build machine - only the DsoFile.dll component
Another alternative would be to store the version number (possibly other configuration data too) on a worksheet in the XLA file. The worksheet would not be visible to users of the XLA. One technique I have used in the past is to store the add-in as an XLS file in source control, then as part of the build process (e.g. in a Post-Build event) run the script below to convert it to an XLA in the output directory. This script could be easily extended to update a version number in a worksheet before saving. In my case I did this because my Excel Add-in used VSTO, and Visual Studio doesn't support XLA files directly.
'
' ConvertToXla.vbs
'
' VBScript to convert an Excel spreadsheet (.xls) into an Excel Add-In (.xla)
'
' The script takes two arguments:
'
' - the name of the input XLS file.
'
' - the name of the output XLA file.
'
Option Explicit
Dim nResult
On Error Resume Next
nResult = DoAction
If Err.Number <> 0 Then
Wscript.Echo Err.Description
Wscript.Quit 1
End If
Wscript.Quit nResult
Private Function DoAction()
Dim sInputFile, sOutputFile
Dim argNum, argCount: argCount = Wscript.Arguments.Count
If argCount < 2 Then
Err.Raise 1, "ConvertToXla.vbs", "Missing argument"
End If
sInputFile = WScript.Arguments(0)
sOutputFile = WScript.Arguments(1)
Dim xlApplication
Set xlApplication = WScript.CreateObject("Excel.Application")
On Error Resume Next
ConvertFileToXla xlApplication, sInputFile, sOutputFile
If Err.Number <> 0 Then
Dim nErrNumber
Dim sErrSource
Dim sErrDescription
nErrNumber = Err.Number
sErrSource = Err.Source
sErrDescription = Err.Description
xlApplication.Quit
Err.Raise nErrNumber, sErrSource, sErrDescription
Else
xlApplication.Quit
End If
End Function
Public Sub ConvertFileToXla(xlApplication, sInputFile, sOutputFile)
Dim xlAddIn
xlAddIn = 18 ' XlFileFormat.xlAddIn
Dim w
Set w = xlApplication.Workbooks.Open(sInputFile,,,,,,,,,True)
w.IsAddIn = True
w.SaveAs sOutputFile, xlAddIn
w.Close False
End Sub
I'm not 100% sure how to do exactly what you have requested. But guessing the goal you have in mind there are a few possibilities.
1) Make part (or all) of your Globals a separate text file that is distributed with the .XLA I would use this for external references such as the version of the rest of your app. Write this at build time and distribute, and read on the load of the XLA.
2) I'm guessing your writing the version of the main component (ie: the non XLA part) of your application. If this is tru why store this in your XLA? Why not have the main part of the app allow certain version of the XLA to work. Version 1.1 of the main app could accept calls from Version 7.1 - 8.9 of the XLA.
3) If you are just looking to update the XLA so it gets included in your version control system or similar (i'm guessing here) maybe just touch the file so it looks like it changed.
If it's the version of the rest of the app that you are controlling i'd just stick it in a text file and distribute that along with the XLA.
You can modify the code in the xla programmatically from within Excel. You will need a reference to the 'Microsoft Visual Basic for Applications Extensibility..' component.
The examples on Chip Pearson's excellent site should get you started.

Resources