开发者

calling delphi dll from c#

I have a Delphi dll defined like this:

TMPData = record
 Lastname, Firstname: array[0..40] of char;
 Birthday: TDateTime;
 Pid: array[0..16] of char;
 Title: array[0..20] of char;
 Female: Boolean;
 Street: array[0..40] of char;
 ZipCode: array[0..10] of char;
 City: array[0..40] of char;
 Phone, Fax, Department, Company: array[0..20] of char;
 Pn: array[0..40] of char;
 In: array[0..16] of char;
 Hi: array[0..8] of char;
 Account: array[0..20] of char;
 Valid, Status: array[0..10] of char;
 Country, NameAffix: array[0..20] of char;
 W, H: single;
 Bp: array[0..10] of char;
 SocialSecurityNumber: array[0..9] of char;
 State: array[0..2] of char;
end;   

function Init(const tmpData: TMPData; var ErrorCode: integer; ResetFatalError: boolean = false): boolean;

procedure GetData(out tmpData: TMPData);

My current c# signatures looks like this:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TMPData
{            
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 40)]
    public string Lastname;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 40)]
    public string Firstname;
    [MarshalAs(UnmanagedType.R8)]
    public double Birthday;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 16)]
    public string Pid;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 20)]
    public string Title;
    [MarshalAs(UnmanagedType.Bool)]
    public bool Female;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 40)]
    public string Street;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 10)]
    public string ZipCode;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 40)]
    public string City;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 20)]
    public string Phone;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 20)]
    public string Fax;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 20)]
    public string Department;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 20)]
    public string Company;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 40)]
    public string Pn;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 16)]
    public string In;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 8)]
    public string Hi;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 20)]
    public string Account;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 10)]
    public string Valid;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 10)]
    public string Status;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 20)]
    public string Country;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 20)]
    public string NameAffix;
    [MarshalAs(UnmanagedType.I4)]
    public int W;
    [MarshalAs(UnmanagedType.I4)]
    public int H;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 10)]
    public string Bp;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 9)]
    public string SocialSecurityNumber;
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 2)]
    public string State;
}

[DllImport("MyDll.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Init(TMPData tmpData, int ErrorCode, bool ResetFatalError);

[DllImport("MyDll.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetData(out TMPData tmpData);

I first call Init setting the BirthDay, LastName and FirstName. I then call GetData but the TMPData structure I get back is incorrect. The FirstName, LastName and Birthday fields are populated but the data is incorrect. Is the mapping correct? ( "array[0..40] of char" equal to "[MarshalAs(UnmanagedType.LPStr, SizeConst = 40)]" )?

Update:

I have updated the c# mapping with the feedback to look like this:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
public struct TMPData 
{             
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 41)] 
    public string Lastname; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 41)] 
    public string Firstname; 
    [MarshalAs(UnmanagedType.R8)] 
    public double Birthday; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)] 
    public string Pid; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)] 
    public string Title; 
    [MarshalAs(UnmanagedType.Bool)] 
    public bool Female; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 41)] 
    public string Street; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)] 
    public string ZipCode; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 41)] 
    public string City; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)] 
    public string Phone; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)] 
    public string Fax; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)] 
    public string Department; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)] 
    public string Company; 
    [MarshalAs(UnmanagedType.ByValT开发者_运维知识库Str, SizeConst = 41)] 
    public string Pn; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)] 
    public string In; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)] 
    public string Hi; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)] 
    public string Account; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)] 
    public string Valid; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)] 
    public string Status; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)] 
    public string Country; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)] 
    public string NameAffix; 
    [MarshalAs(UnmanagedType.I4)] 
    public int W; 
    [MarshalAs(UnmanagedType.I4)] 
    public int H; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)] 
    public string Bp; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] 
    public string SocialSecurityNumber; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 3)] 
    public string State; 
} 

The Init function:

[DllImport("MyDll.dll")] 
[return: MarshalAs(UnmanagedType.Bool)] 
public static extern bool Init(TMPData tmpData, int ErrorCode, bool ResetFatalError); 

