Using VBS to open MS Project file as read only - vba

I am trying to find a quick way to open MS Project files as read-only, preferrably from explorer. I was following the tutorial head to do the same thing from excel using this tutorial which uses the following code:
'-------------------- Open as Read-Only.vbs --------------------
Option Explicit
Dim app 'As Object
Call OpenAsReadOnly
Sub OpenAsReadOnly()
On Error Resume Next
Set app = GetObject(, "Excel.Application")
If IsEmpty(app) Then Set app = CreateObject("Excel.Application")
With app.Workbooks.Open(WScript.Arguments(0))
If Not .ReadOnly Then .ChangeFileAccess 3 'xlReadOnly
app.Visible = True
End With
End Sub
but when I try to modify it for Project like this:
'-------------------- Open as Read-Only.vbs --------------------
Option Explicit
Dim app 'As Object
Call OpenAsReadOnly
Sub OpenAsReadOnly()
On Error Resume Next
Set app = GetObject(, "MSProject.Application")
If IsEmpty(app) Then Set app = CreateObject("MSProject.Application")
With app.Project.Open(WScript.Arguments(0))
If Not .ReadOnly Then .ChangeFileAccess 3 'xlReadOnly
app.Visible = True
End With
End Sub
MS Project flashes open then closes. Any ideas?

The method to open MS Project files is app.FileOpenEx. The first parameter is the file name and the second is the ReadOnly option. There is no ChangeFileAccess method in MS Project.
Try this instead:
Dim app
Set app = GetObject(, "MSProject.Application")
If IsEmpty(app) Then Set app = CreateObject("MSProject.Application")
app.FileOpenEx(WScript.Arguments(0), True)

Related

Ignore an "external" error when opening a Word document

I'm attempting to copy the contents of a word document, but the document has embedded vba which causes a compile error anytime I open it.
The error reads
"The code in this project must be updated for use on 64-bit systems."
I expect this error to occur, so how do I tell my vba code in Access to ignore the errors without changing the word doc?
My vba code so far - the DisplayAlerts = wdAlertsNone does not work, neither does setting it to False:
Private Sub cmdQuickLtr_Click()
Dim wApp As Word.Application
Set wApp = CreateObject("Word.Application")
wApp.DisplayAlerts = wdAlertsNone
Dim doc As Object
Set doc = wApp.Documents.Open(Me.tbLetterPath.Value)
doc.Content.Copy
doc.Close
'do something with the copied content
wApp.DisplayAlerts = wdAlertsAll
Set doc = Nothing
Set wApp = Nothing
End Sub
Thank you all in advance.

Running a standalone excel macro with a batch file [duplicate]

