I am trying to automate some tasks in MS Access which are involving some button presses. Here are my best guesses so far, but I never find a way to finally execute a click...
using Microsoft.Office.Interop.Access;
Application access = new Application();
access.OpenCurrentDatabase("SomeDatabaseName.accdb");
access.DoCmd.OpenForm("SomeFormName", AcFormView.acNormal);
access.DoCmd.GoToControl("SomeButtonName");
access.DoCmd... // I can go to the control, but how do I click it?
Or maybe there is another approach using the Microsoft.Office.Interop.Access.CommandButton instance?
var form = access.Forms["SomeFormName"];
foreach (var control in form.Controls)
{
if (control is CommandButton button)
{
// I can get the CommandButton, but how do I execute the click procedure?
string onClick = button.OnClick; // it's just this string: "[Event Procedure]"
}
}
access.CloseCurrentDatabase();
access.Quit(AcQuitOption.acQuitSaveNone);
Is this possible using the MS Office Interop assemblies? Or are there any alternatives? Unfortunately, these interop assemblies are very poorly documented.
You can use the CommandBars.ExecuteMso method which executes the control identified by the idMso parameter. This method is useful in cases where there is no object model for a particular command. Works on controls that are built-in buttons, toggleButtons, and splitButtons. On failure it returns E_InvalidArg for an invalid idMso, and E_Fail for controls that are not enabled or not visible. For example:
Application.CommandBars.ExecuteMso("Copy")
If you need to execute non-ribbon controls you may consider using Windows API functions or Accessibility API for that.
I have an old excel workbook that I am trying to replace with a c# application. The only bit of functionality that I have not been able to replicate is the code below.
So the code below takes a bloomberg ticker (i.e. "VOD LN") and then with DDEInitiate it loads the bloomberg page.
I have read that C# doesn't support DDE or even if it does it is best avoided. In which case how can I do this via C#?
Public Sub LoadBbergPage(string ticker)
' loads bberg page
Dim strExe As String
Dim channelGP As Long
channelGP = DDEInitiate("Winblp", "BBK")
strExe = "<blp-2><home>" & Strings.Trim(ticker) & "<EQUITY><GO>"
DDEExecute channelGP, strExe
DDETerminate channelGP
End Sub
If you're trying to make it easier for your users to launch data into the terminal, you can use 'B-links'. Access it like any other web link. Below is an example for "IBM US Equity" - replace spaces with %20
https://blinks.bloomberg.com/securities/[ticker]/[function]
https://blinks.bloomberg.com/securities/IBM%20US%20Equity/DES
It will ask the user the first time to allow / remember settings and then should launch to terminal. If there are issues, you can go to https://blinks.bloomberg.com/help. Documentation is available on terminal via DOCS BLINKS<GO> (tons more special syntax)
But if you're trying to do some kind of screen scraping etc via DDE, don't bother; just use the Reference Data API instead: https://www.bloomberg.com/professional/support/api-library/
An Excel workbook is called by a command line, which itself is launched from c# (3.5). The workbook runs, but there is an error in the VBA. For example, a column is missing in a pivot and Excel prompts the user with an error message, with the option to "debug".
From the process in C#, we can detect that the error window is open, and we take a screenshot of the error message, and then we close the error box.
If this was an interactive session, Excel would then present the VBA editor, in debug mode, with the line where the error occurred highlighted.
However, because this is running in an unattended terminal session, we are then unable to take a screenshot of the VBA editor (the screenshot is a black screen).
We are then able to close the excel program, using the windows PID.
The questions is: how do we either get a screenshot of the VBA editor, or how do we bind (with COM or interop) with the Excel in debug mode, and traverse the dom to find the error line, and possibly error message?
(1) If you have authoring control of the Excel workbook(s), you can insert line numbers and a error handler to write to file Err.Number, Err.Description, Err.Source, Erl.
(2) If (1) is not an option, but you can set Macro Security on the host to allow programmatic control of the Visual Basic project, then you can get the active line number:
Dim xl As excel.Application
Dim StartLine As Long, StartColumn As Long, EndLine As Long, EndColumn As Long
Set xl = GetObject(, "Excel.Application")
xl.VBE.ActiveCodePane.GetSelection StartLine, StartColumn, EndLine, EndColumn
Debug.Print StartLine, StartColumn, EndLine, EndColumn
(3) If neither (1) nor (2) is an option, it is a bit hairy, but you can copy out the bitmap contents of the window: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183402%28v=vs.85%29.aspx
The simplest solution if you have access to modify the VBA code might be to use On Error GoTo and a line label. This will stop the Debug message from popping up and instead, when a runtime error occurs, execution will jump to the specified label at which point you can access the information available in the global Err object. VBA does not have the concept of a try/catch block so this is pretty much the nearest proximally.
Sub Main()
On Error GoTo Catch
' Code that may trigger errors here...
GoTo Finally
Catch:
' Log Err info, etc...
Debug.Print "Error " & Err.Number & " - " & Err.Description
Finally:
' This is always reached last (error or no error)
End Sub
Snapshot of data structures, VBA excel
It sounds a lot like the question above in which I suggested to write the state of your code to a separate txt file (in the example, using a FileSystemObject) where you can afterwards analyze where you got stuck:
booleans on conditions / instantiation of objects, number on succesful loops, values contained in variables, well - whatever you want...).
In combination with an error handler (as suggested before) you can see where the code stopped + code / description of the error.
A copy-paste for your sake:
dim sFile As string
Dim FSO As FileSystemObject
Dim FSOFile As TextStream
sFile = "U:/Log.txt"
Set FSO = New FileSystemObject
Set FSOFile = FSO.OpenTextFile(sFile, 2, True)
FSOFile.writeline (<Descriptions+Property/variable value>)
FSOFile.Close set FSO = nothing
I agree that it is a more work, but you'll know where to find the bug.
All depends on how hard and often you need this, in combination with how long the code is that you need to describe. Since I am not aware of your situation, I cannot judge on that.
In the end, it seems a lot of work, but actually it's quite easy since it's just describing the code that is already there.
Hope it helps.
I was wondering if what I am trying to do is possible. I have created a C# class library which invokes a form when called from VBScript using CreateObject.
I have a VBS that passes some data to the Form and once the script is complete, obviously all references are lost. I wanted to know if there is a way for me to connect and use the existing form the next time I call the VBS script again?
Set e = CreateObject("MyObject")
'SendEvents to Form'
'Script ends.. all references lost'
'Script is run again'
Set e = CreateObject("MyObject")
'Is it possible to send events to the existing form, instead of closing it and creating new one?'
*Edit: Currently, I am using my class lib to close the existing form when the script is called again. However, I have a user request to keep it open regardless of how many times the script is called. I am not sure how I can use the existing form for the next time CreateObject is called. Is it possible?
Try it like this
Set e = CreateObject("MyObject")
'SendEvents to Form'
'Script ends.. all references lost'
'Script is run again'
Set e = GetObject(, "MyObject") 'no, the empty parameter is no typo
See http://technet.microsoft.com/en-us/library/ee176980.aspx for more info.
I am trying to develop a .NET class that updates a VB6 Form and its controls with various new captions (It is in an assembly that is COM visible).
I pass a VB6 form ByRef as an object to the .NET class and then update the caption etc on the form as follows:
Public Sub AddFormRefLegacy(ByRef objForm As Object)
Try
objForm.Caption = "some new caption"
For Each ctl As Object In objForm.Controls
Select Case TypeName(ctl)
Case "Label"
ctl.caption = "some new caption"
Case "Frame"
ctl.caption = "some new caption"
Case "CommandButton", "PushButton"
ctl.caption = "some new caption"
'etc etc
This works about 85% of the time but occasioanlly I get a run time error 80131500 no such interface (E_NOINTERFACE)
I'm not sure exactly where this is throwing the error but can anyone see anything obviously wrong with this?
EDIT
The problem seems to be occurring on this section:
Case "ITabStrip" 'MS Common Controls 6
For i = 0 To ctl.Tabs.Count - 1 ' this sometimes throws the error!
ctl.Tabs(i + 1).Caption = FindValue(objForm.Name, ctl.Name, i, ctl.Tabs(i + 1).Caption)
Next
One possible problem might be that VB6 labels aren't windowed controls but are drawn on the form's window. Another possible problem is if you're using certain 3rd party controls it's likely that they're using owner draw techniques that might do unexpected things.
I'd suggest trying to narrow it down by control and see if any of them have the problem.