How do I access the names of VB6 modules from code? - vb6

I am currently maintaining some code, which is likely to be refactored soon. Before that happens, I want to make the standard error handling code, which is injected by an Add-In, more efficient and take up less space. One thing that annoys me is that every module has a constant called m_ksModuleName that is used to construct a big string, which is then rethrown from the error handler so we can trace the error stack. This is all template code, i.e., repetitive, but I could easily strip it down to a procedure call. Now, I have fixed the code so that you can pass the Me reference to the procedure - but you can't do that for the BAS modules. Nor can you access the project name (the part which would be passed as part of a ProgramID, for instance) - although you get given it when you raise an error yourself.
All these strings are contained in the EXE, DLL or OCX - believe me, I've used a debugger to find them. But how can I access these in code?

AFAIK there's no way to get the name of a BAS module in code. The usual solution is to use a module-level constant as in Mike's answer.
AFAIK the only way to get the ProgID (programmatic ID, Project Name in project properties dialog) is to raise an error in a BAS module, trap it, and read the Err.Source.
It's all quite a hassle, and that's why we don't usually bother including the module name or the ProgID in our standard error handlers. We "roll our own" call stack, with the names of the routines. That's always enough information to find out which modules are involved. Routines in BAS modules usually have unique names, right?
Something like this, and you can add this automatically with the free MZTools VB6 add-in.
Sub / Function whatever
On Error Goto Handler
do some stuff
Exit Sub / Function
Handler:
Err.Raise Err.Number, "(function_name)->" & Err.source, Err.Description
End Sub
Every top-level routine in a DLL or OCX has a similar error handler but also includes App.ExeName so we can tell when errors cross component boundaries.

