Python搭建调用本地dll的Windows服务(浏览器可以访问,附测试dll64位和32位文件)
一、前言说明
博客声明:此文链接地址https://www.cnblogs.com/Vrapile/p/14113683.html,请尊重原创,未经允许禁止转载!!!
1. 功能简述
(1)本文提供生成好的测试dll文件,提供用Python调用dll生成windows服务接口的方法,在浏览器可以打开的样例。

(2)网上有调用dll的文章,有生成dll的文章,如果仅仅是尝试做python调用dll的开发,还需要花时间下载visual studio去生成dll
网上基本下载不到测试用的dll,因此整理此文章供读者参阅
(3)参照1:Python之Windows服务,参照2:python实现编写windows服务,参照3:vs2019生成64位dll(动态链接库)并用python3.7调用
(4)提供我封装好的dll路径:点击下载dll(原文链接长期可以访问,如果不能访问很可能是未经允许转载或者复制的)
2. 我的开发环境
(1)Python3.8语言,Pycharm工具,Windows10环境
二、接口源码设计
1. PyCharm界面及项目路径

1. 文件结构说明
(1)MyService是项目名字
(2)build、disk两个文件夹编译启动服务会自动生成
(3)dll路径文件夹,放dll文件
(4)log路径文件夹,放日志文件,没有会自动创建
(5)service包文件夹,主要开发位置,调用dll和业务逻辑等都在此文件夹下
(6)test包文件夹,放测试代码等,可忽略
(7)util包文件夹,封装一些工具类
(8)DllService.py,系统主入口文件,只需要将service包文件夹下的内容引用到此文件的"SvcStop"里即可
(9)DllService.spec,编译启动服务会自动生成
(10)start.bat,编译项目生成服务的启动命令,启动方法要用管理员权限
cd进入文件目录,./start.bat运行脚本,如下图:

2. 运行成功效果如下图:

3. 服务未成功生成,直接看cmd窗口报错信息,如果是生成的服务已停止,说明服务启动报错了,查看报错原因方法:
win10设置——控制面板——管理工具——事件查看器——Windows日志——应用程序——筛选当前日志(勾选错误)

