1

我有一个用 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
4

4 回答 4

3

我找到了解决方案。在 VB6 中,未指定的参数默认作为 ByRef 传递给函数。在 VB.NET 中,如果在函数声明中将参数留空,它会自动插入 ByVal,因此这似乎是事实上的默认值。换句话说,VB.NET 强制您指定,因此如果您将函数声明从 VB6 带到 VB.NET,请知道 VB6 中未标记的默认值是 ByRef。

于 2010-02-01T20:09:12.193 回答
1

是的,VB.NET 数据类型与 VB6 的数据类型不兼容。VB6 整数现在是 Short(又名 Int16)。VB6 Long 现在是一个整数(又名 Int32)。VB6 布尔值是一个奇怪的鸭子,现在等效于 Short,其中 True 为 -1,False 为 0。

首先将函数声明中的 Long 更改为 Integer,这就是 Big One。

于 2010-02-01T16:37:19.680 回答
0

有时需要在带有字符串参数的外部函数中使用 Auto 关键字。

Declare Auto Sub FCC_Curves Lib...
于 2010-02-02T02:53:03.557 回答
0

以为我会将我的经验从 VB.net(.net v5,VS 64bit v17.0.5)传递到使用 gfortran 7.3.0(在 code::blocks 中)创建的 dll 中。该示例显示了传递整数、字节数组以及处理通过字节数组传递的字符串的方法。我不是亲程序员,编程对我来说只是一个工具,所以我的“编程白话”可能不正确。注意我在使用 Windows 10(64 位)的 64 位机器上运行这些代码。
首先,dll的fortran代码:

  subroutine intmul(i,j,k,n)
!GCC$ ATTRIBUTES STDCALL, DLLEXPORT :: intmul
      integer, intent(inout) :: i,j,n
      integer*1, intent(inout) :: k(j)
      character test*100
      test=''
      do 1 m=1,j
      test(m:m)=char(k(m))
    1 continue
      n=22
      i=16
      open (unit=10,file='testchr.txt',status='unknown',
     cform='formatted')
      write (10,2) trim(test)
    2 format (a)
      close (unit=10)

      end subroutine

参数 n 和 j 仅用于演示目的。输入参数j,表示字符串的长度(即Integer*1(字节)数组k中的元素个数。字节数组被重构为文本字符串并写入文件。

调用VB代码为:

    Module Module0
        '
        ' test passing integers and character string via byte array
        'j=length of character string (i.e. n will have j elements 0 to j-1)
        'i and kk are just some dummy test arguments
        Declare Auto Sub intmul Lib "D:\test\vb-fortan\dlls\intandarraychr\bin\Debug\fd1.dll" Alias _
          "intmul_" (ByRef i As Int32, ByRef j As Int32, ByVal n() As Byte, ByRef kk As Int32)
    End Module

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        End
    End Sub

        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            Dim i, j, k As Int32
            Dim m(100) As Byte
            Dim testtext As String
            testtext = TextBox4.Text
            i = TextBox3.Text
            j = testtext.Length   'length of string ==> number of elements in byte array to pass
            k = TextBox5.Text
            '  Convert unicode text (2 bytes/character) to byte array with each element
            'of the array containing the code for each character
            m = System.Text.Encoding.ASCII.GetBytes(testtext)
    
            MsgBox("proceed")
            Call intmul(i, j, m, k)
            TextBox3.Text = i
            TextBox5.Text = k
        End Sub
   
    End Class

注意:VB 代码是使用“Any CPU”平台设置编译的。请注意,数组是通过“byval”传递的——我注意到,即使我在尝试通过引用传递数组时指定了 STDCALL,但数组地址的地址被推送到堆栈而不是数组的地址。如果我通过 byval 数组的地址被推送到堆栈,这是我认为正确的 STDCALL ..??? 无论如何,传递数组 byval 与传递 byref 具有相同的效果。

至于确定 fortran dll 中的依赖关系——我的解决方案,尽管是蛮力的,是在没有任何开发工具的情况下将 gfortran 文件夹中的所有 dll 复制到另一台机器上的文件夹中,其中包含 VB 代码的必要文件本身。运行调用感兴趣的 fortran dll 的代码。然后检查所有 dll 的最后访问日期/时间,以查看哪些已被访问。如果它没有被访问,那么你就不需要它……这是一个替代 Dependency Walker 输出的替代方法。希望这有助于节省我花在运行它上的时间。

于 2022-02-07T18:27:44.857 回答