从C#调用Delphi DLL会产生意想不到的结果 [英] Calling a Delphi DLL from C# produces unexpected results

查看:143
本文介绍了从C#调用Delphi DLL会产生意想不到的结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个我没有写的Delphi DLL,但需要从C#ASP.NET 3.5应用程序调用。这是我从开发人员获得的函数定义:

  function CreateCode(SerialID:String; 
StartDateOfYear,YearOfStartDate, YearOfEndDate,DatePeriod:Word;
CodeType,RecordNumber,StartHour,EndHour:Byte):PChar;
external'CreateCodeDLL.dll';

这里是我的C#代码:

  [DllImport(CreateCodeDLL.dll,
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern IntPtr CreateCode string SerialID,
UInt16 StartDateOfYear,
UInt16 YearOfStartDate,
UInt16 YearOfEndDate,
UInt16 DatePeriod,
字节代码类型,
字节RecordNumber,
字节StartHour,
Byte EndHour);

最后,我打电话给这个方法:

  //输入
String serialID =92F00000B4FBE;
UInt16 StartDateOfYear = 20;
UInt16 YearOfStartDate = 2009;
UInt16 YearOfEndDate = 2009;
UInt16 DatePeriod = 7;
Byte CodeType = 1;
字节RecordNumber = 0;
Byte StartHour = 15;
Byte EndHour = 14;

// DLL调用
IntPtr codePtr = CodeGenerator.CreateCode(serialID,StartDateOfYear,
YearOfStartDate,YearOfEndDate,DatePeriod,CodeType,
RecordNumber,StartHour,EndHour) ;

//取指针并提取字符串中的代码
String code = Marshal.PtrToStringAnsi(codePtr);

每次我重新编译这个确切的代码并运行它,它返回一个不同的值。期望值是由数字组成的10位数代码。返回的值实际上是12位数。



最后一个重要的信息是我有一个测试.EXE,它有一个允许我测试DLL的GUI。使用.EXE的每个测试都会返回相同的10位数字(预期结果)。



所以,我不得不相信我已经将DLL调用不正确。想法?

解决方案

Delphi使用所谓的 fastcall 调用约定默认。这意味着编译器尝试将参数传递给CPU寄存器中的函数,并且只有在空闲寄存器中有更多的参数时才使用该栈。例如,Delphi使用(EAX,EDX,ECX)作为函数的前三个参数。

在您的C#代码中,您实际使用 stdcall 调用约定,它指示编译器通过堆栈传递参数(反向顺序,即最后一个参数被先插入),并让被调用者清理堆栈。

相比之下, c / c ++编译器使用的调用方法强制调用者清理堆栈。

请确保您正在双方使用相同的调用约定。 Stdcall主要使用,因为它几乎可以在任何地方使用,并得到每个编译器的支持(Win32 API也使用这个约定)。

请注意, fastcall 不支持.NET。


I have a Delphi DLL that I did not write, but need to call from a C# ASP.NET 3.5 app. Here is the function definition I got from the developers:

function CreateCode(SerialID : String; 
    StartDateOfYear, YearOfStartDate, YearOfEndDate, DatePeriod : Word; 
    CodeType,RecordNumber,StartHour,EndHour : Byte) : PChar;
    external 'CreateCodeDLL.dll';

And here is my C# code:

[DllImport( "CreateCodeDLL.dll", 
    CallingConvention = CallingConvention.StdCall, 
    CharSet=CharSet.Ansi)]
public static extern IntPtr CreateCode( string SerialID,
                                        UInt16 StartDateOfYear,
                                        UInt16 YearOfStartDate,
                                        UInt16 YearOfEndDate,
                                        UInt16 DatePeriod,
                                        Byte CodeType,
                                        Byte RecordNumber,
                                        Byte StartHour,
                                        Byte EndHour);

And finally, my call to this method:

//The Inputs 
String serialID = "92F00000B4FBE";
UInt16 StartDateOfYear = 20;
UInt16 YearOfStartDate = 2009;
UInt16 YearOfEndDate = 2009;
UInt16 DatePeriod = 7;
Byte CodeType = 1;
Byte RecordNumber = 0;
Byte StartHour = 15;
Byte EndHour = 14;            

// The DLL call
IntPtr codePtr = CodeGenerator.CreateCode(serialID, StartDateOfYear, 
                YearOfStartDate, YearOfEndDate, DatePeriod, CodeType, 
                RecordNumber, StartHour, EndHour);

// Take the pointer and extract the code in a string
String code = Marshal.PtrToStringAnsi(codePtr);  

Every time I re-compile this exact code and run it, it returns a different value. The expected value is a 10-digit code comprised of numbers. The returned value is actually 12 digits.

The last important piece of information is that I have a test .EXE that has a GUI that allows me to test the DLL. Every test using the .EXE returns the same 10-digit number (the expected result).

So, I have to believe that I have declared my call to the DLL incorrectly. Thoughts?

解决方案

Delphi uses the so called fastcall calling convention by default. This means that the compiler tries to pass parameters to a function in the CPU registers and only uses the stack if there are more parameters than free registers. For example Delphi uses (EAX, EDX, ECX) for the first three parameters to a function.
In your C# code you're actually using the stdcall calling convention, which instructs the compiler to pass parameters via the stack (in reverse order, i.e. last param is pushed first) and to let the callee cleanup the stack.
In contrast, the cdecl calling used by C/C++ compilers forces the caller to cleanup the stack.
Just make sure you're using the same calling convention on both sides. Stdcall is mostly used because it can be used nearly everywhere and is supported by every compiler (Win32 APIs also use this convention).
Note that fastcall isn't supported by .NET anyway.

这篇关于从C#调用Delphi DLL会产生意想不到的结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