I am trying to optimize my Excel VBA to SAP connection and don't want to click "OK" on two message boxes that appear when starting the following code:
1 Sub SAP_1()
2
3 Dim obj_Shell As Object
4 Dim obj_SAPGUI As Object
5 Dim obj_Application As Object
6 Dim obj_Connection As Object
7 Dim obj_session As Object
8
9 Application.DisplayAlerts = False
10 Shell "C:\Program Files (x86)\SAP\FrontEnd\SAPgui\saplogon.exe", 4
11 Set obj_Shell = CreateObject("WScript.Shell")
12 Do Until obj_Shell.AppActivate("SAP Logon")
13 application.Wait Now + TimeValue("0:00:01")
14 Loop
15 Set obj_Shell = Nothing
16 Set obj_SAPGUI = GetObject("SAPGUI")
17 Set obj_Application = obj_SAPGUI.GetScriptingEngine
18 Set obj_Connection = obj_Application.OpenConnection(str_ConName, True)
19 Set obj_session = obj_Connection.Children(0)
20 ' rest of the code
21 Application.DisplayAlerts = True
22 End Sub
How can I avoid the following SAP message boxes or click them via VBA:
Line 17: "A script tries to access SAP"
Line 18: "A script opens a connection to the following system: ..."
And what's the differents to the code below? Why is the SAP GUI Scripting asking not to define them as Objects? Is this a better alternative?
1 If Not IsObject(obj_SAPGUI) Then
2 Set obj_SAPGUI = GetObject("SAPGUI")
3 Set obj_Application = obj_SAPGUI.GetScriptingEngine
4 End If
5 If Not IsObject(obj_Connection) Then
6 Set obj_Connection = obj_Application.Children(0)
7 End If
8 If Not IsObject(obj_session) Then
9 Set obj_session = obj_Connection.Children(0)
10 End If
11 If IsObject(obj_WScript) Then
12 obj_WScript.ConnectObject obj_session, "on"
13 obj_WScript.ConnectObject obj_Application, "on"
14 End If
Are there other things in the code that can be optimized?
Thank you for your help.
In order to avoid the messages that a script tires to access resp. connect to the SAPGUI you have to change settings either in the registry or via SAPGUI.
In the SAPGUI press Alt-F12 and then select Options, goto Scripting, and uncheck all check boxes below Enable scripting.
These settings are stored in the registy and one could also use VBA code to set them. The key is HKEY_CURRENT_USER\Software\SAP\SAPGUI Front\SAP Frontend Server\Security\
Thank you very much, that's my final code right now:
Function RegKeyExists(i_RegKey As String) As Boolean
Dim myWS As Object
Set myWS = CreateObject("WScript.Shell")
On Error GoTo ErrorHandler
myWS.RegRead i_RegKey
RegKeyExists = True
Exit Function
ErrorHandler:
RegKeyExists = False
End Function
'
'-----------------------------------------------------------------------
Sub RegKeyReset()
Dim obj_WS As Object
Dim RegKey1 As String
Dim RegKey2 As String
Dim RegKey3 As String
Set obj_WS = CreateObject("WScript.Shell")
RegKey1 = "HKEY_CURRENT_USER\Software\SAP\SAPGUI Front\SAP Frontend Server\Security\UserScripting"
RegKey2 = "HKEY_CURRENT_USER\Software\SAP\SAPGUI Front\SAP Frontend Server\Security\WarnOnAttach"
RegKey3 = "HKEY_CURRENT_USER\Software\SAP\SAPGUI Front\SAP Frontend Server\Security\WarnOnConnection"
' RegKey1
If RegKeyExists(RegKey1) = False Then
Exit Sub
Else
obj_WS.RegWrite RegKey1, 1, "REG_DWORD" ' Value = 1, Type = Boolean
End If
' RegKey2
If RegKeyExists(RegKey2) = False Then
Exit Sub
Else
obj_WS.RegWrite RegKey2, 0, "REG_DWORD" ' Value = 0, Type = Boolean
End If
' RegKey3
If RegKeyExists(RegKey3) = False Then
Exit Sub
Else
obj_WS.RegWrite RegKey3, 0, "REG_DWORD" ' Value = 0, Type = Boolean
End If
End Sub
'
'-----------------------------------------------------------------------
Sub SAPTransaction()
Dim ...
Set ...
Call RegKeyReset ' <--------------------------- Problem solved here...
' rest of the code
End Sub
'
I did it this way, because I wont be the only person/user to use the macro, so I don't have to tell everybody to change their settings in SAP.
Also thanks to: https://www.slipstick.com/developer/read-and-change-a-registry-key-using-vba/
Related
In the code below LastCellR = 49 and LastSheet is 61 (i.e. there are 61 sheets in the workbook and 49 client names in column 1 of the active sheet)
The code runs fine till i gets to 46 and CS gets to 61. Then in Debug, the code stops and highlights
"If Worksheets(CS).Name = ClientName Then".
The goal here is to go down a list of client names and determine if they are existing clients or new ones. So I simply get each client's name and search through the sheet names in the workbook.
Any ideas why it stops dead in it's tracks?
Thanks for any insight you can provide.
For i = 2 To LastCellR
ClientName = Cells(i, 1).Value
If Trim(ClientName) = "" Then Exit Sub
' Check for existing client
For CS = 1 To LastSheet
If Worksheets(CS).Name = ClientName Then
NewClient = False
Exit For
End If
Next CS
Next i
Not the answer to your issues, may be another option to check for the sheet
Public Function WorksheetExists(strName As String) As Boolean
On Error GoTo eHandle
Dim ws As Excel.Worksheet
WorksheetExists = True
Set ws = ThisWorkbook.Worksheets(strName)
Set ws = Nothing
Exit Function
eHandle:
WorksheetExists = False
Resume Next
End Function
Then newclient = not worksheetexists(clientname)
I have the following routine below that is meant to open an Excel spreadsheet and then go row by row to import the results into a table that is passed in. It works fine but the problem is if I try to open that same spreadsheet a second time I get a message that the file is in use and I have to Ctrl-Alt-Del to shut down Excel before I can use it again. I thought that the Set mySheet=Nothing and Set xlApp=Nothing would release the file but apparently not. What more can I do to make sure that Access lets go of the Excel file? Thanks in advance!
Public Sub MakeTempTable(strFilePath As String, tablename As String)
Dim mySheet As Object
Dim xlApp As Object
Dim rs As DAO.Recordset
Dim sql As String
sql = "DELETE * FROM " & tablename
DoCmd.RunSQL sql
Set rs = CurrentDb.OpenRecordset(tablename)
Set xlApp = CreateObject("Excel.Application")
Set mySheet = xlApp.Workbooks.Open(strFilePath).Sheets(1)
xlApp.Visible = False
Set mySheet = xlApp.Sheets("Input")
Dim dRows As Double
dRows = 1
Dim dRow As Double, dCol As Double
dRow = 2
On Error GoTo ERR
Do
dCol = 1
rs.AddNew
If mySheet.cells(dRow, 3) = "" Then Exit Do
Do
If mySheet.cells(dRow, dCol).Value <> "_END_" Then
rs.Fields(dCol).Value = Nz(mySheet.cells(dRow, dCol).Value, "")
dCol = dCol + 1
Else
Exit Do
End If
Loop
rs.Update
dRow = dRow + 1
Loop
EXITSUB:
Set mySheet = Nothing
Set xlApp = Nothing
Exit Sub
ERR:
If ERR.Number = 3265 Then MsgBox "The species selected are incompatible. Canceling import.", vbCritical, "IMPORT ERROR"
GoTo EXITSUB
End Sub
Try using
xlApp.Quit
When you set xlApp to nothing you are only clearing the object within the procedure, you aren't doing anything to the actual Excel instance. All that setting XXX = nothing allows you to do is then reuse that object.
You will need to legally close the workbooks that are open as in
xlApp.Workbooks.Close
EXITSUB:
This will close the instances that are open.
Prior to this, kill all the instances or reboot your machine to clear all the instances that are open.
I have a user form in Excel VBA with a check box for each month.
Selecting one or more cause the required month to be shown on the sheet, I copy-pasted the code 12 times and it works but I'm sure there is a better way doing it with a For loop.
This is a part of my code (it goes on 12 times):
If CheckBox1.Value = True Then
ActiveSheet.PivotTables("PivotTable1").PivotFields("month").PivotItems("1").Visible = True
Else
ActiveSheet.PivotTables("PivotTable1").PivotFields("month").PivotItems("1").Visible = False
End If
If CheckBox2.Value = True Then
ActiveSheet.PivotTables("PivotTable1").PivotFields("month").PivotItems("2").Visible = True
Else
ActiveSheet.PivotTables("PivotTable1").PivotFields("month").PivotItems("2").Visible = False
End If
I tried writing:
for i in range 1 to 12
and then writing my code but there seem to be a problem when I put "i" instead of the numbers.
Assuming you aren't using Tristate checkboxes, then the .Value can only be True or False, so we should be able to get away with something like this:
(Assumes your code runs inside the UserForm, so that Controls is directly accessible)
Dim mthIdx as Long
Dim nm as String
Dim c As Control
With ActiveSheet.PivotTables("PivotTable1").PivotFields("month")
For mthIdx = 1 To 12
nm = "CheckBox" & mthIdx
Set c = Controls(nm)
.PivotItems(mthIdx).Visible = c.Value
Next
End With
(The With clause isn't strictly necessary, but it's usually a good idea to resolve nested COM references as infrequently as possible)
Try this ..
Dim i As Integer
Dim sN As String
Dim chx As MSForms.CheckBox
Dim obj As OLEObject
For i = 1 to 12
sN = format(i)
Set obj = OLEObjects("CheckBox" & sN)
Set chx = obj.Object
If chx.Value = True Then
ActiveSheet.PivotTables("PivotTable" & sN).PivotFields("month").PivotItems(sN).Visible = True
Else
ActiveSheet.PivotTables("PivotTable" & sN).PivotFields("month").PivotItems(sN).Visible = False
End If
Next
I've not checked the code but this should put you along thr right path if it's not spot on though...
For i = 1 to 12
If CheckBox(i).Value = True Then
ActiveSheet.PivotTables("PivotTable1").PivotFields("month").PivotItems(i).Visible = True
Else
ActiveSheet.PivotTables("PivotTable1").PivotFields("month").PivotItems(i).Visible = False
End If
Next i
I have a large excel sheet with a log that consists of around 30000 entries.
The programmer before me has created a removeline.cmd file to remove all extra blank lines in a certain column for the excel file.
The code for the RemoveLine.cmd:
cls
cd\
SET vbfile=newlinetest.exe
K:
cd "IPM - CompOps\Ops Docs\avail-stats\Originals"
%vbfile%
exit
The file runs correctly but at the end it displays this error, which is essentially what I'm trying to get rid of:
Run-time error '1004';
Method '~' of object '~' failed
EDIT:
the program newlinetest.exe was written in VB6 (I have access to it on my machine).
The full source-code for newline.frm is:
VERSION 5.00
Begin VB.Form Form1
Caption = "Form1"
ClientHeight = 4500
ClientLeft = 3435
ClientTop = 3585
ClientWidth = 5175
LinkTopic = "Form1"
ScaleHeight = 4500
ScaleWidth = 5175
Begin VB.CommandButton Command1
Caption = "Excel"
Height = 495
Left = 1800
TabIndex = 0
Top = 3720
Width = 855
End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Private Sub Command1_Click()
Dim oXL As Object ' Excel application
Dim oBook As Object ' Excel workbook
Dim oSheet As Object ' Excel Worksheet
Dim oChart As Object ' Excel Chart
Dim year As String
Dim i As Long
Dim MyRowNumber As Long
Dim Row As Long
Dim comment As String, newline As String
Dim curDate As String
Open "K:\IPM - CompOps\Ops Docs\avail-stats\Originals\Inputavailfile.txt" For Input As #1
Input #1, Data
Close #1
'Start Excel and create a new workbook
Set oXL = CreateObject("Excel.application")
Set oBook = oXL.Workbooks.Add
Set oSheet = oBook.Worksheets.Item(1)
oXL.Visible = True
oXL.UserControl = True
year = Format(Now, "yyyy")
curDate = Date - 3
curDate = Format(curDate, "m/d/yyyy")
Application.DisplayAlerts = False
Workbooks.Open FileName:="K:\IPM - CompOps\Ops Docs\avail-stats\Originals\" + Data
Myfile = "K:\IPM - CompOps\Ops Docs\avail-stats\Originals\" + Data
On Error GoTo Handler
vOurResult = Cells.Find(What:=curDate, LookAt:=xlWhole).Select
If (vOurResult = True) Then
MyRowNumber = ActiveCell.Row
Set ExcelLastCell = ActiveSheet.Cells.SpecialCells(xlLastCell)
'MsgBox vOurResult
Row = ExcelLastCell.Row
col = ExcelLastCell.Column
' MsgBox curDate
Cells(ActiveCell.Row, ActiveCell.Column + 6).Select
comment = ActiveCell.Text
newline = Replace(comment, Chr(10), " ")
ActiveCell.Value = newline
For i = MyRowNumber To Row - 1
comment = ""
newline = ""
Cells(ActiveCell.Row + 1, ActiveCell.Column).Select
comment = ActiveCell.Text
newline = Replace(comment, Chr(10), " ")
ActiveCell.Value = newline
Next i
'MsgBox curDate
ActiveWorkbook.SaveAs FileName:=Myfile, FileFormat:=xlNormal
End If
oXL.Quit
Handler:
oXL.Quit
End Sub
Private Sub Form_Load()
Command1_Click
End
End Sub
Private Sub Label1_Click()
End Sub
You have these lines towards the end of the Sub:
oXL.Quit
Handler:
oXL.Quit
The second Quit call fails, generating the error. You need to exit the procedure just before the Handler (which will only be called in the event of an error):
oXL.Quit
Exit Sub
Handler:
oXL.Quit
That's because the code 'falls through' to your line-label called Handler.
Thus when your Handler then tries to call Method 'Quit' of object 'oXL', that will fail because oXL has already quit.
The obvious solution is to Exit Sub before it reaches the Handler.
The general layout for a Sub (from MSDN):
Sub InitializeMatrix(Var1, Var2, Var3, Var4)
On Error GoTo ErrorHandler
. . .
Exit Sub
ErrorHandler:
. . .
Resume Next
End Sub
Hope this helps!
EDIT:
Seems the original question that I was helping the asker with via chat was deleted and later re-posted (I assume to get some fresh page-views).
Although Andy G has already answered this re-post, I figured not to let my answer go to waste and posted it anyway, hoping the explanation and reference might help future readers.
I have an Excel Spreadsheet (let's say objectdata.xls) which is used to set the widths/lengths of different rectangles. The spreadsheet therefore has 3 columns:
Object Name
Object Width
Object Length
There are approx 100 rectangles defined in the Spreadsheet
What i am try to do is run a macro in a PowerPoint (PP) which will read the data from the Spreadsheet (ideally this info should be stored external to the PP file but if need be it could be a linked or embedded file within PP) and then update the size of the rectangle shapes that I have included in the PP file.
E.g. on slide one, the macro reads row 1 in the spreadhseet and sees that the object width is 5 and length is 10, and so updates the size of the rectangle shape in the PP.
Can anyone tell me if this can be done?
Thanks.
Use GetExcelData to do the work; it calls GetExcel
Function GetExcel() As Object
'---------------------------------------------------------------------------------------
' Procedure : GetExcel
' Author : Naresh Nichani / Steve Rindsberg
' Purpose :
' Check if an instance of Excel is running. If so obtain a reference to the running Excel application
' Otherwise Create a new instance of Excel and assign the XL application reference to oXLApp object
' SR : Modified 2010-02-23 to ALWAYS create a new instance rather than using an existing one, so when we
' : close the one we open, we don't wack the user's other instances of Excel if any
' Params : None
' Returns : An Excel Application object on success, Nothing on failure
'---------------------------------------------------------------------------------------
On Error GoTo GetExcel_ErrorHandler
On Error Resume Next
Err.Number = 0
Dim oXLAPP As Object
' Comment out the following bits to force a new instance of Excel
' and leave any existing instances alone
' Set oXLApp = GetObject(, "Excel.Application")
' If Err.Number <> 0 Then
' Err.Number = 0
Set oXLAPP = CreateObject("Excel.Application")
If Err.Number <> 0 Then
'MsgBox "Unable to start Excel.", vbInformation, "Start Excel"
Exit Function
End If
' End If
On Error GoTo GetExcel_ErrorHandler
If Not oXLAPP Is Nothing Then
Set GetExcel = oXLAPP
Else
[MASTTBAR].rnrErrLog "modExcel:GetExcel - unable to invoke Excel instance"
End If
Set oXLAPP = Nothing
Exit Function
NormalExit:
On Error GoTo 0
Exit Function
GetExcel_ErrorHandler:
Resume NormalExit
End Function
Function GetExcelData(sFilename As String, _
Optional lWorksheetIndex As Long = 1, _
Optional sWorksheetName As String = "") As Variant
'---------------------------------------------------------------------------------------
' Purpose : Gets the "active" data from the file/worksheet specified
Dim oXLAPP As Object
Dim oxlWB As Object
Dim oxlRange As Object
Dim x As Long
Dim y As Long
Dim sMsg As String
Dim lVisibleRowCount As Long
Dim lVisibleColCount As Long
Dim aData() As String
On Error GoTo GetExcelData_ErrorHandler
Set oXLAPP = GetExcel()
If oXLAPP Is Nothing Then
Exit Function
End If
' open the workbook read-only
Set oxlWB = oXLAPP.Workbooks.Open(sFilename, , True)
If oxlWB Is Nothing Then
Exit Function
End If
If Len(sWorksheetName) > 0 Then
Set oxlRange = GetUsedRange(oxlWB.Worksheets(sWorksheetName))
Else
Set oxlRange = GetUsedRange(oxlWB.Worksheets(lWorksheetIndex))
End If
If oxlRange Is Nothing Then
Exit Function
End If
' Get a count of visible rows/columns (ignore hidden rows/cols)
For x = 1 To oxlRange.Rows.Count
If Not oxlRange.Rows(x).Hidden Then
lVisibleRowCount = lVisibleRowCount + 1
End If
Next ' row
For y = 1 To oxlRange.Columns.Count
If Not oxlRange.Columns(y).Hidden Then
lVisibleColCount = lVisibleColCount + 1
End If
Next
ReDim aData(1 To lVisibleRowCount, 1 To lVisibleColCount)
lVisibleRowCount = 0
For x = 1 To oxlRange.Rows.Count
If Not oxlRange.Rows(x).Hidden Then
lVisibleRowCount = lVisibleRowCount + 1
lVisibleColCount = 0
For y = 1 To oxlRange.Columns.Count
If Not oxlRange.Columns(y).Hidden Then
lVisibleColCount = lVisibleColCount + 1
aData(lVisibleRowCount, lVisibleColCount) = oxlRange.Cells(x, y).Text
End If
Next
End If
Next
' return data in array
GetExcelData = aData
NormalExit:
On Error GoTo 0
' Close the workbook
If Not oxlWB Is Nothing Then
oXLAPP.DisplayAlerts = False
oxlWB.Close
oXLAPP.DisplayAlerts = True
End If
'To Close XL application
If Not oXLAPP Is Nothing Then
oXLAPP.Quit
End If
'Set the XL Application and XL Workbook objects to Nothing
Set oxlRange = Nothing
Set oxlWB = Nothing
Set oXLAPP = Nothing
Exit Function
GetExcelData_ErrorHandler:
Resume NormalExit
End Function
Blockquote
Blockquoteenter code here
Yes, this can certainly be done. It takes a bit more code than I have at the tip of my fingers and you'd need to adapt whatever I posted. But have a look here for examples you can start with. These point to the PowerPoint FAQ site that I maintain. No charge for anything.
Controlling Office Applications from PowerPoint (by Naresh Nichani and Brian Reilly)
http://www.pptfaq.com/FAQ00795.htm
Automate Excel from PowerPoint. Automate PowerPoint from Excel. And so on.
http://www.pptfaq.com/FAQ00368.htm
I'd probably do this by opening the excel file, reading the contents into an array, then using the data from the array to do the actual work in PPT.
If you need help with the PPT part, let us know. It'd mostly be a matter of writing a function like [aircode]:
Sub SetRectangleSize ( sRectangleName as string, sngWidth as Single, sngHeight as Single)
Dim oShp as Shape
Set oShp = GetShapeNamed(sRectangleName, lSlideIndex)
If Not oShp is Nothing Then
With oShp
.Width = sngWidth
.Height = sngHeight
End With
End If
End Sub
And
Function GetShapeNamed(sName as String, lSlideIndex as Long) as Shape
On Error Resume Next
Set GetShapeNamed = ActivePresentation.Slides(lSlideIndex).Shapes(sName)
If Err.Number <> 0 Then
' no shape by that name on the slide; return null
Set GetShapeNamed = Nothing
End If
End Function
Incidentally, I would consider using tags to identify the rectangles rather than shape names (which tend to be less reliable).