14 Replies - 2168 Views - Last Post: 29 March 2013 - 04:40 PM Rate Topic: -----

#1 maj3091  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 299
  • View blog
  • Posts: 1,768
  • Joined: 26-March 09

Automation error - ActiveX EXE

Posted 20 March 2013 - 05:07 AM

Interesting issue cropped up at a customers site recently, whereby our server application is reporting Automation Errors and Components hanging. This app has been running for a good number of years, without issues, then this has happened twice in the last week or so. No changes have been made to the host computer or software during that time. When this error occurs, it requires the server app to be restarted as all connected devices can't function (Terminal Emulation on the devices).

Basically, our server app handles incoming socket connections from mobile devices. Each connection has it's own "client session" in the form of an ActiveX EXE.

We have a messagebox on screen also, saying "An action can't be completed, because component (Session X) is not responding. Click Switch to switch to activate the component to correct the problem."

The automation errors are being raised in the SendToClient functions that send data to the client session (ActiveX EXE).

I've done some googling and it's suggesting that because a message hasn't been processed in the message queue and re-entry, but following the associated code examples, our code already seems to follow these methods.

So, in our server, we have the following:

Private WithEvents objSession1 As ClientSession

' TCP Read Initiates the comms with the ActiveX EXE
sub tcp_Read(intIndex as Integer)
On Error GoTo ErrHandler

Dim strData As String

    tcpServer(intIndex).RecvLen = intDataLength
    strData = tcpServer(intIndex).RecvData
    
    'Send the data to the client exe
    'subSendToClient intIndex, strData
    
    Select Case intIndex

    Case 1:
        subSendToClient1 strData
    Case 2:
        subSendToClient2 strData
    .....
    Case X:
        subSendToClientX strData

End Sub

Private Sub subSendToClient1(strData As String)
On Error GoTo ErrHandler

Dim blnTryOnce As Boolean

    'enable timeout timer
    blnSendTimeout(1) = False
    tmrSendTimeout1.Interval = cstSendTimeout
    tmrSendTimeout1.Enabled = True

    blnDoneRead(1) = False
    blnTryOnce = True   'Only send data once, but keep looping...
    'loop until done or timed out
    While (blnDoneRead(1) = False) And (blnSendTimeout(1) = False)
        If blnTryOnce = True Then
            'try to send to session exe
            objSession1.subRead 1, strData
            blnTryOnce = False
        End If
        DoEvents
    Wend
    
    If blnDoneRead(1) = True Then
        blnSendTimeout(1) = False   'reset timeout flag
    Else
        If blnSendTimeout(1) Then
            With udtMessage
                .strUser = "SYSTEM"
                .strSource = "subSendToClient1"
                .strMessage = "Send To Client 1 Timed Out"
                .blnLogIt = True
                .blnError = True
            End With
            subHandleMessage 0, udtMessage
        Else
            With udtMessage
                .strUser = "SYSTEM"
                .strSource = "subSendToClient1"
                .strMessage = "Send To Client 1 Failed"
                .blnLogIt = True
                .blnError = True
            End With
            subHandleMessage 0, udtMessage
        End If
        blnSendTimeout(1) = False   'reset timeout flag
    End If

ExitHandler:
    Exit Sub
ErrHandler:
    If (Err.Number = cstErrServerDoesntExist) Or (Err.Number = cstErrAutomationErr) Then
    'there is no session exe - it has been killed
        With udtMessage
            .strUser = "SYSTEM"
            .strSource = "subSendToClient1"
            .strMessage = "The Client's Process Is no longer running. Socket disconnected."
            .blnLogIt = True
            .blnError = True
        End With
        subHandleMessage 0, udtMessage
        tcpServer(1).Disconnect
    Else
        'new bit - 13/04/2005
        If objSession1 Is Nothing Then
            With udtMessage
                .strUser = "SYSTEM"
                .strSource = "subSendToClient1"
                .strMessage = "The Client's Process Is no longer running. Socket disconnected."
                .blnLogIt = True
                .blnError = True
            End With
            subHandleMessage 0, udtMessage
            tcpServer(1).Disconnect
        End If
        'end of new
        fintGeneralError False, Err.Source, "subSendToClient1 " & Err.Description, Err.Number
    End If
