Restrict number of copies to print - excel

I have 5 computers and two printers connected by LAN. There is one particular excel document in one of the computers (not shared) for which I wish to restrict the number of copies that can be printed, to 4. Meaning the user should not be able to print more than 4 copies of that document.
I am aware of the photocopying (and more) loopholes, but i am still hopeful of print copies getting out in a controlled or limited number.
I have looked through the features of a few print control softwares, but i learnt that they all have a "quota" system with users having to pay for printing after exceeding their limit. I am afraid this wont work for me.
I also read an answer to a similar question posted here, Set number of copies per worksheet
Thankfully this answer very much helped me, except I have no clue on how am going to restrict or limit the user to take printouts beyond the specified number.
I also have read many answers saying that restricting the number of copies is next to impossible, But i still wish to look for help - maybe some solution could come up.
I dont have much deep knowledge in computer/printer programming.Though not a pro, I am a little familiar with excel vba.
Please let me know if there can be any solutions,
As soon as I find something I'll post it here.
Thanks a ton for the help.

This is a crude solution but this would add some limit on number of prints...
Place in ThisWorkbook:
Private Sub Workbook_BeforePrint(Cancel As Boolean)
Cancel = True
If Cancel = True Then
MsgBox "Please use the print button on Sheet1."
End If
End Sub
Add a CommandButton and rename it PrintButton then insert this subroutine (and accompanying functions) into a Module
Private Sub PrintButton_Click()
On Error Resume Next
Application.EnableEvents = False
If (CanWePrint(4)) Then
ActiveSheet.PrintOut
Else
MsgBox ("Sorry this is the maximum number of prints for this document!")
End If
Application.EnableEvents = True
On Error GoTo 0
End Sub
Function CanWePrint(ByVal MaxPrintVal As Integer) As Boolean
Dim CurrentPrintCount As String, SecretFile As String
'PLEASE CHANGE TO YOUR "SECRET" TXT FILE NAME AND LOCATION!
SecretFile = "C:\Users\Matt\Documents\countPrint.txt"
CurrentPrintCount = GetCount(SecretFile)
If (CurrentPrintCount < MaxPrintVal) Then
Call UpdatePrintCount(CurrentPrintCount, SecretFile)
CanWePrint = True
Else
CanWePrint = False
End If
End Function
Function GetCount(ByVal SecretFile As String) As Integer
Dim nSourceFile As Integer
Dim sText As String
Close
nSourceFile = FreeFile
Open SecretFile For Input As #nSourceFile
sText = Input$(LOF(1), 1)
Close
GetCount = CInt(sText)
End Function
Sub UpdatePrintCount(ByVal CurrentVal As Integer, ByVal SecretFile As String)
Dim sBuf As String
Dim sTemp As String
Dim iFileNum As Integer
iFileNum = FreeFile
Open SecretFile For Input As iFileNum
Do Until EOF(iFileNum)
Line Input #iFileNum, sBuf
sTemp = sTemp & sBuf & vbCrLf
Loop
Close iFileNum
sTemp = Replace(sTemp, CurrentVal, CurrentVal + 1)
iFileNum = FreeFile
Open sFileName For Output As iFileNum
Print #iFileNum, sTemp
Close iFileNum
End Sub
What this does
This code will disable the standard Print option in Excel for that workbook. By adding a CommandButton you create a manual print option which will check the print count stored in a .txt file, this will mean the document can be closed and reopened and still only be printed 4 times.
What you need to do
Create a new txt file on the same machine as this document and update the path in code above in CanWePrint.
Draw Backs
Like I said this is a crude solution and there would be many ways around this:
Manually change the value in the .txt file
Save the workbook without VBA
Disabling the VBA

This is not at all crude, its very much sophisticated for me:) I am okay with the drawbacks mentioned. The code worked fine when i pasted it in sheet 1 instead of a module. Theres one catch here though, Matts code is good for one file..what i have is an empty template like file(saved as macro enabled excel workbook, but working like a template for me) which has to be filled up and printed again and again, but I need to avoid duplication, hence not more than 4 copies.
So what i have tried is,
i created a macro with a keyboard shortcut. This macro does this:
It takes 4 printouts.
It inserts a watermark into the file, (thus marking it as invalid)
It exports the selected range as pdf and saves it in my specified folder (so all saved pdfs have watermarks, and its not possible to erase it in adobe reader)
It removes the watermark, clears all entered data, thus providing a new doccument to be created next time.
This is working for me, except that anyone could print any number of copies if they did not use my shortcut.
But with the "beforeprint" code in Matts answer that will be solved.
So thanks a ton Matt!
As you mentioned, there can be more ways around this, I'll keep working,
For now this should work for me.
Please let me know if my way is good and if there are any loops..
Thank you!

Related