now fails with the following error:

"Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

when I call it as shown below:

int errorCode = 0;
bool resetLastError = true;
TMPData tmpData = new TMPData();

        tmpData.Lastname = "TestLastName";
        tmpData.Firstname = "TestName";
        tmpData.Birthday = 28856.0;
        tmpData.Pid = "12345678";
        tmpData.Title = null;
        tmpData.Female = false;
        tmpData.Street = null; 
        tmpData.ZipCode = null;
        tmpData.City = null;
        tmpData.Phone = null;
        tmpData.Fax = null;
        tmpData.Department = null; 
        tmpData.Company = null;
        tmpData.Pn = null;
        tmpData.In = null;
        tmpData.Hi = null;
        tmpData.Account = null;
        tmpData.Valid = null;
        tmpData.Status = null;
        tmpData.Country = null;
        tmpData.NameAffix = null;
        tmpData.W = 0;
        tmpData.H = 0;
        tmpData.Bp = null;
        tmpData.SocialSecurityNumber = 0;
        tmpData.State = null;

bool success = Init(tmpData, errorCode, resetLastError);    

If I change the ByValTStr to LPStr in the struct definition then the Init function succeeds but the GetData function returns incorrect string values. If I change LPStr back to ByValTStr the Init function fails but the GetData function returns the correct strings. I am not sure if I should marshal array[0..x] of char as LPStr of ByValTStr?


okay I finally got it working. thanks for the help.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]  
public struct TMPData  
{              
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 41)]  
    public string Lastname;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 41)]  
    public string Firstname;  
    [MarshalAs(UnmanagedType.R8)]  
    public double Birthday;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]  
    public string Pid;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]  
    public string Title;  
    [MarshalAs(UnmanagedType.Bool)]  
    public bool Female;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 41)]  
    public string Street;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]  
    public string ZipCode;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 41)]  
    public string City;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]  
    public string Phone;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]  
    public string Fax;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]  
    public string Department;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]  
    public string Company;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 41)]  
    public string Pn;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]  
    public string In;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]  
    public string Hi;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]  
    public string Account;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]  
    public string Valid;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]  
    public string Status;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]  
    public string Country;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]  
    public string NameAffix;  
    [MarshalAs(UnmanagedType.R4)]  
    public int W;  
    [MarshalAs(UnmanagedType.R4)]  
    public int H;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]  
    public string Bp;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]  
    public string SocialSecurityNumber;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 3)]  
    public string State;  
}  

[DllImport("MyDll.dll")]         
[return: MarshalAs(UnmanagedType.Bool)]         
public static extern bool Init(ref TMPData tmpData,ref int ErrorCode, bool ResetFatalError);         

[DllImport("MyDll.dll")]         
[return: MarshalAs(UnmanagedType.Bool)]         
public static extern bool GetData(ref TMPData tmpData);    


What version of Delphi is the DLL built with? Delphi 2009 introduced Unicode, which would mean you would need to use a Unicode string type in C#, while if it is pre-Delphi 2009 then there is no Unicode. LPStr is 8 Bit, while the character type of ByValTStr is determined by the System.Runtime.InteropServices.CharSet argument of the System.Runtime.InteropServices.StructLayoutAttribute applied to the containing structure.

See: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.unmanagedtype.aspx

You said originally you were getting data back, but it was incorrect. How was in incorrect? Garbage, or just swapped, truncated, etc?


As dtb mentioned in his comment, 0..40 is 41 characters, not 40. Apparently all your string definitions fail to take into account the 0th element.

Also, if I'm reading this right, (I don't know C# but I do know C,) it looks like you're defining the char arrays as pointers to long (Unicode, 16 bit per char) strings. There are two potential issues with that. First off, a char array declared that way is not a pointer to a string, it's an inline string. Second, it's only an array of WideChars (16 bit per char) if this was built with Delphi version 2009 or later. Otherwise, it an array of Ansi (8 bit per char) chars.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