Cannot find the error in code for identifying if email account exist - vb.net

In the RPA software Blue Prism, I am trying to write some VB.Net code for identifying if an account (i.e. Account_Display_Name) is configured to the desktop Outlook application (including shared mailboxes). The code utilizes Microsoft.Office.Interop.Outlook.dll, and the internal compiler does not identify any errors. However, when I try to run the code, I get the error "Object variable or With block variable not set".
I cannot find what is causing the issue, and would really appreciate your support. Perhaps there is a better way altogether for achieving what I want...
Dim olApp As Object
Dim olNamespace As Object
Dim Account_Exist As Boolean
Dim Account_Display_Name As String
olApp = CreateObject("Outlook.Application")
olNamespace = olApp.GetNameSpace("MAPI")
Account_Exist=False
If Not String.IsNullOrEmpty(Account_Display_Name) Then
Dim aa As List(Of Account) = olNamespace.Accounts.Cast(Of Account).ToList()
For Each x As Account In aa
If x.DisplayName = Account_Display_Name Then
Account_Exist=True
Exit For
End If
Next
End If
I have tried to put the code into online compilers to see if I can get some leads, but to no prevail.

The logic in your loop doesn't appear that it will ever execute. Is that intentional? Dim Account_Display_Name as String will initialize to string.empty and when it hits the if statement, String.IsNullOrEmpty(Account_Display_Name) will return true and then be negated by your not and your inner block will be skipped. (assuming this is a complete excerpt and nothing was ommitted for brevity. If someone was removed I would encourage you to edit your OP and update it with the complete code sample.)
With the above in mind, we know that the for loop logic is not getting hit so we know the issue is likely occurring at olApp.getNameSpace("MAPI"). The most likely culprit is olApp is nothing while you are trying to reference one of its functions.
You can confirm which with a break point and stepping through the debugger.
I have tried to put the code into online compilers to see if I can get some leads, but to no prevail
This is indeed the trouble with logic bugs as syntactically, your code appears to be correct.

Related

Receiving an error from trying to make a startup program shortcut

I am trying to make a shortcut for my program so that it starts automatically with windows. The following code is the sub that creates the shortcut and how it gets it's variables but it fails.
Dim ShortCutPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Startup) & "/ScreenRot.lnk"
Shared Sub CreateShortCut(File As String, ShortCutPath As String)
Dim oShell As Object
Dim oLink As Object
'you don’t need to import anything in the project reference to create the Shell Object
Try
oShell = CreateObject("WScript.Shell")
oLink = oShell.CreateShortcut(ShortCutPath)
oLink.IconLocation = File
oLink.TargetPath = File
oLink.Arguments = ""
oLink.WindowStyle = 1
oLink.Save()
Catch ex As Exception
End Try
End Sub
The line it seems to fail on is.
oLink = oShell.CreateShortcut(ShortCutPath)
The error I am getting is
DirectCast(ex, System.MissingMemberException).Message
Public member 'CreateShortcut' on type 'IWshShell3' not found.
I am using this in my program.
Imports IWshRuntimeLibrary
I have tried a couple different ways to make the shortcut but this seems to be the one that should work for what I need. I've read a bit about using this code and watched a video but nothing talks about the error. I've googled the error but nothing resembles a solution. I've tried to adjust the code slightly by using other examples but it still fails with more or less the same error. I don't really understand what the error is saying, so I can try and figure it out. thanks for your time and any help you guys can give.
After reviewing many posts and trying a lot of different things I found a solution on Stackoverflow which I can confirm actually works.
Stackoverflow Post
This post has the solution, hopefully it helps other people with this problem.

SolidWorks VBA - Translating API Help into useable code