Extract checkbook value from a closed spreadsheet using Excel VBA

I have a fairly larger number of excel workbooks in a folder. Each workbook has one tab - Sheet1. Sheet1 includes three checkboxs: Checkbox 6, Checkbox 7 and Checkbox 8 in addition to some values in cells. I'm using this code:
Link to Code Used
to extract the cell values, but was hoping it would also be possible to determine the value (status checked or not checked) of each of the checkboxes. Is this possible? Note - None of the checkbox are linked to a particular cell.
There is no way to read anything from a closed file. Even the code you are linking to cannot do this. You will always need a program that opens the file, read the data from it, find the information you want and close it again.
For Excel files you usually use Excel, but it could be something else - I know that Python has an library to read & write Excel files (and there are more), but all of them have to open the file. Open means ask the operating system to read the data from disk, maybe set a lock, maybe later write it back, those kind of things.
That said, what you probably want is to access the data (in your case checkbox settings) without the sheet being visible. You can do so by set Application.ScreenUpdating = False, open the file, read the checkbox values, close the file and reset Application.ScreenUpdating = True. The user will not see anything. I strongly assume that the Excel4-Macro does the same, but you will not find many persons around that are able to deal with Excel4-Macros.
Now to be able to read the value of a checkbox, you need to know if you are dealing with ActiveX or Form controls (or both). I wrote a small prove of concept that can deal with both. You pass the name of a workbook, the name (or number) of a sheet and an array with the name of the checkboxes you want to read. Result is an array with the values of the checkboxes. However you need to know that the values of an ActiveX-checkbox is True or False (or Null if you allow TripleState), while for a form-checkbox it is xlOn or xlOff. In the case a sheet doesn't have a checkbox with the specific name, it will return an arbitrary number
Function getCheckBoxValueFromFile(filename As String, sheet As Variant, checkboxNames) As Variant
Const undefinded = -999
Application.ScreenUpdating = False
Dim wb As Workbook
Set wb = Workbooks.Open(filename)
Dim i As Integer, res()
ReDim res(LBound(checkboxNames) To UBound(checkboxNames))
For i = LBound(checkboxNames) To UBound(checkboxNames)
Dim val As Long
val = undefinded
With wb.Sheets(sheet)
On Error Resume Next
' first try ActiveX-CheckBox
val = .OLEObjects(checkboxNames(i)).Object.Value
' if failed, try Form-CheckBox
If val = undefinded Then val = .CheckBoxes(checkboxNames(i)).Value
On Error GoTo 0
res(i) = val
End With
Next i
wb.Close False
getCheckBoxValueFromFile = res
Application.ScreenUpdating = True
End Function
To test the function:
Sub test()
Dim res, i, cbNames
cbNames = Array("CheckBox1", "Check Box 2")
res = getCheckBoxValueFromFile("C:\TEMP\Book1.xlsx", "Sheet1", cbNames)
For i = LBound(res) To UBound(res)
Debug.Print i & ": " & cbNames(i) & " -> " & res(i)
Next i
End Sub

How to programmatically export and import code into Excel worksheet?

We will put 100s of Excel worksheets out in the field this year. The code periodically needs to be updated when bugs are found. For last year's effort, I was able to dynamically have workbooks pull updates for .bas files. This year I want to dynamically have workbooks pull updates for the code embedded in the worksheets too.
EXPORT CODE
The export code is pretty simple, but there are artifacts in the .txt files
Sub SaveSoftwareFile(path$, name$, ext$)
ThisWorkbook.VBProject.VBComponents(name).Export path & name & ext
Example Call: SaveSoftwareFile path, "ThisWorkbook", ".txt"
The problem is that the export has a lot of header information that I don't care about (in red). I just want the part in blue. Is there switch that allows me not to save it, or do I have to manually go into the export and remove it myself?
IMPORT CODE
The import code is pretty straight forward too, but it causes the error "Can't enter break mode at this time", and I'm struggling to figure out the right path forward. If I manually try and delete this code, Excel is also unhappy. So maybe my approach is altogether incorrect. Here's the code:
Sub UpgradeSoftwareFile(path$, name$, ext$)
Dim ErrorCode%, dest As Object
On Error GoTo errhandler
Select Case ThisWorkbook.VBProject.VBComponents(name).Type
Case 1, 3 'BAS, FRM
<Not relevant for this discussion>
Case 100 'Worksheets
Set dest = ThisWorkbook.VBProject.VBComponents(name).codemodule
dest.DeleteLines 1, dest.CountOfLines 'Erase existing | Generates breakpoint error
dest.AddFromFile path & name & ext '| Also generates breakpoint error
End Select
Example Call: UpgradeSoftwareFile path, "ThisWorkbook", ".txt"
Thanks in advance for your help
Please, try the next way of exporting and you will not have the problem any more:
Sub SaveSoftwareFile(path$, sheetCodeModuleName$, FileName$)
Dim WsModuleCode As String, sCM As VBIDE.CodeModule, strPath As String, FileNum As Long
Set sCM = ThisWorkbook.VBProject.VBComponents(sheetCodeModuleName).CodeModule
WsModuleCode = sCM.Lines(1, sCM.CountOfLines)
'Debug.Print WsModuleCode
strPath = ThisWorkbook.path & "\" & FileName
FileNum = FreeFile
Open strPath For Output As #FileNum
Print #FileNum, WsModuleCode
Close #FileNum
End Sub
You can use the above Sub as following:
Sub testSaveSheetCodeModule()
Dim strPath As String, strFileName As String, strCodeModuleName As String
strPath = ThisWorkbook.path
strFileName = "SheetCode_x.txt"
strCodeModuleName = Worksheets("Test2").codename 'use here your sheet name
SaveSoftwareFile strPath, strCodeModuleName, strFileName
End Sub
Now, the created text file contains only the code itself, without the attributes saved by exporting the code...
Import part:
"Can't enter break mode at this time" does not mean that it is an error in the code. There are some operations (allowed only if a reference to Microsoft Visual Basic for Applications Extensibility ... exists) in code module manipulation, which cannot simple be run step by step. VBA needs to keep references to its VBComponents and it looks, it is not possible when changes in this area and in this way are made.
The import code is simple and it must run without problems. You must simple run the code and test its output...

