What type of object is returned by GetObject("SAPGUI")? - vba

TL;DR ---------------
If I properly declare the variables in the initialisation script, I can't attach to the "connection" objects of a SAPFEWSELib.GuiApplication .
The collection MyApplication.Children(0), is empty but if I comment out the declaration block, it just works !?
TL;DR ---------------
I am trying to create a more reliable connection between my SAP client and my excel application.
My current issue is with the establishing of the connection. The SAP client provides a sample vbscript when using its script recording function.
If Not IsObject(MyApplication) Then
Debug.Print "yep"
Set SapGuiAuto = GetObject("SAPGUI")
Set MyApplication = SapGuiAuto.GetScriptingEngine
End If
If Not IsObject(Connection) Then
Set Connection = MyApplication.Children(0)
End If
If Not IsObject(session) Then
Set session = Connection.Children(0)
End If
This other question on stackoverflow came close to answer, but fell short
stackoverflow.com/questions/24738998/vba-using-variables-that-were-not-declared
The original code is here, I have modified slightly so it would work in excel/vba.
(I dropped the IsObject(Wscript) and I had to replace Application with MyApplication)
First thing I want to do is explicitly declare all variables.
Using code I got at the following address
stackoverflow.com/questions/19783180/get-list-of-all-properties-for-an-object
I used the "TypeLib Information" and the function TypeName() to determine each object type. I tried declaring as follows
Dim session as GuiSession
Dim MyApplication as GuiApplication
Dim Connection as ISapConnectionTarget
Dim SapGuiAuto as object
I'm getting the User-defined type not defined error.
After a bit of searching, I found the partial answer at this address.
scn.sap.com/thread/3254335
I feel manual adding the "C:\Program Files (x86)\SAP\FrontEnd\SAPgui\sapfewse.ocx" reference will break easily. If there's a better way to do this please let me know.
Ok now I can declare like this and it works.
Dim MyApplication As SAPFEWSELib.GuiApplication
Dim Connection As SAPFEWSELib.GuiConnection
Dim session As SAPFEWSELib.GuiSession
Dim SapGuiAuto As Object
I don't like the SapGuiAuto As Object as it doesn't really say what it is and I can't use CTRL+SPACE to view available properties and functions of this object (one of the VB features I can't live without now !)
All I know about this object is that it has a .GetScriptingEngine function (method ?).
While doing the research to write this question I came across this thread on the sap.com forums.
scn.sap.com/thread/3448120
Here it is mentioned that "GetScriptingEngine is a method of the class GuiApplication".
So I tried the following declarations and it seems to work.
Dim MyApplication As SAPFEWSELib.GuiApplication
Dim Connection As SAPFEWSELib.GuiConnection
Dim session As SAPFEWSELib.GuiSession
Dim SapGuiAuto As SAPFEWSELib.GuiApplication
Now if I type "session." the list of possible properties appear.
But there is a problem !
If I type the following line
Debug.Print session.FindById("wnd[0]").Text
It gives an error (while it used to work just fine !).
The error is "Object variable or With bock variable not set".
If I comment out the variable declarations, it works just fine !
Using the TLI function while session is undeclared I get the following members
DumpProperties session.FindById("wnd[0]")
output here -> pastebin.mozilla.org/8882551
but if I run the same command with session properly declared I get.. the same error
So after a bit more research, it turns out that the beginning of the script doesn't work the same now.
If Not IsObject(MyApplication) Then
Will not execute if MyApplication has been declared with Dim MyApplication As SAPFEWSELib.GuiApplication
So I tried executing the commands without the IFs.
Set MyApplication = SapGuiAuto.GetScriptingEngine
Set Connection = MyApplication.Children(0)
Set session = Connection.Children(0)
This fails with the "Object variable or With bock variable not set" error on Set MyApplication = SapGuiAuto.GetScriptingEngine
The solution to this issue was to create a new instance of SapGuiAuto like this.
Set SapGuiAuto = New SAPFEWSELib.GuiApplication
Now the above code executes, until it fails at Set Connection = MyApplication.Children(0)
The error : "The enumerator of the collection cannot find en element with the specified index"
A quick test with the following line reveals, the .Children collection is empty.
For Each chld In MyApplication.Children: Debug.Print "one exists": Next
This is the same error I would normally get if I am currently disconnected from SAP, but I am connected and commenting out the declaration block, fixes the issue.
This answer on stackoverflow is ominous
stackoverflow.com/questions/36751819/sap-gui-scripting-error-the-enumerator-of-the-collection-cannot-find-an-elemen
I'm not an administrator, it's 10 PM on a Friday and asking IT anything is a nightmare. Hopefully I don't have to resort to that.
I will try on another computer.
Just tried, I get the same thing. At this point I have to throw in the towel, I can't get this to work without some help or at least a good night's rest !
Thanks for any advice or comments
Some extra links I found very useful for the people on the same path as I am.
SAP GUI Scripting API PDF
Using the VBA debugger to discover SAP GUI properties and functions
scn.sap.com/docs/DOC-39696
SAP GUI Scripting API Documentation (I couldn't open this file but it's full of good stuff)
www.sdn.sap.com/irj/scn/go/portal/prtroot/docs/library/uuid/a034a16b-3bfe-2a10-e2bb-8bd880db0b3c
SAP GUI Scripting API : How to Automate User Interaction (unfortunately my system lacks the "Script development tools")
scn.sap.com/docs/DOC-4614
Also the BIBS function might be helpful to you, however it was not working on my system
scn.sap.com/docs/DOC-4612