I'd like to do what feels like a fairly simple task, and I've found the specific API Help pages which should make it clear, but, I can't actually make things work.
The Key steps that I would like to achieve are:
Rename the active document
Update References to this document to accommodate new name
Save active document.
This help page shows the Usage for renaming the doc, and under the "Remarks" heading, includes links to the next two steps, mentioning them off hand as if implementing them would be easy.
https://help.solidworks.com/2020/English/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IModelDocExtension~RenameDocument.html?verRedirect=1
The trouble is, I'm a bit of a VBA beginner - usually I get by with the 'record' function, and then tidying things up from there - but undertaking the steps above manually doesn't result in anything being recorded at all for one reason or another.
Assuming I am able to pass in the item to be renamed (I'll define a variable at the start of the Sub for this e.g. swModel = swApp.ActiveDoc), and the new name (NewName = "NEW NAME HERE"), How would I translate the Help API into a Sub that I can actually run?
Two of them suggest declaring as a Function, and one as a Public Interface - I've never used these before - do these just run in a standard Module? Do I need to write a 'master Sub' to call the different functions sequentially, or could these be included directly in the sub, if they're only to be used once?
[Feeling a little lost - it's demoralizing when the help files aren't all that helpful]
Let me know if there's any more information missing that I can add to improve my question - as I said, I'm fairly new to this coding thing...
The "record" function is sometimes a good point to start but there are a lot of functions it can't recognize while you execute them manually.
The API Help is then useful to find out how to use a specific function.
In almost every example the use of a specific method (e.g. RenameDocument) is only shown abstract. There is always a instance variable which shows you the object-type needed to call this method. So you can use these in every sub you want, but beforehand need access to the specific instance objects.
For your example the RenameDocument method is called with an object of the type IModelDocExtension. First thing for you to do is to get this object and then you can call the method as described in the help article.
Under Remarks in the article you find additional information for what you maybe have to do before or after calling a method.
For your example it is mentioned that the renaming takes permanently place after saving the document.
And finally here is what you want to do with some VBA code:
Dim swApp As SldWorks.SldWorks
Dim swModel As ModelDoc2
Sub main()
' get the solidworks application object
Set swApp = Application.SldWorks
'get the current opened document object
Set swModel = swApp.ActiveDoc
' get the modeldocextension object
Dim swModelExtension As ModelDocExtension
Set swModelExtension = swModel.Extension
Dim lRet As Long
lRet = swModelExtension.RenameDocument("NEW NAME")
If lRet = swRenameDocumentError_e.swRenameDocumentError_None Then
MsgBox "success renaming"
Else
MsgBox "failed with error: " & lRet
End If
End Sub
Afterwars you have to process the return value to check for errors described in this article: https://help.solidworks.com/2020/English/api/swconst/SolidWorks.Interop.swconst~SolidWorks.Interop.swconst.swRenameDocumentError_e.html

vba code suddenly encounters problems with enumeration

This visual basic code related to outlook used to work without problems
Sub cmdExample()
Dim myOlApp As Object
Set myOlApp = CreateObject("Outlook.Application")
Set myoSession = myOlApp.Session
Set myoCalendar = myoSession.GetDefaultFolder(olFolderCalendar)
End Sub
Now I obtain the runtime error 5 (Invalid procedure call or argument)
Reason found from debugging: at runtime olFolderCalendar is empty (by the way, same problem for other enumerations like olAppointmentItem, olBusy). My workaround in the above code is calling .GetDefaultFolder(9).
However I would rather use the enumerations and I would like to understand why all of them are empty.
If you want to pass literal constants instead of numbers you need to add a COM reference to Outlook. Read more about that in the How to Add an Object Library Reference in VBA article.
Also, you may try to use the Logon method before getting the folder. See NameSpace.Logon for more information.

Can I use late binding to check the existence of a library before using it via early binding?

I like to use early binding in my VBA projects, since I like the auto-complete of method names, etc. during development. I also like the confidence of knowing that the compiler will warn me if I've mis-spelled a method name.
However, to use early binding I need to add a reference to the relevant library (for example, the "Microsoft Scripting Runtime"). That's fine for "standard" libraries like that, but sometimes I want to use a library that may or may not be present on the user's machine.
Ideally, I'd like to display a useful message if the library is not present (such as "xyz is not installed on this computer, and so this feature cannot be used"). If I was using only late binding, then I could do this:
Dim o As Object
Set o = CreateObject("foo", "bar")
If o Is Nothing Then
MsgBox "nope"
End If
But, if I've added a reference to the library in order to use early binding, then if the library is not present I get a compile error when my VBA project is loaded. Thus, none of the code runs (including the code to detect the non-existence of the library).
Is there any way around this catch-22?
You could create a class module as a proxy for an object library --- wrap all the methods, properties, and constants needed by your other procedures.
All those procedures would use the proxy class the same way, so you wouldn't need to revise those procedures to switch between early and late binding. And Intellisense would show you everything you expose with the proxy class.
The class would be a single point of control to switch between early and late binding. You mentioned Excel as one example:
#Const DevStatus = "PROD"
#If DevStatus = "DEV" Then
Private objApp As Excel.Application
Private objBook As Excel.Workbook
Private objSheet As Excel.Worksheet
#Else 'assume PROD
Private objApp As Object
Private objBook As Object
Private objSheet As Object
#End If
If there is a possibility Excel may not be installed on any users' machines, you can check its availability during class initialize.
Dim blnExcelAvailable As Boolean
Private Sub Class_Initialize()
blnExcelAvailable = IsExcelAvailable()
End Sub
Private Function IsExcelAvailable() As Boolean
Dim blnReturn As Boolean
Dim objTest As Object
On Error GoTo ErrorHandler
Set objTest = CreateObject("Excel.Application")
blnReturn = True
ExitHere:
On Error GoTo 0
Set objTest = Nothing
IsExcelAvailable = blnReturn
Exit Function
ErrorHandler:
blnReturn = False
GoTo ExitHere
End Function
Then your procedures which use the proxy class could check a property to see whether Excel is available.
Public Property Get ExcelAvailable() As Boolean
ExcelAvailable = blnExcelAvailable
End Property
I think this approach is possible, and it satisfies your requirements AFAICT. However, I'm unsure whether it's reasonable. Back to the example of Excel, you could do something like this for a manageable subset of its object model. But if you need all or most of its methods, properties, and constants, the proxy class would be a huge undertaking.
Personally I wouldn't use this approach. It's less work for me to manage early/late binding as mwolfe02 and JP. described. However my impression is this is more burdensome in your situation, so perhaps you're willing to invest more effort than I am in something like this.
Not really.
However, one way I've dealt with this in development is to have two separate declaration lines. I comment one or the other depending on whether I am doing dev work or releasing to production. You can leave everything else alone (including the CreateObject line) and then you just need to remember to switch the commented line and add/remove the reference itself.
For example:
Dim o As foo.bar 'Comment out for production'
'Dim o As Object ''Comment out for dev work'
Set o = CreateObject("foo", "bar")
If o Is Nothing Then
MsgBox "nope"
End If

Need to call com function from VB.NET

If anyone wants to take a crack at this I'd really appreciate it. I'm writing a VB.NET app that will control a commercial backup product. One of the things I need to do is loop through all existing jobs and look at the source drive. I am able to do this in VBScript very simply like this:
Dim SP, BackupJob, volumes
Set SP = CreateObject("ShadowStor.ShadowProtect")
For Each Job In SP.Jobs
Set BackupJob = SP.Jobs.GetBackupJob(Job.Description)
BackupJob.GetVolumes volumes
For Each Volume in volumes
WScript.Echo volume
Next
Next
Set SP = Nothing
However nothing I try in VB.NET works. I'm pretty sure it has to do with the fact that the com functions are returning variant data types and arrays (specifically GetVolumes). I have tried using string arrays, object arrays, and even wrapping the return value in a VariantWrapper and I always get errors such as "not implemented" or "the parameter is incorrect." If anyone is bored and wants to write some code I'll gladly give it a shot and report back.
UPDATE:
This is odd. Look at this code:
Dim SP As Object = CreateObject("ShadowStor.ShadowProtect")
Dim gotJob As Object
Dim volumes() As Object
Try
For Each Job As Object In SP.Jobs
gotJob = SP.Jobs.GetBackupJob(Job.Description.ToString())
gotJob.GetVolumes(volumes)
For Each volume As Object In volumes
MsgBox(volume.ToString())
Next
Next
Catch ex As Exception
MsgBox(ex.Message)
End Try
This will display the volume from ONE job, then it crashes if there is more than one jobwith the error "invalid callee."
Locate ShadowStor.ShadowProtect in your registry in HKCR. It will have a CLSID which is a GUID. Search for that GUID, also in HKCR. You should find it in the CLSID section. Under that key you should find the actual dll path under InprocServer32.
Now if that component has an embedded TypeLib you should be able to add a reference to it in Visual Studio. (If you have OLE View installed you can inspect the type lib easily as well).
And if you cannot add a reference to the dll, there might be a seperate .tlb file, and you can find that by searching on the GUID present in the TypeLib value.
For anyone interested, the solution was to Dim volumes() As Object inside the loop and then set volumes = Nothing at the end of the loop so that it was re-created each time. If anyone can explain why this is so I would love to understand it.