Jump to content

Using SetError with a COM handler [Solved]


Recommended Posts

I'm new to COM error handling and trying to understand how to pass something back to the routine in which a COM exception/error occurred so the routine can act on it.
The example program below shows how it can be done using a Global variable, however when I try and use SetError, which I thought was Global, it fails.
Any hints on what I'm doing wrong would be appreciated as I'm stuck.

;
#AutoIt3Wrapper_run_debug_mode=Y    ;use this to debug in console window <--- LOOK
;https://www.autoitscript.com/forum/topic/196419-error-handling-help/
;https://www.autoitscript.com/forum/topic/191401-com-error-handling-in-a-udf-best-practice/
Global $GlobalExceptionFlag = 0 ;init value
Global $Test1Flag = 0   ;init value
Global $oGlobalCOMException = ObjEvent("AutoIt.Error", "_GlobalCOMException") ;Global Exception

;Global test
Pause("Before Global COM Exception" &@CRLF& "$GlobalExceptionFlag = " & $GlobalExceptionFlag)
Global $oNonSense = ObjCreate("Nonsense.Application")       ;trigger Global Exception
Pause("After Global COM Exception" &@CRLF& "$GlobalExceptionFlag = " & $GlobalExceptionFlag)

;Local tests
LocalTest_1()   ;be careful with use of word Local
LocalTest_2()   ;be careful with use of word Local

Exit

;----------- functions ;-----------

Func _GlobalCOMException()
    $GlobalExceptionFlag = 1    ;set it
    Pause("IN: _GlobalCOMException()" &@CRLF& "$GlobalExceptionFlag = " & $GlobalExceptionFlag)
EndFunc

Func LocalTest_1()
    $oLocal1COMException = ObjEvent("AutoIt.Error", "_Local1COMException")  ;Local Exception
    Pause("We are in LocalTest_1() before local exception" &@CRLF& "$Test1Flag = " & $Test1Flag)
    $oX1 = ObjCreate("MoreNonsense.Application")    ;create exception
    Pause("We are in LocalTest_1() after local exception" &@CRLF& "$Test1Flag = " & $Test1Flag)
EndFunc

Func LocalTest_2()
    ;the problem with LocalTest_1 is that it requires a Global flag in the main program
    ;which is not a good idea if the functions we are providing are to be used by other
    ;people - they will not want to put in a Global however that is exactly what we need
    ;there is a solution as @error is Global for all programs
    ;so let's try it

    $oLocal2COMException = ObjEvent("AutoIt.Error", "_Local2COMException") ;Local Exception
    Pause("We are in LocalTest_2() before local exception" &@CRLF& "@error = " & @error)
    $oMoreNonSense = ObjCreate("MoreNonsense.Application");create more nonsense
    $iError = @error ;grab @error as we are getting @error = -2147221005 rather than 23.  It makes no difference still get -2147221005
    ;Pause("We are in LocalTest_2() after local exception, @error = " & @error)
    Pause("We are in LocalTest_2() after local exception" &@CRLF& "@error = " & $iError)
EndFunc

Func _Local1COMException()
    $Test1Flag = 1  ;set it
    Pause("IN: _Local1COMException()" &@CRLF& "$Test1Flag = " & $Test1Flag)
EndFunc

Func _Local2COMException()
    $Local1ExceptionFlag = 1    ;set it
    Pause("IN: _Local2COMException()" &@CRLF& "@error = " & @error)
    SetError(23)    ;set it as last entry (Return SetError(23) makes no difference)
EndFunc

Func    Pause($text="")
    MsgBox(262144, "DEBUG", $text)
EndFunc

 

Edited by ahha
Link to comment
Share on other sites

1 hour ago, ahha said:

I'm new to COM error handling and trying to understand how to pass something back to the routine in which a COM exception/error occurred so the routine can act on it.

Can you be more specific as to what "something" might be and why you need to be able to pass that "something" back to the calling function? 

Unless you encounter a catastrophic or non-trappable COM error, as long as your local or global COM error handler doesn't exit the script, control/execution will be passed back to the caller.  You can just test for @error after any given COM statement and do whatever processing needs to take place.  The variable you used to define the local COM error handler (ObjEvent()) will contain the COM error object if an exception occurs.  You can use the properties of that error object to see what error has occurred and handle it as necessary.  Note that some COM errors cannot be trapped and will cause your script to exit with or without a COM error handler.  Luckily, most of those type of errors can be avoided by coding defensively.  (See example 2 of the ObjEvent() function, in the help file, for an example of local COM error handling. )

FYI, your locally defined COM error handler function is passed a COM error object.  That allows you to be able to interrogate any of the COM error information in the error handler.  Of course the information is also available in the calling function as I stated above.

Func _Local1COMException($oComError)
Edited by TheXman
Link to comment
Share on other sites

The example below shows how the error object can be used within the function.  Notice that all the  COM error handler function does is return control back to the caller so that it can process the error.

#AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 4 -w- 5 -w 6 -d

#include <Constants.au3>


example()

Func example()
    Local $oComErr, $oApp

    ;Define local COM error handler
    $oComErr = ObjEvent("AutoIt.Error", "com_error_handler")
    If @error Then Exit MsgBox($MB_ICONERROR + $MB_TOPMOST, "ERROR", "Unable to create COM error handler - @error = " & @error)

    ;Generate a COM error to show how it can be handled within the function
    $oApp = ObjCreate("SomeApp.Application")
    If @error Then
        With $oComErr
            ConsoleWrite(@CRLF & "COM ERROR DETECTED!" & @CRLF)
            ConsoleWrite("  Error ScriptLine....... " & .scriptline & @CRLF)
            ConsoleWrite("  Error Number........... " & "0x" & Hex(.number) & " (" & .number & ")" & @CRLF)
            ConsoleWrite("  Error WinDescription... " & StringStripWS(.windescription, $STR_STRIPTRAILING) & @CRLF)
            ConsoleWrite("  Error Description...... " & StringStripWS(.description   , $STR_STRIPTRAILING) & @CRLF)
            ConsoleWrite("  Error RetCode.......... " & "0x" & Hex(Number(.retcode)) & " (" & Number(.retcode) & ")" & @CRLF)
        EndWith
        Exit
    EndIf
