要在浏览器中调用本地DLL,常见的方法是使用插件。但是为了安全,现在有的浏览器对插件开发做了限制,不让插件调用外部DLL。比如说Chrome,为了调用外部的DLL,我们只能使用早期的chrome版本。
还有一种方法就是在电脑上安装一个助手程序,浏览器通过HTTP GET请求调用助手程序,助手程序再调用本地DLL,并把调用结果反馈到浏览器中。今天我们就来讨论这种方法。
首先助手程序应该是一个WEB服务器,因为只有WEB服务器才能处理浏览器发过来的HTTP GET请求。
第二个问题:浏览器怎么发送HTTP GET请求?答案是JavaScript。如果您熟悉JQuery, 那么让浏览器发送HTTP GET请求只需要几行代码:
$.get("http://localhost/",function(data,status){
alert("数据: " + data + "\n状态: " + status);
});
如果您不了解JQuery,使用的代码会多一些,如下所示:
//创建XMLHttpRequest对象
var httpRequest
try{ // Firefox, Opera 8.0+, Safari
httpRequest=new XMLHttpRequest();
}
catch (e){
try{// Internet Explorer
httpRequest=new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e){
try{
httpRequest=new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e){}
}
}
httpRequest.open('GET',
'http://localhost/',
true);
httpRequest.send();//发送请求
// 获取数据后的处理程序
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
var str = httpRequest.responseText;//获取到助手程序返回的字符串
alert(str);
}
};
因为浏览器和助手程序都在同一台电脑上,所以代码中HTTP GET的url就是http://localhost
第三个问题:其实浏览器不仅可以用HTTP GET请求调用助手程序,还可以使用HTTP POST,WebSocket协议。
下面我们来看一看流程图:
流程图很简单。这里最麻烦的地方就在于我们要自己写助手程序,还要安装在每一台电脑上。所以我觉得这个方法比较适合企业内部信息管理系统。助手程序可以用来在网页中操作控制本地的读卡器、打印机、扫描仪、高拍仪、U盾等各种硬件设备。
好,原理部分讲完了,如果您看不太懂,问题也不大,现在我们写几个助手程序的实例,估计写完实例大家就会理解了。每个人会的编程语言是不同的,所以我们写的实例,有JAVA版,PHP版和C++版。
所有实例都提供了源代码,源代码放在github上:https://github.com/Dengxd/SimpleDllHelper
DllHelperCpp目录是C++版
DllHelperJava目录是JAVA版
DllHelperPhp目录是PHP版,comATL目录是C++写的com组件,专门给PHP版使用的
TestDll目录是测试用的DLL
test.html文件是浏览器使用的网页JavaScript代码
编写一个测试用的DLL
既然要调用DLL,我们就先写一个测试用的DLL,JAVA和PHP写不了DLL,所以只能用C++
我的开发环境:
Windows 7 旗舰版 SP1 x64
Microsoft Visual Studio Community 2022 (64 位)
主要代码解读:
主要代码就一个函数
__declspec(dllexport) int __stdcall sum(int a, int b)
{
return a + b;
}
这是个求和函数,两个输入参数a和b, 返回a+b
我们的助手程序要调用的就是这个函数
这个项目可以编译出x64和Win32(x86)版本的DLL。Visual Studio自动生成的项目也是奇葩,在主界面上Win32程序叫做x86:
但是到了项目属性配置页面,又变成了Win32:
很容易把初学者搞得无所适从。其实在这两个地方x86和Win32是一回事。一般我们都是编译成Release版本的,如果要编译成64位的,就在主界面选择x64,如果要编译成32位的,就在主界面选择x86
如果您不会C++和Microsoft Visual Studio, 不知道怎么编译出DLL, 源码里面提供了编译好的DLL,Release x64版的TestDll.dll在文件夹TestDll\x64\Release中,Debug x64版的TestDll.dll在文件夹TestDll\x64\Debug中, Release Win32版的TestDll.dll在文件夹TestDll\Release中,Debug Win32版的TestDll.dll在文件夹TestDll\Debug中,一般情况下我们会使用Release版本的DLL,如果操作系统是64位的,最好使用x64版,如果操作系统是32位的,只能使用Win32版。如果您手痒很想自己编译的话,可以参考一下这篇文章:https://www.freesion.com/article/6443438938/ 。声明一下,这篇文章不是我写的,只是我搜索到的文章。
编写JavaScript代码
我们建立一个文件,文件名text.html, 内容如下:
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
</head>
<body>
<form action="" enctype="application/x-www-form-urlencoded">
<input type="button" name="ok" id="ok" value="HTTP GET">
</form>
</body>
<script type="text/javascript" >
window.onload = function () {
document.getElementById("ok").onclick = function () {
//创建XMLHttpRequest对象
var httpRequest
try{ // Firefox, Opera 8.0+, Safari
httpRequest=new XMLHttpRequest();
}
catch (e){
try{// Internet Explorer
httpRequest=new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e){
try{
httpRequest=new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e){}
}
}
httpRequest.open('GET',
//'http://localhost/cgi-bin/dllhelpercpp.cgi',
'http://localhost/',
true);
httpRequest.send();//发送请求
// 获取数据后的处理程序
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
var str = httpRequest.responseText;//获取到助手程序返回的字符串
alert(str);
}
};
}
}
</script>
</html>
这代码熟悉JavaScript的同学都能看得懂,我就不解释了。
我们用浏览器打开这个文件,会显示一个按钮“HTTP GET”,点击这个按钮,浏览器就会向助手程序发送HTTP GET请求
编写JAVA版的助手程序
我的开发环境:
Windows 7 旗舰版 SP1 x64
IntelliJ IDEA 2021.2.1(Ultimate Edition)
java version "1.8.0_121" Java(TM) SE Runtime Environment (build 1.8.0_121-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
我们使用JNA来调用DLL,关于JNA,网络上文章很多了,大家可以自己去搜一搜,我就不献丑了。大家可以参考一下这篇文章:https://www.freesion.com/article/6443438938/
前面我们说过,助手程序应该是一个WEB服务器,所以我们就使用spring boot框架,这货自带了一个TOMCAT,省了不少麻烦
主要代码解读:
public interface Mydll extends Library {
Mydll instance = Native.load("C:\\TestDll.dll", Mydll.class);
int sum(int a,int b);
}
@RequestMapping("/")
@ResponseBody
public int dll(HttpServletResponse resp) {
int ret=Mydll.instance.sum(1,2);
resp.setHeader("Access-Control-Allow-Origin","*");
return ret;
}
"C:\\TestDll.dll" 是DLL文件所在的路径,要改成您相应的文件路径
int sum(int a,int b);声明了DLL中对应的函数
int ret=Mydll.instance.sum(1,2); 就是调用DLL中的sum函数
resp.setHeader("Access-Control-Allow-Origin","*"); 这句是为了解决跨域问题的
修改application.yml
把端口改为80
server:
port: 80
特别注意:如果您的WINDOWS是32位的,只能使用Win32版的DLL和32位的JDK。如果您的WINDOWS是64位的,可以使用x64版的DLL和64位的JDK,也可以使用Win32版的DLL和32位的JDK。归纳为一句话: DLL和JDK位数要一致。
现在我们运行这个JAVA程序,然后在浏览器中打开前面写的test.html文件,点击“HTTP GET”按钮:
成功调用DLL
编写PHP版的助手程序
我的开发环境:
Windows 7 旗舰版 SP1 x64
PHPStudy 8.1.1.3 , Nginx 1.15.11, PHP 7.3.4nts
Microsoft Visual Studio Community 2022 (64 位)
PHP调用普通的DLL有两种方法,一种是开发一个PHP扩展,再由PHP扩展调用DLL,第二种是开发一个com组件,php先调用com组件,再由com组件调用DLL。我们采用的是第二种方法。
Php是写不了com组件的,所以我们用C++来写一个com组件。
开发com组件
可以参考这个网页:https://blog.csdn.net/weixin_33507838/article/details/115411281
主要代码解读:
STDMETHODIMP CATLSimpleObject::atlsum(LONG a, LONG b, LONG* c)
{
// TODO: 在此处添加实现代码
HINSTANCE hinstLib;
hinstLib = LoadLibrary(TEXT("c:\\TestDll.dll"));
if (hinstLib != NULL)
{
typedef int(__stdcall* SUM)(int, int);
SUM sum;
sum = (SUM)GetProcAddress(hinstLib, "sum"); //这是获取dll里面的sum函数
if (NULL != sum)
{
int ret = sum(a, b); //调用dll的sum函数
* c = ret; //这是设置返回值
}
FreeLibrary(hinstLib);
}
return S_OK;
}
"C:\\TestDll.dll" 是DLL文件所在的路径,要改成您相应的文件路径
特别注意:TestDll.dll 和 com组件的位数要一致。要么都是x64 ,要么都是Win32(x86)。千万不能一个x64,另一个Win32(x86)
Com组件编译出来之后,需要注册,注册需要使用命令行:regsvr32 D:\doc\code\SimpleDllHelper\comATL\x64\Release\comATL.dll
这里D:\doc\code\SimpleDllHelper\comATL\x64\Release\comATL.dll是com组件所在的路径,应该改成您本机上实际的文件路径
开发php代码
总共三行代码:
header('Access-Control-Allow-Origin:*'); //这句是为了解决跨域问题的
$com = new COM("ComATL.ATLSimpleObject") or die("无法调用ComTest"); //初始化com组件
echo $com->atlsum(1, 2); //调用com组件的接口
如果说程序代码是美女们的超短裙,那么我最喜欢的就是PHP了,因为越短越好!
还有一点很重要,修改php配置文件,加上一行extension=php_com_dotnet.dll
如果没有这一行, PHP无法调用com组件
最后我们在浏览器中打开前面写的test.html文件,点击“HTTP GET”按钮,将会看到和java助手程序一样的结果
编写C++版的助手程序
我的开发环境:
Windows 7 旗舰版 SP1 x64
Microsoft Visual Studio Community 2022 (64 位)
介绍一下我们的思路:先安装一个APACHE HTTPD服务器,然后写一个CGI的程序,由这个CGI程序来调用DLL,最后把CGI程序部署到APACHE HTTPD服务器里面
安装一个APACHE HTTPD服务器
安装的时候,就按照缺省的设置安装,安装完会自动启动apache2.2服务,系统托盘区也会多一个apache的图标:
打开浏览器,输入网址http://localhost/,显示如下:
出现It works! 就说明已经安装成功。
写一个CGI的程序
由这个CGI程序来调用DLL
主要代码如下:
#include <windows.h>
#include <stdio.h>
int main()
{
HINSTANCE hinstLib;
printf("Access-Control-Allow-Origin:*\n");
printf("Content-type: text/html\n\n");
hinstLib = LoadLibrary(TEXT("c:\\TestDll.dll"));
if (hinstLib != NULL)
{
typedef int(__stdcall* SUM)(int, int);
SUM sum;
sum = (SUM)GetProcAddress(hinstLib, "sum");
if (NULL != sum)
{
int ret = sum(1,2);
printf("%d", ret);
}
else
{
printf("can not find function sum ");
}
FreeLibrary(hinstLib);
}
else
{
printf("load library fail!");
}
return 0;
}
"C:\\TestDll.dll" 是DLL文件所在的路径,要改成您相应的文件路径
printf("Access-Control-Allow-Origin:*\n");这句是为了解决跨域问题的
printf("Content-type: text/html\n\n"); \n\n这个说明HTTP响应头部结束
printf("%d", ret); 这句是真正在网页上显示的内容
注意事项,因为我们安装的APACHE HTTPD是Win32版本的,所以我们使用的TestDll.dll也必须是Win32(x86)版的,这个CGI程序在编译的时候也要选择Win32(x86)版的。
把CGI程序部署到APACHE HTTPD服务器
我们把编译好的exe文件改名为dllhelpercpp.cgi, 复制到APACHE HTTPD的CGI-BIN目录里面。
打开浏览器,打开网址http://localhost/cgi-bin/dllhelpercpp.cgi,如果出现一个数字3,就说明部署成功了。
测试
现在我们修改test.html文件,
把httpRequest.open('GET', 'http://localhost/',true);
改成httpRequest.open('GET', 'http://localhost/cgi-bin/dllhelpercpp.cgi',true);
用浏览器打开test.html文件,点击“HTTP GET”看看得到的结果是不是和JAVA版本的一样?
CGI程序更进一步的开发
可以参考下面两个网页:
https://blog.csdn.net/u010105970/article/details/41278489
https://blog.csdn.net/weixin_39652658/article/details/116873261