百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

Delphi7通过VB6之COM对象调用PowerBASIC写的DLL功能

csdh11 2025-03-14 15:56 1 浏览

Delphi7通过VB6之COM对象调用PowerBASIC写的DLL功能。标题挺长,其实目标很简单,就是在Delphi7中使用PowerBASIC的MKI/CVI, MKS/CVS, MKD/CVD,并顺便加入CRC16检验函数,再进行16进制高低字节调整,方便在VB6、Delphi、Lazarus等环境下利用Modbus协议传送指令和数据时,进行十进制数的浮点转换和数据接收校验。我写的只是一个方法,其实用算法实现也并不十分复杂,但总觉得应该让曾经精典的老古懂们能做点事情。

分三步走:

用PowerBASIC写基本DLL

用VB6写COM组件

用Delphi7写界面验证程序

一、用PowerBASIC写基本DLL

PowerBASIC兼容VB6最好,甚至许多功能完胜VB6,而且QBASIC有的功能它基本上都保留了,只是随着VB6的淡出而停止了前行。如果用现在语言的功能衡量它们,它们确实老了,但在工控领域里还是有许多用武之地的,比如工厂一般使用的总线方面,Modbus在国内比较普及,即使有了TCP也只是从Modbus ASCII或Modbus RTU变成了Modbus TCP,所以小而精的东西在这方面比大而复杂的东西更受青睐。PowerBASIC写DLL很简单,DLL入口出口不用管,写自己的功能函数并EXPORT即可。

下面的MBFIEEE32PD.BAS是用PowerBASIC写的

'MBFIEEE32PD.BAS
'===============================================================================
'
'  Generic DLL Template for PowerBASIC for Windows
'  Copyright (c) 1997-2011 PowerBASIC, Inc.
'  All Rights Reserved.
'
'  LIBMAIN function Purpose:
'
'    User-defined function called by Windows each time a DLL is loaded into,
'    and unloaded from, memory. In 32-bit Windows, LibMain is called each
'    time a DLL is loaded by an application or process.  Your code should
'    never call LibMain explicitly.
'
'    hInstance is the DLL instance handle.  This handle is used by the
'    calling application to identify the DLL being called.  To access
'    resources in the DLL, this handle will need to be stored in a global
'    variable.  Use the GetModuleHandle(BYVAL 0&) to get the instance
'    handle of the calling EXE.
'
'    fdwReason specifies a flag indicating why the DLL entry-point
'    (LibMain) is being called by Windows.
'
'    lpvReserved specifies further aspects of the DLL initialization
'    and cleanup.  If fdwReason is %DLL_PROCESS_ATTACH, lpvReserved is
'    NULL (zero) for dynamic loads and non-NULL for static loads.  If
'    fdwReason is %DLL_PROCESS_DETACH, lpvReserved is NULL if LibMain
'    has been called by using the FreeLibrary API call and non-NULL if
'    LibMain has been called during process termination.
'
' Return
'
'    If LibMain is called with %DLL_PROCESS_ATTACH, your LibMain function
'    should return a zero (0) if any part of your initialization process
'    fails or a one (1) if no errors were encountered.  If a zero is
'    returned, Windows will abort and unload the DLL from memory. When
'    LibMain is called with any other value than %DLL_PROCESS_ATTACH, the
'    return value is ignored.
'
'===============================================================================
 
#COMPILER PBWIN 10
#COMPILE DLL
 
#INCLUDE ONCE "Win32api.inc"
 
GLOBAL ghInstance AS DWORD
 
 
'-------------------------------------------------------------------------------
' Main DLL entry point called by Windows...
'
FUNCTION LIBMAIN (BYVAL hInstance   AS LONG, _
                  BYVAL fwdReason   AS LONG, _
                  BYVAL lpvReserved AS LONG) AS LONG
 
    SELECT CASE fwdReason
 
    CASE %DLL_PROCESS_ATTACH
        'Indicates that the DLL is being loaded by another process (a DLL
        'or EXE is loading the DLL).  DLLs can use this opportunity to
        'initialize any instance or global data, such as arrays.
 
        ghInstance = hInstance
 
        FUNCTION = 1   'success!
 
        'FUNCTION = 0   'failure!  This will prevent the EXE from running.
 
    CASE %DLL_PROCESS_DETACH
        'Indicates that the DLL is being unloaded or detached from the
        'calling application.  DLLs can take this opportunity to clean
        'up all resources for all threads attached and known to the DLL.
 
        FUNCTION = 1   'success!
 
        'FUNCTION = 0   'failure!
 
    CASE %DLL_THREAD_ATTACH
        'Indicates that the DLL is being loaded by a new thread in the
        'calling application.  DLLs can use this opportunity to
        'initialize any thread local storage (TLS).
 
        FUNCTION = 1   'success!
 
        'FUNCTION = 0   'failure!
 
    CASE %DLL_THREAD_DETACH
        'Indicates that the thread is exiting cleanly.  If the DLL has
        'allocated any thread local storage, it should be released.
 
        FUNCTION = 1   'success!
 
        'FUNCTION = 0   'failure!
 
    END SELECT
 