When I use this I also get 0 children as if not connected to SAP:
Set sapCon = New SAPFEWSELib.GuiApplication
By using this instead it's actually working:
Set sapCon = GetObject("SAPGUI").GetScriptingEngine
As per this: https://answers.sap.com/questions/12487790/what-are-the-differences-in-vba-methods-to-connect.html?childToView=12494892

I just came accross this issue myself, and found a solution for the "enumerator" error. Also posted it in the referenced stackoverflow post you mentioned.
Change this:
Set Connection = Sap_Application.Children(0)
Into this:
Set Connection = Sap_Application.Children(1)
As I explained there, not sure why this happens, or what it means, I just messed with the numbers and it worked.
Hope it's not too late, or maybe it will help someone else.
Regards

Related

vbscript starting a specific profile connection in ibm personal communications

I have been given the task of converting the slower Macro Express Pro coding for IBM Personal Communications over to a VBScript/VBA version. The Macro Express Pro coding opens a predetermined profile from a specific location.
This process can take up to 30 seconds. VBScript does this in about a third of the time for a default profile (TN3270.WS). However, when we try opening the specific link highlighted in the UNET.txt file, we get this as an error:
Run-time error '440': Automation error
Here is the VBScript code we are trying to use:
Sub Main()
Dim EName
Dim autECLConnList, objConnMgr
Set objConnMgr = CreateObject("Pcomm.autECLConnMgr")
objConnMgr.autECLConnList.Refresh
objConnMgr.StartConnection ("profile='C:\ProgramData\IBM\Personal Communications\UNET REWORK.ws' connname=a")
Application.Wait (Now + TimeValue("00:00:12"))
objConnMgr.autECLConnList.Refresh
EName = objConnMgr.autECLConnList(1).Name
End Sub
The error occurs on the objConnMgr.StartConnection ("profile='C:\ProgramData\IBM\Personal Communications\UNET REWORK.ws' connname=a") line. We know we have the correct path to the profile because it's we found its location:
IBM says that if the profile name contains blanks, it "must to be surrounded by single quotes":
Can anyone provide some advice on what we're doing wrong or what we're missing?
Thanks.
I found a way of doing what I need to do. I was looking too specific into starting a PCOMM session that I didn't even think of just running the .exe file and send it parameters. Here's how I did it:
Sub Main()
Dim WShell
Set WShell = CreateObject("WSCript.shell")
WShell.Run """C:\Program Files (x86)\IBM\Personal Communications\pcsws.exe"" ""C:\ProgramData\IBM\Personal Communications\UNET REWORK.WS"""
End Sub
I'm sure this is a duplicate answer to another question out there, but most of the answers are more than a few years old and outdated. This solution is current and recently tested, so it is a more reliable source.

Access not exiting