Best way to replace VBA code in multiple files?

I used to use something like this:
Dim vbaComponent As Variant
For Each vbaComponent In inputWorkbook.VBProject.VBComponents
vbaComponent.CodeModule.DeleteLines 1, vbaComponent.CodeModule.CountOfLines
vbaComponent.CodeModule.AddFromFile importComponentFileName
Next vbaComponent
This worked perfectly for some time but now it crashes when the Excel file gets saved. I guess the files got too big or something.
Is there better way to do this?
EDIT:
The problem seems to be frm and cls files. The replacement of bas files works perfectly.
EDIT2:
On some machines even bas files don't work.
EDIT3 (My current solution):
So my current solution was simply doing it by hand once and recording all mouse and keyboard input and then replaying this over and over again.
If there is no proper solution to this I plan on creating an AutoIt script for this.
you will have to export/import components, because not all lines are exposed to CodeModule, here is sample
Private Sub exportImportComponent(Project1 As VBIDE.VBProject, Project2 As VBIDE.VBProject)
Dim i As Long, sFileName As String
With Project1.VBComponents
For i = 1 To .Count
sFileName = "C:\Temp\" & .Item(i).Name
Select Case .Item(i).Type
Case vbext_ct_ClassModule
.Item(i).Export sFileName & ".cls"
Project2.VBComponents.Import sFileName & ".cls"
Case vbext_ct_StdModule
.Item(i).Export sFileName & ".bas"
Project2.VBComponents.Import sFileName & ".bas"
Case vbext_ct_MSForm
.Item(i).Export sFileName & ".frm"
Project2.VBComponents.Import sFileName & ".frm"
Case Else
Debug.Print "Different Type"
End Select
Next
End With
End Sub
I can assure everybody because I am working on this subject for years now (I gave up several times). When the code is programmatically modified either line-based or - what my preferred approach is 1. rename, 2. delete the renamed, 3. re-import from export file, Workbook Save will crash, will say Excel closes the Workbook. In fact my approach works most of the time but since it is unpredictable I learned to live with it. In most cases the code change has already successfully been done. So I just reopen the Workbook and continue.
The code I use. I just removed all the execution trace and execution log code lines but some lines may still look a bit cryptic:
With rn_wb.VBProject
'~~ Find a free/unused temporary name and re-name the outdated component
If mComp.Exists(wb:=rn_wb, comp_name:=rn_comp_name) Then
sTempName = mComp.TempName(tn_wb:=rn_wb, tn_comp_name:=rn_comp_name)
'~~ Rename the component when it already exists
.VBComponents(rn_comp_name).Name = sTempName
.VBComponents.Remove .VBComponents(sTempName) ' will not take place until process has ended!
End If
'~~ (Re-)import the component
.VBComponents.Import rn_raw_exp_file_full_name
'~~ Export the re-newed Used Common Component
Set Comp = New clsComp ' class module provides the export files full name
With Comp
Set Comp.Wrkbk = rn_wb
.CompName = rn_comp_name
End With
.VBComponents(rn_comp_name).Export Comp.ExpFileFullName
'~~ When Excel closes the Workbook with the subsequent Workbook save it may be re-opened
'~~ and the update process will continue with the next outdated Used Common Component.
'~~ The (irregular) Workbook close however may leave the renamed components un-removed.
'~~ When the Workbook is opened again these renamed component may cause duplicate declarations.
'~~ To prevent this the code in the renamed component is dleted.
' EliminateCodeInRenamedComponent sTempName ' this had made it much less "reliablele" so I uncommented it
SaveWbk rn_wb ' This "crahes" every now an then though I've tried a lot
End With
Private Sub SaveWbk(ByRef rs_wb As Workbook)
Application.EnableEvents = False
DoEvents ' no idea whether this helps. coded in desparation. at least it doesn't harm
rs_wb.Save
DoEvents ' same as above, not executed when Excel crashes
Application.EnableEvents = True
End Sub