END FUNCTION
 
 
FUNCTION myMKI ALIAS "myMKI" (BYVAL Param1 AS INTEGER) EXPORT AS STRING
    DIM I AS INTEGER
    DIM TString AS STRING
 
    I=0: TString=""
 
    ' code goes here
    FOR I = 2 TO 1 STEP -1
        TString=TString+RIGHT$(("0"+LTRIM$(HEX$(ASC(MID$(MKI$(Param1),I,1))))),2)
    NEXT I
    FUNCTION = TString
END FUNCTION
 
FUNCTION myCVI ALIAS "myCVI" (BYVAL Param1 AS STRING) EXPORT AS INTEGER
    DIM I AS INTEGER
    DIM TString AS STRING
 
    I=0: TString=""
 
    ' code goes here
    FOR I = 3 TO 1 STEP -2
        TString=TString+CHR$(VAL("&H"+MID$(Param1,I,2)))
    NEXT I
    FUNCTION = CVI(TString)
END FUNCTION
 
FUNCTION myMKL ALIAS "myMKL" (BYVAL Param1 AS LONG) EXPORT AS STRING
    DIM I AS INTEGER
    DIM TString AS STRING
 
    I=0: TString=""
 
    ' code goes here
    FOR I = 4 TO 1 STEP -1
        TString=TString+RIGHT$(("0"+LTRIM$(HEX$(ASC(MID$(MKL$(Param1),I,1))))),2)
    NEXT I
    FUNCTION = TString
END FUNCTION
 
FUNCTION myCVL ALIAS "myCVL" (BYVAL Param1 AS STRING) EXPORT AS LONG
    DIM I AS INTEGER
    DIM TString AS STRING
 
    I=0: TString=""
 
    ' code goes here
    FOR I = 7 TO 1 STEP -2
        TString=TString+CHR$(VAL("&H"+MID$(Param1,I,2)))
    NEXT I
    FUNCTION = CVL(TString)
END FUNCTION
 
FUNCTION myMKS ALIAS "myMKS" (BYVAL Param1 AS SINGLE) EXPORT AS STRING
    DIM I AS INTEGER
    DIM TString AS STRING
 
    I=0: TString=""
 
    ' code goes here
    FOR I = 4 TO 1 STEP -1
        TString=TString+RIGHT$(("0"+LTRIM$(HEX$(ASC(MID$(MKS$(Param1),I,1))))),2)
    NEXT I
    FUNCTION = TString
END FUNCTION
 
FUNCTION myCVS ALIAS "myCVS" (BYVAL Param1 AS STRING) EXPORT AS SINGLE
    DIM I AS INTEGER
    DIM TString AS STRING
 
    I=0: TString=""
 
    ' code goes here
    FOR I = 7 TO 1 STEP -2
        TString=TString+CHR$(VAL("&H"+MID$(Param1,I,2)))
    NEXT I
    FUNCTION = CVS(TString)
END FUNCTION
 
FUNCTION myMKD ALIAS "myMKD" (BYVAL Param2 AS DOUBLE) EXPORT AS STRING
    DIM I AS INTEGER
    DIM TString AS STRING
 
    I=0: TString=""
 
    ' code goes here
    FOR I = 8 TO 1 STEP -1
        TString=TString+RIGHT$(("0"+LTRIM$(HEX$(ASC(MID$(MKD$(Param2),I,1))))),2)
    NEXT I
    FUNCTION = TString
END FUNCTION
 
FUNCTION myCVD ALIAS "myCVD" (BYVAL Param1 AS STRING) EXPORT AS DOUBLE
    DIM I AS INTEGER
    DIM TString AS STRING
 
    I=0: TString=""
 
    ' code goes here
    FOR I = 15 TO 1 STEP -2
        TString=TString+CHR$(VAL("&H"+MID$(Param1,I,2)))
    NEXT I
    FUNCTION = CVD(TString)