2. 文件代码
(1)DllService.py,生成服务文件,此文件基本不用大改动
1 # -*- coding:utf-8 -*-
2 import win32timezone # 虽然看起来没被应用,但是不能少,否则启动服务会报错
3 import win32serviceutil
4 import win32service
5 import win32event
6 import servicemanager
7 import os
8 import sys
9 from util import logger
10
11
12 class DllService(win32serviceutil.ServiceFramework):
13 _svc_name_ = 'DllService' # 服务名,服务启动停止唯一标识
14 _svc_display_name_ = 'DllService' # 服务显示名称,可与服务名不同
15 _svc_description_ = 'DLL本地服务-测试浏览器调用' # 服务描述,可任意填写
16
17 def __init__(self, args):
18 win32serviceutil.ServiceFramework.__init__(self, args)
19 self.stop_event = win32event.CreateEvent(None, 0, 0, None)
20 self.run = True
21
22
23 def SvcDoRun(self):
24 from service.TestService import TestService
25 logger.info(" Service is running ")
26 TestService.run(port=12345)
27
28
29 def SvcStop(self):
30 self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
31 win32event.SetEvent(self.stop_event)
32 self.ReportServiceStatus(win32service.SERVICE_STOPPED)
33 self.run = False
34 logger.info(" Service is stopped ")
35
36
37 if __name__ == '__main__':
38 if len(sys.argv) == 1:
39 try:
40 event_src_dll = os.path.abspath(servicemanager.__file__)
41 servicemanager.PrepareToHostSingle(DllService) # 此处是文件名
42 servicemanager.Initialize('DllService', event_src_dll ) # 此处是服务名
43 servicemanager.StartServiceCtrlDispatcher()
44 except win32service.error as details:
45 import winerror
46 if details == winerror.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
47 win32serviceutil.usage()
48 else:
49 win32serviceutil.HandleCommandLine(DllService) # 此处是文件名
(2)TestService.py,具体服务文件
CORS(TestService, supports_credentials=True) 解决浏览器访问跨域问题
因为sys.path[0]地址为:E:\Python\MyService\dist\DllService\base_library.zip,因此需要返回3级回到主目录boot_path
此处因为我本地Python环境为64位,因此只能调用64位dll,32位dll调用后续如果有人想看再更新
1 # -*- coding:utf-8 -*-
2 import os
3 import sys
4 from flask import Flask, request
5 from flask_cors import *
6 import traceback
7 import json
8 import ctypes
9 from util import ResponseMessage, logger
10
11
12 TestService = Flask(__name__)
13 CORS(TestService, supports_credentials=True)
14
15 boot_path = os.path.abspath(os.path.join(sys.path[0], "../../.."))
16
17
18 # ajaxJson["src"] = "http://127.0.0.1:12345/test";
19 # ajaxJson["data"] = {"data":"{\"aaa\":123,\"bbb\":\"456\"}"};
20 # JL.ajax(ajaxJson);
21 @TestService.route("/test", methods=['GET', 'POST'])
22 def test():
23 # noinspection PyBroadException
24 try:
25 data = request.values.get('data')
26 data_dict = json.loads(data)
27 logger.info("request:" + str(data_dict))
28 dll_path = os.path.join(os.path.join(boot_path, "dll"), "MyDll64.dll")
29 windll = ctypes.windll.LoadLibrary(dll_path)
30 result_add = windll.myAdd(int(data_dict["aaa"]), int(data_dict["bbb"]))
31 result_max = windll.myMax(int(data_dict["aaa"]), int(data_dict["bbb"]))
32 return_data = dict()
33 return_data["add"] = result_add
34 return_data["max"] = result_max
35 return_data["dll_path"] = dll_path
36 except:
37 logger.error("error:" + traceback.format_exc())
38 return ResponseMessage.error(traceback.format_exc())
39 logger.info("response:" + str(ResponseMessage.ok(return_data)))
40 return ResponseMessage.ok(return_data)
3. logger.py,日志工具封装文件
如上文中引用方式:from util import logger, 使用方式:logger.info(" Service is stopped ")
此文已更改日志保存路径为项目根路径的log下,没有会自动创建
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 """
4 Created on Feb 23, 2020
5 @author: Vrapile
6 """
7 import sys
8 import os
9 import datetime
10 import logging
11 from logging.handlers import RotatingFileHandler
12
13
14 boot_path = os.path.abspath(os.path.join(sys.path[0], "../../.."))
15
16 logger = logging.getLogger(__name__)
17 logger.setLevel(level=logging.INFO)
18 log_path = os.path.join(boot_path, 'log')
19 if not os.path.exists(log_path):
20 os.makedirs(log_path)
21 handler = RotatingFileHandler(os.path.join(log_path, '%s.log' % datetime.date.today()), maxBytes=5 * 1024 * 1024, backupCount=10)
22 handler.setLevel(level=logging.INFO)
23 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
24 handler.setFormatter(formatter)
25 logger.addHandler(handler)
26
27
28 def debug(text):
29 logger.debug(text)
30
31
32 def info(text):
33 logger.info(text)
34
35
36 def warning(text):
37 logger.warning(text)
38
39
40 def error(text):
41 logger.error(text)
42
43
44 def critical(text):
45 logger.critical(text)
4. ResponseMessage.py提供标准化响应json数据,引用使用方式见TestService.py
1 #!/usr/bin/python3
2 # -*- coding: utf-8 -*-
3 """
4 Created on Feb 23, 2020
5 @author: Vrapile
6 """
7 import json
8
9
10 def ok(data, code=200):
11 return_dict = dict()
12 return_dict["code"] = code
13 return_dict["success"] = True
14 return_dict["data"] = data
15 return_dict["message"] = None
16 return_dict["flag"] = 0
17 return json.dumps(return_dict, ensure_ascii=False)
18
19
20 def error(message, code=500):
21 return_dict = dict()
22 return_dict["code"] = code
23 return_dict["success"] = False
24 return_dict["data"] = None
25 return_dict["message"] = message
26 return_dict["flag"] = 1
27 return json.dumps(return_dict, ensure_ascii=False)
28
29
30 def res(data):
31 if data[0]:
32 return ok(data[1])
33 else:
34 return error(data[1])
5. start.bat文件,封装编译启动命令,需要管理员权限的cmd,启动方法上文已有说明
其中等待3秒是必须的,等待释放文件资源,否则删除dist和build基本都会报错
1 sc stop DllService
2 sc delete DllService
3 :: 等待3秒
4 TIMEOUT /T 3
5 rmdir /s/q dist
6 rmdir /s/q build
7 del DllService.spec
8 pyinstaller DllService.py
9 dist\DllService\DllService.exe install
10 sc start DllService
11 TIMEOUT /T 30
三、总结
1. 浏览器访问地址:http://127.0.0.1:12345/test?data={"aaa":123,"bbb":456}
2. 通过Python调用dll,提供本地浏览器访问是可以实现的,js访问效果如下:

