views:

436

answers:

6

I've written a Delphi DLL that communicates with a third party program via COM. Some users report that the third party program crashes occasionally. Others using the software in an identical fashion have never experienced a crash. When this crash occurs, the third party program appears to simply become unavailable in my DLL app.

The vendor swears that it is a problem with how the Delphi DLL is coded, although they have not seen the source code and can't tell what what the DLL is doing to cause the crash, but they know it's "something".

Aside from the fact that I believe that the third party program shouldn't be crashing due to some minuscule problem in my DLL, let's assume that there is something in my DLL that needs fixing.

How can I determine how my app might be causing this? Does anyone have experience communicating via COM with a hyper-sensitive program like this? Are there some common things to look for that might be crashing the third party program?

+2  A: 

If their program crashes when you use their published interface, I'm pretty sure there's something wrong with it. Proving this is another thing, altogether.

Can you duplicate the problem reliably with a small Delphi app that you can give to the vendor? Seeing a reproducible failure could help convince them that they need to make a fix. At the very least, it could help pinpoint what they think you are doing "wrong" and tell you how to do it "right".

You could also try to duplicate the failure using C# or even VBScript.

Bruce McGee
I agree strongly. I've been in this place many times before and the fastest solution has always been to create a NEW repeatable test program and to give it to the vendor with complete source. If possible, log all interactions with the API (what parameters are being passed and what is being returned).
skamradt
The vendor is saying Dave's DLL is wrong. The vendor is who should make the test program, not Dave, since the vendor is the one who has actually seen the problem. If not a test program, then at least a list of steps to follow to demonstrate the problem.
Rob Kennedy
I don't think the vendor is seeing this. I thought it was Dave's customers. In any event, I agree that the vendor should step up and at least help isolate the problem. Unfortunately, they don't seem very eager, so it falls to Dave to help convince them that there's a problem or, alternatively, find a work around. Who knows, in duplicating the problem, he might find that he is making a mistake after all. Bottom line; Dave has his customers to worry about.
Bruce McGee
Yes, the vendor is not very helpful at all here. I'm more than happy to admit a mistake if I find it.What are the chances that the third party app doesn't handle the "multi-threadedness" of my DLL? Is it possible it simply doesn't have the capabilities to handle it properly?
Dave
Yes, this is possible. I'd ask the vendor outright if their app supports concurrency. Regardless, it'sprobably a good place to start when trying to reproduce the problem.
Bruce McGee
+4  A: 
  1. Make the customer happy.
  2. Don't assume it's not your dll, it could be. Even if "Others using the software in an identical fashion have never experienced a crash", it could be that with different data, it does different things...
  3. I'd suggest that you setup logging to a textfile in a "special" diagnostic version.
  4. Log everything, your parameters, your exceptions, and the steps you're going through. Maybe even the start and end of every function, and every other line.

Here's how it could look...

Loaded DLL
Started MyFunction1 with parameters: 1,4,hello
   1
   2
   ...
   500
Ended MyFunction1

to make that,,, I'd setup a few functions (in their own unit):

// opens a text file (fixed name), and appends to it.
function InitializeLog; 

// closes the file
function CloseLog;      

//add a log line.
function Log(message:string='', startNewFunction:boolean:False);

you would call it like this:

function MyFunction1(Integer,Integer,String);
begin
  try
    Log('Loaded DLL');

    //use inttostr and do some string concats to get the params
    Log('Started MyFunction1 with parameters: 1,4,hello',true); 

    //Then every other line:
    Log; 
    //this would increment a global variable FuncLine:Integer
    //and write it to the file.    

  except
    On E:Exception (Log('***'+E.Message));
  end;
end;

Something like this should have a {$DEFINE} to enable these logging functions, to enable/disable the diagnostic logging.

This could also be useful.

Osama ALASSIRY
+4  A: 

Look at Quality Central for report 58409.

It's about FPU Mask and Dll's.

To explain it in short:

The settings of the FPU Mask determ how floating point exceptions are handled.

If you have for example an Applicaion_A(coded by some else) which loads Dll_A(also coded by some else) and Dll_B(coded by you), and your Dll changes the FPU Mask, then this change is valid for Application_A and Dll_A as well.

Let's make an example: You have installed for example WinZIP,SubVersion etc. which registers additional functions in the windows file explorer (popup-menu on right mouse click) and you now call TOpenDialog from inside your application.exe, then this additional functions might compromise your FPU settings.

Hope this helps. (Additional hint: Take Sysinternal to see what dll's are loaded by your app)

Samuel - is there way way to tell if this is occurring in my situation?
Dave
Loren Pechtel
+3  A: 

Have you considered using MadExcept? If you catch the error in the interface methods you can either log the call stack or show a dialog to the user and return a standard EOleSysError back to the calling exe.

Something like this:

  except
    on e: Exception do
    begin
      MadExcept.HandleException();

      raise EOleSysError.Create('InitializeObject Failed',
        ErrorNumberToHResult(1 + CODE_BASE), 1);
    end;

If the application hangs but not not throw and exception you can see what is happening by using the MadExcept utility madTraceProcess. That will let you generate a call stack for the running application. Your dll will not be on the main thread, but you will be able to see your call stack. That is a good way to tell if your dll is actually doing anything when the hang happens.

I have a COM dll that interacts with a exe that does not use MadExcept and this approach has worked well for me.

Mark Elder
I'm running EurekaLog in the DLL and it's not raising an exception when the third party app crashes. As far as I can tell it's not being notified at all when it crashes.
Dave
+1  A: 

If I understood the situation correctly, infortunately, there is not much you can do on your side if your DLL is not crashing and the called 3rd party program just stops being responsive. The crash is in their code but only triggered by your DLL calling it. The debug Log should really be done in their app.
What you can do though is to log all the calls your DLL make to this 3rd party program with the parameters and some context information.
Then looking at the last trace before the crash might give you some information...

François
+2  A: 

I don't know how madExcept etc work, but I use jclDebug.pas + JclHookExcept.pas (from JEDI JCL library) a lot. It makes a Windows API hook, so it catches all (!) exceptions, even if you make something like this:

try
  raise exception.create('test');
except
  //eat exception
end;

Normally you don't see this exception because it is "eaten"... But with the hook you get all exceptions. For example: I once got an "catastrophic failure" in Midas.dll, and via the hook I saw an "database connection lost" error in the dll before this exception, so I knew what happened. (btw: JclHookExcept.pas = hook, jclDebug.pas = strack trace).

I see now that JclHookExcept.pas also has an "JclHookExceptionsInModule" procedure, so you can force (?) to hook all exceptions from an specific library...

Some demo code:

procedure AnyExceptionNotify(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean);
begin
  //log exception
end;

initialization
  // Start Exception tracking
  JclStartExceptionTracking;
  JclTrackExceptionsFromLibraries;
  JclStackTrackingOptions := [stStack, stRawMode, stAllModules];
  // Assign notification procedure for hooked RaiseException API call. This
  // allows being notified of any exception
  JclAddExceptNotifier(AnyExceptionNotify);
André