Recently, my Access .mdb database started intermittently not allowing Access (both Access 2003 and 2007) to quit. If I quit (whether by pressing the X button or from the menu, then it closes the database and appears to exit Access, as well, but then it suddenly reappears (without any database open). The only way for me to exit at that point is from the task manager.
There are two significant changes that I did recently that might be related. 1) I started using the WinSCP .Net assembly to access an ftp server, which I had to install and register for COM from the instructions here. 2) I started using ODBC, first as a linked table, and then from VBA ADO code (see this). I doubt that this second change is causing this problem because I've had the problem both when I was using the linked tables and when with ADO.
This doesn't happen every time I open the database, and I haven't noticed a pattern. What could be causing this strange problem?
Edit - I found the root of the problem. By breaking my ftp download code at various points and seeing whether it will exit, I narrowed it down to the following:
Dim PDFFolders As Recordset
Set PDFFolders = CurrentDb.OpenRecordset("PDFFolders")
Dim syncOptions As TransferOptions
Set syncOptions = New TransferOptions
syncOptions.filemask = "*/*.pdf"
On Error Resume Next 'In case it doesn't exist
Do While Not PDFFolders.EOF
sess.SynchronizeDirectories SynchronizationMode_Local, info!RTFFolder, _
info!BasePDFFolder & "/" & PDFFolders!Name, False, , , _
syncOptions
PDFFolders.MoveNext
Loop
PDFFolders.Close
Set syncOptions = Nothing
Set PDFFolders = Nothing
On Error GoTo 0
If it runs the sess.SynchronizeDirectories statement, then access won't exit, otherwise it does. Looks to me like a bug win WinSCP.
I can do other things like downloading files, creating directories, etc. with no problem, but when it gets to this statement, it doesn't exit access afterwards.
Sticky instances of Access usually result from hanging object references.
If Access hangs the way you described, I would suspect a nasty circular reference.
To investigate on circular references, you basically have two options:
Inspect your code on circular dependencies - That might become tedious and not so easy. But if you know your code base deeply, you might have suspects where to look first.
Add logging to your code - In case you cannot spot the problem via inspection alone, you can consider adding consequent logging of object creation/deletion (through Class_Initialize/Class_Terminate). For larger code bases using classes heavily, this is a good investment to start with.
If your problem is with classes where you cannot control the code (as is your case), you might consider using that external classes only through wrapper classes where you can log creation/deletion. Of course in tricky cases, termination of the wrapper class does not mean termination of the wrapped class.
BTW, I strongly recommend to make sure to set every object reference explicitly to Nothing ASAP:
Set MyObj = GetMyObject()
' Proceed with coding here later
' First write the following line
Set MyObj = Nothing
Special thoughts have to be given in the case of local error handling to make sure to set the reference to Nothing in either case.
A good way to ensure this is using a With-block instead of an explicit variable (if the usage pattern allows to):
With GetMyObject()
' Use the object's members here
End With
With this pattern you save declaring the local variable and can be sure that the object reference does not survive the current method.
I still think it's a bug in WinSCP, but I found a workaround. I noticed that it only happened if I took the information from a table in the database, and not if I put in a hard-coded string. So I just added & vbNullString, which concatenates a blank string, which changes the data type from a Field to a String, and now it doesn't happen anymore.

How can a blank MS Access database be created using VBA?

I'm a total noob trying to create a blank MS Access database using VBA in Excel. I want to name the new database "tblImport". This is the code I´m using:
Sub makedb()
Dim accessApp As Access.Application
Set accessApp = New Access.Application
accessApp.DBEngine.CreateDatabase "C:\tblImport.accdb", dbLangGenera
accessApp.Quit
Set accessApp = Nothing
End Sub
I get the following error message:
"Run Time Error 3001: Application Defined or Object Defined Error"
What can I do?
The name of the locale constant in the CreateDatabase method is wrong:
This:
accessApp.DBEngine.CreateDatabase "C:\tblImport.accdb", dbLangGenera
Should be:
accessApp.DBEngine.CreateDatabase "D:\tblImport.accdb", DB_LANG_GENERAL
Change that and your code should work. (It does for me at least).
Old Question. Here are my two cents. You have a typo...
dbLangGenera should be dbLangGeneral
More about it in Workspace.CreateDatabase Method (DAO)
Voting to close this question as per Dealing with questions with obvious replies
Try this. This works.
Sub makedb()
Dim accessApp As Access.Application
Set accessApp = New Access.Application
accessApp.DBEngine.CreateDatabase "C:\tblImport.accdb", dbLangGeneral
accessApp.Quit
Set accessApp = Nothing
End Sub
EDIT: Will delete this answer and post it as a comment once the post is closed.
Old question, but it was useful for me. Seems like you don't even need Access object.
Access.DBEngine.CreateDatabase "D:\tblImport.accdb", DB_LANG_GENERAL
works fine for me.
You should use the first method if you intend to use the automation object you've created. You can open forms, use DoCmd statements, etc., simply by prefacing each statement with "accessApp." For example, I use TempVars to hold user data. I can open a related database using automation, then do
accessApp.TempVars.Add "CurrentUser", TempVars.CurrentUser
to pass the CurrentUser value from the active database to the new database.
When all you want to do is create a new database to do a function such as DoCmd.TransferDatabase on, you can just use
Access.DBEngine.CreateDatabase "D:\tblImport.accdb", DB_LANG_GENERAL

