File extension validation - vba

This searches for the end of a file name removes it's current file type of .docm and converts it to a .docx. Works great.
ActiveDocument.SaveAs2 Left(ActiveDocument.Name, InStrRev(ActiveDocument.Name, ".") - 1), WdSaveFormat.wdFormatXMLDocument
However, I noticed a little bug. If there is a . in the file name, it finds that first and obviously creates a file that is incorrect.
For example:
TestFileV1.2AlexTest.docm
Becomes the file
TestFileV.2AlextTest Where the new file type is a .2ALEXTEST file.
Kind of a funny error, but still a bug none the less.
Best course of action for validation?
Thanks!

Try the VBA.Strings.Split() function, which splits a string into an array.
Split the File name on '.' and the last element in the array will be your extension:
Public Function GetExtension(FileName As String) As String
'Returns a file's extension
' This does not go to the filesystem and get the file:
' The function parses out the string after the last '.'
' Useful for situations where a file does not yet exist
' Nigel Heffernan Excellerando.Blogspot.com
' **** THIS CODE IS IN THE PUBLIC DOMAIN ****
'Print GetExtension("C:\Temp\data.txt1.docx")
'Returns docx
Dim arrayX() As String
Dim iLast As Integer
arrayX = Split(FileName, ".")
iLast = UBound(arrayX)
GetExtension = arrayX(iLast)
Erase arrayX
End Function
If you don't care about readability, the quick-and-dirty answer is:
strExt = Split(strFile, ".")(UBound(Split(strFile, ".")))
However... I think you're looking for something more sophisticated than a string parser to extract the file extension.
Are you actually looking to validate the file extension?
I'm not coding up a registry lookup for the ShellExt application command to open your file, but I had a closely-related issue to yours a year or two ago, when I needed to populate a file dialog's filter list with a list of arbitrary file extensions.
It doesn't 'validate' as such, but unknown extensions will return a string containing 'unknown file type', and you can test for that:
VBA and the Registry: Returning a File Type from a File Extension
Public Function GetExtensionType(strExt As String) As String
' Return a file extension type descriptor, if the OS knows it
' Parses out the string after the last "." and reads the registry
' GetExtensionType("txt") Returns 'Text Document'
' GetExtensionType("SystemORA.user.config") 'XML Configuration File'
' GetExtensionType("Phishy.vbs") 'VBScript Script File'
' Nigel Heffernan Excellerando.Blogspot.com
' **** THIS CODE IS IN THE PUBLIC DOMAIN ****
On Error GoTo ErrSub
Dim strType As String
Dim strTyp1 As String
Dim strTyp2 As String
strExt = Trim(strExt)
' Set a default return: if an error is raised, return this value
GetExtensionType = Trim(strExt & " (unknown file type)")
strExt = Split(strExt, ".")(UBound(Split(strExt, "."))) '
If strExt = "" Then
Exit Function
End If
With CreateObject("WScript.Shell")
' This will go to error if there's no key for strExt in HKCR
strTyp1 = .RegRead("HKCR." & strExt & "\")
If strTyp1 = "" Then
strType = strExt & " File"
Else
' This value isn't very readable, eg: Access.ACCDEFile.14
' But we can use it to retrieve a descriptive string:
strTyp2 = .RegRead("HKCR\" & strTyp1 & "\")
If strTyp2 = "" Then
' So we didn't get a descriptive string:
' Parse some readability out of strType1:
strType = strTyp1
strType = Replace(strType, "File", " File")
strType = Replace(strType, ".", " ")
Else
strType = strTyp2
End If
End If
End With
If strType <> "" Then
GetExtensionType = strType
End If
ExitSub:
Exit Function
ErrSub:
Resume ExitSub
End Function
I made it error-tolerant but I didn't bother idiot-proofing it because someone, somewhere, is building a better idiot; and it's entirely possible that the user was actually right insofar as there really are files called that, and my system didn't have a registry entry for the file type in question.
There is an obvious source of errors in the code: GetExtensionType("docx") will give you 'Microsoft Word Document' on an English-Language workstation. If your user base are working with other languages and locales, they will see the descriptive name 'Microsoft Word Document' in their chosen language; and any validation logic you've coded up will fail to match that string (unless, of course, your string literals are internationalised in a conditional compiler block).
So any validation against a predefined application name or file type needs to be at the language-independent layer of the registry, using 'strTyp1' from the root instead of the locale-dependent strings passed into 'strTyp2'.

Use the FileSystemObject from the Scripting Runtime - it has a .GetBaseName() method to extract the basename from a file path:
'Early bound (reference to Microsoft Scripting Runtime):
Dim fso As New FileSystemObject
ActiveDocument.SaveAs2 fso.GetBaseName(ActiveDocument.Name), WdSaveFormat.wdFormatXMLDocument
'Late bound:
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
ActiveDocument.SaveAs2 fso.GetBaseName(ActiveDocument.Name), WdSaveFormat.wdFormatXMLDocument
You can also retrieve the extension with the .GetExtensionName() method, the path with .GetParentFolderName(), and the drive letter with GetDriveName() (which also works with UNC paths).
If you need to find the registered name of the extension in the current Windows install, you can either use the registry method #Nile answered with or an API call to AssocQueryStringA:
Const ASSOCSTR_FRIENDLYDOCNAME = 3
Private Declare Function AssocQueryString Lib "shlwapi.dll" _
Alias "AssocQueryStringA" ( _
ByRef Flags As Long, _
ByVal str As Long, _
ByVal pszAssoc As String, _
ByVal pszExtra As String, _
ByVal pszOut As String, _
ByRef pcchOut As Long) As Long
Sub Main()
Dim buffer As String
buffer = String$(255, " ")
Dim hresult As Long
hresult = AssocQueryString(0, ASSOCSTR_FRIENDLYDOCNAME, ".docm", _
vbNullString, buffer, 255)
If hresult = 0 Then
'Should be something like "Microsoft Word Macro-Enabled Document"
Debug.Print Trim$(buffer)
End If
End Sub
Note that you can also retrieve addition information about the associated file type by passing different values for the str parameter. See the ASSOCSTR enumeration.

Related

Saving files from OLE Objects (Access) to disc

I have an MS SQL Database from a customer with an Access Application.
This application stores files within this MS SQL database.
I tried to just get the bytes from the database and just save them to the disk.
With some of the files this works and with some it does not.
(images don't work, zips work, wordfiles are to open but word has to recover them)
I found out that Access saves the files within an OLE Object.
So i need to get out the Original Files from the OLE Object!
It seems that this is quite difficult.
I tried to find a .NET library which can seperate the files from the OLE Object.. found nothing...
Now i am trying to get the files out with Access...
It seems that i neet a getChunk Function to do that...
Found this page with a WriteBlob Code... it is said that it would to what i need..
https://support.microsoft.com/en-us/help/210486/acc2000-reading--storing--and-writing-binary-large-objects-blobs
Now i can write Files to the Harddisc... but the files are still not able to open!
Something's wrong here...
My complete VBA Code is this:
Option Compare Database
Const BlockSize = 32768
Sub xxx()
Dim id As Integer
Debug.Print "****************************************************"
Debug.Print "****************************************************"
Debug.Print "****************************************************"
Debug.Print "****************************************************"
Dim unnoetig As Variant
Dim dok() As Byte
Dim sql As String
sql = "select top 1 idCaseDetail, idCase, Dokument from dbo_law_tbl_CaseHistory where idCaseDetail = ""1"""
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset(sql)
If Not rst.EOF Then
Do While Not rst.EOF
Debug.Print "idcasehistory: " & rst.Fields(0)
Debug.Print "idcase: " & rst.Fields(1)
If Not IsNull(rst.Fields(2).Value) Then
dok = rst.Fields(2)
unnoetig = WriteBLOB(rst, "Dokument", "c:\temp\ole.doc")
End If
rst.MoveNext
Loop
End If
End Sub
'**************************************************************
' FUNCTION: WriteBLOB()
'
' PURPOSE:
' Writes BLOB information stored in the specified table and field
' to the specified disk file.
'
' PREREQUISITES:
' The specified table with the OLE object field containing the
' binary data must be opened in Visual Basic code and the correct
' record navigated to prior to calling the WriteBLOB() function.
'
' ARGUMENTS:
' T - The table object containing the binary information.
' sField - The OLE object field in table T containing the
' binary information to write.
' Destination - The path and filename to write the binary
' information to.
'
' RETURN:
' The number of bytes written to the destination file.
'**************************************************************
Function WriteBLOB(T As DAO.Recordset, sField As String, _
Destination As String)
Dim NumBlocks As Integer, DestFile As Integer, i As Integer
Dim FileLength As Long, LeftOver As Long
Dim FileData As String
Dim RetVal As Variant
On Error GoTo Err_WriteBLOB
' Get the size of the field.
FileLength = T(sField).FieldSize()
If FileLength = 0 Then
WriteBLOB = 0
Exit Function
End If
' Calculate number of blocks to write and leftover bytes.
NumBlocks = FileLength \ BlockSize
LeftOver = FileLength Mod BlockSize
' Remove any existing destination file.
DestFile = FreeFile
Open Destination For Output As DestFile
Close DestFile
' Open the destination file.
Open Destination For Binary As DestFile
' SysCmd is used to manipulate the status bar meter.
RetVal = SysCmd(acSysCmdInitMeter, _
"Writing BLOB", FileLength / 1000)
' Write the leftover data to the output file.
FileData = T(sField).GetChunk(0, LeftOver)
Put DestFile, , FileData
' Update the status bar meter.
RetVal = SysCmd(acSysCmdUpdateMeter, LeftOver / 1000)
' Write the remaining blocks of data to the output file.
For i = 1 To NumBlocks
' Reads a chunk and writes it to output file.
FileData = T(sField).GetChunk((i - 1) * BlockSize _
+ LeftOver, BlockSize)
Put DestFile, , FileData
RetVal = SysCmd(acSysCmdUpdateMeter, _
((i - 1) * BlockSize + LeftOver) / 1000)
Next i
' Terminates function
RetVal = SysCmd(acSysCmdRemoveMeter)
Close DestFile
WriteBLOB = FileLength
Exit Function
Err_WriteBLOB:
WriteBLOB = -Err
Exit Function
End Function
Do you have any suggestions?
Important to say is:
It is an MS SQL Database... not an Access Database.. there are some tools which maybe could word with access-Databases.. but not mit ms sql
Is there a .NET way or an VBA way to save the files to disc?
An easy alternative to using DAO for saving OLE objects, is to use the ADODB.Stream object:
Public Sub SaveOLEObject(OleObjectField As Field, Filelocation As String)
Dim adoStream As Object 'ADODB.Stream
Set adoStream = CreateObject("ADODB.Stream")
adoStream.Type = 1 'adTypeBinary
adoStream.Open
adoStream.Write OleObjectField.Value
adoStream.SaveToFile Filelocation, adSaveCreateOverWrite
adoStream.Close
End Sub
Call it:
SaveOLEObject(rst.Fields("Dokument"), "c:\temp\ole.doc")
Note that, of course, your documents might just be corrupt, and that might explain the problem.
If your objects are stored in SQL Server, I'd prefer directly opening an ADO recordset containing the binary data from SQL server over creating a linked table and opening a DAO recordset from the linked table.
In Access, create a corresponding Access Form with all relevant fields. Use the VBA code provided in the link and you should be able to export some of the most common file types in an automated fashion. Good luck.
https://medium.com/#haggenso/export-ole-fields-in-microsoft-access-c67d535c958d

Excel VBA bug accessing HelpFile property from macro-disabled instance?

I think I've stumbled upon a bug in Excel - I'd really like to verify it with someone else though.
The bug occurs when reading the Workbook.VBProject.HelpFile property when the workbook has been opened with the opening application's .AutomationSecurity property set to ForceDisable. In that case this string property returns a (probably) malformed Unicode string, which VBA in turn displays with question marks. Running StrConv(..., vbUnicode) on it makes it readable again, but it sometimes looses the last character this way; this might indicate that the unicode string is indeed malformed or such, and that VBA therefore tries to convert it first and fails.
Steps to reproduce this behaviour:
Create a new Excel workbook
Go to it's VBA project (Alt-F11)
Add a new code module and add some code to it (like e.g. Dim a As Long)
Enter the project's properties (menu Tools... properties)
Enter "description" as Project description and "abc.hlp" as Help file name
Save the workbook as a .xlsb or .xlsm
Close the workbook
Create a new Excel workbook
Go to it's VBA project (Alt-F11)
Add a fresh new code module
Paste the code below in it
Adjust the path on the 1st line so it points to the file you created above
Run the Test routine
The code to use:
Const csFilePath As String = "<path to your test workbook>"
Sub TestSecurity(testType As String, secondExcel As Application, security As MsoAutomationSecurity)
Dim theWorkbook As Workbook
secondExcel.AutomationSecurity = security
Set theWorkbook = secondExcel.Workbooks.Open(csFilePath)
Call MsgBox(testType & " - helpfile: " & theWorkbook.VBProject.HelpFile)
Call MsgBox(testType & " - helpfile converted: " & StrConv(theWorkbook.VBProject.HelpFile, vbUnicode))
Call MsgBox(testType & " - description: " & theWorkbook.VBProject.Description)
Call theWorkbook.Close(False)
End Sub
Sub Test()
Dim secondExcel As Excel.Application
Set secondExcel = New Excel.Application
Dim oldSecurity As MsoAutomationSecurity
oldSecurity = secondExcel.AutomationSecurity
Call TestSecurity("enabled macros", secondExcel, msoAutomationSecurityLow)
Call TestSecurity("disabled macros", secondExcel, msoAutomationSecurityForceDisable)
secondExcel.AutomationSecurity = oldSecurity
Call secondExcel.Quit
Set secondExcel = Nothing
End Sub
Conclusion when working from Excel 2010:
.Description is always readable, no matter what (so it's not like all string properties behave this way)
xlsb and xlsm files result in an unreadable .HelpFile only when macros are disabled
xls files result in an unreadable .HelpFile in all cases (!)
It might be even weirder than that, since I swear I once even saw the questionmarks-version pop up in the VBE GUI when looking at such a project's properties, though I'm unable to reproduce that now.
I realize this is an edge case if ever there was one (except for the .xls treatment though), so it might just have been overlooked by Microsoft's QA department, but for my current project I have to get this working properly and consistently across Excel versions and workbook formats...
Could anyone else test this as well to verify my Excel installation isn't hosed? Preferably also with another Excel version, to see if that makes a difference?
Hopefully this won't get to be a tumbleweed like some of my other posts here :) Maybe "Tumbleweed generator" might be a nice badge to add...
UPDATE
I've expanded the list of properties to test just to see what else I could find, and of all the VBProject's properties (BuildFileName, Description, Filename, HelpContextID, HelpFile, Mode, Name, Protection and Type) only .HelpFile has this problem of being mangled when macros are off.
UPDATE 2
Porting the sample code to Word 2010 and running that exhibits exactly the same behaviour - the .HelpFile property is malformed when macros are disabled. Seems like the code responsible for this is Office-wide, probably in a shared VBA library module (as was to be expected TBH).
UPDATE 3
Just tested it on Excel 2007 and 2003, and both contain this bug as well. I haven't got an Excel XP installation to test it out on, but I can safely say that this issue already has a long history :)
I've messed with the underlying binary representation of the strings in question, and found out that the .HelpFile string property indeed returns a malformed string.
The BSTR representation (underwater binary representation for VB(A) strings) returned by the .HelpFile property lists the string size in the 4 bytes in front of the string, but the following content is filled with the ASCII representation and not the Unicode (UTF16) representation as VBA expects.
Parsing the content of the BSTR returned and deciding for ourselves which format is most likely used fixes this issue in some circumstances. Another issue is unfortunately at play here as well: it only works for even-length strings... Odd-length strings get their last character chopped off, their BSTR size is reported one short, and the ASCII representation just doesn't include the last character either... In that case, the string cannot be recovered fully.
The following code is the example code in the question augmented with this fix. The same usage instructions apply to it as for the original sample code. The RecoverString function performs the needed magic to, well, recover the string ;) DumpMem returns a 50-byte memory dump of the string you pass to it; use this one to see how the memory is layed out exactly for the passed-in string.
Const csFilePath As String = "<path to your test workbook>"
Private Declare Sub CopyMemoryByte Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Byte, ByVal Source As Long, ByVal Length As Integer)
Private Declare Sub CopyMemoryWord Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Integer, ByVal Source As Long, ByVal Length As Integer)
Private Declare Sub CopyMemoryDWord Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Long, ByVal Source As Long, ByVal Length As Integer)
Function DumpMem(text As String) As String
Dim textAddress As LongPtr
textAddress = StrPtr(text)
Dim dump As String
Dim offset As Long
For offset = -4 To 50
Dim nextByte As Byte
Call CopyMemoryByte(nextByte, textAddress + offset, 1)
dump = dump & Right("00" & Hex(nextByte), 2) & " "
Next
DumpMem = dump
End Function
Function RecoverString(text As String) As String
Dim textAddress As LongPtr
textAddress = StrPtr(text)
If textAddress <> 0 Then
Dim textSize As Long
Call CopyMemoryDWord(textSize, textAddress - 4, 4)
Dim recovered As String
Dim foundNulls As Boolean
foundNulls = False
Dim offset As Long
For offset = 0 To textSize - 1
Dim nextByte As Byte
Call CopyMemoryByte(nextByte, textAddress + offset, 1)
recovered = recovered & Chr(CLng(nextByte) + IIf(nextByte < 0, &H80, 0))
If nextByte = 0 Then
foundNulls = True
End If
Next
Dim isNotUnicode As Boolean
isNotUnicode = isNotUnicode Mod 2 = 1
If foundNulls And Not isNotUnicode Then
recovered = ""
For offset = 0 To textSize - 1 Step 2
Dim nextWord As Integer
Call CopyMemoryWord(nextWord, textAddress + offset, 2)
recovered = recovered & ChrW(CLng(nextWord) + IIf(nextWord < 0, &H8000, 0))
Next
End If
End If
RecoverString = recovered
End Function
Sub TestSecurity(testType As String, secondExcel As Application, security As MsoAutomationSecurity)
Dim theWorkbook As Workbook
secondExcel.AutomationSecurity = security
Set theWorkbook = secondExcel.Workbooks.Open(csFilePath)
Call MsgBox(testType & " - helpfile: " & theWorkbook.VBProject.HelpFile & " - " & RecoverString(theWorkbook.VBProject.HelpFile))
Call MsgBox(testType & " - description: " & theWorkbook.VBProject.Description & " - " & RecoverString(theWorkbook.VBProject.Description))
Call theWorkbook.Close(False)
End Sub
Sub Test()
Dim secondExcel As Excel.Application
Set secondExcel = New Excel.Application
Dim oldSecurity As MsoAutomationSecurity
oldSecurity = secondExcel.AutomationSecurity
Call TestSecurity("disabled macros", secondExcel, msoAutomationSecurityForceDisable)
Call TestSecurity("enabled macros", secondExcel, msoAutomationSecurityLow)
secondExcel.AutomationSecurity = oldSecurity
Call secondExcel.Quit
Set secondExcel = Nothing
End Sub

