从 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)
我的猜测是,以字符串形式传递文件名或通过引用传递双精度数的方式存在问题?然而,我真的不知道,也不知道如何进一步调查这个问题。
任何想法都非常感激。
谢谢。
I am trying to call a C DLL from C#, but I'm not having any joy. The documentation for the DLL provides an example function delaration for VB that looks like;
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
I have therefore done the following;
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));
}
}
I'm still not having much luck and have tried various other settings in the DLLImport statment such as;
SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)
The output I get from the code is;
Result was 4690529317195612196, Lat: 0, Long: 0
Last error recieved was 6
If I'm right in looking at the info for Win32 errors, I think that refers to;
ERROR_INVALID_HANDLE The handle is invalid.
6 (0x6)
My guess is there is either a problem with passing in the filename as a string or the way I am passing doubles by ref? However, I really don't know, and I am at a loss as to how to investigate the issue further.
Any ideas are much appreciated.
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我通过使用一个名为的工具找到了尝试失败的原因;
Microsoft(R) P/Invoke Interop Assistant,如 此线程的答案。
我利用这个工具输入一些 C 函数原型并让它代表我生成所需的 C# 原型。 C 原型如下所示;
当将其输入 Interop 助手工具时,它表明不应使用 long(正如我在最初的问题中所做的那样),而应将其声明为 int。它产生了以下输出,这意味着我上面的代码现在可以按照我的希望工作。耶。
感谢大家对此的帮助。
I found the reason for my failed attempts by utilising a tool called;
Microsoft(R) P/Invoke Interop Assistant as suggested by an answer on this thread.
I utilised this tool to input some of the C function prototypes and get it to generate the required C# prototype on my behalf. The C prototype looked like the following;
When entering this into the Interop assistant tool, it showed that rather than using longs (as I had done in my original question), these should be declared as an int. It produced the following output that meant my code above now worked as I'd hoped. Yay.
Thanks for everyones help with this.
您可能希望使用字符串参数的编组属性来定义 C# 签名。
我还会引用 Mark Sowul 的回答,并说尝试使用 StdCall 而不是 Cdecl 进行呼叫。
另外,作为预防措施,我可能会仔细检查以确保编译器设置为编译 x86 代码,以防其编译为 64 位。
You may want to define your c# signatures using marshalling attributes for your string parameters.
I'll also piggy-back on Mark Sowul's answer and say try calling with StdCall instead of Cdecl.
Also, as a precaution, I'd probably double-check to make sure that the compiler is set to compile x86 code, in case its compiling for 64-bit.
尝试将
string sGridFile
更改为StringBuilder sGridFile
C++ 有许多不同类型的字符串,因此在管理代码和非托管代码之间编组字符串可能会很棘手。
Try changing
string sGridFile
toStringBuilder sGridFile
C++ has so many different kinds of strings that marshaling strings between manage and unmanaged code can be tricky.
看来您不是唯一遇到该问题的人,您尝试过 StdCall 吗?这对这个人有用: http://www.mail -archive.com/[电子邮件受保护]/msg01227.html
Looks like you aren't the only one to encounter that issue, have you tried StdCall? That worked for this fellow: http://www.mail-archive.com/[email protected]/msg01227.html