END FUNCTION
 
FUNCTION myCRC16 ALIAS "myCRC16" (BYVAL Param1 AS STRING) EXPORT AS STRING
    'An input string converted to a 4-byte HEX string
    DIM DataA() AS BYTE
    DIM CRC16Lo AS BYTE, CRC16Hi AS BYTE        'CRC寄存器
    DIM CL     AS BYTE, CH       AS BYTE                  '多项式码&HA001
    DIM SaveHi     AS BYTE, SaveLo       AS BYTE
    DIM I     AS INTEGER
    DIM Flag     AS INTEGER
    DIM strMsg AS STRING
    DIM intLen AS INTEGER
    strMsg = Param1
    REPLACE " " WITH "" IN StrMsg
    intLen = LEN(strMsg) / 2 - 1
    REDIM DataA(0 TO intLen) AS BYTE
    FOR I = 0 TO intLen
        DataA(I) = VAL("&H" & MID$(strMsg, I * 2 + 1, 2))
    NEXT
    CRC16Lo = &HFF
    CRC16Hi = &HFF
    CL = &H1
    CH = &HA0
    FOR I = 0 TO UBOUND(DataA, 1)
        CRC16Lo = CRC16Lo XOR DataA(I)
        FOR Flag = 0 TO 7
            SaveHi = CRC16Hi
            SaveLo = CRC16Lo
            'CRC16Hi = CRC16Hi \ 2
            SHIFT RIGHT CRC16Hi, 1
            'CRC16Lo = CRC16Lo \ 2
            SHIFT RIGHT CRC16Lo, 1
            IF ((SaveHi AND &H1) = &H1) THEN
                CRC16Lo = CRC16Lo OR &H80
            END IF
            IF ((SaveLo AND &H1) = &H1) THEN
                CRC16Hi = CRC16Hi XOR CH
                CRC16Lo = CRC16Lo XOR CL
            END IF
        NEXT Flag
    NEXT
    ERASE DataA
    FUNCTION = RIGHT$("0" & HEX$(CRC16Lo), 2) & RIGHT$("0" & HEX$(CRC16Hi), 2)
END FUNCTION
 
FUNCTION myINSTRU ALIAS "myINSTRU" (BYVAL Param1 AS STRING) EXPORT AS STRING
    DIM LParam1 AS STRING
    DIM RETURNSTR AS STRING
 
    RETURNSTR = "UNKNOWN"
 
    LParam1 = TRIM$(Param1)
    SELECT CASE LParam1
        CASE "VERSION"
             RETURNSTR = "VERSION 1.00 9AUG2023"
        CASE "AUTHOR"
             RETURNSTR = "Mongnewer"
    END SELECT
    FUNCTION = RETURNSTR
END FUNCTION

不难看出,MKI/CVI MKS/CVS MKD/CVD这些函数在PowerBASIC里是保留的关键字,CRC16计算是我从CSDN上载了贴上去的,在这里感谢那位CSDN朋友的贡献。Modbus RTU一般使用十六进制浮点传送,因此程序里做了变换处理。

二、用VB6写COM组件

用VB6调用刚才编译后的MBFIEEE32PD.DLL非常容易,不需要做任何字符串处理,两者是100%一致的。做声明定义时完全按VB6的原则来即可,PowerBASIC是无条件遵从的。如果是写VB6应用程序,直接调用DLL中的函数,直接应用就可以了,这里路过就不多说了,还是接着往下写COM组件。

Private toSingle As Single
Private toDouble As Double
 
Private Declare Function myMKI Lib "MBFIEEE32PD" (ByVal a As Integer) As String
Private Declare Function myCVI Lib "MBFIEEE32PD" (ByVal b As String) As Integer
Private Declare Function myMKL Lib "MBFIEEE32PD" (ByVal a As Long) As String
Private Declare Function myCVL Lib "MBFIEEE32PD" (ByVal b As String) As Long
Private Declare Function myMKS Lib "MBFIEEE32PD" (ByVal a As Single) As String
Private Declare Function myCVS Lib "MBFIEEE32PD" (ByVal b As String) As Single
Private Declare Function myMKD Lib "MBFIEEE32PD" (ByVal a As Double) As String
Private Declare Function myCVD Lib "MBFIEEE32PD" (ByVal b As String) As Double
Private Declare Function myCRC16 Lib "MBFIEEE32PD" (ByVal a As String) As String
Private Declare Function myINSTRU Lib "MBFIEEE32PD" (ByVal a As String) As String
 
