为什么我在从 VB.NET 向 FORTRAN DLL 传递参数或从 FORTRAN DLL 传递参数时遇到问题
我有一个用 Fortran 编写的 DLL,我知道它可以工作,因为我是从我们日常使用的工作软件包中获取它的。我试图从任何地方调用它,但我相信在将正确格式的参数传入和传出时遇到问题,所以我决定尝试从 VB 程序访问它。我选择VB是因为我已经联系了我们正在使用的软件包的开发人员,他给了我他在VB6中的DLL函数调用,尽管他不被允许向我展示FORTRAN源代码。
问题是(也许,我认为)我是用 VB.NET 编写的,它的数据类型与 VB6 不同。谁能明白为什么我遇到问题。
我运行时遇到的错误是:“AccessViolationException 未处理:尝试读取或写入受保护的内存。这通常表明其他内存已损坏。”
这是我提供的函数调用:
Declare Sub FCC_Curves Lib "FCC_Curves.dll" (Read_Flag As Boolean, First_Flag As Boolean, Frequency_MHz As Single, IQT As Integer, ByVal Radiation_Opt As String, ByVal L1 As Long, ERP_Watts As Single, T_PowerWatts As Single, Tx_GaindBi As Single, T_Loss As Single, ByVal Service As String, ByVal L2 As Long, Reliability As Integer, ByVal FCC_CurvesDataPath As String, ByVal L3 As Long, ByVal TerrainRoughness As String, ByVal L4 As Long, Haatm As Single, NPRfl As Integer, NFCCCurve As Integer, HPRfl As Single, Crve As Single, FCC_Loss As Single, Code As Integer, FS As Single)
这是我的代码:
Module Module1
Declare Sub FCC_Curves Lib "FCC_Curves.dll" (ByVal Read_Flag As Boolean, ByVal First_Flag As Boolean, ByVal Frequency_MHz As Single, ByVal IQT As Int16, ByVal Radiation_Opt As String, ByVal L1 As Int32, ByVal ERP_Watts As Single, ByVal T_PowerWatts As Single, ByVal Tx_GaindBi As Single, ByVal T_Loss As Single, ByVal Service As String, ByVal L2 As Int32, ByVal Reliability As Int16, ByVal FCC_CurvesDataPath As String, ByVal L3 As Int32, ByVal TerrainRoughness As String, ByVal L4 As Int32, ByVal Haatm As Single, ByVal NPRfl As Int16, ByVal NFCCCurve As Int16, ByVal HPRfl() As Single, ByVal Crve() As Single, ByRef FCC_Loss As Single, ByRef Code As Int16, ByRef FS() As Single)
Sub Main()
Dim Read_Flag As Boolean
Dim First_Flag As Boolean
Dim Frequency_MHz As Single
Dim IQT As Int16
Dim Radiation_Opt As String
Dim L1 As Int32
Dim ERP_Watts As Single
Dim T_PowerWatts As Single
Dim Tx_GaindBi As Single
Dim T_Loss As Single
Dim Service As String
Dim L2 As Int32
Dim Reliability As Int16
Dim FCC_CurvesDataPath As String
Dim L3 As Int32
Dim TerrainRoughness As String
Dim L4 As Int32
Dim Haatm As Single
Dim NPRfl As Int16
Dim NFCCCurve As Int16
Dim HPRfl(12) As Single
Dim Crve(5) As Single
Dim FCC_Loss As Single
Dim Code As Int16
Dim FS(15) As Single
'Dim HPRfl As Single
'Dim Crve As Single
'Dim FCC_Loss As Single
'Dim Code As Int16
'Dim FS As Single
Read_Flag = True
First_Flag = True
Frequency_MHz = 98.1
IQT = 0
Radiation_Opt = "ERP"
L1 = 3
ERP_Watts = 1000
T_PowerWatts = 1000
Tx_GaindBi = 2.15
T_Loss = 0
Service = "Broadcast"
L2 = 9
Reliability = 50
FCC_CurvesDataPath = "C:\Program Files\CSPT_Extension\data\"
L3 = 37
TerrainRoughness = "Off"
L4 = 3
Haatm = 1500
NPRfl = 13
NFCCCurve = 6
Dim i As Int16
HPRfl(0) = 11
HPRfl(1) = 128
For i = 2 To 12
HPRfl(i) = 1500
Next
For i = 0 To 5
Crve(i) = i * 15
Next
FCC_Curves(Read_Flag, First_Flag, Frequency_MHz, IQT, Radiation_Opt, L1, ERP_Watts, T_PowerWatts, Tx_GaindBi, T_Loss, Service, L2, Reliability, FCC_CurvesDataPath, L3, TerrainRoughness, L4, Haatm, NPRfl, NFCCCurve, HPRfl, Crve, FCC_Loss, Code, FS)
End Sub
End Module
I have a DLL written in fortran that I know works because I am getting it from a working software package that we use daily. I am trying to call it from anything but am having problems i believe with passing the correctly formatted arguments in and out of it, so i decided to try and access it from a VB program. I chose VB because I have contacted the developer of the software package we are using and he gave me his DLL function call in VB6, even though he is not allowed to show me the FORTRAN source.
Problem is (perhaps, i think) that I am writing in VB.NET, which has different data types than VB6. Can anyone see why i am am having problems.
The error i get upon running is: "AccessViolationException was unhandled: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
Here is the function call i was supplied with:
Declare Sub FCC_Curves Lib "FCC_Curves.dll" (Read_Flag As Boolean, First_Flag As Boolean, Frequency_MHz As Single, IQT As Integer, ByVal Radiation_Opt As String, ByVal L1 As Long, ERP_Watts As Single, T_PowerWatts As Single, Tx_GaindBi As Single, T_Loss As Single, ByVal Service As String, ByVal L2 As Long, Reliability As Integer, ByVal FCC_CurvesDataPath As String, ByVal L3 As Long, ByVal TerrainRoughness As String, ByVal L4 As Long, Haatm As Single, NPRfl As Integer, NFCCCurve As Integer, HPRfl As Single, Crve As Single, FCC_Loss As Single, Code As Integer, FS As Single)
and here is my code:
Module Module1
Declare Sub FCC_Curves Lib "FCC_Curves.dll" (ByVal Read_Flag As Boolean, ByVal First_Flag As Boolean, ByVal Frequency_MHz As Single, ByVal IQT As Int16, ByVal Radiation_Opt As String, ByVal L1 As Int32, ByVal ERP_Watts As Single, ByVal T_PowerWatts As Single, ByVal Tx_GaindBi As Single, ByVal T_Loss As Single, ByVal Service As String, ByVal L2 As Int32, ByVal Reliability As Int16, ByVal FCC_CurvesDataPath As String, ByVal L3 As Int32, ByVal TerrainRoughness As String, ByVal L4 As Int32, ByVal Haatm As Single, ByVal NPRfl As Int16, ByVal NFCCCurve As Int16, ByVal HPRfl() As Single, ByVal Crve() As Single, ByRef FCC_Loss As Single, ByRef Code As Int16, ByRef FS() As Single)
Sub Main()
Dim Read_Flag As Boolean
Dim First_Flag As Boolean
Dim Frequency_MHz As Single
Dim IQT As Int16
Dim Radiation_Opt As String
Dim L1 As Int32
Dim ERP_Watts As Single
Dim T_PowerWatts As Single
Dim Tx_GaindBi As Single
Dim T_Loss As Single
Dim Service As String
Dim L2 As Int32
Dim Reliability As Int16
Dim FCC_CurvesDataPath As String
Dim L3 As Int32
Dim TerrainRoughness As String
Dim L4 As Int32
Dim Haatm As Single
Dim NPRfl As Int16
Dim NFCCCurve As Int16
Dim HPRfl(12) As Single
Dim Crve(5) As Single
Dim FCC_Loss As Single
Dim Code As Int16
Dim FS(15) As Single
'Dim HPRfl As Single
'Dim Crve As Single
'Dim FCC_Loss As Single
'Dim Code As Int16
'Dim FS As Single
Read_Flag = True
First_Flag = True
Frequency_MHz = 98.1
IQT = 0
Radiation_Opt = "ERP"
L1 = 3
ERP_Watts = 1000
T_PowerWatts = 1000
Tx_GaindBi = 2.15
T_Loss = 0
Service = "Broadcast"
L2 = 9
Reliability = 50
FCC_CurvesDataPath = "C:\Program Files\CSPT_Extension\data\"
L3 = 37
TerrainRoughness = "Off"
L4 = 3
Haatm = 1500
NPRfl = 13
NFCCCurve = 6
Dim i As Int16
HPRfl(0) = 11
HPRfl(1) = 128
For i = 2 To 12
HPRfl(i) = 1500
Next
For i = 0 To 5
Crve(i) = i * 15
Next
FCC_Curves(Read_Flag, First_Flag, Frequency_MHz, IQT, Radiation_Opt, L1, ERP_Watts, T_PowerWatts, Tx_GaindBi, T_Loss, Service, L2, Reliability, FCC_CurvesDataPath, L3, TerrainRoughness, L4, Haatm, NPRfl, NFCCCurve, HPRfl, Crve, FCC_Loss, Code, FS)
End Sub
End Module
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我找到了解决方案。在 VB6 中,未另行指定的参数默认以 ByRef 形式传递给函数。在 VB.NET 中,如果在函数声明中将参数留空,它会自动插入 ByVal,因此这似乎是事实上的默认值。 VB.NET 强制您以其他方式进行指定,因此,如果您将函数声明从 VB6 转移到 VB.NET,请注意 VB6 中未标记的默认值是 ByRef。
I found the solution. In VB6 the argument that do not otherwise specify are by default passed to functions as ByRef. In VB.NET if you leave an argument blank in the function declaration, it automatically inserts ByVal, which seems to therefore be the defacto default. VB.NET forces you to specify in other words, so if you are taking a function declaration from VB6 to VB.NET, know that the unmarked default in VB6 is ByRef.
是的,VB.NET 数据类型与 VB6 数据类型不兼容。 VB6 Integer 现在是 Short(又名 Int16)。 VB6 Long 现在是 Integer(又名 Int32)。 VB6 布尔值是一种奇怪的鸭子,现在相当于 Short,其中 True 为 -1,False 为 0。
首先将函数声明中的 Long 更改为 Integer,即 Big One。
Yes, the VB.NET data types are not compatible with the VB6 ones. A VB6 Integer is now a Short (aka Int16). A VB6 Long is now an Integer (aka Int32). A VB6 Boolean was an odd duck, now equivalent to Short where the value True is -1 and False is 0.
Start by changing Long in the function declaration to Integer, that's the Big One.
有时需要在带有字符串参数的外部函数中使用 Auto 关键字。
Sometimes it's necessary to use the Auto keyword in an external function with string parameters.
我想我会发布将参数从 VB.net(.net v5、VS 64 位 v17.0.5)传递到使用 gfortran 7.3.0 创建的 dll(在代码::块中)的经验。该示例展示了传递整数、字节数组以及处理通过字节数组传递的字符串的方法。我不是一个专业程序员,编程对我来说只是一个工具,所以我的“编程白话”可能不正确。注意我在使用 Windows 10(64 位)的 64 位计算机上运行这些代码。
首先,dll 的 fortran 代码:
参数 n 和 j 仅用于演示目的。 的元素数量。字节数组被重建为文本字符串并写入文件。
调用 VB 代码为:
输入参数 j 表示字符串的长度(即 Integer*1(字节)数组 k中 :VB 代码是使用“Any CPU”平台设置编译的。
请注意,数组是“byval”传递的 --- 我注意到,即使当我尝试通过 byref 传递数组时指定了 STDCALL,数组地址的地址也会被推送到堆栈,而不是数组的地址。如果我通过 byval ,数组的地址就会被推送到堆栈,这就是我认为正确的 STDCALL ..???无论如何,传递数组 byval 与传递 byref 具有相同的效果。
至于确定 fortran dll 中的依赖关系,我的解决方案(尽管是蛮力)是将 gfortran 文件夹中的所有 dll 复制到另一台计算机上的文件夹中,无需任何开发工具,并且其中包含 VB 代码所需的文件本身。运行调用感兴趣的 fortran dll 的代码。然后检查所有 dll 的最后访问日期/时间以查看哪些已被访问。如果它没有被访问,那么你就不需要它......这是遍历 Dependency Walker 输出的替代方法。
希望这有助于节省我花在实际运行上的时间。
Thought I'd post my experience passing arguments from VB.net (.net v5, VS 64bit v17.0.5) to dll created with gfortran 7.3.0(in code::blocks). The example shows passing integers, a byte array, and a way to handle a character string passing via the byte array. I'm not a pro-programmer, programming is just a tool for me, so my "programming vernacular" may not be correct. Note I'm running these codes on a 64bit machine using Windows 10 (64bit).
First, the fortran code for the dll:
The arguments n and j are just for demonstration purposes. Argument j is input and represents the length of the string (i.e. the number of elements in the Integer*1 (byte) array k. The byte array is reconstructed as a text string and written to a file.
The calling VB code is:
Note: VB code was compiled using the "Any CPU" platform setting.
Note the array is passed "byval" --- I noticed even though I specified STDCALL when I tried to pass an array byref the address of the address of the array was pushed to the stack rather than the address of the array. If I pass byval the address of the array is pushed to the stack which is what I thought was proper STDCALL..??? Anyway passing an array byval has the same effect as passing byref.
As to determining the dependencies in the fortran dll -- my solution, albeit brute force, is to copy ALL the dlls within the gfortran folders to a folder on another machine without any of the development tools and which contains the necessary files for the VB code itself. Run the code that calls the fortran dll of interest. Then examine the last access date/time of the all the dll's to see which have been accessed. If it wasn't accessed then you don't need it...this is an alternative to wading through the output of Dependency Walker.
Hope this helps save someone the amount of time I spent running this to ground.