I have an Excel VBA macro which I need to run when accessing the file from a batch file, but not every time I open it (hence not using the open file event). Is there a way to run the macro from the command line or batch file? I'm not familiar with such a command.
Assume a Windows NT environment.
You can launch Excel, open the workbook and run the macro from a VBScript file.
Copy the code below into Notepad.
Update the 'MyWorkbook.xls' and 'MyMacro' parameters.
Save it with a vbs extension and run it.
Option Explicit
On Error Resume Next
ExcelMacroExample
Sub ExcelMacroExample()
Dim xlApp
Dim xlBook
Set xlApp = CreateObject("Excel.Application")
Set xlBook = xlApp.Workbooks.Open("C:\MyWorkbook.xls", 0, True)
xlApp.Run "MyMacro"
xlApp.Quit
Set xlBook = Nothing
Set xlApp = Nothing
End Sub
The key line that runs the macro is:
xlApp.Run "MyMacro"
The simplest way to do it is to:
1) Start Excel from your batch file to open the workbook containing your macro:
EXCEL.EXE /e "c:\YourWorkbook.xls"
2) Call your macro from the workbook's Workbook_Open event, such as:
Private Sub Workbook_Open()
Call MyMacro1 ' Call your macro
ActiveWorkbook.Save ' Save the current workbook, bypassing the prompt
Application.Quit ' Quit Excel
End Sub
This will now return the control to your batch file to do other processing.
The method shown below allows to run defined Excel macro from batch file, it uses environment variable to pass macro name from batch to Excel.
Put this code to the batch file (use your paths to EXCEL.EXE and to the workbook):
Set MacroName=MyMacro
"C:\Program Files\Microsoft Office\Office15\EXCEL.EXE" "C:\MyWorkbook.xlsm"
Put this code to Excel VBA ThisWorkBook Object:
Private Sub Workbook_Open()
Dim strMacroName As String
strMacroName = CreateObject("WScript.Shell").Environment("process").Item("MacroName")
If strMacroName <> "" Then Run strMacroName
End Sub
And put your code to Excel VBA Module, like as follows:
Sub MyMacro()
MsgBox "MyMacro is running..."
End Sub
Launch the batch file and get the result:
For the case when you don't intend to run any macro just put empty value Set MacroName= to the batch.
you could write a vbscript to create an instance of excel via the createobject() method, then open the workbook and run the macro. You could either call the vbscript directly, or call the vbscript from a batch file.
Here is a resource I just stumbled accross:
http://www.codeguru.com/forum/showthread.php?t=376401
If you're more comfortable working inside Excel/VBA, use the open event and test the environment: either have a signal file, a registry entry or an environment variable that controls what the open event does.
You can create the file/setting outside and test inside (use GetEnviromentVariable for env-vars) and test easily. I've written VBScript but the similarities to VBA cause me more angst than ease..
[more]
As I understand the problem, you want to use a spreadsheet normally most/some of the time yet have it run in batch and do something extra/different. You can open the sheet from the excel.exe command line but you can't control what it does unless it knows where it is. Using an environment variable is relatively simple and makes testing the spreadsheet easy.
To clarify, use the function below to examine the environment. In a module declare:
Private Declare Function GetEnvVar Lib "kernel32" Alias "GetEnvironmentVariableA" _
(ByVal lpName As String, ByVal lpBuffer As String, ByVal nSize As Long) As Long
Function GetEnvironmentVariable(var As String) As String
Dim numChars As Long
GetEnvironmentVariable = String(255, " ")
numChars = GetEnvVar(var, GetEnvironmentVariable, 255)
End Function
In the Workbook open event (as others):
Private Sub Workbook_Open()
If GetEnvironmentVariable("InBatch") = "TRUE" Then
Debug.Print "Batch"
Else
Debug.Print "Normal"
End If
End Sub
Add in active code as applicable. In the batch file, use
set InBatch=TRUE
Instead of directly comparing the strings (VB won't find them equal since GetEnvironmentVariable returns a string of length 255) write this:
Private Sub Workbook_Open()
If InStr(1, GetEnvironmentVariable("InBatch"), "TRUE", vbTextCompare) Then
Debug.Print "Batch"
Call Macro
Else
Debug.Print "Normal"
End If
End Sub
I have always tested the number of open workbooks in Workbook_Open(). If it is 1, then the workbook was opened by the command line (or the user closed all the workbooks, then opened this one).
If Workbooks.Count = 1 Then
' execute the macro or call another procedure - I always do the latter
PublishReport
ThisWorkbook.Save
Application.Quit
End If
# Robert: I have tried to adapt your code with a relative path, and created a batch file to run the VBS.
The VBS starts and closes but doesn't launch the macro... Any idea of where the issue could be?
Option Explicit
On Error Resume Next
ExcelMacroExample
Sub ExcelMacroExample()
Dim xlApp
Dim xlBook
Set xlApp = CreateObject("Excel.Application")
Set objFSO = CreateObject("Scripting.FileSystemObject")
strFilePath = objFSO.GetAbsolutePathName(".")
Set xlBook = xlApp.Workbooks.Open(strFilePath, "Excels\CLIENTES.xlsb") , 0, True)
xlApp.Run "open_form"
Set xlBook = Nothing
Set xlApp = Nothing
End Sub
I removed the "Application.Quit" because my macro is calling a userform taking care of it.
Cheers
EDIT
I have actually worked it out, just in case someone wants to run a userform "alike" a stand alone application:
Issues I was facing:
1 - I did not want to use the Workbook_Open Event as the excel is locked in read only.
2 - The batch command is limited that the fact that (to my knowledge) it cannot call the macro.
I first wrote a macro to launch my userform while hiding the application:
Sub open_form()
Application.Visible = False
frmAddClient.Show vbModeless
End Sub
I then created a vbs to launch this macro (doing it with a relative path has been tricky):
dim fso
dim curDir
dim WinScriptHost
set fso = CreateObject("Scripting.FileSystemObject")
curDir = fso.GetAbsolutePathName(".")
set fso = nothing
Set xlObj = CreateObject("Excel.application")
xlObj.Workbooks.Open curDir & "\Excels\CLIENTES.xlsb"
xlObj.Run "open_form"
And I finally did a batch file to execute the VBS...
#echo off
pushd %~dp0
cscript Add_Client.vbs
Note that I have also included the "Set back to visible" in my Userform_QueryClose:
Private Sub cmdClose_Click()
Unload Me
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
ThisWorkbook.Close SaveChanges:=True
Application.Visible = True
Application.Quit
End Sub
Anyway, thanks for your help, and I hope this will help if someone needs it
I'm partial to C#. I ran the following using linqpad. But it could just as easily be compiled with csc and ran through the called from the command line.
Don't forget to add excel packages to namespace.
void Main()
{
var oExcelApp = (Microsoft.Office.Interop.Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
try{
var WB = oExcelApp.ActiveWorkbook;
var WS = (Worksheet)WB.ActiveSheet;
((string)((Range)WS.Cells[1,1]).Value).Dump("Cell Value"); //cel A1 val
oExcelApp.Run("test_macro_name").Dump("macro");
}
finally{
if(oExcelApp != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(oExcelApp);
oExcelApp = null;
}
}
I generally store my macros in xlam add-ins separately from my workbooks so I wanted to open a workbook and then run a macro stored separately.
Since this required a VBS Script, I wanted to make it "portable" so I could use it by passing arguments. Here is the final script, which takes 3 arguments.
Full Path to Workbook
Macro Name
[OPTIONAL] Path to separate workbook with Macro
I tested it like so:
"C:\Temp\runmacro.vbs" "C:\Temp\Book1.xlam" "Hello"
"C:\Temp\runmacro.vbs" "C:\Temp\Book1.xlsx" "Hello" "%AppData%\Microsoft\Excel\XLSTART\Book1.xlam"
runmacro.vbs:
Set args = Wscript.Arguments
ws = WScript.Arguments.Item(0)
macro = WScript.Arguments.Item(1)
If wscript.arguments.count > 2 Then
macrowb= WScript.Arguments.Item(2)
End If
LaunchMacro
Sub LaunchMacro()
Dim xl
Dim xlBook
Set xl = CreateObject("Excel.application")
Set xlBook = xl.Workbooks.Open(ws, 0, True)
If wscript.arguments.count > 2 Then
Set macrowb= xl.Workbooks.Open(macrowb, 0, True)
End If
'xl.Application.Visible = True ' Show Excel Window
xl.Application.run macro
'xl.DisplayAlerts = False ' suppress prompts and alert messages while a macro is running
'xlBook.saved = True ' suppresses the Save Changes prompt when you close a workbook
'xl.activewindow.close
xl.Quit
End Sub
You can check if Excel is already open. There is no need to create another isntance
If CheckAppOpen("excel.application") Then
'MsgBox "App Loaded"
Set xlApp = GetObject(, "excel.Application")
Else
' MsgBox "App Not Loaded"
Set wrdApp = CreateObject(,"excel.Application")
End If

Object error when calling application

I'm trying to open an excel document when a user clicks a button. There are multiple buttons that open the same document but I want it to change the worksheet if the document is already opened and not another instance of the document
Public objExcel As Object
Sub Main()
Set objExcel = CreateObject("Excel.Application")
End Sub
Public Sub QE1_Click()
Call Main
If objExcel Is Nothing Then
objExcel.Visible = True
objExcel.Workbooks.Open "H:\My Documents\Flowchart to Word\Quality and Environmental management system flowchart.xlsm"
objExcel.Worksheets("Project enquiry").Activate
Else
objExcel.Worksheets("Project enquiry").Activate
End If
End Sub
Public Sub QE2_Click()
Call Main
If objExcel Is Nothing Then
objExcel.Visible = True
objExcel.Workbooks.Open "H:\My Documents\Flowchart to Word\Quality and Environmental management system flowchart.xlsm"
objExcel.Worksheets("Order and project release").Activate
Else
objExcel.Worksheets("Order and project release").Activate
End If
End Sub
Running the code gives me the error: Application-defined or object-defined error
Can anyone point out what's causing the error?
This code here:
Set objExcel = CreateObject("Excel.Application")
Is creating a new Excel Application. Then this one here:
objExcel.Worksheets("Project enquiry").Activate
already assumes that the new application is having a worksheet called Project enquiry, which cannot be true. Thus, you are getting the 1004 error. Refine your business logic and it should work.
In general, try to delete this condition If objExcel Is Nothing Then because objExcel will never be Nothing, you are calling Main which assigns object to it. Then the code may work.
A little upgardes to get your code to work more efficiently:
Option Explicit
Public objExcel As Object
Sub Main()
' don't need to open another instance of Excel, can use the same instance
On Error Resume Next
Set objExcel = GetObject(, "Excel.Application") ' check if there is an open instance of Excel running
On Error GoTo 0
If objExcel Is Nothing Then
Set objExcel = CreateObject("Excel.Application")
End If
End Sub
'==================================================================
Public Sub QE1_Click()
Dim wb As Workbook
Dim sht As Worksheet
If objExcel Is Nothing Then
Main ' call sub that initializes an Excel application object
End If
objExcel.Visible = True
Set wb = objExcel.Workbooks.Open("H:\My Documents\Flowchart to Word\Quality and Environmental management system flowchart.xlsm")
On Error Resume Next
Set sht = wb.Worksheets("Project enquiry")
On Error GoTo 0
If sht Is Nothing Then ' sheet doesn't exist >> raise an error
MsgBox "Workbook doesn't have a sheet named 'Project enquiry'", vbCritical, "Sheet critical error"
Else ' sheet object created successfully
sht.Activate ' <-- NOT SURE why you need to use Activate ?
End If
End Sub
Note: same modifications should be applied to Sub QE2_Click().
If you want to control an Office application from within a different one - Excel from within Word, for example, you first need to decide whether you want to write your code using Intellisense and what's called "early-binding" or whether you want to use "late-binding", which does not have Intellisense but has the advantage that you don't need to rely on a link to the other (Excel) VBA code library.
In order to use early-binding you must go to Tools/References in the VBA editor and activate the checkbox next to the entry for the other application (Excel). Only then can you use something like Dim wb As Workbook.
If you don't want to use early-binding, then you must declare things as Dim wb as Object, same as you've done for the Excel.Application.
In order have code decide whether it needs to use an running instance of the other application (Excel) or start a new instance, use the method GetObject. This can be used to pick up any running instance, or to check for a specific file.
Set ojbExcel = GetObject(,"Excel.Application")
vs
Set objExcel = GetObject("H:\My Documents\Flowchart to Word\Quality and Environmental management system flowchart.xlsm")
If what you're looking for with GetObject isn't currently available, you'll get an error which you can check and subsequently use CreateObject in order to start the application.
Option Explicit
Public objExcel As Object
Sub Main()
''' Try to re-use an existing instance
' If that instance does not exist, an error will be generated
' So temporarily turn off error messages
On Error Resume Next
' check if there is an open instance of Excel running
Set objExcel = GetObject(, "Excel.Application")
' Turn error messages back on
On Error GoTo 0
If objExcel Is Nothing Then
Set objExcel = CreateObject("Excel.Application")
End If
End Sub
Note that you should usually not turn off error-messaging - you need a really good reason to do so and you should turn it on again as soon as possible.
If all your buttons are essentially the same - in the two procedures you show everything is the same except the Else step - then you can cut down on the duplicate code. (That will also make maintenance simpler if you don't have to make changes in all the procedures).
Also, since the Else action is the same as the last action in the If you can simply put that after End If.
Private Sub ActivateWorksheet(wsName as String)
If objExcel Is Nothing Then
objExcel.Visible = True
objExcel.Workbooks.Open "H:\My Documents\Flowchart to Word\Quality and Environmental management system flowchart.xlsm"
End If
objExcel.Worksheets(wsName).Activate
End Sub
Public Sub QE1_Click()
Call Main
ActivateWorksheet "Project enquiry"
End Sub
Public Sub QE2_Click()
Call Main
ActivateWorksheet "Order and project release"
End Sub

Excel vba, Opening new Application: Microsoft Excel is waiting for another application to complete an OLE action

I have the following vba code. It creates new Excel application and uses it to open a file. Then it MsgBoxes some cell's value in this file.
Sub TestInvis()
Dim ExcelApp As Object
Set ExcelApp = CreateObject("Excel.Application")
Dim WB As Workbook
Set WB = ExcelApp.Application.Workbooks.Open("Y:\vba\test_reserves\test_data\0503317-3_FO_001-2582480.XLS")
Dim title As String
title = WB.Worksheets(1).Cells(5, 4).Value
MsgBox (title)
WB.Save
WB.Close
ExcelApp.Quit
Set ExcelApp = Nothing
End Sub
The problem is that after MsgBoxing it slows down and eventually gives a Microsoft Excel is waiting for another application to complete an OLE action window. Why does it do this? It's not like there are any hard commands being implemented. And how should I deal with it?
This happens because the Excel instance in ExcelApp is waiting for User Input, most likely.
You can try to add ExcelApp.DisplayAlerts = False to skip any pop-ups that might be there.
Also, while troubleshooting add the line ExcelApp.Visible = True so you can see what's going on in the second instance and troubleshoot there.
I encountered this problem in the following situations:
An alert was opened by the Application Instance and it was awaiting user input.
While opening a file, it was coming up with some message about a crash when the file was previously opened and whether I wanted to open the saved version or the in memory version (although this should happen before the msgBox)
If you run the code multiple times and it crashes, it might have the file open as read only since there's another hidden instance of Excel that locked it (check your task manager for other Excel processes)
Rest assured that in any case the problem is not with your code itself - It runs fine here.
Code that works for me.
You can select the file from FileDialog. In comments You have code that close the workbook without saving changes. Hope it helps.
Option Explicit
Sub Import(Control As IRibbonControl)
Dim fPath As Variant
Dim WB As Workbook
Dim CW As Workbook
On Error GoTo ErrorHandl
Set CW = Application.ActiveWorkbook
fPath = Application.GetOpenFilename(FileFilter:="Excel file, *.xl; *.xlsx; *.xlsm; *.xlsb; *.xlam; *.xltx; *.xls; *.xlt ", Title:="Choose file You want to openn")
If fPath = False Then Exit Sub
Application.ScreenUpdating = False
Set WB = Workbooks.Open(FileName:=fPath, UpdateLinks:=0, IgnoreReadOnlyRecommended:=True)
Set WB = ActiveWorkbook
MsgBox("File was opened.")
'Application.DisplayAlerts = False
'WB.Close SaveChanges:=False
'Application.DisplayAlerts = True
'MsgBox ("File was closed")
Exit Sub
ErrorHandl:
MsgBox ("Error occured. It is probable that the file that You want to open is already opened.")
Exit Sub
End Sub
None of these methods worked for me. I was calling a DLL for MATLAB from VBA and a long simulation would pop up that Excel was waiting on another application OLE action, requiring me to click it off for the routine to continue, sometimes quite a few times. Finally this code worked (saved in a new module): https://techisours.com/microsoft-excel-is-waiting-for-another-application-to-complete-an-ole-action/
The way I used it is a little tricky, as the directions don't tell you (here and elsewhere) which causes various VBA errors, so I add to the description for what works in Excel 365:
Create a new module called "ToggleOLEWarning" (or in any new module, important!) which only contains the following code:
Private Declare Function CoRegisterMessageFilter Lib "ole32" (ByVal IFilterIn As Long, ByRef PreviousFilter) As Long
Public Sub KillOLEWaitMsg()
Dim IMsgFilter As Long
CoRegisterMessageFilter 0&, IMsgFilter
End Sub
Public Sub RestoreOLEwaitMsg()
Dim IMsgFilter As Long
CoRegisterMessageFilter IMsgFilter, IMsgFilter
End Sub
Then in your main function, just decorate the long running OLE action with a couple lines:
Call KillOLEWaitMsg
'call your OLE function here'
Call RestoreOLEwaitMsg
And it finally worked. Hope I can save someone the hour or two it took for me to get it working on my project.

Using VB script to run VBA Macros and save the excel workbook

I am trying to run a macro from a * .vbs file. You can find the code below. The module runs with no issue (this has been tested). But workbook is not saved. I tried adding ThisWorkbook.Save into the module itself but it causes an error and tells me that my file is ReadOnly. To clarify, I want to know how to save the workbook after running a macro through VBScript.
Option Explicit
'On Error Resume Next 'Comment-out for debugging
ExcelMacro
Sub ExcelMacro()
Dim xlApp
Dim xlBook
Set xlApp = CreateObject("Excel.Application")
Set xlBook = xlApp.Workbooks.Open("\\internal.company.com\River_Automated.xlsm", 0, True)
xlApp.Run "Update"
xlBook.Save
xlBook.Close
xlApp.Quit
Set xlBook = Nothing
Set xlApp = Nothing
End Sub
Some side notes that may be helpful:
After running VBScript I got this message that:
Resume.xlsw already exists. Do you want to replace it?
I added lines that create a text file right before End Sub and it creates it. So, it runs the macro but does not save the workbook.
This line:
Set xlBook = xlApp.Workbooks.Open("\\internal.company.com\River_Automated.xlsm", 0, True)
Is causing the file to be opened in ReadOnly mode. The third argument of the Open method determines whether to open the file in ReadOnly mode if True, or otherwise False.
ReadOnly | Optional | Variant | True to open the workbook in read-only mode.
The solution would be either to open the file in ReadOnly = False via:
Set xlBook = xlApp.Workbooks.Open("\\internal.company.com\River_Automated.xlsm", 0, False)
Or, to check if the file is ReadOnly and if so, save it as something else.
If Not xlBook.ReadOnly Then
xlBook.Save
Else
xlBook.SaveAs ... '### Modify as needed
End If
xlBook.Close
xlApp.Quit
Set xlBook = Nothing
Set xlApp = Nothing