# WinIO 头文件

#ifndef WINIO_H
#define WINIO_H
//#include "..\drv\winio_nt.h"
#include "winio_nt.h"
#ifndef WINIO_DLL
#define WINIO_API _declspec(dllimport)
#else
#define WINIO_API 
#endif
extern "C"
{
   //// 初始化 WinIO 函数库
  WINIO_API bool _stdcall InitializeWinIo();
  // 关闭 WinIO 函数库  
  WINIO_API void _stdcall ShutdownWinIo();
  // 将物理内存映射到一个 32 位应用程序的线性地址空间
  WINIO_API PBYTE _stdcall MapPhysToLin(tagPhysStruct &PhysStruct);
    // 物理映射线性:使用此函数解除原先使用 MapPhysToLin 函数映射的一段物理内存区域,该区域被映射到
  // 所属的线性地址空间
  WINIO_API bool _stdcall UnmapPhysicalMemory(tagPhysStruct &PhysStruct);
  // 从指定的物理地址读取一个双字数据
  WINIO_API bool _stdcall GetPhysLong(PBYTE pbPhysAddr, PDWORD pdwPhysVal);
   // 将一个双字型数据写入指定的物理地址
  WINIO_API bool _stdcall SetPhysLong(PBYTE pbPhysAddr, DWORD dwPhysVal);
   // 从一个输入或输出端口读取一个字节 / 字 / 双字数据
  WINIO_API bool _stdcall GetPortVal(WORD wPortAddr, PDWORD pdwPortVal, BYTE bSize);
  // 写一个字节 / 字 / 双字的数据写入输入或输出接口
  WINIO_API bool _stdcall SetPortVal(WORD wPortAddr, DWORD dwPortVal, BYTE bSize);
  // 安装 WinIODriver  
  WINIO_API bool _stdcall InstallWinIoDriver(PWSTR pszWinIoDriverPath, bool IsDemandLoaded = false);
  // 移除 WinIODriver
  WINIO_API bool _stdcall RemoveWinIoDriver();
}
extern HANDLE hDriver;
extern bool IsWinIoInitialized;
extern bool g_Is64BitOS;
bool _stdcall StartWinIoDriver();
bool _stdcall StopWinIoDriver();
#endif

# 函数

# 初始化

本函数初始化 WioIO函数库

必须在调用所有其他 函数之前 调用 本函数

bool _stdcall InitializeWinlo();

# 关闭 WinIO

在内存中 清除WinIO 库,必须在 中止函数之 前或者 不需要WinIO库 时调用

void _stdcall ShutdownWinIo();

#

从一个 输入输出端口 读取 一个节/字/双字 数据

bool _stdcall GetPortVal(WORD wPortAddr,PDWORD pwdPortVal,BYTE bSize);
//wPortAddr : 输入输出端口地址
//pdwPortVal : 指向双字变量的指针, 接收从端口得到的数据
//bSize : 需要读的字节数,可以是 1 (BYTE),2 (WORD) or 4 (DWORD)

#

一个字节/字/双字 的数据 写入输入或输出 接口

bool _stdcall SetPortVal(WORD wPortAddr, DWORD dwPortVal, BYTE bSize);
//wPortAddr  输入输出口地址
//dwPortVal  要写入口的数据
//bSize  要写的数据个数 可以是 1 (BYTE),2 (WORD) or 4 (DWORD)

# 物理内存映射

物理内存映射 到一个 32位应用程序线性地址空间

PBYTE _stdcall MapPhysToLin(PBYTE pbPhysAddr, DWORD dwPhysSize, HANDLE *pPhysicalMemoryHandle);
//pbPhyAddr  -- 指向物理地址的指针
//dwPhySize -- 需要映射的字节数
//uPhysicalMemoryHandle 变量指针,如果调用成功,负责接收物理内存句柄,随后本句柄在调用 UnmapPhysicalMemory 函数时作为其第一个参数
// 案例
PBYTE pbLinAddr;
HANDLE PhysicalMemoryHandle;
pbLinAddr = MapPhysToLin(0xA0000, 65536, &PhysicalMemoryHandle);

