And the different code gen from XE3:


And the different code gen from XE3:

Comments

  1. I'm not ASM genius, but I can see that there is a difference of 2 bytes going on there.  Hmm.

    ReplyDelete
  2. Are you sure you have 'stdcall' specifier in the correct place? This looks like 'register/fastcall' convention to me ('nil' is sent in the ECX parameter).

    ReplyDelete
  3. Primož Gabrijelčič Thanks for your help:

    I have:

    type
      TGeoEngCheckDbAvailability    = function(pSearchPath: pAnsichar; pDbName: PAnsiChar; pUserDictPath: PAnsiChar; pDbFlags: pshort): short; stdcall;

    and then 

    var
      _GeoEngCheckDbAvailability:    TGeoEngCheckDbAvailability = nil;

    and

    function GeoEngCheckDbAvailability(pSearchPath: PAnsiChar; pDbName: PAnsiChar; pUserDictPath: PAnsiChar; pDbFlags: pshort): short;
    begin result := _GeoEngCheckDbAvailability(pSearchPath, pDbName, pUserDictPath, pDbFlags); end;

    I don't know where else to put the calling convention.

    ReplyDelete
  4. This looks fine. I'll take a better look at it in the morning (it's 11:30 pm here) - unless somebody solves the problem before.

    ReplyDelete
  5. I see nothing special in asm here - GeoEngCheckDbAvailability called  with fastcall convention, as you requested. You should show disasm  of GeoEngCheckDbAvailability where _GeoEngCheckDbAvailability called if you suspect Delphi in srong opcode generation

    ReplyDelete
  6. Is the variable you are casting to PAnsiChar of string or AnsiString? If it is a string you have to cast it to AnsiString first.

    ReplyDelete
  7. 1. This is definitely not stdcall calling convention. Stdcall looks like this:

    dllUse1.pas.48: retCode := hProc(PAnsiChar(par1), PAnsiChar(par2), nil,@dbFlags);
    0045BF61 8D45F6           lea eax,[ebp-$0a]
    0045BF64 50               push eax
    0045BF65 6A00             push $00
    0045BF67 8B45F8           mov eax,[ebp-$08]
    0045BF6A E8918DFAFF       call @LStrToPChar
    0045BF6F 50               push eax
    0045BF70 8B45FC           mov eax,[ebp-$04]
    0045BF73 E8888DFAFF       call @LStrToPChar
    0045BF78 50               push eax
    0045BF79 FFD6             call esi
    0045BF7B 8BF0             mov esi,eax

    2. How are you linking to this method? Statically?

    ReplyDelete
  8. Stefan --

    Everything is an AnsiString.

    Primoz --

    It's dynamic. The code to get the method looks like this:

     @_GeoEngCheckDbAvailability:= GetProcAddress(MMPlusHandleID, 'GeoEngCheckDbAvailability');

    ReplyDelete
  9. OK. I have almost the same code (have built a simple DLL with a method having the same signature) but the call sequence looks totally different from yours. Do you have 'stdcall' in the type which defines _GeoEngCheckDbAvailability?

    ReplyDelete
  10. Alexey Petushkov It has the stdcall marker on it, so I assume it is using stdcall, but it appears that somehow the XE3 compiler is treating it differently.

    ReplyDelete
  11. Even your D2007 call sequence is not stdcall.

    ReplyDelete
  12. Stefan Glienke Hard casting the parameters to AnsiString results in the same code gen, so they definitely are AnsiString.

    ReplyDelete
  13. Compare your code with http://www.gabrijelcic.org/dlltest.zip. Archive contains test DLL (containing a method with your signature) and test clients. Tested in D2007 and XE3 - both work correctly. Compare CPU view with yours and you'll see that parameter passing is completely different.

    ReplyDelete
  14. And I mispoke -- it's not an AV, it's an exception:

    ---------------------------
    Debugger Exception Notification
    ---------------------------
    Project HDM.exe raised exception class $C0000005 with message 'access violation at 0x061b205d: read of address 0x04a70100'.
    ---------------------------
    Break   Continue   Help   
    ---------------------------

    ReplyDelete
  15. Even weirder here's the (yucky) code with an exception handler.  The ShowMessage call doesn't happen, even though an exception is raised and there is an exception handler:

      try
        retCode := GeoEngCheckDbAvailability(PAnsiChar(MMPath), PAnsiChar(dbName), nil,@dbFlags);
      except
        ShowMessageApp('ERROR: Exception calling GeoEngCheckDbAvailability.'); exit;
      end;

    And again, I see this exception only in the debugger, not when I run the EXE outside the debugger.

    ReplyDelete
  16. Did you do some logging of the arguments before the call and inside the dll routine yet?

    ReplyDelete
  17. Not yet -- but the debugger shows that "all is well" going in.  What kind of logging would you recommend?

    The weird part is the except block being ignored.  Then stepping into it merely goes to _LStrToPChar and that's it -- I see no other code in the debugger. 

    This is really weird.

    ReplyDelete
  18. Comparing asm is moot, especially when you add additional variables into the routine from where you are calling as Primoz did. Of course the asm changes then because offsets change and the compiler cannot optimize things.

    I am pretty sure the errors origin is something different. Also of course this exception is an AV, it even says so.

    Debug the dll if you can.

    ReplyDelete
  19. Stefan Glienke Just wanted to be clear that it was being reported as an exception, not a "pure" AV -- sorry if I wasn't clear about that.

    Anyway, thanks for your help, everybody.  I'm going to leave this one for a while because it is starting to piss me off.  ;-)  

    Let me know if you have any insightful revelations.  :-)

    ReplyDelete
  20. Debugging the DLL is not impossible (although definitely not pretty) - just F7 on the appropriate CPU instruction that calls the DLL and you'll be in the DLL code.

    Alternatively, send me an NDA, grant me remote access, and I'll find the problem. And send you the bill. ;)

    ReplyDelete
  21. No, it's returning a short -- it won't compile with your suggestion.....

    ReplyDelete
  22. The first two offsets to ebp are greater by 4 than in D7. [ebp - $0a] instead of [ebp - $08], and so on. but the third offset is identical in both. Looks as though either the parameters are in different order, or one of them is being handled as 64-bit....

    ReplyDelete
  23. Okay, so this made the AV go away:

      FillChar(MMPathAC255, SizeOf(ArrayOfAnsiChar255), 0);
      FillChar(dbNameAC255, SizeOf(ArrayOfAnsiChar255), 0);
      StrPCopy(MMPathAC255, MMPath);
      StrPCopy(dbNameAC255, dbName);

      try
        retCode := GeoEngCheckDbAvailability(@MMPathAC255[1], @MMPathAC255[1], nil,@dbFlags);

    But now it fails to initialize properly.  At least it is returning a known error code.

    ReplyDelete
  24. Nick Hodges  Blech! The first char of each array should be at index 0, not 1. Or so it seems to me....

    ReplyDelete
  25. Sounds like the reason you got those AVs is because strings in Delphi are constants in memory, and because you're now allocating them on the stack, or in heap, the AVs disappear.

    String assignments are also +1 back to the string constant.

    ReplyDelete

Post a Comment