首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 列印機枚舉與驅動程式識別:從 WMI 到 Win32 API 的實戰遷移

     編輯:關於C語言

前言

在為客戶開發配套列印軟體的過程中,我深入研究了 C# 中列印機的枚舉與資訊獲取。.NET Framework 1.1 提供的 PrinterSettings.InstalledPrinters 雖能列出已安裝列印機名稱,但無法取得驅動程式類型等詳細資訊。由於客戶未提供 SDK,且出於商業授權考量,軟體必須僅在特定品牌列印機上運行,因此必須透過驅動程式名稱來識別硬體。

方案一:使用 WMI 查詢列印機資訊

WMI (Windows Management Instrumentation) 提供了統一、可擴展的系統管理介面。.NET 中 System.Management 命名空間下的 ManagementObjectSearcher 可執行 WQL 查詢,輕鬆獲取 Win32_Printer 類別的屬性。

程式碼範例:依驅動程式名稱篩選列印機

public StringCollection GetPrintersByDriverName(string driverName)
{
    var printers = new StringCollection();
    string condition = string.Empty;

    if (!string.IsNullOrEmpty(driverName) && driverName != "*")
    {
        condition = " WHERE DriverName LIKE '" + driverName + "'";
    }

    string query = "SELECT Name FROM Win32_Printer" + condition;
    using (var searcher = new ManagementObjectSearcher(query))
    using (var collection = searcher.Get())
    {
        foreach (ManagementObject printer in collection)
        {
            printers.Add(printer.Properties["Name"].Value.ToString());
        }
    }
    return printers;
}

雖然 WMI 功能強大,且透過 VBScript 幾乎能涵蓋所有 Windows 管理操作,但在實際部署中發現穩定性不足:偶爾會無法正確取得列印機清單,導致客戶投訴。參考 MSDN 文件可深入了解 WMI 架構。

方案二:呼叫 Win32 API EnumPrinters

為了追求穩定與效能,轉而使用 winspool.drv 中的 EnumPrinters 函數。透過 P/Invoke (DllImport) 簽章如下:

[DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumPrinters(
    [MarshalAs(UnmanagedType.U4)] PRINTER_ENUM flags,
    [MarshalAs(UnmanagedType.LPStr)] string name,
    uint level,
    IntPtr pPrinterEnum,
    uint cbBuf,
    [MarshalAs(UnmanagedType.U4)] ref uint pcbNeeded,
    [MarshalAs(UnmanagedType.U4)] ref uint pcReturned);

關鍵挑戰:PRINTER_INFO_2 結構對應

EnumPrinters 的 Level 參數決定回傳結構,需用 Level=2 才能取得驅動程式名稱 (pDriverName)。但 .NET 無內建 PRINTER_INFO_2 定義,且該結構包含嵌套的 DEVMODE 結構,手工對應極易出錯。

靈感來自於「用類別替代結構」的想法:定義 PRINTER_INFO_2 與 DEVMODE 為 class,將所有 DWORD 對應 Int32,指標類型 (LPTSTR, LPDEVMODE, PSECURITY_DESCRIPTOR) 對應 IntPtr。再利用 Marshal.PtrToStructure 將非受控記憶體區塊依序轉換為受控物件:

var pi = new PRINTER_INFO_2();
for (int i = 0; i < numPrinters; i++)
{
    Marshal.PtrToStructure(ptr, pi);
    string driver = Marshal.PtrToStringAuto(pi.pDriverName);
    if (string.IsNullOrEmpty(targetDriver) || driver.ToLower().Contains(targetDriver))
    {
        // 符合授權條件,進行後續處理
    }
    // 移動指標至下一個結構起始位置
    ptr = new IntPtr(ptr.ToInt32() + Marshal.SizeOf(typeof(PRINTER_INFO_2)));
}

補充:C# 2.0 的 fixed 關鍵字

C# 2.0 引入 fixed 關鍵字,允許在 struct 中定義固定大小陣列緩衝區,簡化非受控記憶體對應。但需注意:fixed 僅能用於 struct,不能用於 class。

結語

從 WMI 到 Win32 API 的遷移,解決了穩定性與授權檢查的核心需求。C# 與非受控程式碼的互操作 (Interop) 雖具挑戰,但掌握 Marshal 類別與記憶體佈局技巧後,能極大拓展 .NET 應用的邊界。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved