Calling C DLL from C#

本文关键字:from DLL Calling | 更新日期: 2023-09-27 17:50:23

我正试图从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 (0 x6)

我的猜测是有一个问题在文件名作为一个字符串传递或我通过ref传递双精度的方式?然而,我真的不知道,我不知道如何进一步调查这个问题。

有任何想法都非常感谢。

谢谢。

Calling C DLL from C#

我通过使用一个叫做;微软(R) P/调用互操作助手建议的答案在这个线程。

我使用这个工具输入一些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辅助工具中输入它时,它显示应该将其声明为int而不是使用long(就像我在最初的问题中所做的那样)。它产生了以下输出,这意味着我上面的代码现在可以像我希望的那样工作了。耶。

    /// 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