End Sub

' Event raised from ClientSession (ActiveX EXE)
Private Sub objSession1_DoneRead()
    tmrSendTimeout1.Enabled = False
    blnDoneRead(1) = True
End Sub




The code in the ActiveX EXE for receiving the data from the server:
Public Sub subRead(ByVal lngIndex As Long, ByVal strData As String)
On Error GoTo ErrHandler

    'let the server carry on with its business
    'and let the timer event trigger the data processing
    mlngIndex = lngIndex
    strDataToProcess = strData
    
    'Raise the event here to avoid delays
    RaiseEvent DoneRead
    
    tmrProcessData.Interval = cstProcessDataTimerInterval
    tmrProcessData.Enabled = True

ExitHandler:
    Exit Sub
ErrHandler:
    fintClientError Err.Source, "subRead " & Err.Description, Err.Number
End Sub



Maybe I'm misunderstanding what I've found on google, but I don't undetstand why I see automation errors on other devices trying to communicate, as the threads I found on the subject suggests it's re-entry into the same code.

I have tried to reproduce this error, but for the life of me can't.

Any pointers would be useful.

Is This A Good Question/Topic? 0
  • +

Replies To: Automation error - ActiveX EXE

#2 BobRodes  Icon User is offline

  • Your Friendly Local Curmudgeon
  • member icon

Reputation: 573
  • View blog
  • Posts: 2,989
  • Joined: 19-May 09

Re: Automation error - ActiveX EXE

Posted 20 March 2013 - 08:50 PM

First, are your ActiveX exe's singleuse or multiuse?
Was This Post Helpful? 0
  • +
  • -

#3 maj3091  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 299
  • View blog
  • Posts: 1,768
  • Joined: 26-March 09

Re: Automation error - ActiveX EXE

Posted 21 March 2013 - 01:06 AM

The class that the ActiveX EXE is instantiated with is single use.

[UPDATE]
On further testing and investigation this morning, I've been able to reproduce the issue.

I recreated the ActiveX EXE so that I could send it into an an infinite loop on demand.

I connected 2 devices to the server, then forced the infinite loop on one of the client sessions.

This loop didn't affect any other client sessions or the server, up to the point of the device whose client was "hung", trying to send another message (pressed Enter on the device), at this point, it hung the server completely. Killing off the hung ActiveX EXE released the server and then allowed other devices to continue as normal.

I modified the code in the subSendToClient routine as follows:

Reduced the timeout value of cstSendTimeout down to 20ms - on further investigation, it was found that this was at 20s from before the code became disconnected using ActiveX EXE.