Public Function ModbusRoutines(ByVal commandno As Integer, ByVal commandval As String) As String
    Select Case commandno
    Case 1
        'MKI
        ModbusRoutines = setMKI(Val(commandval))
    Case 2
        'MKL
        ModbusRoutines = setMKL(Val(commandval))
    Case 3
        'MKS
        ModbusRoutines = setMKS(Val(commandval))
    Case 4
        'MKD
        ModbusRoutines = setMKD(Val(commandval))
    Case 5
        'CVI
        ModbusRoutines = Str$(getCVI(commandval))
    Case 6
        'CVL
        ModbusRoutines = Str$(getCVL(commandval))
    Case 7
        'CVS
        toSingle = getCVS(commandval)
        toDouble = toSingle
        ModbusRoutines = Str$(toDouble)
    Case 8
        'CVD
        ModbusRoutines = Str$(getCVD(commandval))
    Case 9
        'CRC16
        ModbusRoutines = getCRC16(commandval)
    Case 10
        'Version
        ModbusRoutines = getINSTRU(commandval)
    End Select
End Function
Private Function setMKI(ByVal a As Integer) As String
    M2I3HiddenWND.Text1.Text = myMKI(a)
    setMKI = M2I3HiddenWND.Text1.Text
End Function
Private Function getCVI(ByVal a As String) As Integer
    M2I3HiddenWND.Text2.Text = a
    getCVI = myCVI(M2I3HiddenWND.Text2.Text)
End Function
Private Function setMKL(ByVal a As Long) As String
    M2I3HiddenWND.Text3.Text = myMKL(a)
    setMKL = M2I3HiddenWND.Text3.Text
End Function
Private Function getCVL(ByVal a As String) As Long
    M2I3HiddenWND.Text4.Text = a
    getCVL = myCVL(M2I3HiddenWND.Text4.Text)
End Function
Private Function setMKS(ByVal a As Single) As String
    M2I3HiddenWND.Text5.Text = myMKS(a)
    setMKS = M2I3HiddenWND.Text5.Text
End Function
Private Function getCVS(ByVal a As String) As Single
    M2I3HiddenWND.Text6.Text = a
    getCVS = myCVS(M2I3HiddenWND.Text6.Text)
End Function
Private Function setMKD(ByVal a As Double) As String
    M2I3HiddenWND.Text7.Text = myMKD(a)
    setMKD = M2I3HiddenWND.Text7.Text
End Function
Private Function getCVD(ByVal a As String) As Double
    M2I3HiddenWND.Text8.Text = a
    getCVD = myCVD(M2I3HiddenWND.Text8.Text)
End Function
Private Function getCRC16(ByVal a As String) As String
    getCRC16 = myCRC16(a)
End Function
Private Function getINSTRU(ByVal a As String) As String
    getINSTRU = myINSTRU(a)
End Function

打开VB6,选Active X,把上面的码贴进去,添加个无边的小窗体,放上Text1到Text7共7个文本框,Form的名字 M2I3HiddenWND,属性是 Hidden 隐藏的。文件名 MBFMODIEEE,类名 MBFIEEECRC,存盘、生成 MBFMODIEEE.DLL,即为其它开发环境使用的COM了。

加这个Hidden窗口是这么想的,VB6和PowerBASIC变量和字符串完全兼容,但Delphi7就不一定了,尤其是字符串存储方式的转换。从Delphi来的字符串显示在VB6的文本框可以,但直接传送给PowerBASIC或许有问题,于是就想让文本框做个过渡,或许直接传也不是问题,我没做验证。

因为这个DLL是COM,需要将 MBFMODIEEE.DLL和MBFIEEE32PD.DLL放在同一目录下,并在目录中放入Delphi7应用程序。为了让程序能互访,在CMD窗口里,转到它们所在的目录下,用regsvr32将MBFMODIEEE.DLL注册到系统中。regsvr32 MBFMODIEEE.DLL 回车即可。

三、用Delphi7写界面验证程序

在Delphi下引用刚才注册的MBFMODIEEE.DLL

在弹出的列表中选中刚才注册的MBFMODIEEE,并点击 Create Unit生成 MBFMODIEEE_TLB声明文件,刚才注册的DLL中要调用的类和接口就都有了。

在USE中引用生成的PAS,然后为接口声明个handle

在Form产生时创建对象

然后在需要的地方就可以通过接口使用对象中的功能函数了