把物理地址范围 0xA0000 - 0xAFFFF 的地 址空间映射应用的线性地址 空间。返回值为一个与物理地址 0xA0000 相关的线性地址。如出现 错误 ,则返回值 NULL

# 物理映射线性

使用本函数解除原先使用 MapPhysToLin 函数映射的一段 物理内存区域 ,该区域被映射到应用程序所属的 线性地址空间

bool _stdcall UnmapPhysicalMemory(HANDLE PhysicalMemoryHandle,PBYTE pbLinAddr);
  • PhysicalMemoryHandle物理 内存区域的 句柄 ,此参数由 MapPhysToLin 函数的调用返回
  • pbLinAddrMapPhysToLin – 函数调用 返回的线性地址

# 读双字数据

从指定的 物理地址 读取一个 双字数据

bool _stdcall GetPhysLong(PBYTE pbPhysAddr,PDWORD pdwPhysVal);
//pbPhysAddr  -- 指向物理地址的指针
//pdwPhysVal  -- 指向一个双字变量的指针,接收从物理内存中传来的数据
// 成功 返回 非零值
// 失败 返回 零值

# 双字型写入物理地址

将一个 双字型 数据 写入 指定的物理地址

bool _stdcall SetPhysLong(PBYTE pbPhysAddr,DWORD dwphysVal);
//pbPhysAddr  -- 指向物理地址的指针
//pdwPhysVal  -- 指向待写入物理内存地址的双字型数据

# GetProcAddress 函数

用于 获取DLL导出函数的地址 (显式链接使用)

GetProcAddress 将 DLL 模块处理 (由 LoadLibrary , 或 GetModuleHandle 返回的 参数 ),并采用要 调用的函数的名称函数的导出序号

因为通过 指针 调用 DLL函数 ,并且 没有编译时 类型检查,所以 确保 参数 正确 ,以便 不会 超过在 堆栈上 分配的 内存 以及导致 访问冲突 。帮助提供类型安全的一种方法时查看 导出函数函数原型 ,并为函数指针创建匹配的 typedef

# 官方 demo

#include "windows.h"
typedef HRESULT (CALLBACK* LPFNDLLFUNC1)(DWORD,UINT*);
HRESULT LoadAndCallSomeFunction(DWORD dwParam1, UINT * puParam2)
{
    HINSTANCE hDLL;               // Handle to DLL
    LPFNDLLFUNC1 lpfnDllFunc1;    // Function pointer
    HRESULT hrReturnVal;
    hDLL = LoadLibrary("MyDLL");
    if (NULL != hDLL)
    {
        lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL, "DLLFunc1");
        if (NULL != lpfnDllFunc1)
        {
            // call the function
            hrReturnVal = lpfnDllFunc1(dwParam1, puParam2);
        }
        else
        {
            // report the error
            hrReturnVal = ERROR_DELAY_LOAD_FAILED;
        }
        FreeLibrary(hDLL);
    }
    else
    {
        hrReturnVal = ERROR_DELAY_LOAD_FAILED;
    }
    return hrReturnVal;
}

如何指定 调用 GetProcAddress时 所需的函数取决于 DLL的 生成方式。

仅当要 链接到DLL 使用模块定义 ( .def ) 文件生成,并且序号随函数在 DLL.def 文件的 EXPORTS节 中列出时,才能获取 导出序号 。如果 DLL 具有许多导出函数,则与使用函数名称相比,使用 导出 需要调用 GetProcAddress 会稍微 快一些, 因为导出序号充当 DLL 导出表中的索引。使用导出序号, GetProcAddress 可以 直接查找函数 ,而不是将指定名称与 DLL 导出表中的函数名进行比较。但是当仅当可控制将序号分配给 .def文件 中的导出函数时,才应使用 导出序号 调用 GetProcAddress

# Qt 环境搭建

# pro 文件

DEFINES += WINIO_DLL
unix|win32: LIBS += -L$$PWD/./ -lWinIo64
RESOURCES += \
    source.qrc

# 头文件存储函数指针