I also then put in code to kill the object reference to the session that had timed out (I didn't want to risk initiating a TerminateProcess as that also would have the potential to hang).

The problem now is, that as soon as it hits the line to make the call to the Session (objSession1.subRead), it stops there and doesn't seem to execute the loop, so the timeout never occurs. Once the client session has been killed off manually through task manager, it reports the error "Automation Error. RPC call failed".

Not sure if that helps.

This post has been edited by maj3091: 21 March 2013 - 05:37 AM

Was This Post Helpful? 0
  • +
  • -

#4 maj3091  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 299
  • View blog
  • Posts: 1,768
  • Joined: 26-March 09

Re: Automation error - ActiveX EXE

Posted 21 March 2013 - 11:45 AM

Further update on this.

I've now managed to get it to detect that the Session is hung, by using the SendMessageTimeout API.

The problem I'm having now is that I can't seem to kill off the reference to the object.

So the code I have added at the top of the subSendToClient is as follows: (from memory as I'm at home now).

' Check if session is responding
If not fblnCheckSessionAlive(lngSessionID) then
  'Disconnect the socket
  tcpServer(1).Disconnect
  'Release the object reference
  set objSession1 = Nothing
  Exit Sub
Endif



The downside to this is that when I try to release the object reference, it hangs at that point.

Not sure I'm getting any closer or just moving the issue around.
Was This Post Helpful? 0
  • +
  • -

#5 BobRodes  Icon User is offline

  • Your Friendly Local Curmudgeon
  • member icon

Reputation: 573
  • View blog
  • Posts: 2,989
  • Joined: 19-May 09

Re: Automation error - ActiveX EXE

Posted 23 March 2013 - 08:06 PM

Several questions: when you say that the class that the activex exe is instantiated with is singleuse, are you saying that the class that instantiates is a dll, or is it itself an exe? If the latter, are they both singleuse? Having trouble getting clear on the pattern of what instantiates what. But if your server is singleuse, that means that each client has its own instance, whcih suggests that they wouldn't cause trouble for one another since they would all be isolated processes.
Your server exe hangs when you set the object to nothing? What error do you get? What happens on your client?

When you say kill off the reference to the object, do you mean just release the reference, or kill the object process too?

Edit: maybe it would help if you told me what the type (exe or dll) of both your client and your server are, as well as the instancing property of each, as well as which instantiates which. (Generally, a client creates an instance of the server and then calls methods on it, but that isn't a requirement. Also, if async the server may call a proc on the client when it's done with whatever background work it's supposed to do.)

This post has been edited by BobRodes: 23 March 2013 - 08:10 PM

Was This Post Helpful? 0
  • +
  • -

#6 maj3091  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 299
  • View blog
  • Posts: 1,768
  • Joined: 26-March 09

Re: Automation error - ActiveX EXE

Posted 24 March 2013 - 01:30 AM

Sorry for the confusion and misuse of terminology Bob (had my head up my backside chasing this one).

What I've referred to as the server, is the top level tier (Standard EXE) that accepts the mobile device connections, which I believe is what you call the client.

What I've referred to as the client, is in fact an ActiveX EXE, what you refer to as the server.

So using your terminology (and probably the standard), it works as follows:

A mobile device connects to the client and we create an instance of the Session Class (Single Use) in the ActiveX Exe.
The client calls public methods in the Server and the server responds by the use of events (so should work async).

Albeit, my understanding was the same as you have described that they should cause issues for each other, I believe (from googling) it's an underlying RPC issue (probably caused through our coding initially).

If one of the server instances becomes hung/non-responsive and the client tries to communicate with it by calling the subRead method (see original code post), it blocks on that method call, which in turn hangs the client and affects all mobile devices that are connected.

After a timeout period (no idea where it's set), a messagebox appears on screen showing "An action can't be completed, because component (Session X) is not responding. Click Switch to switch to activate the component to correct the problem."

If I was then to kill that hung process in task manager, the client will then report Automation errors for all the other messages from the mobile devices that where hung during that period.

So, that was the initial issue....hopefully I explained that a little better this time.

In trying to solve this issue (or handle it gracefully), I started to modify the code, to try and detect a hung session by using the SendMessageTimeOut API, which does seem to work in detecting that a session has hung or become non-responsive.

So what I've tried to do is set the object reference to the server to nothing, using the code below, but it hangs on setting the session to nothing (no error message, just hung - I'm guessing if I left it long enough, I would get the same "Component not responding message" as before):

' Check if session is responding
If not fblnCheckSessionAlive(lngSessionID) then
  'Disconnect the socket
  tcpServer(1).Disconnect
  'Release the object reference
  set objSession1 = Nothing
  Exit Sub
Endif




In my initial trials, I didn't try calling the TerminateProcess API, as I wasn't sure whether that would block also (like when TaskManager sometimes takes 2-3 times to kill a process), so I was hoping to just release the reference and create a new instance when the device reconnects and then worry about killing off dead processes in another way (maybe log the processID and have a background task do it).

I didn't get any further as I was off ill, so all this is from memory now as to what I've done, so hopefully it answers your questions and clears up some of the confusion (and not make it worse).
Was This Post Helpful? 0
  • +
  • -

#7 BobRodes  Icon User is offline

  • Your Friendly Local Curmudgeon
  • member icon

Reputation: 573
  • View blog
  • Posts: 2,989
  • Joined: 19-May 09

Re: Automation error - ActiveX EXE

Posted 24 March 2013 - 09:02 AM

Actually, now that you've explained things a bit, I would call your standard exe the server and the activex exe's the clients, brokering communication between the mobile devices and the common functionality that the standard exe serves up. I assume that your standard exe is what is raising the event when the async work is completed.

Now, you may want to consider implementing async functionality by the use of the more sophisticated callback model rather than the event model. Events are like a broadcast, and callbacks are like a telephone call--the client, instead of having an event handler, passes a reference to a callback procedure as one of the arguments to the server method. The server then calls that procedure when the work is done. This allows the server to isolate the interaction with each of the clients from one another, and can therefore error handle each client independently.

Microsoft put out some examples to illustrate the differences using a coffee pot metaphor. For these "Coffee" code examples, see here. A client starts a pot of coffee, getting control back from the coffee pot (the server, as it were), and being notified by the coffee pot when the coffee is ready.

This post has been edited by BobRodes: 24 March 2013 - 09:04 AM

Was This Post Helpful? 0
  • +
  • -

#8 maj3091  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 299
  • View blog
  • Posts: 1,768
  • Joined: 26-March 09

Re: Automation error - ActiveX EXE

Posted 24 March 2013 - 11:26 AM

It's actually what I referred to originally as the client (the ActiveX exe) that raises the event back to the server.

So our server (standard exe), gets a message from a device and calls a method in the client to pass that message data, as shown in the subRead code in the first post (basically, sets a variable with the data, raises an event back to the server and sets a timer to initiate processing of that message). The client raises another event when it's finished processing and this event handler in the server sends the response back to the device.

The way the code is written and the client objects declared in the server, each client has it's own event handlers, so handled independently (as per my understanding).
Was This Post Helpful? 0
  • +
  • -

#9 BobRodes  Icon User is offline

  • Your Friendly Local Curmudgeon
  • member icon

Reputation: 573
  • View blog
  • Posts: 2,989
  • Joined: 19-May 09

Re: Automation error - ActiveX EXE

Posted 24 March 2013 - 03:42 PM

Ok, I see. That's backwards from usual: usually a server does the work and raises an event to the client. The server would get the message and raise an event, and the client would then handle that event. That doesn't mean that this won't work, just hard to wrap my head around it.

My concern is that you can have multiple clients raising an event, all of which are handled by the server's event handler in order in the event queue. So, if one of them locks up for some reason, all subsequent calls from the other activex exe's are stuck in the queue. Have you tried putting doevents in the server event handler? That should flush the "good" calls that are stuck behind any given call that's misbehaving.
Was This Post Helpful? 0
  • +
  • -

#10 maj3091  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 299
  • View blog
  • Posts: 1,768
  • Joined: 26-March 09

Re: Automation error - ActiveX EXE

Posted 25 March 2013 - 02:00 AM

The subSendToClient function in the first post, does have DoEvents in whilst waiting for the event and all the other events are being captured OK. The code has been running for years at this customers, up until this issue cropping up twice in the space of a week or so.

It seems to me (in my understanding) that the issue is more low-level around the windows messaging between the server and clients, in that if one client hangs and another message comes in for that same device, it stops everything, which is why I tried adding the SendMessageTimeout API to determine if the client is "alive" before calling the method.

The problem now is gracefully getting rid of that client and re-using the object associated with it. The code, when written, isn't very scaleable, as we have objSession1 which handles client 1, etc. There are X of these pre-defined, it not a dynamic list of objects, so I can't just leave that one and create another object to use and keep the same number of devices.

All I really want to be able to do, is set the object to Nothing, so I can use it again.

I hope I'm making some sort of sense.....

I'm going to have another play today and I'll implement the TerminateProcess API and see if that works without potentially hanging the server again.

Thanks for your help on this one.
Was This Post Helpful? 0
  • +
  • -

#11 thava  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 180
  • View blog
  • Posts: 1,606
  • Joined: 17-April 07

Re: Automation error - ActiveX EXE

Posted 25 March 2013 - 02:46 AM

well seems some sort of long running loop is the problem,
i have a suggestion to you instead of raising a event why don't you create a public variable for that class that to verify the current message is read or not, this will avoid a some sort of problems for me while using a MSCOMM
so here is my idea
ok i will give little sample here
public blnReadCompleted as Boolean
Public Sub subRead(ByVal lngIndex As Long, ByVal strData As String)
On Error GoTo ErrHandler

    'let the server carry on with its business
    'and let the timer event trigger the data processing
    mlngIndex = lngIndex
    strDataToProcess = strData
    
    'Raise the event here to avoid delays
    'RaiseEvent DoneRead
     blnReadCompleted =True
    
    tmrProcessData.Interval = cstProcessDataTimerInterval
    tmrProcessData.Enabled = True

ExitHandler:
    Exit Sub
ErrHandler:
    fintClientError Err.Source, "subRead " & Err.Description, Err.Number
End Sub



chage your while loop like this
    objSession1.blnReadCompleted = False
    While (objSession1.blnReadCompleted = False) And (blnSendTimeout(1) = False)
        If blnTryOnce = True Then
            'try to send to session exe
            objSession1.subRead 1, strData
            blnTryOnce = False
        End If
        DoEvents
    Wend


in the same manner you can also remove the timer also,
now the code is as straight as it would be, so you can get the control over the server from your client
this is only my suggestion well tell me whether this will lead a path to solve your problem or not

This post has been edited by thava: 25 March 2013 - 02:47 AM

Was This Post Helpful? 0
  • +
  • -

#12 maj3091  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 299
  • View blog
  • Posts: 1,768
  • Joined: 26-March 09

Re: Automation error - ActiveX EXE

Posted 25 March 2013 - 03:57 AM

Thanks for your suggestion Thava.

The code you've posted, works in a similar manner to what I already have, whereby the event raised in the subRad, sets a variable blnDoneRead in the event handler and allows us out of the route.

The subRead method doesn't do any processing, so the event is raised right away. The timer that is set in subRead, initiates the processing. Once the processing is complete, another event is raised to indicate that processing is complete.

The problem seems to be around re-entry into the subSentToClient function, where when the ActiveX EXE is busy processing, we need to prevent new messages being sent to that client, otherwise the RPC gets blocked on calling the method and it hangs the server, which affects all connected devices.

It's like it needs another flag wrapping the subSendToClient code, to indicate that the client is still processing, but it can't be a client flag, otherwise it uses RPC, which in turn locks out.

So the code I'm thinking of will be something like:
Private WithEvents objSession1 As ClientSession

Private Sub subSendToClient1(strData As String)
On Error GoTo ErrHandler

Dim blnTryOnce As Boolean

    'enable timeout timer
    blnSendTimeout(1) = False
    tmrSendTimeout1.Interval = cstSendTimeout
    tmrSendTimeout1.Enabled = True

    'Check if the client is already processing - prevent re-entry
    if blnProcessing(1) then exit sub


    blnDoneRead(1) = False
    blnTryOnce = True   'Only send data once, but keep looping...
    'loop until done or timed out
    While (blnDoneRead(1) = False) And (blnSendTimeout(1) = False)
        If blnTryOnce = True Then
            'try to send to session exe
            objSession1.subRead 1, strData
            blnTryOnce = False
        End If
        DoEvents
    Wend
    
    If blnDoneRead(1) = True Then

        'Set the processing flag here to indicate the client is doing some work
        blnProcessing(1)=true

        blnSendTimeout(1) = False   'reset timeout flag
    Else
        If blnSendTimeout(1) Then
            With udtMessage
                .strUser = "SYSTEM"
                .strSource = "subSendToClient1"
                .strMessage = "Send To Client 1 Timed Out"
                .blnLogIt = True
                .blnError = True
            End With
            subHandleMessage 0, udtMessage
        Else
            With udtMessage
                .strUser = "SYSTEM"
                .strSource = "subSendToClient1"
                .strMessage = "Send To Client 1 Failed"
                .blnLogIt = True
                .blnError = True
            End With
            subHandleMessage 0, udtMessage
        End If
        blnSendTimeout(1) = False   'reset timeout flag
    End If

ExitHandler:
    Exit Sub
ErrHandler:
    If (Err.Number = cstErrServerDoesntExist) Or (Err.Number = cstErrAutomationErr) Then
    'there is no session exe - it has been killed
        With udtMessage
            .strUser = "SYSTEM"
            .strSource = "subSendToClient1"
            .strMessage = "The Client's Process Is no longer running. Socket disconnected."
            .blnLogIt = True
            .blnError = True
        End With
        subHandleMessage 0, udtMessage
        tcpServer(1).Disconnect
    Else
        'new bit - 13/04/2005
        If objSession1 Is Nothing Then
            With udtMessage
                .strUser = "SYSTEM"
                .strSource = "subSendToClient1"
                .strMessage = "The Client's Process Is no longer running. Socket disconnected."
                .blnLogIt = True
                .blnError = True
            End With
            subHandleMessage 0, udtMessage
            tcpServer(1).Disconnect
        End If
        'end of new
        fintGeneralError False, Err.Source, "subSendToClient1 " & Err.Description, Err.Number
    End If
End Sub

' Event raised from ClientSession (ActiveX EXE) when processing complete
Private Sub objSession1_DoneProcessing()
  'Clear the flag here, to allow the client to process new messages.  
  blnProcessing(1) = False
End Sub



I think I'll also need to add a timer along with this flag, otherwise, I'll never be able to recover a hung session.


Hope that makes sense.

This post has been edited by maj3091: 25 March 2013 - 03:57 AM

Was This Post Helpful? 0
  • +
  • -

#13 BobRodes  Icon User is offline

  • Your Friendly Local Curmudgeon
  • member icon

Reputation: 573
  • View blog
  • Posts: 2,989
  • Joined: 19-May 09

Re: Automation error - ActiveX EXE

Posted 25 March 2013 - 08:54 PM

Another possibility might be to add a broker process that monitors all the other processes to make sure they are available. Like a multiuse activex exe that keeps track of which of your clients is available. The client event that gets raised when done could pass an id to itself and the broker's event handler could set a flag locally to itself in an array of them. Then the server would just send requests to the broker, which would act as a proxy to the real clients from the standpoint of the server.
Was This Post Helpful? 0
  • +
  • -

#14 maj3091  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 299
  • View blog
  • Posts: 1,768
  • Joined: 26-March 09

Re: Automation error - ActiveX EXE

Posted 29 March 2013 - 12:29 AM

For now, after talking it through with the customer and considering it's happened twice in almost a decade of running, they didn't want to change anything to drastic (just nervous I guess).

So, I ended up introducing a flagging system so the EXE knows when the ActiveX EXE is processing and therefore avoiding re-entry into the the RPC call functionality.

Thanks for your help and pointers Bob.
Was This Post Helpful? 0
  • +
  • -

#15 BobRodes  Icon User is offline

  • Your Friendly Local Curmudgeon
  • member icon

Reputation: 573
  • View blog
  • Posts: 2,989
  • Joined: 19-May 09

Re: Automation error - ActiveX EXE

Posted 29 March 2013 - 04:40 PM

If that works, that sounds like the simplest solution. You're most welcome.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1