如何使用Java调用上古语言VB中的代码?

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

下面来做一件没有用的事情。

由于对VB还情有独钟,所以想到能不能用Java调用VB的代码?

但事实上,非常麻烦,但也不是不可能,原因有以下几点:

  1. VB无法生成标准DLL,他虽然可以制作DLL,但是无法导出函数,其他程序也就无法调用,只有VB本身可以调用,但这个其实是可以解决的,只不过是使用一种非官方手法。

  2. 64位JVM无法加载32位DLL,而VB又只能生成32位DLL,所以,在未找到解决方法前,我们只能通过更改JVM位数来解决。

  3. Java代码不能直接调用VB中的代码,需要做中转,也就是使用JNI,然后在C代码中通过LoadLibraryEx来加载VB生成的DLL,并且调用GetProcAddress来找到函数地址,就可以调用了。

先来解决第一个问题。

下面代码用来更改链接参数,把这个代码放入VB的模块中,移除默认的窗口,并且设置主入口为Main。

Public Sub Main()
Dim SpecialLink As Boolean, fCPL As Boolean, fResource _
As Boolean
Dim intPos As Integer
Dim strCmd As String
Dim strPath As String
Dim strFileContents As String
Dim strDefFile As String, strResFile As String
Dim oFS As New Scripting.FileSystemObject
Dim fld As Folder
Dim fil As File
Dim ts As TextStream, tsDef As TextStream

strCmd = Command

intPos = InStr(1, strCmd, ".OBJ", vbTextCompare)
strPath = Mid(strCmd, 2, intPos + 2)
intPos = InStrRev(strPath, "")
strPath = Left(strPath, intPos - 1)

Set fld = oFS.GetFolder(strPath)


For Each fil In fld.Files
If UCase(oFS.GetExtensionName(fil)) = "DEF" Then
strDefFile = fil
SpecialLink = True
End If
If UCase(oFS.GetExtensionName(fil)) = "RES" Then
strResFile = fil
fResource = True
End If
If SpecialLink And fResource Then Exit For
Next

If SpecialLink Then

Set tsDef = oFS.OpenTextFile(strDefFile)
strFileContents = tsDef.ReadAll
If InStr(1, strFileContents, "CplApplet", _
vbTextCompare) > 0 Then
fCPL = True
End If

intPos = InStr(1, strCmd, "/DLL", vbTextCompare)
If intPos > 0 Then
strCmd = Left(strCmd, intPos - 1) & _
" /DEF:" & Chr(34) & strDefFile & Chr(34) & _
" " & _
Mid(strCmd, intPos)
End If

If fResource Then
intPos = InStr(1, strCmd, "/ENTRY", vbTextCompare)
strCmd = Left(strCmd, intPos - 1) & Chr(34) & _
strResFile & _
Chr(34) & " " & Mid(strCmd, intPos)
End If

If fCPL Then
strCmd = Replace(strCmd, ".dll", ".cpl", 1, , _
vbTextCompare)
End If

End If


Shell "linklnk.exe " & strCmd
If Err.Number <> 0 Then
Err.Clear
End If

End Sub

之后生成exe,把这个exe重复名为link.exe,

在进入VB98目录,位于安装目录下,会发现也有个link.exe,把这个link重复名为linklnk.exe,并且把我们重复名后的link.exe放入到这里,这个工作就是在VB真正link目标前,做一些手脚,最后把新的参数传递给VB本身的link,达到函数导出的目的。

下面就是新建dll了,

创建一个新工程,添加一个模块,注意不是类模块,否则会无法导出函数。

Public Const DLL_PROCESS_DETACH = 0
Public Const DLL_PROCESS_ATTACH = 1
Public Const DLL_THREAD_ATTACH = 2
Public Const DLL_THREAD_DETACH = 3

Public Function DllMain(hInst As Long, fdwReason As Long, _
lpvReserved As Long) As Boolean
Select Case fdwReason
Case DLL_PROCESS_DETACH

Case DLL_PROCESS_ATTACH
DllMain = True
Case DLL_THREAD_ATTACH

Case DLL_THREAD_DETACH

End Select
End Function

' umber.
Public Function test(ByVal N As Long) As Long
test = N
End Function

之后在你要输出的目录下,创建一个test.def文件,内容如下。

NAME MathLib
LIBRARY MathMod
DESCRIPTION "Add-on Library of Mathematical Routines"
EXPORTS DllMain @1
test @2

这时候就可以直接生成dll了,生成后可以用工具查看导出的函数。

如何使用Java调用上古语言VB中的代码?

下面就是Java中调用了,首先通过javah生成头文件,具体就不说了。

package com.company;

public class Main {
    public native int doWork(int value);

    static{
        System.load("F:\project\vs\Win32Project3\Release\Win32Project3.dll");
    }
    public static void main(String[] args) {
        System.out.println(new Main().doWork(23));
    }
}

下面是这个native的具体实现。

#include "com_company_Main.h"
#include <stdio.h>
#include <windows.h>
int CallVbFunction(jint value) {
 HMODULE hModule = LoadLibraryEx(TEXT("F:\temp\test.dll"), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
 FARPROC fProc = GetProcAddress(hModule, "test");
 return  fProc(value);
}

JNIEXPORT jint JNICALL Java_com_company_Main_doWork
(JNIEnv * env, jobject obj, jint value) 
{
 CallVbFunction(value);
}

再回到java中,就可以看到输出的结果。

如何使用Java调用上古语言VB中的代码?

- END -


原文始发于微信公众号(十四个字节):如何使用Java调用上古语言VB中的代码?