typedef bool( _stdcall* LS_GetPhysLong)(PBYTE pbPhysAddr, PDWORD pdwPhysVal);
typedef bool( _stdcall* LS_SetPhysLong)(PBYTE pbPhysAddr, DWORD dwPhysVal);
typedef bool( _stdcall* LS_InitializeWinIo)();
typedef bool( _stdcall* LS_ShutdownWinIo)();
typedef bool( _stdcall* LS_GetPortVal)(WORD wPortAddr, PDWORD pdwPortVal, BYTE bSize);
typedef bool( _stdcall* LS_SetPortVal)(WORD wPortAddr, DWORD dwPortVal, BYTE bSize);
// 对函数指针进行重定义。
LS_GetPhysLong Lib_GetPhysLong;
LS_SetPhysLong Lib_SetPhysLong;
LS_InitializeWinIo Lib_InitializeWinIo;
LS_ShutdownWinIo Lib_ShutdownWinIo;
LS_SetPortVal Lib_SetPortVal;
LS_GetPortVal Lib_GetPortVal;
HMODULE m_hDllFile;  // 初始化 WinIO 文件

# 函数初始化

放到函数的头文件中

// 加载动态链接库,并通过 GetProcAddress 方法调用取函数空间地址
    // @ 传入所需的动态链接库
    // 返回 m_hDllFile 将作为 GetProcAddress 去调用 WinIO 的函数地址使用
    m_hDllFile = LoadLibrary(L"WinIo64");
    if(m_hDllFile == NULL)
    {
        Bugart("加载WinIo64失败");
    }
    // 对函数指针初始化 Lib_InitializeWinIo -> InitializeWinIo
   Lib_InitializeWinIo=(LP_InitializeWinIo)::GetProcAddress(m_hDllFile,"InitializeWinIo");
    if(NULL == Lib_InitializeWinIo)
    {
        Bugart("load error Lib_InitializeWinIo ");
    }
    //qDebug ()<< "后:"<< Lib_InitializeWinIo;
    // 函数指针初始化  Lib_ShutdownWinIo ->  ShutdownWinIo
 	Lib_ShutdownWinIo=(LP_ShutdownWinIo)::GetProcAddress(m_hDllFile,"ShutdownWinIo");
    if(NULL == Lib_ShutdownWinIo)
    {
        Bugart("load error Lib_ShutdownWinIo ");
    }
    // 函数指针初始化 Lib_GetPhysLong -> GetPhysLong
    Lib_GetPhysLong=(LP_GetPhysLong)::GetProcAddress(m_hDllFile,"GetPhysLong");
    if(NULL == Lib_GetPhysLong)
    {
        Bugart("load error Lib_GetPhysLong ");
    }
    // 函数        Lib_SetPhysLong  -> SetPhysLong
    Lib_SetPhysLong=(LP_SetPhysLong)::GetProcAddress(m_hDllFile,"SetPhysLong");
    if(NULL == Lib_SetPhysLong)
    {
        Bugart("load error Lib_SetPhysLong ");
    }
    Lib_SetPortVal=(LP_SetPortVal)::GetProcAddress(m_hDllFile,"SetPortVal");
    if(NULL==Lib_SetPortVal)
    {
        Bugart("load error Lib_SetPortVal ");
    }
    // 初始化操作
    Lib_GetPortVal=(LP_GetPortVal)::GetProcAddress(m_hDllFile,"GetPortVal");
    if(NULL==Lib_GetPortVal)
    {
        Bugart("load error Lib_GetPortVal ");
    }

# Qt WinIO 调用

  • 确保自己 系统处于 测试模式 \n

  • 以管理员权限运行 CMD \n

  • 在输入框输入 \n

bcdedit /set testsigning on # 并执行 重启即可进入测试模
  • 确保使用 管理员权限 打开软件 \n
    • 确保 使用的版本设备型号 一致 \n
    • 确保将 软件 目录的 WinIo64.sys 文件的 签名 安装到 本地
  • 右键点击 WinIo64.sys
  • 点击属性 \n * 点击上面的数字签名
  • 选择第一个签名,查看详细信息
  • 选择 查看证书
  • 选择 安装证书
  • 存储位置选择 本地计算机 ,点击 下一步
  • 将所有的证书都放入下列 存储
  • 点击浏览 ,选择 受信任 的根证书颁发机构
  • 点击 确认
  • 点击 下一步
  • 点击 完成 导入成功即可