I'm not sure of an easy way to programmatically get the name of the module that you are in. The usual solution is to set a variable at the top of each method to the name of the module, and then it is available to the error handler for use in logging:
'In MyModule.bas'
Public Sub Foo()
Const MODULE_NAME As String = "MyModule"
On Error GoTo ErrorHandler
' Code here '
Exit Sub
ErrorHandler:
LogError Err.Number, Err.Description, MODULE_NAME
End Sub
If you are using an add-in such as MZTools, you have it generate this boilerplate code for you.
As for getting the current component name, you can access this using App.EXEName (despite the name, this works for other project types such as DLL's). This value is pulled from the Project Name field in the project's properties (Project -> Properties) when running in the IDE, and from the name of the compiled binary file (minus the file extension) when running outside the IDE.

Related

Type Mismatch in VB6 on call to method in .NET DLL

I have a .NET DLL that serves as a wrapper to a 3rd party application. The DLL is registered for COM-Interop as follows:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /tlb /codebase "C:\Dev\1) Source\TAC10_CAD\Lib\PowerPhoneInterface\PowerPhoneInterface.dll"
There are multiple methods in the DLL that are called from VB6. All work perfectly except one. The .NET function prototype for that method is as follows:
int SetAniAli([Optional] string incidentType);
In VB6, I invoke the problematic method as follows:
If GetSettingBitValue("G_POWERPHONE_ENABLE") Then
If CheckPowerPhoneConnection() Then
frmParent_Dispatch.PowerPhoneInterface.SetCallerInformation person,
phone, Callback, Address.FullText
frmParent_Dispatch.PowerPhoneInterface.SetAniAli
rc = frmParent_Dispatch.PowerPhoneInterface.SetAniAli
If rc = 10 Then
PowerPhoneCallInit = True
Else
Call modCAD.SetWarning("Attempt to set ANIALI information failed.")
End If
End If
End If
PowerPhoneInterface is the object reference to my DLL. The first method call (SetCallerInformation) works great. In addition, there are two other DLL calls invoked from the function CheckPowerPhoneConnection that also work great. However, SetAniAli gives a "type mismatch" error every time. I have rc defined as a Long in VB6, but also tried Variant. No success with either.
I have googled until I get blurred vision and cannot seem to find what is wrong with the call.
Any suggestions would be greatly appreciated.
I'm not sure if this is the reason or not, but I modified the SetAniAli method to make the incidentType parameter required vs optional and the problem is no more. If someone definitively knows that VB6 has a problem with optional parameters or if they require special handling on the VB6 side, please post a comment so that others can benefit.

VBscript that looks correct but errors 800A09D5

I Have been searching for an answer to this but it looks like my vbscript is correct. The macro is not in a module of the same name "MasterSAP". Can’t see what is wrong, you would think this is simple, but I can’t figure it out.
Dim accessApp
set accessApp = createobject("Access.Application")
accessApp.OpenCurrentDataBase("S:\Salem\SCHED\SeesStuff\Resources\MasterSAPData.accdb")
accessApp.Run "MasterSAP"
AccessApp.Quit
Set accessApp = nothing
It's hard to guess because the rest of the code is missing but you should have a procedure in a standard module that Run() is calling if you don't and you are just passing the name of a macro this error will occur.
You should have your code in a standard module with the following definition;
Public Sub MasterSAP()
...
End Sub
You also want to be careful that this procedure is not placed inside a standard module called MasterSAP as this can also cause the error as both the procedure and the module will share the same namespace.

QTP - if object exists in object repository

In QTP is there any way in the code to check to see if a specific object exists in the object repository. I have tried the following code:
If JavaWindow(className).JavaDialog(dialogName).Exist Then
doThisStuff
Else
doThisStuffInstead
End If
But from what I have gleamed from the Internets, this is similar to a isVisible method, only resulting in true if the specified object is currently visible. When I use the above code I receive a "JavaDialog object was not found in the Object Repository." Is there a method or any way to prevent this very error and check to see if the object does indeed exist?
Thank you for your time
I'm not sure what you're trying to accomplish here, one typically knows if an object exists in the object repository before using it. The doubt is usually whether there is a corresponding control in the AUT (Application Under Test).
If you really face the situation that sometimes the object is in the repository and sometimes it isn't (I can think of several ways for this to happen but none of them make much sense) then you can use VBScript's error handling mechanism.
On Error Resume Next ' Turn off error handling
' Just check if object is in repository, there's no need to do anything with it
Dim Exists: Exists=JavaWindow(className).JavaDialog(dialogName).Exist
If Err.Number <> 0 Then
doThisStuff 'Exists is still empty
Else
doThisStuffInstead ' Exists is properly set
End If
On Error Goto 0 ' Resume regular error handling
So, from the error you get, either the dialog that appears is different from the one you've stored in the repository or you don't have it there.
Have you checked it is really present in the Repository? You can try to just locate this element button.
Using the method of "if object not in the repository - skip the step" is not really a good idea. 1. Why would you want to skip the test/part of the test if the object was not saved in the repository?
2. If it's not there, so you need to make sure to store it.
I would assume that this "missing" object might have some values by which it's matched to the object from the repository different from test to test. You can tune the "matching" mechanism by manually setting the values by which you want QTP to locate it.

Check for COM Object Collection Propertys Existence

I have been pondering this one for a while. I am programatically (in VB6) sifting through Outlook PSTs, indexing items that are in them. I have happened on to one that is a little corrupted and this is where I am having difficulty. My program attaches the PST and starts drilling down through the folders (olFolder). These Outlook created objects are supposed to have a collection that would normally hold sub folders (appropriately named Folders).
During the execution of my code I recursively call the process folder function to process all folders in the current folder, but I have one that does not have a collection. This causes an exception because I am checking the count of folders in the collection and there is nothing to check. I understand how to check for the existence of an object, but I am having a hard time checking for the existence of a collection in and object.
Update
The expression:
If Not fl.Folders Is Nothing Then
ends up throwing the exception too. The exception that I am getting is the following:
Could not complete the operation because the service provider does not support it.
This is only when trying to access this corrupted folder which appears to have a Folders Collection Property that is FUBARed.
Final
Alright; in this case I am just going to put in some in-line error handling.
If Not Err.Number = -2147221246 Then
'Do the thing with the other thing
End If
Err.Clear
If you are talking about checking whether obj.Coll.Count = 0, then see if obj.Coll Is Nothing.
well when i work web services on vb6 i have check some cases if property exist. i used that method.
It is slow but i hope that help you.
Public Function HasProperty(ByRef obj As Object, ByVal nameProperty As String) As Boolean
On Local Error GoTo hasProperty_Error
Dim Result
Result = CallByName(obj, nameProperty, VbGet)
hasProperty = True
hasProperty_Done:
Exit Function
hasProperty_Error:
If Err.Number = 438 Then
hasProperty = False
End If
Resume hasProperty_Done
End Function

Add item to Error List in Macro

I want to notify the user of the macro if something went wrong during the execution of the macro. I was wondering if it would be possible to add an item to the Visual Studio error list?
It is possible to do so from within an AddIn (like here), but I would like to do the same thing from a macro.
Edit
To further clarify what i want to achive, here is the sample from the Samples macro library (Alt+F8 -> Samples -> Utilities -> SaveView())
Sub SaveView()
Dim name As String
name = InputBox("Enter the name you want to save as:", "Save window layout")
If (name = "") Then
MsgBox("Empty string, enter a valid name.")
Else
DTE.WindowConfigurations.Add(name)
End If
End Sub
Instead of the MsgBox("...") alert I want to put the error into the VS error list.
You can add an item in the Task List easily from your macro. Just use the AddTaskToList method from that article and change m_objDTE to DTE. I've tried it and it worked.
However, adding the item in Error List, is probably impossible. You need to call VS services, see how adding an error is done in an add-in. I created a macro from this code and it didn't work. In general, VS services don't work in macros. I was able to create ErrorListProvider successfully. I could access it's methods and properties. But calling ErrorListProvider.Task.Add caused COM exception. If you want to play with it, several notes:
As described in the article, you need to get 4 assemblies out of the GAC e.g. to c:\dlls\ directory. Since Macros IDE doesn't allow you to browse when you Add Reference, you need to copy these dlls into ...\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies directory (change the 10.0 to your VS version). Then, when you Add Reference in Macros IDE, you should see the assemblies.
The GetService function always returned Nothing. Add the following field to the class:
Private serviceProvider As IServiceProvider = New Microsoft.VisualStudio.Shell.ServiceProvider(CType(DTE, Microsoft.VisualStudio.OLE.Interop.IServiceProvider))
and in GetService function change line:
objService = Microsoft.VisualStudio.Shell.Package.GetGlobalService(serviceType)
to
objService = serviceProvider.GetService(serviceType)
As I wrote, everything seems OK then but ErrorListProvider.Task.Add fails.
I think that for your situation outputting something to your own output pane would be more suitable. The error list is generally used for errors within the project the user is working on, not for errors caused by running macros. Especially when someone says it can't be done. :)
Outputting to your own output pane is pretty easy:
DTE.Windows.Item(Constants.vsWindowKindOutput).Activate()
Dim panes As OutputWindowPanes = window.OutputWindowPanes
Dim my_pane As OutputWindowPane
Try
my_pane = panes.Item("SaveView")
Catch exception As System.ArgumentException
my_pane = panes.Add("SaveView")
End Try
my_pane.Activate()
my_pane.OutputString("Empty string, enter a valid name." + vbCrLf)
Hope this helps.
Cheers,
Sebastiaan
Is this not what you want?
HOWTO: Add an error with navigation to the Error List from a Visual Studio add-in
http://www.mztools.com/articles/2008/MZ2008022.aspx

Resources