VBA - force excel to save in custom format - vba

For a project at work i would like to allow the user to create an export of data to be saved for a single project, since it will not be possible to save my main workbook for each project.
I have created a function that exports the data to another workbook, but now i want to make sure that the user won't change the exported data directly (By setting another file format, i think the users would think more about opening the workbook, and therefore not do it by "accident")
So my question is:
Can i force the user to save the file as a .clg file?
Edit:
I solved the problem by saving the file with .SaveAs and specifying the format as "51" (The same as .xlsx"). I used GetSaveAsFilename to allow the user to decide the location (i.e. the project folder), and only browse for .clg files.
Dim sFullName As String
sFullName = exportFile.Application.GetSaveAsFilename(thisFile.Sheets("Project information").Cells(1, 2).Value, "Export files (*.clg),*.clg", , "Choose export location", "Export")
If Not Len(sFullName) = 0 Or sFullName = "False" Then
exportFile.SaveAs Filename:=sFullName, FileFormat:=51
End If
exportFile.Close
If someone wants to use Mr. Monshaw's approach, one way to go is to create a class module, which has a variable withevents - which means that any event that fires in the variable, fires in your module.
Public WithEvents wbk As Workbook
Just remember to keep the instance of the class in memory, for example by adding it to a collection.
Another way to use the BeforeSave is to dynamically write the code, see http://www.ozgrid.com/forum/showthread.php?t=163903

Is 'clg' a format you use? or just a random not xls/x extension?
If the file does need to be viewed and viewed in excel (guessing your not exporting windows catalogue files?) then changing the extension might not be the best move.
If you create your export using VBA, then you can automate some things to make it more apparent to the users that they should not be editing the data.
The Workbook.SaveAs method has the option for "ReadOnlyRecommended" which will inform the user that the workbook should be opened read-only when opening it. (its optional though, so they could ignore it)
Assuming the data is always read-only you could also lock the workbook so that no changes can be made.
Update
If you want to save an Excel Workbook with a custom extension (and not as a custom format) then using the Workbook.SaveAs method will work. ex: ThisWorkbook.SaveAs "Report.clg"
You'll either need to have your export code do this for you, or if its a more manual process you can just create a macro with a button to invoke the save-as for you instead of using the standard save-as dialogue.
If you want to save as a custom format, and it is not one of the ones supported by Workbook.SaveAs (supported formats) you will have to do as #Mr.Monshaw suggested and watch for the onbeforesave and generate the file manually. (unlikely someone has done an excel to windows catalogue converter)

im not sure what the constant for clg would be but the fileformat property of SaveAs in vba would allow you to decide what format to save in
i saw a clever solution to something similar to this if you can find the fileformat, just add a macro to your book with a workbook event like this:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Call SaveAsCLG(SaveAsUI) 'set save type
Cancel = True 'cancel user save
End Sub
that will override a user save-as request

Related

VBA SOLDIWORKS PackAndGo save modified assembly

I've run into a problem while trying to automate SOLIDWORKS API VBA PackAndGo.
The order of action I take are:
Programmatically open an assembly
Programmatically modify this assembly and it's components while renaming the modified components
Programmatically engage pack and go
The unexpected problem is that the exported components have a correct / changed filename (as expected and as intended) but the actual models are the original none modified ones. This is something that I've only come across while trying to programmatically engage PackAndGo. When I do this routine manually from this modified assembly - the models that get renamed and exported are the actual new / modified ones.
My question is: "Why does this happen and how do I make it export teh models from active model?"
The code without the renaming routine etc. that I have written is:
Sub PackAndGo ()
'Set variables
Set swPackAndGo = swRootAssembly.Extension.GetPackAndGo
'Pack And Go
With swPackAndGo
.IncludeDrawings = True
.SetSaveToName True, OutputDirectory
.GetDocumentNames DocumentNamesArray
.GetDocumentSaveToNames DocumentSaveToNamesArray, 0
'Here I do the renaming of components in DocumentSaveToNamesArray
.SetDocumentSaveToNames DocumentSaveToNamesArray
.FlattenToSingleFolder = True
End With
'Pack and go
swRootAssembly.Extension.SavePackAndGo swPackAndGo
End Sub
Maybe I'm missing some additional method or function to make it copy the opened / active assembly components, rather than ones from the hard drive which are in fact the same models, but are not modified or altered in any way.
A simple example would be that I programmatically open a brick that's 20mm long and then programmatically change it's length to 50mm. Now I expect packandgo to save / export this 50mm long one but what it in fact does when run programmatically is export the same 20mm long brick, but with a different filename (again, different filename is what is expected)

Disable Save As but not Save in Word 2010

I am looking to disable Save As in a Word 2010 file but still allow save. In other words I want users to be able to update the existing file but not create copies. I realize that this is impossible to truly do for people who know workarounds but for the general user I have successfully done this in Excel but am pretty new to word VBA.
When I add the following to a brand new document everything works as intended:
Sub FileSaveAs()
MsgBox "Copies of this file cannot be created. Please save changes in the original document." & _
, , "Copy Cannot be Created"
End Sub
My document has other macros for various command buttons but none of them involve saving the document (under original name or save as). There is also a macro running on open but that is 1 line going to a bookmark. When I try to "save as" in this document I get the message box as intended. When I try to "save" though things get strange: I get the save as dialogue (problem 1). Whether I try to save either under same name or other name the dialogue behaves as it normally would except it doesn't save and the dialogue box opens again automatically essentially creating an endless loop until I hit cancel (problem 2). I also intermittently get a "disk is full" warning pop-up after trying to save which I can dismiss but appears a few minutes later as long as he file is open (perhaps related to autosave?)
Since the macro works in the test file I assumed this strange behavior must be something elsewhere in my code but my document with the other macros saves normally as long as I don't include the save as code above so now I'm totally confused. Before I put up the rest of my code which is lengthy and for the reasons stated above I would not think impact things, I figured I'd ask this:
1. Is there any place other than my other command button macros that could be causing this behavior?
2. Is there a better method people recommend to achieve my ultimate goal of disabling save as but not save?
Thanks in advance for any advice you can provide.
The Word application has a DocumentBeforeSave event. To enable application events I suggest to create a class module by the name of ThisApplication and paste the following code into it.
Option Explicit
Private WithEvents App As Application
Private Sub Class_Initialize()
Set App = Word.Application
End Sub
Private Sub App_DocumentBeforeSave(ByVal Doc As Document, _
SaveAsUI As Boolean, _
Cancel As Boolean)
If SaveAsUI Then
MsgBox "Please always use the ""Save"" command" & vbCr & _
"to save this file.", _
vbExclamation, "SaveAs is not allowed"
Cancel = True
End If
End Sub
Add the following code to your ThisDocument module.
Dim WdApp As ThisApplication
Private Sub Document_Open()
Set WdApp = New ThisApplication
End Sub
You may add the Set App = ... line to your existing Document_Open procedure. After the WdApp variable has been initialised all application events will be received by the ThisApplication class where the DocumentBeforeSave event procedure is programmed not to allow SaveAs.
Of course, this is a blanket refusal for all documents. Therefore you may wish to add code to the procedure to limit the restriction to certain documents only. The proc receives the entire document object with all its properties, including Name, Path, FullName and built-in as well as custom properties. You can identify the files you wish to be affected by any of these.
Note that the WdApp variable will be erased in case of a program crash. If this happens the application events will no longer fire. It may be useful to know that application events occur before document events. This is if you wish to use the application's DocumentOpen event as well as or instead of the document's Document_Open event.

Can a Variable be stored in an Excel File that can not be accessed through Excel

I just discovered that in MS Word it is possible to store a Variable in a MS Word File that can not be accessed through the regular interface when running Microsoft Word.
Sub SetMyVariable()
Dim VARNAME As String
VARNAME = "HiddenVar"
ActiveDocument.Variables.Add VARNAME, "My special info"
End Sub
This gets saved in the XML Schema under word\settings.xml
I have tried using the ThisWorkbook Object in Excel, but it doesn't seem to have a Variable object that can be added like in word.
I want to know if there is something similar in Excel to store information/varialbes that get saved with the file.
PS: the closest thing I can think of (and use in codig) is a hidden named range.
You can try with the CustomXMLParts property of the Workbook which from the link seems a generic feature of Office products and available in Excel. Given you noted that a user would have to manually inspect the XML within the unzipped xlsx files then this seems to map to the Word Variables feature. The code sample just substitutes ThisWorkbook for ActiveDocument:
Option Explicit
Sub TextXMLPart()
Dim objXMLPart As CustomXMLPart
'add
Set objXMLPart = ThisWorkbook.CustomXMLParts.Add("<foo>bar</foo>")
'inspect
For Each objXMLPart In ThisWorkbook.CustomXMLParts
Debug.Print objXMLPart.XML
Next objXMLPart
End Sub
The accepted answer to this question (which focuses on Excel and vsto) states that:
Custom XML parts For an application-level add in, this is my preferred method of storing any application data that needs to be persisted in a saved xls file without ever being visible to the user.

How to access a Word public variable in Excel VBA

I'm trying to automate some report generation where Excel VBA is doing all the work. My employer has a standardized set of templates of which all documents are supposed to be generated from. I need to populate one of these templates from Excel VBA. The Word templates utilize VBA extensively.
This is (some of) my Excel VBA code:
Sub GenerateReport() ' (Tables, InputDataObj)
' code generating the WordApp object (works!)
WordApp.Documents.Add Template:="Brev.dot"
' Getting user information from Utilities.Userinfo macro in Document
Call WordApp.Run("Autoexec") ' generating a public variable
Call WordApp.Run("Utilities.UserInfo")
' more code
End sub
In the Word VBA Autoexec module, a public variable named user is defined and declared. The Userinfo sub from the Utilities module populates user. Both these routines are run without any complaints from VBA. I would then like to be able to access the user variable in my Excel VBA, but I get the following error
Compile Error: Variable not yet created in this context.
How can I access the Word VBA variable in Excel VBA? I thought it more or less was the same?
EDIT: the user variable is a user defined Type with only String attributes. Copying the Word VBA functions that populate the user variable is absolutely doable, just more work than I though was necessary...
In a Word module:
Public Function GetUserVariable() As String '// or whatever data type
GetUserVariable = user
End Function
In an Excel module:
myUser = WordApp.Run("GetUserVariable")
Alternatively, you could be able to replicate the variables value - as it's called user I suspect it is returning some information about a user, or author, of a document. In which case one of the following might be what you're after:
'// Username assigned to the application
MsgBox WordApp.UserName
'// Username defined by the system
MsgBox Environ$("USERNAME")
'// Name of the author of the file specified
MsgBox CreateObject("Shell.Application").Namespace("C:\Users\Documents").GetDetailsOf("MyDocument.doc", 9)
Another option - if you could only add a line of code to the Utilities.UserInfo sub (after setting your public variable):
ActiveDocument.Variables("var_user") = user
Then you could access it easily afterwards in Excel:
Sub GenerateReport() ' (Tables, InputDataObj)
' code generating the WordApp object (works!)
'I am assuming your WordApp object is public, as you don't declare it.
'Capture the new document object
Dim newdoc as Object
set newdoc = WordApp.Documents.Add(Template:="Brev.dot")
' Getting user information from Utilities.Userinfo macro in Document
Call WordApp.Run("Autoexec") ' generating a public variable
Call WordApp.Run("Utilities.UserInfo")
'Get and show the value of "user"
Dim user as String
user = newdoc.Variables("var_user")
msgbox, user
End Sub
This is assuming that useris a string.
EDIT: As it is a requirement to work only on the Excel VBA, I would definely try the approach suggested by Scott and MacroMan - replicating the same functionality of the Word macros in Excel - if possible.
I assume that you've already ruled out the possibility of using an edited copy of the original template, set in a public folder...
For the sake of completness, there is another possibility: actually it is possible to inject VBA code in a Word document without the VBProject Object Model, by "brute force". If you rename a Word document as a .zip file and open it, you will notice a \word\vbaProject.bin file in it. This file contains the VBA project for the document and, in principle, one could add or change VBA code by modifying or replacing it.
I did some tests transplanting code from one document to another by simply copying the vbaProject.bin file, and the concept works. If you are interested in learning more about this file, this topic could be of use.
Notice, however, that to do what you want with such a technique would be somewhat complex (it would involve, for starters, updating zip files from your Excel VBA), and would require a lot of experimentation to mitigate the risk of accidentally corrupting your files. Definetly not recommended if you are looking for an easy and simple solution - but it is possible.

Excel-2010 - CustomUI - Backstage : Errors when various files are opened in the same instance

Firstly, i'm very happy to join the community. I hope we could often exchange advice. I'm french so excuse me for the mistakes in the sentences.
I try to explain my problem :
I had the "good" idea to use the Backstage of my Excel file to create a small dashboard. It works very well.
The problem arises when this file is opened at the same time as another file, in the same instance of Excel. The second file is trying to access functions "Backstage_OnShow" and "Backstage_OnHide" of my workbook so I have a message "Impossible to run the macro 'Backstage_OnShow' (or 'Backstage_OnHide'). It is possible that the macro is not available in this workbook ..." <- This is a translation to the french error message.
How can I do to not have this message or rather to ensure that the backstage is specific to my file and not the instance of Excel?
I show with my code snippets. It will be more clear.
In my XML, I have this :
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad ="Ribbon_Load">
<ribbon startFromScratch="false"/>
<backstage onShow="Backstage_onShow" onHide="Backstage_onHide">
...
</backstage>
</customUI>
In my Excel file, i have this :
Public Sub Ribbon_Load(ribbon As IRibbonUI)
Set Ruban = ribbon
End Sub
Public Sub Backstage_onShow(ByVal contextObject As Object)
'Rafraichissement du ruban
Ruban.Invalidate
End Sub
Public Sub Backstage_onHide(ByVal contextObject As Object)
End Sub
All this is contained in an Excel file, which is normal.
In Excel 2010, Excel files open by default in the same instance, which does not bother me, but, when a "normal" file is opened in the same instance as my customized backstage file, the normal file tries, I do not know by what miracle, to access the function Backstage_onShow Backstage_onHide and as soon as I display its backstage. However, this file should not even knows these functions exist because they are not reported for him.
Thank you in advance.
Sincerely,
Patrice.
PS : this is a link if you want to show my file. It's a safe code snippets of course !
#David, i have a solution !!!!
Thanks you to have take your time to answer me !!!
I try to explain you.
So, if we think a few moment, we note the problem is we need a stated place to store our code snippets. This place is in the XLSTART !!! If we use the PERSONAL.XLSB to store that :
Public Sub Ribbon_Load(ribbon As IRibbonUI)
Set Ruban = ribbon
End Sub
Public Sub Backstage_onShow(ByVal contextObject As Object)
'Rafraichissement du ruban
Ruban.Invalidate
End Sub
Public Sub Backstage_onHide(ByVal contextObject As Object)
'ErreurSaisieTaux = 0
End Sub
After, we can modify the customui.xml like that :
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad ="PERSONAL.XLSB!Ribbon_Load">
<ribbon startFromScratch="false"/>
<backstage onShow="PERSONAL.XLSB!Backstage_onShow" onHide="PERSONAL.XLSB!Backstage_onHide">
...
</backstage>
</customUI>
I have tried and it works.
What do you think about this idea ? Do you see drawbacks with this method ?
Patrice.
The Backstage is part of the Application, and you have specified that certain macros should be called for the onShow and onHide events.
Excel will always assume that an unqualified macro, e.g., Backstage_onShow should be found within scope of the ActiveWorkbook. When this macro does not exist in the ActiveWorkbook, the error raises.
To resolve this, revise the XML so that the macro call qualifies a specific workbook:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad ="Ribbon_Load">
<ribbon startFromScratch="false"/>
<backstage onShow="Exemple.xltm!Backstage_onShow" onHide="Exemple.xltm!Backstage_onHide">
...
</backstage>
</customUI>
Regarding Whether This Method Can Work on an XLTM File
Follow-up from comments:
When you use the above method on an XLSM file, the file which contains the Backstage_OnShow macro is open, and so calls to Exemple.xlsm!Backstage_OnShow work, because Exemple.xlsm is an open workbook file.
When you use this method on an XLTM file, to create a new file from template, the explicit XML with onShow ="Exemple.xltm!Backstage_OnShow will fail because the macro cannot be found. The macro cannot be found because Exemple.xltm is not a valid member of the Workbooks collection.
If, as you previously attempt, the XML does not fully qualify the macro (e.g., onShow="Backstage_OnShow), you will get the same error if you navigate to any open workbook which was not created by the XLTM, for the same reason: the macros cannot be found. In this case, the macro cannot be found in the scope of ActiveWorkbook.
It seems this simply may not work with an XLTM file (at least not without considerable effort)
You might be able to make it work if, instead of hijacking the Backstage you create a custom tab.
Alternatively, I think you would have to modify the Ribbon XML for each new instance of the file, and while it is possible to modify the Ribbon XML, I think it is not possible to do while the file is open.
Best solution I can think of would be to create an add-in or macro that does:
1) Prompt user for new filename
2) Create new file from template, save & close it
3) Modify the contents of the new file's Ribbon XML
4) Open the new file
You could see this for information about unpacking and modifying the XML:
http://www.jkp-ads.com/articles/Excel2007FileFormat02.asp)