VBA duplicate PDF file - vba

I really need help. I need a VBA function to make copies of a single PDF file. For example a file with the reference/name 1, I would need an amount x of copies lets say 1 to 10. In order to avoid coping and paste 9 times and renaming them manually I am sure there must be a function to do this job. I am very basic with VBA so any help would be much appreciated.
Many Thanks

First you will need to add a reference to Microsoft Scripting Runtime in the VBA editor. Then the following will work...
Public Sub Test()
CopyFile "C:\Users\randrews\Desktop\1.gif", "C:\Users\randrews\Desktop", 10
End Sub
Public Sub CopyFile(OriginalPath As String, DestinationFolderPath, Copies As Integer)
Dim fs As New FileSystemObject
For i = 1 To Copies
OrigName = fs.GetFileName(OriginalPath) 'file name with extention e.g. 1.gif
OrigNumber = CInt(Left(OrigName, Len(OrigName) - 4)) 'file name converted to a number - this will crash if the file name contains any non numeric chars
DestName = OrigNumber + i & "." & fs.GetExtensionName(OriginalPath) 'new file name = original number + i + the file extension
fs.CopyFile OriginalPath, DestinationFolderPath & "\" & DestName
Next i
End Sub

Related

Adding footer with a file name, before printing and without deleting existing footer/s

I need to create a macro in Word 2010, that will add a footer with the file name, without the extension.
I found a code that displays the file name without the extension when you use a docvariable field:
Sub autofilenamefooter()
Dim fname As String
fname = ActiveDocument.Name
fname = Left(fname, InStr(fname, ".") - 1)
With ActiveDocument
.Variables("fname").Value = fname
.PrintPreview
.ClosePrintPreview
End With
End Sub
How can I use this to create a macro that:
Foots the document with the file name before printing (so that the field will be updated each time you print the document).
In addition, the new footer (with the file name) will not delete existing footers, if any. This requirement is due to the fact that the macro should be applied to all documents in our document management system, and some of the documents already have footers (such as firm papers with logos) that I would like to keep.
Thanks in advance!
The code you have doesn't need further code for implementation. Simply place a reference field in the footer in whatever place you want the file name to appear. From the Insert tab select Quick Parts > Fields and insert a DocVariable field. Name the field "Fname" (or any other name matching the name in the code). Then run the code below while the document where you want the file name is active.
Sub SetDocVariable()
' 28 Dec 2017
Dim Sp() As String
Dim Test As String
Dim VarName As String
Dim VarValue As String
VarName = "Fname"
With ActiveDocument
Sp = Split(.Name, ".")
ReDim Preserve Sp(UBound(Sp) - 1)
VarValue = Join(Sp, ".")
' VarValue = "" ' Use this line to show nothing
On Error Resume Next
Test = .Variables(VarName).Value
If Err Then
.Variables.Add VarName
Err.Clear
End If
If .Variables(VarName).Value <> VarValue Then .Variables(VarName).Value = VarValue
End With
End Sub
This code will write the file name to the document variable, adding the variable to the document if it didn't exist. The field you inserted will show nothing while the variable is blank. It will show an error if the variable doesn't exist. Set the document variable to "" to have no error and no file name.
A perhaps even better way would be to use one of the built-in document properties (or a custom document property if none of the built-in ones can be diverted for the purpose) in place of the document variable, together with the appropriate reference field (Quick Parts > Document Properties). You would have to type the file name into the property dialog but wouldn't need any code at all.
BTW, the 'FileName' reference field (Quick Parts > Fields > FileName) displays the file name in the format you want but it can't be made to be blank.

Filename in variable used for formulas and copying

