Powerpoint Visual Basic code not working - vba

Recently I have been using a lot of macro visual basic in my PowerPoint. Everything was working fine and then I needed to add a Try and Catch statement. It seemed not to work but I thought maybe it changed in newer versions. Now after trying to make a dictionary in visual basic using PowerPoint the same thing happened. It didn't work and the text was red.
This is the code for the dictionary:
Dim test As New Dictionary(Of String, Integer)
test.Add("1", 1)
Is there anyway of adding dictionaries to it when using PowerPoint? or is this code wrong? The same thing with try and catch statements?

You need to ensure the reference to System.Collections.Generic is declared.
At the top of your code add:
Imports System.Collections.Generic

You are very unclear on the end result but try something like this.
Sub test()
On Error Goto Handler
Dim test AS New Collection
test.Add "1", "27"
test.Add 15, "Monkeys"
Exit Sub
Handler:
Msgbox "Error Number: " & Err & " Occurred. Its message text is: " & Error(Err)
End Sub
VBA Does not have Try and Catch you must handle all errors with On Error GoTo then you can process the Errors by the Error Number. See Here
Also note I used a Collection which uses an Item and Key structure. Not sure what you are going for with this but you can retrieve Items by Key in a Collection which might be fine for you. e.g.
test.Item(1) 'will return "1" by index
test.Item("27") 'will return "1" by key
test.Item(2) ' will return 15 by index
test.Item("Monkeys") 'will return 15 by key

Related

How to get data in Access VBA from a C# method that returns an object List

