COM Call Fails with Member Not Found - com

I have an application (for which I don't have control of the source), and it exposes a COM interface that works fine from VBA, for example:
Sub test()
Set myApp = CreateObject("MyApp.Application")
val1 = myApp.SubPart.Size
MsgBox CStr(val1)
myApp.SubPart.IncreaseSize
End Sub
This works perfectly. When I do the equivalent in AutoHotkey-L:
myApp := ComObjCreate("V6.Application")
val1 := myApp.SubPart.Size
MsgBox %val1%
myApp.SubPart.IncreaseSize
The message box fires, and gives me the correct value. The last line halts with an error:
0x80020003 - Member not found
Specifically: IncreaseSize
What can I do to get this to function the same in AHK?

For anyone else stuck with this, I solved it by first changing to use the 32-bit ANSI AutoHotkey executable. For me, this is at:
C:\Program Files\AutoHotkey\AutoHotkeyA32.exe
I then changed any COM method calls to be embedded into a ScriptControl call:
myApp := ComObjCreate("MyApp.Application")
val1 := myApp.SubPart.Size
MsgBox %val1%
SC := ComObjCreate("ScriptControl")
SC.Language := "VBScript"
SC.Timeout := -1
code =
(
Set MyApp = CreateObject("MyApp.Application")
MyApp.SubPart.IncreaseSize
)
sc.ExecuteStatement(code)
Of course, the Size read could be moved into the VB code block too.

Related

run external program and exit from calling program

n the closing procedure of a program (just before running Application.exit) I need to run an external program to pass a parameter and exit the calling program.
The program called (FilmDB_Update.exe) has the task of overwriting the main program or a dll library.
I tried to use the "process.start" technique, but apparently, the calling program remains in use and does not allow me to overwrite it.
This is the code that I write:
Private Sub AggiornaPgm()
Dim ws_file As String = "FilmDB_Update.exe"
Dim ws_proc_param As String = """" + ws_working_path + """ " + ws_temp_path
Dim ws_fullPath As String = Path.Combine(ws_temp_path, ws_file)
If File.Exists(ws_fullPath) Then
File.Copy(ws_fullPath, ws_file, True)
End If
Dim proc As New System.Diagnostics.Process()
proc = Process.Start(ws_file, ws_proc_param)
End Sub
I wanted to try using the shell command, but I can not pass the parameters to the called program.
Does any of you have any other ideas about it?
Thank you
Marcello
As Ahmed suggested, I added the test for the calling process to the program called.
p = Process.GetProcessesByName(ws_calling_pgm)
While p.Count > 0
Threading.Thread.Sleep(3000)
p = Process.GetProcessesByName(ws_calling_pgm)
End While
p = Nothing
When I exit the While loop, the calling process is terminated. I do not understand why, despite the process no longer exists, the main program is still in use.

How to detect if COM Class method has failed in VBA

So I have this code in VBA that uses a third party COM class to add a record to a database
Function AddPatientToDolphin(ID As String, LastName As String, FirstName As String)
On Error GoTo CatchError
Dim Plst As DOLDBSVRLib.DolphinPatients
Dim Patient As DOLDBSVRLib.DolphinPatient
Set Plst = New DOLDBSVRLib.DolphinPatients
Set Patient = New DOLDBSVRLib.DolphinPatient
Patient.patientID = ID
Patient.LastName = LastName
Patient.FirstName = FirstName
Plst.AddPatient Patient
Set Plst = Nothing
Set Patient = Nothing
NormalExit:
Exit Function
CatchError:
MsgBox (Err.Description )
GoTo NormalExit
End Function
The code is fine and it works correctly, except when the ID is already present in the database, then the record will not be added but no error is raised!
in the documentation of the 3rd party dll, there is this instruction regarding this method
HRESULT AddPatient(LPDISPATCH newPatient)
Add a patient represented by the patient object submitted.
After some research, I learned that every method in a COM class returns a value called Hresult. This is supposed to be 0 when everything runs correctly, and not 0 otherwise. My question is how could I pick up and test this value in VBA??
HRESULT AddPatient(LPDISPATCH newPatient)
This function is declared that so that it has no returncode in VBA. If the returncode is a HRESULT error code, VBA translates this into an exception and sets the code into the Err object.
If the function reacts normal and returns and doesn't show any fault, than the only way is to check if the ID exists. But this is a race condition. Another user may execute the same code.
There is no real answer for this kind of interface if it behaves in this way.
Or contact the guy who is responsable for this COM interface.
You will have to inspect the return value of the external function. To catch it in a variable:
Dim RetVal As Long
RetVal = Plst.AddPatient(Patient)
Then, compare to 0 and eventually display an error message:
If RetVal <> 0 Then
MsgBox "Something went wrong"
End If
Added:
Ok, the call as a function is not allowed in this case, so try this:
Plst.AddPatient Patient
If Err.LastDLLError <> 0 Then
MsgBox "Something went wrong"
End If

Why Dim as New and Dim/Set in VBA behave differently when I call a COM server?

I have made an out-of-process COM server (C++) that is called from VBA.
For an unknown reason when I call it multiple times (at least two times in the same sub) I'm only able to call it using Dim xx As New xxx.
When I try to call it using Dim xxx As xxx and then Set xx = new xxx , my com server raises a violation reading exception and VBA returns the error code 800706BE.
The following code does work (pseudo code - I removed the irrelevant part). Note that the 'Main' sub call the 'aux' function and that both the Sub and the 'aux' function call my COM server (two different classes).
Function aux() As Double()
Dim com As New COMServer.classe2
Dim Returns() As Double
Returns = com.Method2 'actual call to the COM Server
aux = Returns
End Function
Sub Main()
Dim Resultat() As Double
Dim com1 As New COMServer.classe1
Dim Returns() As Double
Returns = aux ' call Function aux
Resultat = com1.Method1(Returns) 'actual call to the COM Server
End Sub
The following does not work :
Function aux() As Double()
Dim com As COMServer.classe2
Set com = New COMServer.classe2
Dim Returns() As Double
Returns = com.Method2 'actual call to the COM Server
aux = Returns
End Function
Sub Main()
Dim Resultat() As Double
Dim com1 As COMServer.classe1
Set com1 = New COMServer.classe1
Dim Returns() As Double
Returns = aux ' call Function aux
Resultat = com1.Method1(Returns) 'a violation reading (c++) Exception is thrown here
End Sub
Can someone explain me why my code only works in the first case ?
Also note that if I only call the server once in the sub (no call to aux), then both methods ( Dim as New and Dim/Set ) work.
EDIT
I have noticed that in case 1 (the case that works) : my server automatically start and stop two times consecutively (seen in the Windows Task Manager ).
Whereas in second case (the buggy one) : my server start only once - didn't stop and raise the error.
Now I have just modified the second case in the following manner and the exception disappears :
Sub Main()
Dim Resultat() As Double
Dim Returns() As Double
Returns = aux ' call Function aux
Dim com1 As COMServer.classe1
Set com1 = New COMServer.classe1
Resultat = com1.Method1(Returns) 'no more Exception
End Sub
The only difference is that I set my server just before to call it (instead to initialize it before to call my 'aux" function).
Does it makes sense to someone ?
Dim statements aren't executable. Set statements are.
When you do Dim foo As New Bar you're creating an auto-instantiated object variable, which incurs a bit of overhead in the VBA runtime (every call against it validates whether there's a valid object reference).
This is how auto-instantiated objects bite:
Dim foo As New Collection
Set foo = Nothing
foo.Add 42 'runtime error 91? nope.
Debug.Print foo.Count ' prints 1
Set foo = Nothing
Debug.Print foo.Count ' runtime error 91? nope. prints 0
So As New makes VBA go out of its way to ensure there's always a valid object reference for that pointer, no matter what. Every member call on object variables declared As New is valid: VBA will create a new instance before making the member call if the reference points to Nothing - that's the overhead I mentioned earlier, and contradicts LS_dev's answer. Auto-instantiated object variables aren't "only instantiated on first member call" - they're instantiated whenever they need to be.
The answer is likely in your C++ code, not in the client VBA code. Something is wrong with how you're cleaning things up, there's loose ends somewhere - using As New to work around a sloppy COM server doesn't strike me as a good idea (As New should generally be avoided, as a matter of fact, for the unintuitive behavior depicted above).
Problem may be in sequence of call. From my experience, objects declare with As New are only instantiated in first member call, while Set ... = New instantiate object immediately.
Said so, in first case classe2 is created before classe1 which is only created when you call com1.Method1.
In second case, classe1 is created in Set, before classe2.
taking this into account, it seams your COM code somehow create a memory violation if classe1 is created before classe2.

Invoke a COM Method from a dll

The question is this: i've been inspecting kaspersky gadget and found out it uses a COM object as i can see declared in the gadget's main html file, so i looked in the registry by the CLSID and got to the "gadget.dll" located in the kaspersky instalation folder.
Mi interest is to invoke specific kaspersky's app tab just the same way they do. Examining the .js files on the gadget's folder i could see the syntax of the method i would need to use which is "OpenWindow(WindowID)" and the WindowID's are also specified in another file.
I've been trying this from a simple VisualBasic Script:
Set kavCOM = WScript.CreateObject("KISGadgetCOM.COMClass.1")
kavCOM.OpenWindow(1)
that should invoke the Main Window, also tried a AutoHotKey Script:
^!k::
{
kavCOM := ComObjCreate("{ED6E691B-E662-4aae-AECC-705C9B014C75}")
kavCOM.OpenWindow(1)
}
they both result in the error: 80004004 (Operation aborted) at the line with "kavCOM.OpenWindow(1)
what's wrong?
Ok, the problem was the Object instantiation delay, i solved it by adding
WScript.Sleep 1000
just before calling the "OpenWindow" method.
My final(finer) solution so that it doesn't wait more than needed:
Dim kavCOM, Cnt
Cnt = 0
Set kavCOM = CreateObject("KISGadgetCOM.COMClass.1")
On Error Resume Next
Do
Cnt = Cnt + 1
kavCOM.OpenWindow(1)
If Err = 0 Then
Exit Do
Else
Err.Clear
End If
WScript.Sleep 10
Loop
On Error Goto 0
MsgBox "It took " & 10*Cnt & " miliseconds."
Set kavCOM = Nothing

How to view VB6 control-level variables in WinDbg?

I have a crash file where I can see that one of my own VB6 user controls is responsible for the crash; i.e. one of its methods is part of the stack trace and I can see the line responsible.
From here, I'd like to inspect the state of its member variables. How do I do this?
Note: I also have the private symbols for my controls. The problem is being able to inspect "Me". The command !object address_of_Me doesn't seem to do the trick and so I'm at a loss.
Thank you.
It's been 10 years since I had to do this in VB6, but I remember a lot of Printer.Print statements in my past life :)
I used to do things like this for debugging (but not for release code)
Sub MySub
On Error Goto ErrorTrap
Dim intX as integer
Dim intY as integer
' do some horrible error here
Exit Sub
ErrorTrap:
Printer.Print "Error"
Printer.Print intX
Printer.Print intY
Printer.Print ...
End Sub
well, codeSMART have one option install global handle on your application first call to SetUnhandledExceptionFilter (win api) is should be installed when load your module main or master form when is closing the program so call to SetUnhandledExceptionFilter.
The code is little long so copy methods names y api calls
Public Sub InstallGlobalHandler()
On Error Resume Next
If Not lnFilterInstalled Then
Call SetUnhandledExceptionFilter(AddressOf GlobalExceptionHandler)
lnFilterInstalled = True
End If
End Sub
Public Sub UninstallGlobalExceptionHandler()
On Error Resume Next
If lnFilterInstalled Then
Call SetUnhandledExceptionFilter(0&)
lnFilterInstalled = False
End If
End Sub
Also here is Record Structure y apis declarations for the module
- CopyMemory
- SetUnhandledExceptionFilter
- RaiseException
' Public enums
-EExceptionType
-EExceptionHandlerReturn
-Private Const EXCEPTION_MAXIMUM_PARAMETERS = 15
' Private record structure
-Private Type CONTEXT
'Structure that describes an exception.
-Private Type EXCEPTION_RECORD
'Structure that contains exception information that can be used by a debugger.
-Private Type EXCEPTION_DEBUG_INFO
-Private Type EXCEPTION_POINTERS
Take a revised that How to route the exe exception back to VB6 app?