I am using a Function that seems not compatible with VB strict option
It shoots a warning
Late bound resolution; runtime errors could occur.
Public Function RegRead(ByVal Path As String) As String
Dim ws As Object
On Error GoTo ErrHandler
ws = CreateObject("WScript.Shell")
RegRead = ws.RegRead(Path)
Return RegRead
Exit Function
ErrHandler:
RegRead = ""
End Function
it sugests something related to "ws.RegRead"
Can someone tell me how to modify this to not have the warning ?
Your problem is that you have
Dim ws As Object
This means that the compiler doesn't know what methods are available on ws, so all method calls will be late bound. Since the compiler can't check whether that method call is correct (name, parameter types are correct), this check happens at run-time. If anything is wrong then you will have a run-time error. With Option Strict it will give you a warning on that.
Instead you should use APIs designed for .NET, such as the Registry class.
Related
Question: Is there any way to access Shape objects for each node inside of a SmartArt object? It seems like SmartArtNode objects have a Shapes property that could be used for this purpose, but I am getting Type mismatch errors when I try to send shapes from the SmartArtNode.Shapes to other subroutines.
Background: I am writing an iterator subroutine that takes in 2 parameters: a SmartArt object and a custom class object that has a method to perform actions on Shape objects. The subroutine is supposed to iterate over the Shape objects for each node inside the SmartArt, and call a method from the custom class object on each Shape.
Here is code for the iterator subroutine:
Public Sub IterateOverShapesInSmartArt(mySmartArt As SmartArt, manipulator As ShapeManipulator)
Dim node As SmartArtNode
Dim shpRange As ShapeRange
For Each node In mySmartArt.AllNodes
Set shpRange = node.Shapes
If shpRange.count > 0 Then
manipulator.ManipulateShape shpRange.Item(1)
End If
Next node
End Sub
For reference, the signature on the custom class (ShapeManipulator) method being called is as follows:
Public Sub ManipulateShape(myShape As Shape)
Specific Problem: When I try running this code, I get a Run-time error '13': Type mismatch triggered by the line Set shpRange = node.Shapes. Actually, I originally tried forgoing assignment of node.Shapes to a temporary variable and using the method call manipulator.ManipulateShape node.Shapes(1) instead, but then that method call produced the same Type mismatch error. I've also tried using a For loop with counter variables instead of For Each loop only to get the same error message. What is going on? When I debug, the Locals window shows the right types that match my declarations, so I am at a loss.
I tested your code and I can confirm that Set shpRange = node.Shapes causes a type mismatch error.
I noticed, though, if shpRange is declared as a generic object instead (ie Dim shpRange as Object), it gets resolved to a ShapeRange, and there's no error.
In any case, you can avoid the assignment as follows...
Public Sub IterateOverShapesInSmartArt(mySmartArt As SmartArt, manipulator As ShapeManipulator)
Dim node As SmartArtNode
For Each node In mySmartArt.AllNodes
With node
If .Shapes.Count > 0 Then
manipulator.ManipulateShape .Shapes.Item(1)
End If
End With
Next node
End Sub
I'm trying to use VBA code to invoke a protected API which need authentication with OAuth2. Once I try to open a URL, I'm redirected to the ADFS page for authentication and than I come back.
Now for some applications using CreateObject("InternetExplorer.Application") and the .Navigate URL works fine, for other web app I need to use New InternetExplorerMedium in order to have the code working.
Can you tell me the differences between these objects and why some websites work with one and some work with the other?
Thank you
This way of referring to objects is called "Early" and "Late Binding". From MSDN:
The Visual Basic compiler performs a process called binding when an object is assigned to an object variable.
An object is early bound when it is assigned to a variable declared to be of a specific object type. Early bound objects allow the compiler to allocate memory and perform other optimizations before an application executes.
By contrast, an object is late bound when it is assigned to a variable declared to be of type Object. Objects of this type can hold references to any object, but lack many of the advantages of early-bound objects.
You should use early-bound objects whenever possible, because they allow the compiler to make important optimizations that yield more efficient applications. Early-bound objects are significantly faster than late-bound objects and make your code easier to read and maintain by stating exactly what kind of objects are being used.
TL DR:
The difference is that in early binding you get intellisense and compiling time bonus, but you should make sure that you have added the corresponding library.
Example usage of late binding:
Sub MyLateBinding()
Dim objExcelApp As Object
Dim strName As String
'Definition of variables and assigning object:
strName = "somename"
Set objExcelApp = GetObject(, "Excel.Application")
'A Is Nothing check:
If objExcelApp Is Nothing Then Set objExcelApp = CreateObject("Excel.Application")
'Doing something with the Excel Object
objExcelApp.Caption = strName
MsgBox strName
End Sub
Example usage of early binding:
First make sure that you add the MS Excel 16.0 Object Library from VBE>Extras>Libraries:
Sub MyEarlyBinding()
Dim objExcelApp As New Excel.Application
Dim strName As String
'Definition of variables and assigning object:
strName = "somename"
'A IsNothing check:
If objExcelApp Is Nothing Then Set objExcelApp = CreateObject("Excel.Application")
'Doing something with the Excel Object
objExcelApp.Caption = strName
MsgBox strName
End Sub
Related articles:
Early and Late Binding (my site)
Early Binding vs Late Binding (ex-SO Documentation)
Say you have one slide with one chart on it, and you run this code(in a version of Office later than 2007):
Dim pptWorkbook As Object
Dim result As Object
Set pptWorkbook = ActivePresentation.slides(1).Shapes(1).Chart.ChartData.Workbook
Set result = pptWorkbook.ContentTypeProperties
You will generate an error:
Application-defined or object-defined error
I believe this is because "Smart tags are deprecated in Office 2010."(Source), Generally to avoiding this sort of issue from throwing an error and exiting your VBA you can take one of two different approaches:
//Method 1
If OfficeVersion <= 2007
Set result = pptWorkbook.ContentTypeProperties
//Method 2
On Error Resume Next // or GOTO error handler
Set result = pptWorkbook.ContentTypeProperties
Method one requires that you know the specific reason why the property would cause an error, which is easy in this case but may not be as easy with other properties. Method two requires that you use some form of error handling to deal with the error AFTER the fact, my understanding of most other Microsoft languages is that is typically discouraged(example, another example). Is this standard practice in VBA?
In VBA, is there any other way to determine whether a property of an object would throw an error if invoked, BEFORE invoking that property, and without knowing the specifics of that invoked property?
What I like to do for this situation is create a separate function that checks if the property exists and returns a Boolean. In this case it would look something like this:
Public Function CheckIfExists(targetObj As Object) As Boolean
Dim testObj As Object
On Error GoTo failedTest:
Set testObj = targetObj.ContentTypeProperties
CheckIfExists = True
Exit Function
failedTest:
CheckIfExists = False
End Function
Which would return false if that property causes an error and true if not-
Then modify your sub to be:
Public Sub FooSub()
Dim pptWorkbook As Object
Dim result As Object
Set pptWorkbook = ActivePresentation.slides(1).Shapes(1).Chart.ChartData.Workbook
If CheckIfExists(pptWorkbook) Then
Set result = pptWorkbook.ContentTypeProperties
End If
... rest of your code or appropriate error handling...
Hope this helps,
TheSilkCode
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.
I have been looking through old code to get familiar with the system I use and found a piece of code that I feel can be used better.
What goes on here is some data gets added to the collection(around 150 string variables, some with two variables(variableName/VariableValue), most with only one(VariableName)). It will try to set a module level string variable to the item of the collection passing it the index(variableName) then if there's a value setting the VariableVAlue to the module level variable.
What I feel needs work is that if the collection is passed a variable and the variable doesn't have a value it will return a "" which would cause a runtime error hence there's a On Error GoTo Handler code to manually add a "" to the collection. I feel there's a better way to do this rather than knowing there will be a runtime issue then solving it after catching it. Would there be a way to have a return "" not throw an exception or would the use of an Array also work here since it's a "collection" as well?
Here's an example to try to help visualize:
Public Function GetCollectionVariable(ByVal varName as string) as String
If collection1 Is Nothing Then
m_collection1 = New Collection
End If
On Error GoTo Handler
GetCollectionVariable = collection1.Item(VarName)
exit function
Handler:
collection1.add("", VarName)
GetCollectionVariable = ""
End FUnction
Thanks for your time!!
If Collection1 is a dictionary, you can use TryGetValue.