Saving extracted content from VBA in powerpoint as a new excel file

I'm new to coding, VBA and VBA in powerpoint, so this might be basic but I cant seem to get around it. I'm trying to extract some text from powerpoint files and store it separately in a new file. I found a code online that helps me output the required text onto a notepad file. However, I need it as an excel file. Every search online leads me to excel based VBA but I need to export from Powerpoint to Excel (and maybe do so basic formatting in it, like converting text to numbers or making a column bold). Following is the relevant/ exporting portion of my whole code. Please take a look and help thanks.
PS: I plan to compile the code as an Add-in for a few people with limited technical expertise, so If possible, I'd request a simple/straightforward solution or something that will work on any PC or version of powerpoint/excel. I've read making your code reference-dependant may require whoever runs the code to make references,etc before they are successfully able to run it.
Dim strFilename As String
Dim intFileNum As Integer
Dim PathSep As String
....Code that does the extraction from the Active Presentation....
On Error GoTo ErrorHandler
intFileNum = FreeFile
' PC-Centricity Alert!
' This assumes that the file has a .PPT extension and strips it off to make the text file name.
strFilename = ActivePresentation.Path _
& PathSep _
& Mid$(ActivePresentation.Name, 1, Len(ActivePresentation.Name) - 4) _
& "_Titles.TXT"
Open strFilename For Output As intFileNum
Print #intFileNum, strTitles
NormalExit:
Close intFileNum
Exit Sub
ErrorHandler:
MsgBox Err.Description
Resume NormalExit
End Sub

VBA - How do I remove a file from the recent documents list in excel 2007?

The recent documents feature in Office is really useful, but I moved a file to a new directory and now I can't get Excel to stop hitting me with a "can't find this file" notification whenever I open a workbook. The Excel options seem only to control how many of these "recent documents" are displayed and not how many are actually saved. So I;'m wondering if there's a way in VBA to get at the list and remove the offending file.
Try this...
Public Function TestIt()
For i = 1 To Application.RecentFiles.Count - 1
Dim answer As String
answer = MsgBox("Delete " & Application.RecentFiles(i).Name, vbYesNo)
If answer = vbYes Then
answer = MsgBox("Are you sure?", vbYesNo)
If answer = vbYes Then
Application.RecentFiles(i).Delete
End If
End If
Next i
End Function
Not a VBA solution, but open up Regedit and you can remove files from the list at will.
The "File MRU" list is what you're after; for Excel 2007 it's under
HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Excel\File MRU
Adjust the version number accordingly.
Close Excel, delete the offending file's entry from the list found there, and restart.
Facing the same issue I wrote this litte macro, removing all files from the recent file list, which are not accessible:
Public Function CheckRecentFiles() As Integer
Dim i As Integer, c As Integer
For i = Application.RecentFiles.count To 1 Step -1
'Debug.Print Application.RecentFiles(i).name
If Dir(Application.RecentFiles(i).name) = "" Then
Debug.Print "Delete from recent file list: " & Application.RecentFiles(i).name
Application.RecentFiles(i).Delete
c = c + 1
End If
Next i
Debug.Print c & " files removed."
CheckRecentFiles = c
End Function
Try the routine above not as function but as SUB.
And in the second line remove "-1" at its end, because the last entry will not be handled else.
Then the routine will work properly.
Based on #GunnarBernsteinI 's answer,I just added this to my Personal Macro Book. This is going to be super handy to clean up the temp files that I create to answer questions on SO.
Public Sub CleanRecentFiles()
Const ReviewEntry As Boolean = False
Dim f As RecentFile
For Each f In Application.RecentFiles
If Len(Dir(f.Name)) = 0 Then
f.Delete
ElseIf ReviewEntry Then
Debug.Print f.Name
Stop
End If
Next
End Sub
Demo
Open the Recent Workbooks List. Right click quickly and firmly between the icon and text for the document you wish to remove from the list. A dropdown list appears. It is the list which allows you to pin an item to the list. Choose Remove from List. It does work but it can be a bit tricky to time it correctly. If you are too slow it will just try to open the file.

Resources