从C#调用C DLL

| 我正在尝试从C#调用C DLL,但是我没有任何乐趣。 DLL的文档提供了VB的示例函数声明,如下所示:
Declare Function TransGeogPt Lib \"c:\\DLLS\\GDAit.dll\" (ByVal sGridFile As String, ByVal lDirection As
Long, ByVal dLat As Double, ByVal dLong As Double, pdLatNew As Double, pdLongNew As Double,
pdLatAcc As Double, pdLongAcc As Double) As Long

Declare Function TransProjPt Lib \"c:\\DLLS\\GDAit.dll\" (ByVal sGridFile As String, ByVal lDirection As
Long, ByVal dLat As Double, ByVal dLong As Double, ByVal lZone As Long, pdLatNew As Double,
pdLongNew As Double, pdLatAcc As Double, pdLongAcc As Double) As Long
因此,我做了以下工作:
public class GDAIt
{
    public static string gridFileName = @\"C:\\Nat84.gsb\";

    [DllImport(@\"c:\\GDAit.dll\")]
    public static extern long TransGeogPt(string sGridFile, long lDirection, double dLat, double dLong, ref double pdLatNew, ref double pdLongNew, ref double pdLatAcc, ref double pdLongAcc);

    [DllImport(@\"c:\\GDAit.dll\")]
    public static extern long TransProjPt(string sGridFile, long lDirection, double dLat, double dLong, long lZone, ref double pdLatNew, ref double pdLongNew, ref double pdLatAcc, ref double pdLongAcc);

    public static long CallTransGeogPt(string sGridFile, long lDirection, double dLat, double dLong, ref double pdLatNew, ref double pdLongNew, ref double pdLatAcc, ref double pdLongAcc)
    {
        return TransGeogPt(sGridFile, lDirection, dLat, dLong, ref pdLatNew, ref pdLongNew, ref pdLatAcc, ref pdLongAcc);
    }

    public static long CallTransProjPt(string sGridFile, long lDirection, double dLat, double dLong, long lZone, ref double pdLatNew, ref double pdLongNew, ref double pdLatAcc, ref double pdLongAcc)
    {
        return TransProjPt(sGridFile, lDirection, dLat, dLong, lZone, ref pdLatNew, ref pdLongNew, ref pdLatAcc, ref pdLongAcc);
    }


    public static void Process()
    {
        double latitude = 0.0;
        double longitude = 0.0; 
        double latAcc = 0.0; 
        double longAcc = 0.0;

        long result = 0;
        result = CallTransProjPt(gridFileName,
                                        1,
                                        394980,
                                        7619799,
                                        51,
                                        ref latitude,
                                        ref longitude,
                                        ref latAcc,
                                        ref longAcc);
        Console.WriteLine(string.Format(\"Result was {0}, Lat: {1}, Long: {2}\", result, latitude, longitude));

        int error = Marshal.GetLastWin32Error();

        Console.WriteLine(string.Format(\"Last error recieved was {0}\", error));

    }

}
我仍然不太走运,并且尝试了DLLImport语句中的其他各种设置,例如; SetLastError = true,CharSet = CharSet.Auto,CallingConvention = CallingConvention.Cdecl) 我从代码中得到的输出是;
Result was 4690529317195612196, Lat: 0, Long: 0
Last error recieved was 6
如果我正确地查看有关Win32错误的信息,我认为这是指的。 ERROR_INVALID_HANDLE句柄无效。 6(0x6) 我的猜测是将文件名作为字符串传递是否存在问题,或者我通过ref传递双精度数的方式吗?但是,我真的不知道,而且我对如何进一步调查该问题不知所措。 任何想法都非常感谢。 谢谢。     
已邀请:
我通过使用一个名为的工具找到了失败的原因。 Microsoft(R)P / Invoke Interop Assistant,如该线程的答案所建议。 我利用此工具输入了一些C函数原型,并代表我生成了所需的C#原型。 C原型如下所示;
long __stdcall TransProjPt(LPSTR psGridFile, long lDirection, double dEasting, double
dNorthing, long lZone, double* pdEastNew, double* pdNorthNew, double* pdEastAcc,
double* pdNorthAcc) 
当将其输入到Interop助手工具中时,它表明而不是使用long(就像我在原始问题中所做的那样),这些应该声明为int。它产生以下输出,这意味着我上面的代码现在可以按我希望的那样工作了。好极了。
    /// Return Type: int
    ///psGridFile: LPSTR->CHAR*
    ///lDirection: int
    ///dEasting: double
    ///dNorthing: double
    ///lZone: int
    ///pdEastNew: double*
    ///pdNorthNew: double*
    ///pdEastAcc: double*
    ///pdNorthAcc: double*
    [System.Runtime.InteropServices.DllImportAttribute(\"<Unknown>\", EntryPoint=\"TransProjPt\", CallingConvention=System.Runtime.InteropServices.CallingConvention.StdCall)]
public static extern  int TransProjPt([System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] System.Text.StringBuilder psGridFile, int lDirection, double dEasting, double dNorthing, int lZone, ref double pdEastNew, ref double pdNorthNew, ref double pdEastAcc, ref double pdNorthAcc) ;
谢谢大家的帮助。     
您可能希望使用字符串参数的封送处理属性来定义c#签名。
[DllImport(@\"c:\\GDAit.dll\")]
public static extern long TransGeogPt([MarshalAs(UnmanagedType.LPStr)] string sGridFile, long lDirection, double dLat, double dLong, ref double pdLatNew, ref double pdLongNew, ref double pdLatAcc, ref double pdLongAcc);

[DllImport(@\"c:\\GDAit.dll\")]
public static extern long TransProjPt([MarshalAs(UnmanagedType.LPStr)] string sGridFile, long lDirection, double dLat, double dLong, long lZone, ref double pdLatNew, ref double pdLongNew, ref double pdLatAcc, ref double pdLongAcc);
我还将背负Mark Sowul的回答,并说尝试使用StdCall而不是Cdecl进行调用。 另外,作为预防措施,我可能会仔细检查以确保编译器设置为编译x86代码,以防它编译为64位。     
尝试将
string sGridFile
更改为
StringBuilder sGridFile
C ++的字符串种类繁多,以至于在管理和非托管代码之间编排字符串可能很棘手。     
看起来您不是唯一遇到此问题的人,您是否尝试过StdCall?对这个人有用:http://www.mail-archive.com/delphi@ns3.123.co.nz/msg01227.html     

要回复问题请先登录注册