My company recently upgraded from Attachmate EXTRA! to Attachmate Reflection. We have a number of macros that ran on extra to screen scrape. Basically, you would press a button to start the macro, it would ask you to select which extra screen you wanted to run it on (we always have 3+ open for different systems), and then the macro would just use the most recently selected (active) session. Here's the code for that:
Private Function GetReflectionWindow(sessionName As String) As ExtraScreen
'Gets the most recent active reflection or extra window.
'Requires Reference: EXTRACOM
Dim sys As ExtraSystem: Set sys = CreateObject("EXTRA.System")
Dim sess As ExtraSession
'Set current sesion to most recently active session
If MsgBox("Click on the " & sessionName & " session and click 'OK' to continue.", vbOKCancel) = vbCancel Then End
Set sess = sys.ActiveSession
'Checks the session
If sess Is Nothing Then
MsgBox "Could not locate a Reflection or Extra session!", vbCritical
End
Else
Set GetReflectionWindow = sess.Screen
sess.Activate
End If
End Function
This no longer works for Reflection systems. Instead I've looked at this documentation here. The problem is that when you use CreateObject or GetObject, it only looks at the first open instance, not the active instance.
Sub GetNewReflectionWindow()
Dim App As Attachmate_Reflection_Objects_Framework.ApplicationObject
Dim screen As Attachmate_Reflection_Objects_Emulation_IbmHosts.IbmScreen
Dim Terminal As Attachmate_Reflection_Objects_Emulation_IbmHosts.IbmTerminal
Dim Frame As Attachmate_Reflection_Objects.Frame
Dim View As Attachmate_Reflection_Objects.View
Set App = GetObject(, "Attachmate_Reflection_Objects_Framework.ApplicationObject")
End Sub
I can't see anything in the documentation or object browser that would let me select the active session like Extra.System did.
I had the same issue and finally found the solution in the MicroFocus docs of all places! Accessing All Open "Reflection Workspace" Objects in VBA Macro Code
Basically, continue to use CreateObject to get the Workspace, but do it in a loop renaming each one as you go:
Dim oSession As Object
'find all open sessions
Do
Set oSession = CreateObject("Reflection Workspace")
oActiveSession.AutomationServerName = "Unused"
Loop
'rename sessions back to original
Do
Set oActiveSession = CreateObject("Unused")
oActiveSession.AutomationServerName = "Reflection Workspace"
Loop
I put this into its own function as the CreateObject throws an error when you run out of Workspaces.
Related
I have a workbook that is password protected and I’d like to create a read only copy that other users can view on a different location on the network drive.
I know it’s a strange request as the other people could open the original as read only, but we don’t want them to know the location of the original or have anything to do with it, should they figure out my colleagues password.
The other issue we had was that people were opening as read only and it was still telling my colleague that it was locked by another user and he needs it for most of the day so that issue is annoying
Thanks in advance
What you could do is add the following event procedure to the ThisWorkbook module:
Const RemotePath As String = "D:\YourRemoteLocation\"
Private Sub Workbook_AfterSave(ByVal Success As Boolean)
On Error GoTo CleanUp
If Success And InStr(ThisWorkbook.Name, "Copy of ") = 0 Then
Dim CopyFullName As String
CopyFullName = RemotePath & "Copy of " & ThisWorkbook.Name
Application.EnableEvents = False
Dim fso As FileSystemObject 'Requires the Microsoft Scripting Runtime Library
Set fso = New FileSystemObject
fso.CopyFile Source:=ThisWorkbook.FullName, Destination:=CopyFullName
Dim ReadOnlyWorkbook As Workbook
Set ReadOnlyWorkbook = Workbooks.Open(Filename:=CopyFullName)
Application.DisplayAlerts = False
ReadOnlyWorkbook.SaveAs Filename:=CopyFullName, Password:=""
Application.DisplayAlerts = True
ReadOnlyWorkbook.Close SaveChanges:=False
End If
CleanUp:
Application.EnableEvents = True
Application.DisplayAlerts = True
Set fso = Nothing
End Sub
This code will run every time the workbook is saved and export the current file to the remote location. Then it will open the copy and save it as a workbook without password.
Note that I've added InStr(ThisWorkbook.Name, "Copy of ") = 0 as a condition to the If-statement. Instr returns the position where a substring (arg2) appears in the main string (arg1) or zero if the substring is absent from the main string. In this context, we want it to be zero since we don't want to run the code in the workbook copy.
In this method, the owner of the original file will have to supply their password every time they save. You could automate this by passing the password as an argument to the Open method like this:
Set ReadOnlyWorkbook = Workbooks.Open( _
Filename:=CopyFullName, _
Password:="MyPassword")
However, the password would then be accessible by people looking into the VBA code.
Alternatively you could get the password from a local file that wouldn't be accessible from the Network, but then the file path would be visible.
And if the remote folder is not already set to be Read-only mode, you can make sure that people opening the remote version of the file do so in Read-Only mode by adding the following event procedure after the previous one.
Private Sub Workbook_Open()
If InStr(ThisWorkbook.Name, "Copy of ") = 0 Then
ThisWorkbook.ChangeFileAccess Mode:=xlReadOnly
End If
End Sub
Obviously this will only work if they enable macros.
We copy data from Excel cells to a new Word document based on a .docx template document. The positioning in the Word document is found with a bookmark.
The VBA code has been working, but since upgrading to MS Office 2016 from 2010 we have been getting errors:
run time error 4605 this method or property is not available because
the clipboard is empty or not valid
And then I get
4605 This method or property is not available because this command is
not available for reading
I tried the wdDoc.Bookmarks… and I get
6124 You are not allowed to edit this selection because it is
protected
I checked all the protection, trust center settings, etc. and all look correct.
"editProject" is a single cell label.
Dim wdApp As Object
Dim wdDoc As Object
Set wdApp = CreateObject("Word.application")
Set wdDoc = wdApp.Documents.Open(Filename:=WdocT, ReadOnly:=True)
' Project NAME
wdDoc.Bookmarks("BOOKMARK1").Range.Select ' wdDoc.Bookmarks("CLIENT").Range.Select
xlData = Sheets("Data Input").Range("editProject") ' get the data
'THIS IS THE PROBLEM LINE
wdApp.Selection.TypeText Text:=xlData ' place in doc '8/10/19 FALLING OVER HERE
I know the doc is opening, and the bookmark is found, as I put in the following to check:
'temp TRY THIS
Dim tempRange As Word.Range
Dim tempStart As Long
Dim tempEnd As Long
' Set tempRange = wdDoc.Bookmarks("BOOKMARK1").Range
Set tempRange = wdDoc.Bookmarks("BOOKMARK2").Range ' THIS WORKS
tempStart = tempRange.Start
tempEnd = tempRange.End
I tried clearing the clipboard with the following:
' 8/10/19 Bruce the following may help with clipboard error message 4605
wdDoc.UndoClear
Dim oData As New DataObject ' object to use the clipboard
oData.SetText Text:=Empty ' clear
oData.PutInClipboard ' take in the clipboard to empty it
'Application.Wait (Now + TimeValue("00:00:10")) ' this is required to stop clipboard overflow error
Application.CutCopyMode = False ' 8/10/19 Bruce - this should clear the clipboard
DoEvents ' test fixing error 4605
' DoEvents passes control to the operating system.
' Control is returned after the operating system has
' finished processing the events in its queue
I tried the following in the appropriate place, but it makes no difference:
Dim wdApp As Word.Application
Dim wdDoc As Word.Document
wdApp.Visible = True
I tried the following line instead of the copy, but the same error is thrown;
wdApp.Selection.MoveDown Unit:=wdLine, Count:=1
If Excel General setting "Open e-mail attachments and other uneditable files in reading view" is NOT ticked, it seems to work.
I am having a similar issue after my environment was upgraded to Office 2016. The code I had was working perfectly before the upgrade.
One thing I noticed later was that, after the upgrade to Office 2016, the same VBA code that used to work would open a Word doc in the "reading mode". In this mode, no editing of the document is allowed, no matter via the UI or via code. This is the meaning of the cryptic error message "4605 This method or property is not available because this command is not available for reading".
To get around this, I added the following line to make Word switch back to the "Print layout view".
objWord.ActiveWindow.View.Type = wdPrintView
where objWord is the Word object created by code not listed here.
Then I can use my old code like these to type text via VBA:
objWord.Selection.GoTo What:=wdGoToBookmark, Name:=bookmarkName
objWord.Selection.TypeText Text:=someText
So if you notice your VBA code happens to open a Word doc in the "Reading mode", you can try this work around.
A similar situation is described at this link and I came across it after I found my work around:
https://blogs.msmvps.com/wordmeister/2013/02/22/word2013bug-not-available-for-reading/
I have an Excel Workbook that provides a suite of reports. The data comes from an Access database, ole db connection etc.
I have a switchboard type screen on the main Excel worksheet, with buttons to view the various reports, and to open Access to an add new data form, and to an edit selected data form. I have all this working perfectly on my machine, with Full Access installed, using the following code in an Excel Module.
Sub OpenFormAmend()
Dim ac As Object
Dim strID As String
strID = Range("IniId").Value
On Error Resume Next
Set ac = GetObject(, "Access.Application")
If ac Is Nothing Then
Set ac = GetObject("", "Access.Application")
ac.OpenCurrentDatabase "C:\Database.accdb"
ac.DoCmd.OpenForm "frm_Amend", , , "ID =" & strID
ac.UserControl = True
Set ac = Nothing
End If
AppActivate "Microsoft Access"
ac.OpenCurrentDatabase "C:\Database.accdb"
ac.DoCmd.OpenForm "frm_Amend", , , "ID =" & strID
End Sub
This however doesn't do anything at all on a user's machine with Access Runtime only. I have been able to get the database to open with the following code pinched from a similar question here.
CreateObject("WScript.Shell").Run ("""C:\Database.accdb""")
But I have no clue how to go about getting the above open to the particular form, or to open to the selected record. Any help on getting started would be greatly appreciated!
You are calling GetObject() again after it has failed to return a currently running instance. Your second call should be to CreateObject().
Using On Error Resume Next is understandable before the first GetObject call, but you should turn on error reporting immediately afterwards (e.g. On Error Goto MyErrorHandler) otherwise you will have no idea what has gone wrong...
I have the following code:
Option Explicit
Randomize
Dim a, song, album
a = Int((Rnd*195)+1)
song = "B" & a
album = "A" & a
Dim objApp, objWbs, objWorkbook, objSheet
Set objApp = CreateObject("Excel.Application")
Set objWbs = objApp.WorkBooks
objApp.Visible = False
Set objWorkbook = objWbs.Open("C:\Users\Name\Documents\Music.xlsx")
Set objSheet = objWorkbook.Sheets("Sheet1")
song = objSheet.Range(song).Value
album = objSheet.Range(album).Value
objWorkbook.Close False
objWbs.Close
objApp.Quit
Set objSheet = Nothing
Set objWorkbook = Nothing
Set objWbs = Nothing
Set objApp = Nothing
MsgBox("Album name: " & album & vbNewLine & "Song name: " & song)
It prints two random cells between row 1 and row 195 from the Excel sheet "Music". One of them - the one in column A - represents the album, and the other represents the song. The problem is that it takes quite a long time to return the results, about 20 seconds.
I was wondering whether there was a more efficient method I could use to get the results more quickly.
I think Ansgar Wiechers' answer is probably correct that starting Excel is the slowest part of the script. You could try using ADO to connect to the Excel file as if it were a database. This would avoid starting Excel:
Option Explicit
Randomize
Dim conn, rst, song, album
Set conn = CreateObject("ADODB.Connection")
conn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\Users\Name\Documents\Music.xlsx;" & _
"Extended Properties='Excel 12.0 Xml;HDR=NO';"
' Select a random record; reference https://stackoverflow.com/a/9937263/249624
' Asc(album) is just a way to get some numeric value from the existing data
Set rst = conn.Execute("SELECT TOP 1 F1 AS album, F2 as song FROM [Sheet1$] ORDER BY Rnd(-(100000*Asc(F1))*Time())")
If rst.EOF Then
song = "[NO RECORDS]"
album = "[NO RECORDS]"
Else
song = rst("song").Value
album = rst("album").Value
End If
MsgBox("Album name: " & album & vbNewLine & "Song name: " & song)
The one possible snag here is that VBScript is run by default using the 64-bit version of wscript.exe, and the 64-bit ACE.OLEDB is only available if you installed the 64-bit version of Office 2010 or higher. This can be worked around, though, by running the script with the 32-bit version of wscript.exe (e.g., see How do I run a VBScript in 32-bit mode on a 64-bit machine?).
If you decide to go this route and can control the input Excel file, I would recommend adding a header row to the spreadsheet and changing HDR=NO to HDR=YES in the connection string. That way, you can refer to the columns by name in the query (e.g., SELECT TOP 1 album, song ...) instead of relying on the "F1" syntax.
The most time-consuming steps in your script are most likely
starting Excel and
opening the workbook.
One thing you could do is using an already running Excel instance instead of creating a new one all the time:
quitExcel = False
On Error Resume Next
Set objApp = GetObject(, "Excel.Application")
If Err Then
Set objApp = CreateObject(, "Excel.Application")
quitExcel = True
End If
On Error Goto 0
The variable quitExcel indicates whether you need to close Excel at the end of your script (when you created a new instance) or not (when you used an already running instance).
You could also check if the workbook is already open:
wbOpen = False
For Each wb In objWbs
If wb.Name = "Music.xlsx" Then
Set objWorkbook = wb
wbOpen = True
Exit For
End If
Next
If Not wbOpen Then
Set objWorkbook = objWbs.Open("C:\Users\Name\Documents\Music.xlsx")
End If
Other than that your only options are changing the way the data is stored or buying faster hardware, AFAICS.
Cheran, I disagree with the answers here.
I just ran your script on my 5 year old laptop, and got the answer in about 2 seconds. Whether an instance of Excel was already open made no difference in run time.
(I created a test Music.xlsx spreadsheet by entering "A1" in cell A1, and "B1" in cell B1, and dragged those cells down to row 195 to get a nice set of unique sample data).
Why don't you make Excel visible when it runs, so that you can see for yourself what is going on?
You might see, for example, that Excel takes one second to open, and the Excel Add-ins you have are taking the other fifteen seconds to initialize. It's also possible that your machine and/or hard drive is slow and does indeed take 20 seconds to run this. Who knows...
To get some insight, please make objApp.Visible = True and rerun.
You might also comment out the final eight lines, except for the MsgBox line so that your Excel file stays open after script is done, so that you might see other clues.
Other observations:
1) Your method of opening Excel with CreateObject from a .vbs script seems to be the most reliable/accepted method of automating Excel.
2) It's not stated here HOW you are running the .vbs script (command line vs. double-click from Explorer). Your script is running, but be aware that using cscript.exe to run the .vbs is also common when people try to automate this.
3) I'm not used to seeing an external vbs interact with the data inside Excel...I'm used to having vbs open Excel.xlsm, then letting a Macro do the number crunching. But, Macros bring an entirely different set of headaches. I'm not saying your method is good or bad...just not used to that approach.
Good luck!
The first part is now working [
I have the following which just seems to hang; the part that adds/deletes the module works when running in VBA
I note that I'm prompted with a dialog saying 'this workbook contains links to other data sources' which I ok to, then it hangs
So I tried setting the second argument to 0 and also tried 2 but still it hangs
(2nd arg is UpdateLinks as can be found here )
]
dim objExcel
dim objWorkbook
Set objExcel = CreateObject("Excel.Application")
Set objWorkbook = objExcel.Workbooks.Open( "H:\M\X\C.xls", 0 , , ,"PASSWORD!" )
Const modpath = "H:\M\V\"
Const modtest = "TEST.cls"
Const modname = "TEST"
On Error Resume Next
Dim vbcomp
Set vbcomp = ActiveWorkbook.VBProject.VBComponents(modname)
objWorkbook.VBProject.VBComponents.Remove vbcomp
objWorkbook.VBProject.VBComponents.Import modpath & modtest
objWorkbook.Save
objWorkbool.Close
set vbcomp = nothing
set objworkbook = nothing
set objExcel = nothing
edited again 14/04/2009
I have now also allowed the 'tools - macro - security - vbproject access'
The script now finishes, however, when trying to open the xls to see if the changes have been made, I get a message informing me that the sheet is locked by "account used to run script"; open 'read only'/notify
Why isn't it releasing control correctly**?**
First thought: Does your workbook already contain a reference (within VBA) to the "Microsoft Visual Basic for Applications Extensibility" library? You'll need it to be able to talk to the VBProject object. Actually, you probably do have the reference if your code works in VBA.
Second thought: ActiveWorkbook is probably not defined outside of an actual workbook. Try replacing it with your objWorkbook object.
Here's a third thought. Did you try setting the Application's DisplayAlerts property to FALSE before you include the module?
The edited script works.
The problem was caused by the fact that I was supplying the password at the workbook level and not at the VBA project level.
A quick search on the web reveals that it is not possible to do this anyway (sendkeys etc) so after manually removing the password on the project, the problem is solved