we have a third party program which uses OLE to write Data to Excel. Unfortunately, the programmers of that application do not understand that making excel invisible and hiding updates speed up excel enormously.
At this moment, large jobs take up to 15 minutes to complete which is quite annoying.
Therefore I want to write an Addin, which shuts down visibillity etc. when the program writes to excel. Is there a way to do that? It is the only application which uses this technique, so detecting COM / OLE Interaction would be sufficient.
Rather that have excel check to see if it is in the hands of OLE automation, it is possible to check windows for the name of the process that is runnning the OLE
something like
Function isRunning(ProcessName) As Boolean
Dim objWMIcimv2 As Object
Dim objList As Object
Set objWMIcimv2 = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\.\root\cimv2") 'Connect to CIMV2 Namespace
Set objList = objWMIcimv2.ExecQuery _
("select * from win32_process where name='" & ProcessName & "'")
If objList.Count > 0 Then
isRunning = True
End If
End Function
From there you can suppress screen updating pretty easily. I am making a guess that the third party application doesn't usually sit running idle on the users desktop. if it does then obviously this approach won't help you.
Code inspired from this link on terminating programs
Related
I have an app I coded in Excel that suits the needs of my project; it serves the purpose of keeping track of quite a lengthy process and prerequisites and such.
It feeds off of a certain number of tables in my file.
The thing is, only one user can currently work on that file; and since we have multiple teams working on different parts in parallel, it would be nice to host that somehow in a way that would remove the single-user restriction.
Do any of you have an idea of how I could work around this?
I worked on a solution for a very similar project of keeping track of a hospital's labor utilization (nursing employee census, if you will) on a day-to-day basis across every nursing-based department in the hospital system. This solution relies on a couple conditions:
That it will be unlikely two or more people will need to save data to the final file at the same time (meaning within seconds of each other).
All the various users of the file will have access to at least one commonly-shared network drive or location.
In our case, we created a new file each day, but it wouldn't be difficult to adjust the data-writing code to append data, rather than create a new file and dump data into that new file.
The rough outline of the process is this:
Create a read-only destination file (.xlsx in our case) in a network location that contains tables of data split between n worksheets.
Create an interactive form (.xlsm) that allows user input and then on form submission, opens the destination .xlsx file and saves the form data to it, then closes it. This interactive .xlsm file can be placed in the same network location, with shortcuts created on as many peoples' desktops (or departmental shares, for example) as necessary.
With the speed of Excel and VBA, this means you're only "opening" the destination file for a second or two to write the form data, no matter how long one user may have a copy of the form open.
One thing that will be necessary is to check if the file is open, and gracefully alert the user if they need to try again, which you can do with a function covering the related error codes, for example:
Function IsFileOpen(FileName As String)
Dim iFilenum As Long
Dim iError As Long
On Error Resume Next
iFilenum = FreeFile()
Open FileName For Input Lock Read As #iFilenum
Close iFilenum
iError = Err
On Error GoTo 0
Select Case iError
Case 0: IsFileOpen = False
Case 70: IsFileOpen = True
Case 53: IsFileOpen = "Not Found"
Case Else: Error iError
End Select
End Function
which can be called via some code like (pseudo code):
Private Sub UpdateData(ByVal thesheet As String)
Dim xlApp As New Excel.Application
Dim xlWkbk As New Excel.Workbook
If Not IsFileOpen(FileName) Then
Set xlWkbk = xlApp.Workbooks.Open(filename)
xlWkbk.Worksheets(theSheet).Activate
Else
MsgBox "Sorry, the file is currently in use. Please try again", vbOKOnly
Exit Sub
End If
End Sub
Or you could have it simply wait a few seconds (e.g. Wait 5) or more if the writing process doesn't cover that much data. The specific amount of seconds to wait would depend on testing write times based on your scenario and your data. That would be added as a nested If Not statement inside the previous one.
Then, when the result is that the file is not in use, simply write a series of subroutines to write the form data (stored as variables) to the destination sheet. End with something like
xlWkbk.Save
xlWkbk.Close
Set xlWkbk = Nothing
Set xlApp = Nothing
to save and close the workbook and clear your variables (memory cleanup and all that).
You may already be aware of this practice, but while you'll want to keep Excel visible during development, you'll definitely want to set Application.Visible = False on the production files for two reasons:
This will prevent users from getting confused by a lot of automation
It covers Application.Updating as well, which will really speed up data processing.
I'm currently using VBA to automate pulling of large data-sets. However, it often happens that either
the sub simply gets stuck and does not update any more/connection breaks (application is still responsive) or
the application becomes totally unresponsive.
Is there a way to implement a time-out function which automatically closes the application after say 15 minutes elapse?
In other programming languages, I would have solved this using multi-threading. But not sure if something similar is feasible in VBA given that it is entirely single-threaded.
The basic algorithm I employed to automate the data looks like this:
Sub automateFetchData()
Dim requestTable As Workbook
Set requestTable = Workbooks.Open(requestTablePath)
Run ("'" & requestTable.Name & "'" & "!fetchData")
' This is where the application starts fetching data and occasionally gets stuck
debug.print "Data retrieval complete"
End Sub
For those familiar with Thomson Reuters Datastream, I am using request tables and the pre-programmed macro within the request table file to connect and fetch data from the datastream server.
Therefore, I unfortunately do not know/understand in detail what is happening when I run:
Run ("'" & requestTable.Name & "'" & "!fetchData")
I just know that once I call it, a progress bar appears looking like this:
Which then transitions to:
If the data retrieval is successful, the progress bar disappears, output is pasted to a worksheet and the code above reaches the
debug.print "Data retrieval complete"
Statement.
If it is not successful, it just freezes with the progress bar from the second screenshot still open and running. At this point, I would want the time-out to kick-in and exit the function which called
Run ("'" & requestTable.Name & "'" & "!fetchData")
Please excuse me for being so vague in the description of the issue. I'd be glad if someone could nonetheless help me out! Always happy to share more info where I can. Cheers!
I am new to UI Automation. In my current organisation I was tasked with making an automated tool using GUI(Graphics User Interface) screen reading, but it is not working perfectly with other my colleague's machine because of a difference in screen resolution.
I watched this link on you-tube to try and understand UI Automation with excel, but I can't find much on this topic anywhere else.
Can anyone direct me toward resources on UI Automation? I Would like to know where I can learn it, read about it, and how to implement it with Excel.
Thanks in advance I really appreciate if anyone could help me.
UIAutomation from Microsoft is very powerfull and works well with windows 7, 8, 10 also from visual basic for applications (32 and 64 bits) and can be handy used to do some nice GUI Automation without expensive tools.
Make sure in VBA reference you have UIAutomationCore.Dll references (and weird enough sometimes on some computers you have to copy this to your documents folder)
Below you can see 2 base examples but as MS Automation is a huge library for all routines you can read a lot on MSDN for full documentation.
I use the MS UIA routines in AutoIt and in VBA
For AutoIt its shared over here
https://www.autoitscript.com/forum/topic/153520-iuiautomation-ms-framework-automate-chrome-ff-ie/
For VBA I do not have a standard library but someone did a try with
this
https://github.com/mhumpher/UIAutomation_VBA
Option Explicit
Sub test()
Dim c As New CUIAutomation
Dim oDesktop As IUIAutomationElement
Set oDesktop = c.GetRootElement
Debug.Print oDesktop.CurrentClassName & vbTab & oDesktop.CurrentName & vbTab & oDesktop.CurrentControlType
End Sub
'Test uia just dumps all windows of the desktop to the debug window
Sub testUIA()
Dim allEl As IUIAutomationElementArray 'Returns an element array with all found results
Dim oElement As IUIAutomationElement 'Reference to an element
Dim ocondition As IUIAutomationCondition
Dim i As Long
Dim x As New clsUIA
'Just reference the three mainly used properties. many more are available when needed
Debug.Print x.oDesktop.CurrentName & x.oDesktop.CurrentClassName & x.oDesktop.CurrentControlType
Set ocondition = x.oCUIAutomation.CreateTrueCondition 'Filter on true which means get them all
Set allEl = x.oDesktop.FindAll(TreeScope_Children, ocondition) 'Find them in the direct children, unfortunately hierarchies are strange sometimes
'Just reference the three mainly used properties. many more are available when needed
For i = 0 To allEl.Length - 1
Set oElement = allEl.GetElement(i)
' If oElement.CurrentClassName = "PuTTY" Then
Debug.Print oElement.CurrentClassName & oElement.CurrentName & oElement.CurrentControlType
' Debug.Print oElement.CurrentBoundingRectangle
oElement.SetFocus
DoEvents
Sleep 2000
' End If
Next
End Sub
Seems like you are doing the automation using the coordinates, which changes when you switch to other resolution. If this is the case, please automate your application using ID, Class, Xpath, CSS etc. This link will help you in that: http://www.guru99.com/locators-in-selenium-ide.html
For automation using Excel, please look into the following link:
http://www.guru99.com/all-about-excel-in-selenium-poi-jxl.html
create clsUIA class then insert this code
'clsUIA with some logic like
'start by add the following code
Dim c As New CUIAutomation
Public oCUIAutomation As New CUIAutomation
Function oDesktop() As IUIAutomationElement
Set oDesktop = c.GetRootElement
End Function
In order to head off a storm of "comment it out" replies, here is my situation:
I have a process is normally run 1 iteration by 1 iteration. A user manually hits a button that calls a macro which, upon completion, pops up a message box that reports the total time length the macro ran for. It's pretty handy for diagnosing issues. This code is locked down and I cannot modify it.
I am trying to do this at scale. Because the code in the main spreadsheet and workbook are locked, I have a separate workbook open in the same instance of excel with a macro that operates the locked down workbook. Rather than 1 by 1, I've got a set of 300 I'm trying to run through. Right now I have to babysit the thing and hit space to get past the MsgBox. Does anyone know of any tricks to prevent me having to monitor the thing? Either disabling the pop-ups or some way to make them non-modal. Maybe a trick to make the mouse click?
You're right in knowing that the best way to fix the issue is to correct the code. In which case you would probably make the pop-ups toggle-able.
However, I wrote this for you which could be used as a potential work around. It utilizes VBScript to "sort-of" simulate multithreading so that you can send a key to the modal Msgbox. Assuming you can do what you want to do via code, simply call SendDelayedKeys before the action that will cause a Msgbox. You may have to tinker with the Delay based upon your circumstances as 100 milliseconds may not be enough. To change the Delay, just call like this: SendDelayedKeys 500 for 500 milliseconds.
Sub SendDelayedKeys(Optional Delay As Long = 100, Optional keys As String = """ """)
Dim oFSO As Object
Dim oFile As Object
Dim sFile As String
sFile = "C:\SendKeys.vbs" 'Make this a valid path to which you can write.
'Check for the .vbs file.
If Not Len(Dir$(sFile)) Then
'Create the vbs file.
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFile = oFSO.CreateTextFile(sFile)
oFile.WriteLine "Set WshShell = WScript.CreateObject(""WScript.Shell"")"
oFile.WriteLine "WScript.Sleep CLng(WScript.Arguments(0))"
oFile.WriteLine "WshShell.SendKeys WScript.Arguments(1)"
oFile.Close
End If
Shell "wscript C:\SendKeys.vbs " & Delay & " " & keys
End Sub
Sub ProofOfConcept()
'Using default parameters which sends a space after 100 milliseconds
SendDelayedKeys
MsgBox "I disappear on my own!"
End Sub
A word of warning: Any solution that utilizes SendKeys is a fragile solution and should be avoided when possible. However, when your options are limited and you need to avoid a manual process, sometimes it's your only option.
Since SiddhartRout rightly pointed out that this could be solved using API calls: here's a link with C# code that would close your msgbox every second.
The problem here really isn't strictly a problem more code can (or indeed should) solve.
There are a great many things to consider and any solution will be more complex AND less reliable than the problem it is initially trying to solve. But lets look at your options...
SendKeys is not reliable for that kind of use, what happens if the dialogue says "would you like me to save this workbook?" just after making a change that was meant to be temporary or "would you like to play global thermonuclear war?" Plus with a batch process like that you want to get on with something else while you wait, even if it's only to come here to downvote trolls. If nothing else you may not be in control of this code so what kind of mess will it cause when the maintainers realise msgbox is bad UX and kill it?
FindWindow API calls would let you check the content in the window to make sure it says what you're expecting but then you're potentially asking some bit of quick & dirty vbscript to go into a race condition until the right window comes up. Can you guarantee that the threads won't lock up?. What about platform issues - what happens if someone wants to run your code on their shiny new surface? What happens when your 64 bit modal Excel dialogue window can't be seen by the 32-bit api calls you were making? What about a new version of office that doesn't present modal dialogues in the same way? None of those problems are insurmountable but each adds complexity (aka opportunity for failure.)
The best solution is fix the actual problem you have identified from the outset which is that the original code throws up an unnecessary modal dialogue. Someone needs to fix that - it doesn't have to be you but if you estimate how much time that modal dialogue wastes in lost productivity that should get you a solid business case for getting it sorted.
Versions
Excel 2003Windows XP SimaPro 7.3.0 Developer Version Using a Work computer but was made administrator on this machine Libraries referenced in Excel/VBA: Visual Basic for Applications; Microsoft Excel 11.0 Object Library; OLE Automation; Microsoft Office 11.0 Object Library; Microsoft Forms 2.0 Object Library; COM+ 1.0 Admin Type Library; COM MakeCab 1.0 Type Library; COM+ Services Type Library; SimaPro Library Me: Beginner
What I'm trying to do
I am using a program called SimaPro that stores databases of "Life Cycle Analysis" information. The program has built in COM interface functionality. The program states that it does, indeed support Excel/VBA (but it doesn't specify versions).
I am trying to connect this program and/or COM server to excel so that I can interact with the information through excel.
What I've done
I've done the procedure they list:
-Open SimaPro
-Register COM Server
-Then I pasted the below code into VBA and tried to run it. This code is the sample code provided by the software company, I edited only the SP.Server, SP.Alias, SP.Login, and SP.OpenProject fields (below is as edited).
What Happened
Run-time Error: '-2147418113 (8000ffff)':
Automation Error
Catastrophic Failure
Question(s)
-Is the server name right? I've been reading a little on COM servers and I don't know if the way I put it in is in the right "form"
-Could it have something to do with certain registered/unregistered DLLs? I've worked with the company's IT people, and software programmers. None of them were very familiar with COM but one person suggested the DLLs might be the issue.
Thanks for your help!!
Here is the code that I'm inputting:
Sub CreateProcess()
Dim SP As SimaProServer
Dim PC As Process
Dim PC2 As Process
Dim PL As ProcessLine
Dim Param As ParamLine
Dim Subs As Substance
Set SP = New SimaProServer
SP.Server = "Local Server"
SP.Alias = "C:\Documents and Settings\All Users\Documents\SimaPro\Database\"
SP.Database = "Professional"
SP.OpenDatabase
SP.Login "", ""
SP.OpenProject "PROJECT", ""
' Not project's actual name, not allowed to state name of project
SP.CreateSubstance "Air", Subs
Subs.CASNumber = "4-5-13"
Subs.Name = "Some substance"
Subs.DefaultUnit = "kg"
Subs.Update
SP.CreateProcess ptMaterial, PC
Set PL = PC.AddLine(ppProduct, -1)
PL.ObjectName = "Steel 2"
PL.UnitName = "kg"
PL.Amount = "2"
PL.Comment.Add ("My new created process")
PL.CategoryPath = "Chemicals\inorganic"
PC.Update
' create second material process Case
SP.CreateProcess ptMaterial, PC2
Set PL = PC2.AddLine(ppProducts, 0)
PL.ObjectName = "Case 2"
PL.UnitName = "kg"
PL.Amount = "10"
Set Param = PC2.AddParamLine(ptInputParameter, -1)
Param.Name = "A"
Param.Value = "2,3"
' add input from Steel
Set PL = PC2.AddLine(ppMaterialsFuels, -1)
' input from steel
PL.SetProduct "Introduction to SimaPro 7", ptMaterial, "Steel 2"
PL.Amount = "8"
PL.UnitName = "kg"
Set PL = PC2.AddLine(ppAirborneEmissions, -1)
' input from steel
PL.SetSubstance "Some substance", ""
PL.Amount = "A+1"
PL.UnitName = "kg"
PC2.Update
SP.Logout
SP.CloseDatabase
Set SP = Nothing
End Sub
Given that this was more than one year ago. I'm assuming you got this working. If you haven't yet, I might know what the root cause might be.
I used to get the same error and from your changes for server, alias and login, I was able to make it run. One thing that is different is that you have changed the name of the project to "Project" from "Introduction to SimaPro 7". I honestly have zero (not being humble here) VBA knowledge. So, I'm speculating that there is no project names "Project" to open. I'm not sure if VBA would create a project automatically, if it can't find it. You can either try creating a project named "Project" or just rename it back. I am interested to see if that worked.
Automation Error usually means that there was a problem within the COM library you try to use. As it is a run-time error, it could be something very stupid, as missing parameter or wrong path or access rights. In my opinion, it also means that the library is not very well designed.
As you are not the author of the library you do not have many options. You can try to contact the vendor to get more documentation. You can also pray that the designer thought about logging - check the event log; if you are lucky you may find something interesting there.
Answering your first question, if you referenced the library and the code compiles - that means that you did everything right there.