然后就是正常的开发应用程序,编译后运行(有时开发环境下可能出现异常,但编译后运行是比较好的方法。都是老顽固,稳定可靠,但要就着它们的性子,不能太勉强了)。

BTW:这些功能除PowerBASIC外,FreeBASIC里更齐全,甚至包括了QBASIC的全部关键字,但它的字符串不同于VB和Delphi,需要另外处理。不过它可以写COM,除32位编译器,它还有64位编译器。

相关推荐

PromptDA:4K分辨率精准深度估计!(分辨率4k是多少p)

这里是FoxFeed,一个专注于科技的内容平台。背景介绍在计算机视觉领域,深度估计一直是一个重要的研究方向。近日,由DepthAnything团队开发的...

m4a怎么转换成mp3?教你这样转换音频格式

m4a怎么转换成mp3?M4A是MPEG-4音频标准的文件的扩展名,它可以存储各种类型的音频内容,运用比较广泛,尽管m4a被很多媒体应用兼容,但仍有很多应用无法打开它,将m4a转换成mp3就是一个很不...

“讲述初心故事 传递使命情怀”2019第五届江苏医院微电影节启动

“讲述初心故事传递使命情怀”,2019第五届江苏医院微电影节9月16日启动。江苏医院微电影节由新华网江苏有限公司和江苏省医院协会联合举办,扬子江药业集团协办,秉承“讲述初心故事传递使命情怀”为活动...

短视频宝贝=慢?阿里巴巴工程师这样秒开短视频

前言随着短视频兴起,各大APP中短视频随处可见,feeds流、详情页等等。怎样让用户有一个好的视频观看体验显得越来越重要了。大部分feeds里面滑动观看视频的时候,有明显的等待感,体验不是很好。针对这...

阿里巴巴工程师这样秒开短视频(阿里巴巴的工程师多少钱一个月)

前言随着短视频兴起,各大APP中短视频随处可见,feeds流、详情页等等。怎样让用户有一个好的视频观看体验显得越来越重要了。大部分feeds里面滑动观看视频的时候,有明显的等待感,体验不是很好。针对这...

旗鱼浏览器1.0 RC正式版候选版:增账户同步等

从9月19日发布第一个Beta版至今,约80天的时间便这么飞走了,作为2015年底的一个答卷,今天旗鱼浏览器1.0RC(正式版候选版)发布,如果没有意外,明天我们将发布电脑版和安卓版的第一个1.0正...

5种方法,教你将m3u8转换为mp4格式

m3u8格式在许播放器中不受支持,只能在浏览器中进行在线观看,然而,在线观看可能会不大方便,如果网络卡顿的话就会影响观感。想要将...

kgma格式怎么转换为mp3?试试这5种简单的音频转换方法!

由于kgma格式的特殊性和平台限制,除了专属的音乐平台外,其他设备和网络平台是无法识别或播放kgma格式的音乐的,因此为了方便使用,我们就必须将kgma格式转换为mp3。接下来,小编就为大家推荐5种简...

500+本程序员值得看的书籍,7大类,1大合集,收藏,日后有用

一、Golang书籍推荐入门《Go入门指南》...

教你编写最简单的CM3操作系统,160行实现任务创建与切换

如题,任务创建与上下文切换是跟硬件息息相关的,而这恰恰是RTOS编写的最难点,抛开这些功能,剩下的就是双向链表增删改操作了,本例用最精简的方式实现了任务创建与切换,OS启动等功能,并运用了Cortex...

Hot 3D 人体姿态估计 HPE Demo复现过程

视频讲解...

各编程语言相互调用示例,代码简单,生成的软件体积也很小

aardio支持混入很多不同的编程语言,代码简单,生成的软件体积也很小。下面看示例。...

你知道shell脚本中$0 $1 $# $@ $* $? $$ 都是什么意思吗?

一、概述shell中有两类字符:普通字符、元字符。1.普通字符...

NDK打印调用堆栈(logger.error打印堆栈信息)

虽然android源码里有android::CallStack用来打印堆栈,但是NDK里面并没有包含它,所以不能直接调用它,所以要尝试用动态调用的方式来实现。我测试的手机是安卓8.1.0版本,...

小白都能看得懂的Cgo入门教程(cgo2.0教程)

在Go语言开发过程中,尽管Go本身功能强大,但仍然有许多C语言库可以复用,如操作系统API、高性能计算库、数据库驱动等。Go提供了一种强大的机制——Cgo,让我们可以在Go代码中调用C...