Get and edit a file name

I'm looking to retreive a txt file and then edit the file name (adding "converted" to the file name) and extension (from .r01 to .txt).
The purpose for this is so I can know if the txt file has been converted
Here's my code so far;
Dim infilename As Variant
infilename = Application.GetOpenFilename("Text & r01 Files (*.r01;*.txt),*.r01;*.txt", , "Open Neutral File", "OPEN")
InStrRev will allow you to find the last . and remove it and everything following from the string
FileNameWithoutExt = Left(Filename, InStrRev(Filename, ".") - 1)
An example with the workbooks FullName:
?activeworkbook.FullName
Z:\Individual Folders\Sean\transfers2.xlsx
?Left(activeworkbook.FullName, InStrRev(activeworkbook.FullName, ".") - 1)
Z:\Individual Folders\Sean\transfers2
You can wrap these in a function to make them easier to use. I've also added a function that will give the filename only instead of the one with the full path
Function FileNameOnly(fName)
'Changes "C:\Path\Filename.ext" to "Filename.ext"
FileNameOnly=mid(fName,instrrev(fName,"\")+1)
End Function
Function DelExt(fName)
'Changes "C:\Path\Filename.ext" to "C:\Path\Filename"
DelExt=left(fName,instrrev(fName,".")-1)
End Function
You can then use these in your program, with a line like NewFileName=DelExt(infilename) & "CONVERTED.txt"
I managed to get what I was looking for using part of Sean Cheshire's code.
Dim newFileName As Variant
newFileName = Left(inFileName, (InStrRev(inFileName, ".") - 1)) & "CONVERTED.txt"

ASP File Extension Split

I have the following code:
Public Sub Save(path)
Dim streamFile, fileItem, filePath, allowedExtensions
allowedExtensions = ".jpg, .gif, .png, .zip, .7z, .exe, .bmp, .pdf, .doc, .docx"
if Right(path, 1) <> "\" then path = path & "\" '"
if not uploadedYet then Upload
For Each fileItem In UploadedFiles.Items
Dim MyArray, extension
MyArray = Split(fileItem, ".")
extension = MyArray(UBound(MyArray)-1)
'' # var extension = UCase(right(fileItem.FileName,5,);
if(allowedExtensions.Contains(extension)) then
filePath = path & fileItem.FileName
Set streamFile = Server.CreateObject("ADODB.Stream")
streamFile.Type = adTypeBinary
streamFile.Open
StreamRequest.Position=fileItem.Start
StreamRequest.CopyTo streamFile, fileItem.Length
streamFile.SaveToFile filePath, adSaveCreateOverWrite
streamFile.close
Set streamFile = Nothing
fileItem.Path = filePath
end if
Next
End Sub
I cannot seem to get this line correct:
MyArray = Split(fileItem, ".")
The browser is telling me:
Microsoft VBScript runtime error '800a01b6'
Object doesn't support this property or method
/up/freeaspupload.asp, line 90
Everywhere I look up, it shows this is how you do it.
Anyone have any ideas what I am doing wrong or a way around this?
I just want to only allow certain extensions to be uploaded.
In VBScript primitive types has not built-in methods. So, allowedExtensions cannot have Contains method. I think this is why the error occurred. Line MyArray = Split(fileItem, ".") is correct and clear.
if(allowedExtensions.Contains(extension)) then
You could use InStr to search a word within another.
'For case insensitive search
If InStr(1, BeingSearched, SearchedFor, vbTextCompare) Then
'Contains
End If
Well Kul-Tigin has spotted another problem with your code that you would crash into once you got past the Split problem that this question is actually about. The reason your Split function fails I suspect is that it should look like this:
MyArray = Split(fileItem.FileName, ".")
Note that you should probably be passing the value of the FileName property of the fileItem object. It would seem that fileItem does not have a default property specified.

AutoUpdate VBA startup macro?

I'm building some Word 2003 macro that have to be put in the %APPDATA%\Microsoft\Word\Startup folder.
I can't change the location of this folder (to a network share). How can I auto update this macros ?
I have tried to create a bootstrapper macro, with an AutoExec sub that copy newer version from a file share to this folder. But as Word is locking the file, I get a Denied Exception.
Any idea ?
FYI, I wrote this code. The code is working fine for update templates in templates directory, but not in startup directory :
' Bootstrapper module
Option Explicit
Sub AutoExec()
Update
End Sub
Sub Update()
MirrorDirectory MyPath.MyAppTemplatesPath, MyPath.WordTemplatesPath
MirrorDirectory MyPath.MyAppStartupTemplatesPath, MyPath.WordTemplatesStartupPath
End Sub
' IOUtilities Module
Option Explicit
Dim fso As New Scripting.FileSystemObject
Public Sub MirrorDirectory(sourceDir As String, targetDir As String)
Dim result As FoundFiles
Dim s As Variant
sourceDir = RemoveTrailingBackslash(sourceDir)
targetDir = RemoveTrailingBackslash(targetDir)
With Application.FileSearch
.NewSearch
.FileType = MsoFileType.msoFileTypeAllFiles
.LookIn = sourceDir
.SearchSubFolders = True
.Execute
Set result = .FoundFiles
End With
For Each s In result
Dim relativePath As String
relativePath = Mid(s, Len(sourceDir) + 1)
Dim targetPath As String
targetPath = targetDir + relativePath
CopyIfNewer CStr(s), targetPath
Next s
End Sub
Public Function RemoveTrailingBackslash(s As String)
If Right(s, 1) = "\" Then
RemoveTrailingBackslash = Left(s, Len(s) - 1)
Else
RemoveTrailingBackslash = s
End If
End Function
Public Sub CopyIfNewer(source As String, target As String)
Dim shouldCopy As Boolean
shouldCopy = False
If Not fso.FileExists(target) Then
shouldCopy = True
ElseIf FileDateTime(source) > FileDateTime(target) Then
shouldCopy = True
End If
If (shouldCopy) Then
If Not fso.FolderExists(fso.GetParentFolderName(target)) Then fso.CreateFolder (fso.GetParentFolderName(target))
fso.CopyFile source, target, True
Debug.Print "File copied : " + source + " to " + target
Else
Debug.Print "File not copied : " + source + " to " + target
End If
End Sub
' MyPath module
Property Get WordTemplatesStartupPath()
WordTemplatesStartupPath = "Path To Application Data\Microsoft\Word\STARTUP"
End Property
Property Get WordTemplatesPath()
WordTemplatesPath = "Path To Application Data\Microsoft\Templates\Myapp\"
End Property
Property Get MyAppTemplatesPath()
MyAppTemplatesPath = "p:\MyShare\templates"
End Property
Property Get XRefStartupTemplatesPath()
MyAppStartupTemplatesPath = "p:\MyShare\startup"
End Property
[Edit] I explored another way
Another way I'm thinking about, is to pilot the organizer :
Sub Macro1()
'
' Macro1 Macro
' Macro recorded 10/7/2011 by beauge
'
Application.OrganizerCopy source:="P:\MyShare\Startup\myapp_bootstrapper.dot", _
Destination:= _
"PathToApplication Data\Microsoft\Word\STARTUP\myapp_bootstrapper.dot" _
, Name:="MyModule", Object:=wdOrganizerObjectProjectItems
End Sub
This is working, but has limitations :
either I have to hard-code modules to organize
or I have to change the option "Trust VBA project" to autodiscover items like this (which is not acceptable as it requires to lower the security of the station) :
the code of the project enumeration is this one :
Public Sub EnumProjectItem()
Dim sourceProject As Document
Dim targetProject As Document
Set sourceProject = Application.Documents.Open("P:\MyShare\Startup\myapp_bootstrapper.dot", , , , , , , , , wdOpenFormatTemplate)
Set targetProject = Application.Documents.Open("PathToApplication Data\Microsoft\Word\STARTUP\myapp_bootstrapper.dot", , , , , , , , , wdOpenFormatTemplate)
Dim vbc As VBcomponent
For Each vbc In sourceProject.VBProject.VBComponents 'crash here
Application.ActiveDocument.Range.InsertAfter (vbc.Name + " / " + vbc.Type)
Application.ActiveDocument.Paragraphs.Add
Next vbc
End Sub
[Edit 2] Another unsuccessful try :
I put, in my network share, a .dot with all the logic.
In my STARTUP folder, I put a simple .Dot file, that references the former one, with a single "Call MyApp.MySub".
This is actually working, but as the target template is not in a trusted location, a security warning is popped up each time word is launched (even if not related to the current application macro)
At least, I succeed partially using these steps :
Create a setup package. I use a NSIS script
the package detect any instance of Winword.exe and ask the user to retry when word is closed
extract from the registry the word's option path
deploy the files into the word's startup folder
add an uninstaller in the local user add/remove programs
I put the package in the remote share. I also added a .ini file containing the last version of the package (in the form "1.0")
In the macro itself, I have a version number ("0.9" for example).
At the startup (AutoExec macro), I compare the local version to the remote version
I use shell exec to fire the setup if a newer version is found.
The setup will wait for Word to close
A bit tricky, but it works on Word 2K3 and Word 2K10.