I'm calling a C# WebService method called getInterventions() on a VBA Access 2003 application through a custom DLL. The signature of this method is as follows:
List<Item> getInterventions(string, string, string, string, string, string)
Item is a custom defined class.
When I try to retrieve the result of getInterventions() on VBA Access code, it pops a VBA Runtime error 242 : object required
The following is my code:
Dim WsObject As Object
Dim result As Object
Set WsObject = CreateObject("Namespace1.Path.To.Class") 'This isn't the actual DLL path as I cannot share that
'Set result = WsObject .getSingleIntervention("123, "0", "123456789", "") ' this works
Set result = WsObject .getInterventions("", "", "123456789", "", "", "") 'this doesn't work
If result Is Nothing Then
'do some stuff
Else
'do some other stuff
End If
getSingleIntervention() is a similar method which returns a single object rather than a list of objects. Returning the result of this method works without issues (see commented line). This proves that both the WS & DLL calls work. This method is defined as follows:
Item getSingleIntervention(string, string, string, string)
I have tested calling getInterventions() directly from the C# code via Visual Studio 2015 with the same parameters I'm passing in my VBA code and it worked. This proves that it's not an issue with parameters or the method content.
My conclusion:
I am guessing it's something to do with the fact that I can't simply store a C# List of objects into a VBA Object.
Any help would be appreciated, thank you.
Please, automatically add mscorlib.tlb reference, running the next code:
Sub addMscorlibRef() 'necessary to use ArrayList
'Add a reference to 'Mscorlib.dll':
'In case of error ('Programmatic access to Visual Basic Project not trusted'):
'Options->Trust Center->Trust Center Settings->Macro Settings->Developer Macro Settings->
' check "Trust access to the VBA project object model"
If dir("C:\Windows\Microsoft.NET\Framework64\v4.0.30319", vbDirectory) = "" Then _
MsgBox "You need to install ""Framework version 3.5"".": Exit Sub
On Error Resume Next
Application.VBE.ActiveVBProject.References.AddFromFile "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorlib.tlb"
If err.Number = 32813 Then
err.Clear: On Error GoTo 0
MsgBox "The reference already exists...": Exit Sub
Else
On Error GoTo 0
MsgBox """Mscorlib"" reference added successfully..."
End If
End Sub
Then try declaring Dim result As ArrayList.
The ArrayList is the same one that is used in C#. Then, adapt the dll to use such an object. As you deduced, no any object able to be used in VBA can receive the C# list object content.

Runtime error 438 on remove dynamically added userform control

I've got the following code:
Private Sub cboA_change()
'Something to determine number of controls there should be, variable gC
'Something to determine number of controls there are, variable nC
'The first time the code runs, the following code runs:
For i = nC to gC
frmA.Frame1.Controls.Add("txtGroup" & i)
Next
'The second time the code runs, the following is executed:
For i = 7 To nC
Me.Frame1.Controls("txtGroup" & i).Remove 'ERROR HERE
Next
For i = nC to gC
frmA.Frame1.Controls.Add("txtGroup" & i)
Next
End Sub
Something like this, the code is way bigger and I tried to clear it up so if the structure doesn't seem right, that doesn't matter really.
I debugged the Add statement and I know there is a control added to the userform, called txtGroup7. However, when I later try to remove this control, I get Run-time Error 438: Object Doesn't Support This Property or Method. I tried changing the code to:
Me.Frame1.Controls.Remove ("txtGroup" & i)
But this didn't work either.
Can anybody point me in the right direction?
Edit:
I know the help says the following:
"This method deletes any control that was added at run time. However,
attempting to delete a control that was added at design time will
result in an error."
But since the control is added in run-time (dynamically, with VBA code) this shouldn't be a problem, right?
Edit 2:
I don't get why this works, but it seems to work:
q=0
While q < Me.Frame1.Controls.Count
If Me.Frame1.Controls(q).Name = "txtGroup7" Then
Me.Frame1.Controls.Remove q
Else
q = q + 1
End If
Wend
You must have something else wrong in your code, because Removeshould be working. Tried :
Private Sub ToggleButton1_Click()
If ToggleButton1.Value = True Then
Me.Frame1.Controls.Add "Forms.TextBox.1", "Text1", True
Else
Me.Frame1.Controls.Remove "Text1"
End If
End Sub
on a UserForm with a ToggleButton and a Frame and it correctly add and remove the TextBox when pressed.

Proper release of COM objects in code

I have just started to migrate some code from VBA to VB.Net. So I am an absolute beginner in VB.Net – but I want to do things right. Maybe some of my questions are stupid but I guess that is because I am a beginner.
So as a first exercise I have developed my first piece of code (see below). Now I thought I have to release ALL COM objects again. Two of them throw errors already while writing the code. And others throw errors at runtime.
But the funny thing is: Weather I release the rest of the COM objects or not (by making the relevant not yet commented lines of Marshal.Release to comments as well – then all lines starting with Marshal.Release are comment lines) the behavior of the code is absolutely the same to my eyes.
Can anybody tell me where I can see/find the difference?
The internet tells me that there must be a difference?
But I guess I just don’t understand (till now).
Besides this many more questions are in my head:
Does every “Dim” statement create a COM Object - that has to be released later on?
If not how do I detect whether a COM object has been created or not? Which “Dim” statements create COM object and which don't?
In this example: Dim ActiveWindow As Object = Nothing Try ActiveWindow = Me.HostApplication.ActiveWindow() Catch End Try
Is
Marshal.ReleaseComObject(ActiveWindow)
identical to
Marshal.ReleaseComObject(Me.HostApplication.ActiveWindow())?
According to this:
http://www.codeproject.com/Tips/235230/Proper-Way-of-Releasing-COM-Objects-in-NET
Would it not be better to release each "level" separately like this:
Marshal.ReleaseComObject(Me.HostApplication.ActiveWindow())
Marshal.ReleaseComObject(Me.HostApplication)
Marshal.ReleaseComObject(Me)
Overall: Am I trying to release too much? Or is it correct / good practie?
And what does "GC.Collect()" and "… = Null" have to do with all this? I have not used it at all. Should I better use it? Why? ( "... = Null" I have seen here:
http://www.codeproject.com/Tips/162691/Proper-Way-of-Releasing-COM-Objects-in-NET)
Why do I get “ShapeCount was not declared …” - Error if I try to do “Marshal.ReleaseComObject(ShapeCount)”? The same with “ShRange”. I think these are COM objects as well?!?
How do I notice when is the best time to release the COM object again? When I process/debug my code step by step with F11 will it be possible for me to determine the best (soonest) point of release? So far I have no “feeling” about when the COM object is not needed anymore and I can release it.
Any help and explanations very welcome.
Here is the code I am talking about:
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports System.Windows.Forms
Imports AddinExpress.MSO
Imports PowerPoint = Microsoft.Office.Interop.PowerPoint
'Add-in Express Add-in Module
<GuidAttribute("D75C609E-7632-400F-8A6F-6A6E6E744E75"),
ProgIdAttribute("MyAddin8.AddinModule")> _
Public Class AddinModule
Inherits AddinExpress.MSO.ADXAddinModule
#Region " Add-in Express automatic code "
[…]
#End Region
Public Shared Shadows ReadOnly Property CurrentInstance() As AddinModule
Get
Return CType(AddinExpress.MSO.ADXAddinModule.CurrentInstance, AddinModule)
End Get
End Property
Public ReadOnly Property PowerPointApp() As PowerPoint._Application
Get
Return CType(HostApplication, PowerPoint._Application)
End Get
End Property
Private Sub AdxRibbonButton2_OnClick(sender As Object, control As IRibbonControl, pressed As Boolean) Handles AdxRibbonButton2.OnClick
MsgBox(GetInfoString2())
End Sub
Friend Function GetInfoString2() As String
Dim ActiveWindow As Object = Nothing
Try
ActiveWindow = Me.HostApplication.ActiveWindow()
Catch
End Try
Dim Result As String = "No document window found!"
If Not ActiveWindow Is Nothing Then
Select Case Me.HostType
Case ADXOfficeHostApp.ohaPowerPoint
Dim Selection As PowerPoint.Selection =
CType(ActiveWindow, PowerPoint.DocumentWindow).Selection
Dim WindowViewType As PowerPoint.PpViewType = PowerPoint.PpViewType.ppViewNormal
Dim SlideRange As PowerPoint.SlideRange = Selection.SlideRange
Dim SlideCountString = SlideRange.Count.ToString()
If WindowViewType = 9 And SlideCountString < 2 Then
Dim ShRange As PowerPoint.ShapeRange = Nothing
Try
ShRange = Selection.ShapeRange
Catch
End Try
If Not ShRange Is Nothing Then
Dim ShapeCount = ShRange.Count.ToString()
Result = "You have " + ShapeCount _
+ " shapes selected."
Else
Result = "You have 0 shapes selected."
End If
End If
'Marshal.ReleaseComObject(ShapeCount)
'Marshal.ReleaseComObject(ShRange)
'Marshal.ReleaseComObject(WindowViewType)
'Marshal.ReleaseComObject(SlideCountString)
Marshal.ReleaseComObject(SlideRange)
Marshal.ReleaseComObject(Selection)
Case Else
Result = AddinName + " doesn't support " + HostName
End Select
'Marshal.ReleaseComObject(Me.HostType)
'Marshal.ReleaseComObject(Result)
Marshal.ReleaseComObject(Me.HostApplication.ActiveWindow())
Marshal.ReleaseComObject(Me.HostApplication)
'Marshal.ReleaseComObject(Me)
End If
Return Result
End Function
End Class
The ReleaseComObject method of the Marshal class decrements the reference count of the specified Runtime Callable Wrapper (RCW) associated with the specified COM object, it doesn't release an object. It comes from the COM nature.
Typically you need to release every object returned from the Office (PowerPoint in your case) object model. Exceptions are objects passed to event handlers as parameters.
You may read more about that and find answers to your multiple questions in the When to release COM objects in Office add-ins developed in .NET article.
FinalReleaseComObject calls ReleaseComObject til it returns 0 which means release of COM object. Calling them in reverse order as in Excel objects(Application, Workbook, Worksheet) is the proper way to dispose of COM objects that are related.
Exception Condition
ArgumentException
o is not a valid COM object.
ArgumentNullException
o is null.

Kill Excel on Error

I am hoping you can help me here, in the past you all have been great. I have tried every variation of the kill script for killing excel from vb.net, to no avail.
First I can't post explicit code on here because it is my company's proprietary software, but I can tell you a few things. Also there are over 28,000 lines of code.
I am not using
Imports Excel = Microsoft.Office.Interop.Excel
due to the fact that we have to accommodate different variations of clients software. I am creating the new excel as an object as such
Dim XLObj As Object = CreateObject("Excel.Application")
I have seen this used on several other sites but the kill function they are using is when you save and then close it, which I'm not doing.
The error message I am getting says that "Com object that has been separated from its underlying RCW cannot be used". I'm not sure where this com object is because I have released the sheets, workbook and then the application.
Oh and I don't want to use the excel.kill() because if a client already has the excel open I don't want to kill it without saving it. I only want to kill the newly generated excel process that doesn't have a window open associated with it.
My questions are as follows
I need to be able to close the Excel application when/if the open fails. So say I am click a link and it opens the dialog box to select an Excel template to load but either the data from the database is corrupt or the sql statement is broken. The program throws and error and then Excel should close in the Task Manager. Unfortunately it doesn't close hence the problem.
is there a way to close only the newly created process id? I have tried to use the directions here but it doesn't work either. When I do that it gives me a different error "Value cannot be null Parameter name: o". The line that is throwing the error is on (from the link)
Marshal.FinalReleaseComObject(tempVar)
I only tried this because we are using the With on the XLObj. The With is in reference to the workbook itself so shouldn't it be released when I close the workbook? And being as I'm causing it to error on purpose at the moment it shouldn't reach the With statement anyway.
Is there a way to tell which com object is not closing?
Things I have tried:
This releaseObject that I found on the internet. (don't ask me where I've been through about 75 pages)
Private Sub releaseObject(ByRef obj As Object)
Try
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(obj)
If obj Is Nothing Then
Else
obj = Nothing
End If
Catch ex As Exception
If obj Is Nothing Then
Else
obj = Nothing
End If
Finally
GC.Collect()
GC.WaitForPendingFinalizers()
End Try
End Sub
This is used in conjunction with this function (which was pieced together from the many sites I have been on)
Public Sub CloseExcel(ByRef WorkBook As Object, ByRef Application As Object)
Dim xLSheet As Object = WorkBook.Sheets
For Each xLSheet In WorkBook.Sheets
If xLSheet IsNot Nothing Then
releaseObject(xLSheet)
End If
If xLSheet IsNot Nothing Then
Kill(xLSheet)
End If
Next
If WorkBook IsNot Nothing Then
WorkBook.Close(False)
End If
If WorkBook IsNot Nothing Then
Kill(WorkBook)
End If
releaseObject(WorkBook)
If Application IsNot Nothing Then
Application.Quit()
End If
If Application IsNot Nothing Then
Kill(Application)
End If
releaseObject(Application)
GC.Collect()
GC.WaitForPendingFinalizers()
Application.Quit()
End Sub
and because it is also referenced the Kill function
Public Sub Kill(ByRef obj As Object)
Try
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(obj)
Catch ex As Exception
MessageBox.Show("moduleExcel.Kill " & ex.Message)
Finally
obj = Nothing
End Try
End Sub
any help would be greatly appreciated.
Ok so for those of you having this exact same issue. I do have a solution for you. Yes the above code does work but for a few minor adjustments.
you need to take out all the code in the CloseExcel sub and place it EXACTLY where you want it to close. So if you want it to close if the program errors out, put after the catch statement. You cannot call a Sub and pass in your objects and expect it to kill the process.
you need a few bits above the opening of the new Excel process. and they are as follows.
'declare process for excel
Dim XLProc As Process
'loads the financials excel bookmarks
'this will be where you declare your new excel opbject
Dim XLObj As Object = CreateObject("Excel.Application")
'get window handle
Dim xlHWND As Integer = XLObj.hwnd
Dim ProcIDXL As Integer = 0
'get the process ID
GetWindowThreadProcessId(xlHWND, ProcIDXL)
XLProc = Process.GetProcessById(ProcIDXL)
and of course you will need the GetWindowThreadProcessId which I got from the link I included in the original question. I am posting it here so you don't have to search for it.
<System.Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
Private Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer
End Function
This code will only close the single process you have it associated with, it will not close other open Excel files. Our clients sometimes will have multiple files open and we don't want to close them without telling them. This KILLS the Excel process that was created at run time when the system Errors out.

VBA Excel "Variables are required"

Got a trouble in a VBA excel program.
Sub code(s)
...
code = t
End Sub
And then :
Sub CommandButton1_Click()
...
For i = 0 To size
current_y = code(string_array(i))
...
End Sub
When i run the program, i got this error "Variables are required" (not sure, i'm working on a japanese version of excel). Sub CommandButton1_Click is highlighted and code is selected inside CommandButton1_Click. Can't figure out why, though it must be simple...
You're trying to return a result from a Sub. Try declaring it as a function instead, as this is able to return values to the caller:
Function code(s)
...
code = t
End Function
If it makes it any clearer, on my English version the error message is:
Expected Function or variable
Does the code include Option Explicit? Perhaps the error translates to "Variable declaration required"? Try removing option explicit - if that fixes it remove that line, then go through checking all variables are declare (e.g. dim current_y as string).