3. 主要解决现在很多银行都是提供dll文件支付,而公司业务又是浏览器系统,不能直接访问本地dll,因此此文方案可以解决此类问题
Python搭建调用本地dll的Windows服务(浏览器可以访问,附测试dll64位和32位文件)的更多相关文章
- 网页调用本地程序(Windows下浏览器全兼容)
用网页调用本地应用程序的思路是,先进行注册表注册自定义一个URL Protocol协议,再利用URL Protocol实现网页调用本地应用程序. 1.先写一个注册表文件,将其保存为.reg后缀的注册表 ...
- 检查DLL,EXE文件是64位或者32位的方法
检查DLL,EXE文件是64位或者32位:输入corflags <assembly path>:
- 使用C#创建及调用WCF完整实例 (Windows服务宿主)
关于WCF的概念.原理.优缺点等,在这里就不多说了,网上很多,可以自行搜索,比我解释的要专业的多. 这里直接说使用Windows 服务(Windows Service)作为宿主如何实现,其它方式不在此 ...
- [转]在C#中调用C语言函数(静态调用Native DLL,Windows & Microsoft.Net平台)
原文:https://blog.csdn.net/yapingxin/article/details/7288325 对于不太了解.Net的人,如果想要了解.Net,我必须给他介绍P/Invoke.P ...
- Windows服务调试小结(附Demo)
本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作. 阅读目录 介绍 搭建环境 调试方式 Demo下载 本文版权归mephisto和博客园共有,欢迎转载,但须 ...
- WindowsService(Windows服务)开发步骤附Demo
1.打开VS,新建项目,选择Windows服务,然后设置目录及项目名称后点击确定. 2.展开Service1服务文件,编写service1.cs类文件,不是Service1[设计].然后修改OnSta ...
- WindowsService(Windows服务)开发步骤附Demo 【转】
转http://www.cnblogs.com/moretry/p/4149489.html 1.打开VS,新建项目,选择Windows服务,然后设置目录及项目名称后点击确定. 2.展开Service ...
- 错误: 未能完成程序集的安装(hr = 0x8007000b),.net程序关于使用Oracle.DataAccess.dll不同版本x86和x64问题,即oracle odp.net 32位/64位版本的问题
如果你的机器上安装了odp.net,且确信machine.config也有类似以下结节:(64位+.net 4.0环境下,machine.config可能会有4份,分别对应于.net2.0/4.0的3 ...
- 用anaconda保证64位和32位的python共存
conda info # 查看当前工作平台 set CONDA_FORCE_32BIT=1 # 切换到32位 set CONDA_FORCE_32BIT=0 # 切换到64位 conda create ...
随机推荐
- 利用火焰图分析ceph pg分布
前言 性能优化大神Brendan Gregg发明了火焰图来定位性能问题,通过图表就可以发现问题出在哪里,通过svg矢量图来查看性能卡在哪个点,哪个操作占用的资源最多 在查看了原始数据后,这个分析的原理 ...
- Cpython的全局解释器锁(GIL)
# Cpyrhon解释器下有个全局解释器锁-GIL:在同一 # 在同一时刻,多线程中只有一个线程访问CPU # 有了全局解释器锁(GIL)后,在同一时刻只能有一个线程访问CPU. # 全局解释器锁锁的 ...
- 学习笔记:[算法分析]数据结构与算法Python版[基本的数据结构-上]
线性结构Linear Structure ❖线性结构是一种有序数据项的集合,其中 每个数据项都有唯一的前驱和后继 除了第一个没有前驱,最后一个没有后继 新的数据项加入到数据集中时,只会加入到原有 某个 ...
- bugku-PHP_encrypt_1(ISCCCTF)
前言 懒得写详细wp了.... 我佛了 这个题纠结好久......... 前言 需要解密的密文: fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA ...
- 阿里面试官:小伙子,你给我说一下JVM对象创建与内存分配机制吧
内存分配机制 逐步分析 类加载检查: 虚拟机遇到一条new指令(new关键字.对象的克隆.对象的序列化等)时,会先去检查这个指令的参数在常量池中定位到一个类的符号引用,并且这个符号引用代表的类是否 ...
- FL Studio杂项设置页讲解(上)
今天我们来看一下FL Studio通道设置窗口中的杂项设置页面.该页面存在于FL Studio绝大多数的通道中,我们可以通过它来设置一些发生器或者第三方插件的参数,接下来就让我们一起来学习下这些参数的 ...
- Guitar Pro指弹入门——特殊拍号
在吉他演奏技术不断提高的同时,我们经常会遇到一些奇怪的曲谱.他们的拍号不是正常的4/4拍或者3/4拍,而是5/4或者5/8等等我们不太了解的拍号,致使我们在演奏和练习之中陷入纷乱的节奏. 那么本期文章 ...
- jQuery 第一章 $()选择器
jquery 是什么? jquery 其实就是一堆的js函数(js库),也是普通的js而已. 有点像我们封装一个函数,把他放到单独的js 文件,等待有需要的时候调用它. 那么使用它有啥好处呢? jqu ...
- Java IDEA根据database以及脚本代码自动生成DO,DAO,SqlMapper文件(一)
根据数据库代码自动生成的插件挺多的,这里主要分享两种: 1.根据database以及脚本代码自动生成 2.根据mybatis-generator-core自动生成(下一章节进行分享,包含sqlserv ...
- java基础:CompletionStage接口
CompletionStage是Java8新增接口,用于异步执行中的阶段处理:先看接口 可以简单划分为三类: 1.在上一阶段执行结束之后,一阶段结果作为指定函数的参数执行函数产生新的结果,apply/ ...