EndFunc

Func com_error_handler($oError)
    Return ; Return so @error can be trapped by the calling function
EndFunc

Console

COM ERROR DETECTED!
  Error ScriptLine....... 16
  Error Number........... 0x800401F3 (-2147221005)
  Error WinDescription... Invalid class string
  Error Description...... 
  Error RetCode.......... 0x00000000 (0)

 

Edited by TheXman
Link to comment
Share on other sites

TheXman,

I am accessing a cell in a Word table.  When the Table has a merged cell that I try and access an exception is thrown.

I can see the Error and the exception data.  I want to continue the main program, however, I'd like to let the program know that it hit an exception, so that it can continue operation.  That is if I'm traversing a 2x4 table where the Row 1 Col 1 and Row 2 Col 1 are merged and I try to access it it throws the error  "The requested member of the collection does not exist." which I detect.  Now I want to let the main program know that it hit a merged cell.  The example program whittles the issue down to how to avoid using a Global variable and use instead SetError.  When you run the program you'll see the Global works fine but SetError(23) returns -2147221005 rather than 23.
  This is where I'm stuck.

Link to comment
Share on other sites

17 minutes ago, ahha said:

I do not see any values returned.

That's my point.  There's no need to pass any values back.  The error object and all of it's properties already exist within the function.  As you can see, the function was able to recognize the exception by checking @error after the COM statement and then it displayed several properties from the error object.

Most exceptions can be prevented by coding more defensively.  You obviously are trying trying to process cells that don't exist.  That's a coding problem.  But if you don't want to fix you logic/coding issue, you can check for @error after the offending line to intercept the exception and handle it as needed.

You appear to be concentrating too much on your solution to your problem instead of the actual problem itself.  Your problem isn't really related to COM error handling.  It has more to do with properly processing tables in Word.

Edited by TheXman
Link to comment
Share on other sites

re: fix you logic/coding issue

I have searched and searched and all I find is that the Word Object simply does not support cleanly addressing merged cells in a Table.

In the above example of the 2x4 Table the Word Object does not tell you which are merged cells just that it's 2x4.  So I'm left having to detect an exception.  I agree it's a lousy way of detecting but I have found no other way.  I was hoping that rather than just detecting @error I could use SetError to try and be a little more intelligent.

Link to comment
Share on other sites

5 minutes ago, ahha said:

In the above example of the 2x4 Table the Word Object does not tell you which are merged cells just that it's 2x4.

I must be missing something.  I don't see any reference to Word in you example above. :think:  Where's the script, a sample document, and an explanation of the desired result?

Edited by TheXman
Link to comment
Share on other sites

There is no reference to Word.  I tried to whittle down the issue to point to the exact issue I'm having which is that it appears that one cannot use SetError to pass back from a COM error handler.  It appears only that @error is set and that's what you must use.  Thanks "always wanted"😉

Edited by ahha
misc
Link to comment
Share on other sites

I see.  Yes, it appears that you are correct.  One cannot set/change the @error and @extended values when returning from a COM error handler.

Like I said, there's probably a better way to process the table in Word.  I can take a look at it if you provide the necessary information.

Link to comment
Share on other sites

TheXman,

Thanks for the offer but I don't expect you to solve my coding issues.

Water has done a Word UDF that is excellent, the _Word_DocTableRead tries a different approach to get the cell contents (but no CR LF).  I want to be able to get the cell contents, merged or not and I'm hoping to be able to read the CRLF in a cell as most merged cells have several lines of text.

FYI here's the MS link https://docs.microsoft.com/en-us/visualstudio/vsto/working-with-tables?view=vs-2019

As I said thanks for the offer.

Link to comment
Share on other sites

You're welcome.  :)

4 minutes ago, ahha said:

Thanks for the offer but I don't expect you to solve my coding issues.

Looking through your previous posts just now, it looks like you've already asked for help solving your coding issue related to the Word table.  But if you don't want me to try to help with it, I understand and won't bother.

Good luck with it.

Link to comment
Share on other sites

38 minutes ago, ahha said:

In the above example of the 2x4 Table the Word Object does not tell you which are merged cells just that it's 2x4.  So I'm left having to detect an exception. 

I meant to mention this the other day: you should take a look at the Uniform property in the Word VBA object model:

Quote

True if all the rows in a table have the same number of columns. Read-only Boolean.

expression.Uniform

expression Required. An expression that returns a Table object.

Perhaps checking this would make it so you don’t wait for an exception to be thrown?

Code hard, but don’t hard code...

Link to comment
Share on other sites

TheXman - Once I'm stuck again I'll seek help - it's not fair to bother anyone until I have nailed down my issue.

JackoDundee - thanks.  However knowing the table has a split/merged cells still does not solve the issue of traversing the table.  I'm working on it now and as noted it appears throwing an exception is one way to be alerted that the accessed cell is a split/merged cell.

This keyboard cowboy is back to the QWERTY mines 🤪

 

 

Link to comment
Share on other sites

  • ahha changed the title to Using SetError with a COM handler [Solved]

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...