从C#调用C DLL

时间:2011-04-14 14:26:42

标签: c# c dll dllimport

我试图从C#调用一个C DLL,但我没有任何快乐。 DLL的文档为VB提供了一个示例函数delaration,看起来像;

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传递双精度的方式存在问题?但是,我真的不知道,我不知道如何进一步调查这个问题。

非常感谢任何想法。

感谢。

4 个答案:

答案 0 :(得分:6)

我通过使用一个名为的工具找到了我尝试失败的原因; Microsoft(R) P/Invoke Interop Assistant建议的 an answer on this thread

我利用这个工具输入一些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助手工具中时,它表明不应该使用longs(正如我在原始问题中所做的那样),这些应该被声明为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) ;

感谢大家帮忙解决这个问题。

答案 1 :(得分:3)

您可能希望使用字符串参数的编组属性来定义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位。

答案 2 :(得分:2)

尝试将string sGridFile更改为StringBuilder sGridFile

C ++有很多不同类型的字符串,在管理和非托管代码之间编组字符串可能很棘手。

答案 3 :(得分:1)

看起来你不是唯一遇到这个问题的人,你试过StdCall吗?这适用于这个人:http://www.mail-archive.com/delphi@ns3.123.co.nz/msg01227.html