I am trying to use a wildcard filename as a variable so I can use it to copy and do some formulas. And then I want to flatten all the formulas.
It looks like this:
This first part works (first thing opens wildcard file from a cell formula and second assigns only filename without path to variable Prod - hovering over variable prod gives exactly what it should)
Dim wbProd As Workbook
Windows("SB.xlsm").Activate
Set wbProd = Workbooks.Open(FileNAME:=Sheets("refs").Range("B48").Value)
Dim Prod As String
Windows("SB.xlsm").Activate
Prod = Worksheets("refs").Range("B49").Value
Windows("Weekly.xlsx").Activate
With Workbooks(" & Prod & ").Sheets("Report 1")
.Range("A2:BG10", .Range("A2:BG10").End(xlDown)).Copy Workbooks("WeeklyData X.xlsx").ActiveSheet.Range("A2")
End With
Windows("WeeklyData X.xlsx").Activate
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Report 1")
ws.UsedRange.Value = ws.UsedRange.Value
I am getting an error with this first part of copying: With Workbooks(" & Prod & ").Sheets("Report 1"). When I use this copying method without using filename in a variable it works and also when I use variable filename to do Vlookups it works. I dont know what would be the reason not to work here.
Also if you have better way to flatten all the formulas and preseve formats (coz of dates) it would be great.
Thanks,
A quick fix would be to create a Workbook variable (Dim myWB as Workbook),
Then do Set myWB = Workbooks(Prod). Then just do With myWB.Sheets("Sheet1").
The issue is that Excel needs quotes in the sheet name, and so your book is literally being understood as being titled & Prod &. So, to keep your current idea, you need to just add an additional quote to each quote: With Workbooks("" & Prod & "").Sheets("Report 1").
Personally I recommend setting up a workbook variable, but either works!
Edit:
#drLecter - Very welcome! You'll also run into the "double quotes" issue when trying to set up formulas that have quotes in them. IE The worksheet formula =Vlookup("myText",A1:D1,2,False) would, in VBA, become
Cells(1,1).Formula = "=Vlookup(""myText"",A1:D1,2,False)".
As you can see, if I didn't use double quotes, VBA would stop reading the formula at
Cells(1,1).Formula = "=Vlookup(
Use dir() !
Microsoft Documentation link - dir() function
-Returns a string representing the name of a file, directory, or folder that matches a specified pattern or file attribute, or the volume label of a drive.
just adapt something like this::
Sub LoopThroughFiles()
Dim MyObj As Object, MySource As Object, file As Variant
file = Dir("c:\testfolder\")
While (file <> "")
If InStr(file, "test") > 0 Then
MsgBox "found " & file
Exit Sub
End If
file = Dir
Wend
End Sub

How to embed large (max 10Mb) text files into an Excel file

What is the best way to store a large text file (max 10Mb) in an Excel file?
I have a couple of requirements:
It has to be embedded so that the excel file can be moved and sent to a different computer and all the text files will follow.
It needs to be done from a macro.
And a macro needs to be able to read the file contents after it has been embedded.
I already tried to store it by breaking the text into several chunks enough small to fit into a cell (~32 000 chars), but it didn't work. After my macro had inserted the first 150 000 characters it gave me an "Out of Memory" error.
I remember seeing one web page with a couple of options for this I but cannot find it anymore. Any suggestions are most welcome. I will try them out if you are not sure if it works or not.
It would likely be best to simply save the .txt file alongside the Excel file, and have the macro pull the text as needed from that folder. To read more on importing files see this:
http://answers.microsoft.com/en-us/office/forum/office_2010-customize/vba-code-to-import-multiple-text-files-from/525bd388-0f7d-4b4a-89f9-310c67227458
Keeping the .txt within the Excel file itself is not necessary and will likely make it harder to transfer files in the long run. For example, if you cannot e-mail a file larger than 10MB, then you can simply break your .txt file in half and e-mail separately - using a macro which loads the text into Excel locally.
Very simple CustomXMLPart example:
Sub CustomTextTester()
Dim cxp1 As CustomXMLPart, cxp2 As CustomXMLPart
Dim txt As String
'read file content
txt = CreateObject("scripting.filesystemobject").opentextfile( _
"C:\_Stuff\test.txt").readall()
'Add a custom XML part with that content
Set cxp1 = ThisWorkbook.CustomXMLParts.Add("<myXMLPart><content><![CDATA[" & txt _
& "]]></content></myXMLPart>")
Debug.Print cxp1.SelectSingleNode("myXMLPart/content").FirstChild.NodeValue
End Sub
Consider the method shown below. It uses Caption property of Label object located on a worksheet for data storage. So you can create a number of such containers with different names.
Sub Test()
Dim sText
' create special hidden sheet for data storage
If Not IsSheetExists("storage") Then
With ThisWorkbook.Worksheets.Add()
.Name = "storage"
.Visible = xlVeryHidden
End With
End If
' create new OLE object TypeForms.Label type as container
AddContainer "test_container_"
' read text from file
sText = ReadTextFile("C:\Users\DELL\Desktop\tmp\tmp.txt", 0)
' put text into container
PutContent "test_container_", sText
' retrieve text from container
sText = GetContent("test_container_")
' show length
MsgBox Len(sText)
' remove container
RemoveContainer "test_container_"
End Sub
Function IsSheetExists(sSheetName)
Dim oSheet
For Each oSheet In ThisWorkbook.Sheets
If oSheet.Name = sSheetName Then
IsSheetExists = True
Exit Function
End If
Next
IsSheetExists = False
End Function
Sub AddContainer(sName)
With ThisWorkbook.Sheets("storage").OLEObjects.Add(ClassType:="Forms.Label.1")
.Visible = False
.Name = sName
End With
End Sub
Sub RemoveContainer(sName)
ThisWorkbook.Sheets("storage").OLEObjects.Item(sName).Delete
End Sub
Sub PutContent(sName, sContent)
ThisWorkbook.Sheets("storage").OLEObjects.Item(sName).Object.Caption = sContent
End Sub
Function GetContent(sName)
GetContent = ThisWorkbook.Sheets("storage").OLEObjects.Item(sName).Object.Caption
End Function
Function ReadTextFile(sPath, iFormat)
With CreateObject("Scripting.FileSystemObject").OpenTextFile(sPath, 1, False, iFormat)
ReadTextFile = ""
If Not .AtEndOfStream Then ReadTextFile = .ReadAll
.Close
End With
End Function

Extract All Named Ranges Into A Class

I have a workbook with a very large amount of named ranges (well over 200). I really need a way to work quickly and easily with all of the named ranges so I can then work with / populate them using VBA.
My solution up until now has been to have code inside a bunch of get properties in my public NamedRanges module, to set the property equal to the named range, like so:
Public Property Get LotNumber49() As range
Set LotNumber49 = Common.GetRange(Strings.LotNumber49)
End Property
Where Strings.LotNumber49 is a property which contains the name of the named range as recorded in the workbook, and Common.GetRange is a method that returns a new instance of the desired range object.
While this solution works well (I can now access an instance of that named range by calling NamedRanges.LotNumber49) It is definitely time consuming and tedious to type up the property in the Strings class and another property in the NamedRanges class.
Is there a better way to accomplish this quick referencing of named ranges that anyone can think of? Perhaps iterating over the collection returned by the Workbook.Names property?
Thank you all, I have this workbook to work on as well as four others, which means a whole lot of named ranges!
Get Named Range by String
Why not a simple procedure like so:
Function GetNR(namedRange as String) as Range
Set GetNR = ActiveWorkbook.Names(namedRange).RefersToRange
End Function
Then simply get the named range like so:
Sub Example()
Debug.Print GetNR("NAME").Value
End Sub
Named Range Suggestion in VBA Project
Alternatively if you want the names to popup in your VBA project you need to redefine the Constants in the Strings class. Try this procedure:
Sub GetAllNames()
Dim res As String, n As Name
For Each n In ActiveWorkbook.Names
If InStr(n.Name, "!") = 0 Then res = res & "Const " & n.Name & "=""" & n.Name & """" & vbNewLine
Next n
Dim fFile As Long
fFile = FreeFile
Open "out.txt" For Output As #fFile
Print #fFile, res
Close #fFile
End Sub
You need to repeat this occasionally when modifying the named ranges:
Run the GetAllNames procedure
Open the out.txt file
Copy the outputs to your Strings class or whatever
Now to get a named range use your Common.GetRange method along with your Strings name or simply use the approach above to generate also the Getter code like so:
Sub GetAllGetters()
Dim res As String, n As Name
For Each n In ActiveWorkbook.Names
If InStr(n.Name, "!") = 0 Then res = res & "Public Property Get " & n.Name & "() As range" & vbNewLine & "Set " & n.Name & " = Common.GetRange(Strings." & n.Name & ")" & vbNewLine & "End Property"
Next n
Dim fFile As Long
fFile = FreeFile
Open "outGetters.txt" For Output As #fFile
Print #fFile, res
Close #fFile
End Sub

Is it possible in Excel VBA to change the source code of Module in another Module

I have an Excel .xlam file that adds a button in the ribbon to do the following:
Scan the ActiveSheet for some pre-set parameters
Take my source text (a string value, hard coded directly in a VBA Module) and replace designated areas with the parameters retrieved from step 1
Generate a file containing the calculated text
I save the source text this way because it can be password protected and I don't need to drag another file around everywhere that the .xlam file goes. The source text is saved in a separate module called "Source" that looks something like this (Thanks VBA for not having Heredocs):
'Source Module
Public Function GetSource() As String
Dim s As String
s = ""
s = s & "This is the first line of my source text" & vbCrLf
s = s & "This is a parameter {par1}" & vbCrLf
s = s & "This is another line" & vbCrLf
GetSource = s
End Function
The function works fine. My problem is if I want to update the source text, I now have to manually do that in the .xlam file. What I would like to do is build something like a Sub ImportSource() in another module that will parse some file, rebuild the "Source" Module programatically, then replace that Module with my calculated source code. What I don't know is if/how to replace the source code of a module with some value in a string variable.
It's like metaprogramming at its very worst and philosophically I'm against doing this down to my very core. Practically, however, I would like to know if and how to do it.
I realize now that what you really want to do is store some values in your document in a way that is accessible to your VBA, but that is not readable to a user of the spreadsheet. Following Charles Williams's suggestion to store the value in a named range in a worksheet, and addressing your concern that you don't want the user to have access to the values, you would have to encrypt the string...
The "proper way" to do this is described in this article - but it's quite a bit of work.
A much shorter routine is found here. It just uses simple XOR encryption with a hard coded key - but it should be enough for "most purposes". The key would be "hidden" in your macro, and therefore not accessible to prying eyes (well, not easily).
Now you can use this function, let's call it encrypt(string), to convert your string to a value in the spreadsheet:
range("mySecretCell").value = encrypt("The lazy dog jumped over the fox")
and when you need to use it, you use
Public Function GetSource()
GetSource = decrypt(Range("mySecretCell").value)
End Function
If you use the XOR version (second link), encrypt and decrypt would be the same function...
Does that meet your needs better?
As #brettdj already pointed out with his link to cpearson.com/excel/vbe.aspx , you can programmatically change to code of a VBA module using the VBA Extensibility library! To use it, select the library in the VBA editor Tools->References. Note that you need to also change the options in your Trust center and select: Excel Options->Trust Center->Trust Center Settings->Macro Settings->Trust access to the VBA project object model
Then something like the following code should do the job:
Private mCodeMod As VBIDE.CodeModule
Sub UpdateModule()
Const cStrModuleName As String = "Source"
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Set VBProj = Workbooks("___YourWorkbook__").VBProject
'Delete the module
VBProj.VBComponents.Remove VBProj.VBComponents(cStrModuleName)
'Add module
Set VBComp = VBProj.VBComponents.Add(vbext_ct_StdModule)
VBComp.Name = cStrModuleName
Set mCodeMod = VBComp.CodeModule
'Add procedure header and start
InsertLine "Public Function GetSource() As String"
InsertLine "Dim s As String", 1
InsertLine ""
'Add text
InsertText ThisWorkbook.Worksheets("Sourcetext") _
.Range("___YourRange___")
'Finalize procedure
InsertLine "GetSource = s", 1
InsertLine "End Function"
End Sub
Private Sub InsertLine(strLine As String, _
Optional IndentationLevel As Integer = 0)
mCodeMod.InsertLines _
mCodeMod.CountOfLines + 1, _
Space(IndentationLevel * 4) & strLine
End Sub
Private Sub InsertText(rngSource As Range)
Dim rng As Range
Dim strCell As String, strText As String
Dim i As Integer
Const cLineLength = 60
For Each rng In rngSource.Cells
strCell = rng.Value
For i = 0 To Len(strCell) \ cLineLength
strText = Mid(strCell, i * cLineLength, cLineLength)
strText = Replace(strText, """", """""")
InsertLine "s = s & """ & strText & """", 1
Next i
Next rng
End Sub
You can "export" and "import" .bas files programmatically. To do what you are asking, that would have to be the approach. I don't believe it's possible to modify the code in memory. See this article