c++ 获取设备连接的网络打印机设备信息

以下是一个跨平台的C++方案,结合系统API和SNMP协议实现网络打印机信息获取:

#include <iostream>
#include <vector>
#include <string>

// 平台抽象层
#ifdef _WIN32
#include <windows.h>
#include <winsock.h>
#include <winprinter.h>
#elif defined(__linux__)
#include <cups/cups.h>
#endif

// SNMP抽象层
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>

struct PrinterInfo {
    std::string name;
    std::string ip;
    std::string mac;
    std::string serial;
    std::string manufacturer;
};

class PrinterEnumerator {
public:
    std::vector<PrinterInfo> getNetworkPrinters() {
        std::vector<PrinterInfo> printers;
        
        // 获取基础打印机信息
        #ifdef _WIN32
        enumerateWindowsPrinters(printers);
        #elif defined(__linux__)
        enumerateLinuxPrinters(printers);
        #endif

        // 通过SNMP补充详细信息
        for (auto& printer : printers) {
            if (!printer.ip.empty()) {
                querySNMPInfo(printer);
            }
        }
        
        return printers;
    }

private:
    #ifdef _WIN32
    void enumerateWindowsPrinters(std::vector<PrinterInfo>& printers) {
        DWORD needed = 0;
        DWORD returned = 0;
        EnumPrinters(PRINTER_ENUM_NETWORK, NULL, 4, NULL, 0, &needed, &returned);
        
        std::vector<BYTE> buffer(needed);
        if (EnumPrinters(PRINTER_ENUM_NETWORK, NULL, 4, buffer.data(), 
                       buffer.size(), &needed, &returned)) {
            PRINTER_INFO_4* info = reinterpret_cast<PRINTER_INFO_4*>(buffer.data());
            for (DWORD i = 0; i < returned; ++i) {
                PrinterInfo pi;
                pi.name = info[i].pPrinterName;
                // 需要解析端口信息获取IP
                parseWindowsPort(pi);
                printers.push_back(pi);
            }
        }
    }
    #elif defined(__linux__)
    void enumerateLinuxPrinters(std::vector<PrinterInfo>& printers) {
        cups_dest_t* dests = cupsGetDests(CUPS_HTTP_DEFAULT);
        for (int i = 0; i < cupsGetDests(&dests); ++i) {
            PrinterInfo pi;
            pi.name = dests[i].name;
            // 解析设备URI获取IP
            const char* uri = cupsGetOption("device-uri", 
                dests[i].num_options, dests[i].options);
            if (uri && strstr(uri, "socket://")) {
                pi.ip = extractIPFromURI(uri);
            }
            printers.push_back(pi);
        }
        cupsFreeDests(dests);
    }
    #endif

    void querySNMPInfo(PrinterInfo& printer) {
        // 公共OID查询
        getSNMPValue(printer.ip, "1.3.6.1.2.1.1.1.0", printer.manufacturer);
        getSNMPValue(printer.ip, "1.3.6.1.2.1.43.5.1.1.17.1", printer.serial);
        
        // MAC地址需要接口索引,这里简化处理
        std::string macHex;
        if (getSNMPValue(printer.ip, "1.3.6.1.2.1.2.2.1.6.1", macHex)) {
            printer.mac = formatMAC(macHex);
        }
    }

    bool getSNMPValue(const std::string& ip, const char* oid, std::string& output) {
        // SNMP实现(需包含net-snmp库)
        // 此处实现与之前示例类似,需处理Windows/Linux差异
        // ...
        return true;
    }

    // 辅助函数
    std::string formatMAC(const std::string& hex) {
        std::string mac;
        for (size_t i = 0; i < hex.length(); i += 2) {
            if (!mac.empty()) mac += ":";
            mac += hex.substr(i, 2);
        }
        return mac;
    }
};

int main() {
    PrinterEnumerator enumerator;
    auto printers = enumerator.getNetworkPrinters();
    
    for (const auto& p : printers) {
        std::cout << "Printer: " << p.name << "\n"
                  << "  IP: " << p.ip << "\n"
                  << "  MAC: " << p.mac << "\n"
                  << "  Serial: " << p.serial << "\n"
                  << "  Manufacturer: " << p.manufacturer << "\n\n";
    }
    return 0;
}

实现说明

  1. 跨平台架构

    • 使用预处理器指令区分Windows/Linux实现
    • Windows使用Win32打印API
    • Linux使用CUPS API
  2. 信息获取流程

    graph TD A[开始] --> B[枚举网络打印机] B --> C[获取基础信息] C --> D[解析IP地址] D --> E[SNMP查询详细信息] E --> F[输出结果]
  3. 关键组件

    • 打印机枚举:通过系统原生API获取打印机列表
    • IP解析
      • Windows:解析打印机端口配置
      • Linux:解析CUPS设备URI
    • SNMP查询:使用net-snmp库获取详细设备信息
  4. 平台差异处理

功能 Windows 实现 Linux 实现
打印机枚举 EnumPrinters API cupsGetDests
IP地址解析 解析端口配置注册表 解析CUPS设备URI
SNMP通信 WinSNMP库 或 net-snmp跨平台编译 net-snmp库

编译与依赖

  1. Windows要求

    • 安装net-snmp Win32开发包
    • 链接库:wsnmp32.lib, winsock32.lib
  2. Linux要求

    sudo apt-get install libcups2-dev libsnmp-dev
    
  3. 编译命令

    # Windows
    cl /EHsc printer_info.cpp /link wsnmp32.lib
    
    # Linux
    g++ -o printer_info printer_info.cpp -lcups -lnetsnmp
    

增强建议

  1. IP地址解析增强

    #ifdef _WIN32
    void parseWindowsPort(PrinterInfo& pi) {
        // 通过注册表解析端口配置
        HKEY hKey;
        if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
            "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Ports", 
            0, KEY_READ, &hKey) == ERROR_SUCCESS) {
            // 解析端口配置...
            RegCloseKey(hKey);
        }
    }
    #endif
    
  2. SNMP异常处理

    bool getSNMPValue(...) {
        try {
            // SNMP实现
            return true;
        } catch (const std::exception& e) {
            std::cerr << "SNMP Error: " << e.what() << std::endl;
            return false;
        }
    }
    
  3. 厂商OID扩展

    const std::map<std::string, std::string> VENDOR_OIDS = {
        {"HP", "1.3.6.1.4.1.11.2.3.9.1"},
        {"Canon", "1.3.6.1.4.1.1602.4.1.1.1"},
        {"Epson", "1.3.6.1.4.1.1248.1.1.1.1"}
    };
    

注意事项

  1. 权限要求

    • Windows需要管理员权限访问注册表
    • Linux需要cups权限和SNMP访问权限
  2. 网络配置

    • 确保打印机SNMP服务已启用(UDP 161端口)
    • 可能需要配置SNMP community string
  3. 性能优化

    // 使用异步SNMP请求
    void asyncSNMPQuery(...) {
        // 实现异步请求处理
    }
    
  4. 备用方案

    // 当SNMP不可用时尝试其他协议
    void fallbackProtocolQuery(...) {
        // 实现LPD/JetDirect等协议查询
    }
    

该方案通过结合系统级打印机枚举和标准SNMP协议,实现了跨平台的网络打印机信息获取。实际部署时需要根据具体网络环境和打印机型号调整OID配置,并处理不同厂商的特殊实现。

posted @ 2025-02-13 15:46  BaldButStrong  阅读(144)  评论(0)    收藏  举报