How do I programatically add a reference to a VBA project?

I'm deploying an early bound styled VBA module that needs Scripting.Dictionary and RegExp.
The script, predictably, fails when it runs on another computer.
The user has to go to Tools->Reference in the VBA IDE and add a reference to those two libraries manually to make it work.
Hence lies the problem. Asking the non-technical end user to go to the IDE and manually add references is asking way too much of them.
The other alternative is to rewrite the whole (very long script written by someone else) to use late binding. I rather not take this path if there are other methods.
As an altervative, some people suggest adding a reference programatically like so:
Application.VBE.ActiveVBProject.References.AddFromFile [Path to library]
Is this the correct solution and if so are there any downsides of this strategy?
If not, are there other methods that will to enable the code to remain early bound yet does not require references to be added manually by the user.
Suggestions involving direct calls to the Win32/64 API are also welcome.
Thanks.
In my own limited environment (small # of other people using spreadsheets I develop, relatively standard machine setups), if I create the file and add the references, and then give a copy to someone else, they can open it with no problems and not have to do anything, so keep that in mind with this answer. (I'm wondering why that doesn't work for you.) Also, this was with Excel.
Rather than adding a reference from a file path, you might consider using the GUID property instead.
Here is some code I once used to automatically create references in a newly created workbook. (It's part of a script that would export code, references, and unit tests on worksheets to text for use with Subversion and then later reconstitute the workbook from the text files.) You might find it useful to your situation. (EH and cleanup removed to keep it short...)
'Export refs in existing workbook to text file
Private Sub exportRefs_(srcWbk As Workbook)
Dim fs As FileSystemObject
Set fs = New FileSystemObject
Dim tsout As TextStream
Set tsout = fs.CreateTextFile(fs.BuildPath(getTargetPath_(srcWbk), "refs.refs"))
Dim ref As Reference
For Each ref In Application.ThisWorkbook.VBProject.References
Call tsout.WriteLine(ref.GUID)
Next ref
'<EH + cleanup...>
End Sub
'Add refs to newly created workbook based on previously exported text file
Private Sub importRefs_(wbk As Workbook, path As String)
Dim fs As FileSystemObject
Set fs = New FileSystemObject
Dim tsin As TextStream
Set tsin = fs.OpenTextFile(path)
Dim line As String
Dim ref As Reference
While Not tsin.AtEndOfStream
line = tsin.ReadLine()
Set ref = Nothing
On Error Resume Next
Set ref = wbk.VBProject.References.AddFromGuid(line, 0, 0)
On Error GoTo 0
If ref Is Nothing Then
Debug.Print "add failed: " & line
End If
Wend
'<EH + cleanup...>
End Sub
Like, I said, limited environment, but hopefully it helps.

How do I connect to a Lotus Notes DB through VBA?

I'm trying to connect to a Lotus Notes database via VBA in Microsoft Access 2003. The code I have is as follows:
Set nSession = CreateObject("Notes.NotesSession")
Set nDatabase = nSession.GetDatabase("CN=MT_N01/O=Org Name", "LossPrevention\BrchPrVI.nsf", False)
I've tried variations for the server name, but nothing seems to work. In Lotus Notes it appears as "Server: MT_N01/Org Name". I've tried just "MT_N01" and "MT_N01/Org Name", but the NotesDatabase variable keeps ending up empty.
Any tips?
I might be wrong here, but I think you should call the Initialize() method of the session prior to using it.
Take a look at the samples here: http://www.ibm.com/developerworks/lotus/library/domino-msnet/index.html
I think that behaviour is normal; VBA knows nDatabase is a variant/object, but does not know anything about its contents. At least that is what I get when I try your code in Excel 2003.
Try to return something from the database object, for example nDatabase.ReplicaID should return the database identity string. If the databas is not properly loaded, you will get an error.
Many good links are posted in response to the ".NET and Lotus Notes Interop." question.
Unless you also need to us the NotesUIWorkspace class and its child classes to manipulate the Notes client user interface, you should be using the "Lotus.NotesSession" class instead of the "Notes.NotesSession" class. And as per the answer from #Sagultay, you should be calling nSession.Intialize().