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

Java 整合 TWAIN 掃描儀:JNI 呼叫原生 DLL 完整實作

     編輯:Java 相關知識

架構概覽

Java 無法直接存取硬體裝置,需透過 JNI (Java Native Interface) 呼叫平台原生程式碼。本方案採用:Java UI 層 (Swing) → JTwain 介面類 → jtwain.dll (C/C++ 實作 TWAIN 協定) → 掃描儀驅動程式。

Java 端核心類別設計

package edu.ctgu.twain;

public class JTwain {
    private static final JTwain INSTANCE = new JTwain();
    private static final String DLL_NAME = "jtwain"; // jtwain.dll / libjtwain.so
    
    private JTwain() {
        loadLibrary();
    }
    
    public static JTwain getInstance() {
        return INSTANCE;
    }
    
    // 原生方法宣告
    public native boolean isTwainAvailable();
    public native String[] getAvailableSources();
    public native String acquire(); // 使用預設來源
    public native String acquire(String sourceName); // 指定來源
    
    private void loadLibrary() {
        try {
            System.loadLibrary(DLL_NAME);
        } catch (UnsatisfiedLinkError e) {
            System.err.println("載入 " + DLL_NAME + " 失敗: " + e.getMessage());
            // 可嘗試 System.load("完絕對路徑") 作為備援
        }
    }
}

Swing UI 整合範例 (JTwacker)

public class JTwacker extends JFrame {
    private JTwain twain = JTwain.getInstance();
    private JComboBox sourceCombo;
    private JPEGPanel imagePanel; // 自訂 JPanel 顯示 BufferedImage
    
    protected void acquireImage() {
        if (!twain.isTwainAvailable()) return;
        
        String source = (String) sourceCombo.getSelectedItem();
        String filename = twain.acquire(source); // 阻塞直到掃描完成
        
        if (filename != null && !filename.isEmpty()) {
            showImage(new File(filename)); // 非同步載入顯示
        }
    }
    
    protected void showImage(File file) {
        new Thread(() -> {
            try {
                BufferedImage img = ImageIO.read(file); // 現代 API 替代 JPEGCodec
                SwingUtilities.invokeLater(() -> imagePanel.setImage(img));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

JNI 原生層實作要點 (C/C++)

// jtwain.cpp 關鍵片段
#include 
#include  // TWAIN 標頭檔

// 全域 TWAIN 狀態管理
static DSMENTRYPROC dsmEntry = NULL;
static TW_IDENTITY appId = {0};
static TW_IDENTITY srcId = {0};
static HWND hwnd = NULL; // 需要視窗控制代碼作為訊息泵

JNIEXPORT jboolean JNICALL Java_edu_ctgu_twain_JTwain_isTwainAvailable
  (JNIEnv *env, jobject obj) {
    return DSM_Entry(&appId, NULL, DG_CONTROL, DAT_PARENT, MSG_OPENDSM, &hwnd) == TWRC_SUCCESS;
}

JNIEXPORT jobjectArray JNICALL Java_edu_ctgu_twain_JTwain_getAvailableSources
  (JNIEnv *env, jobject obj) {
    // 列舉來源:DG_CONTROL / DAT_IDENTITY / MSG_GETFIRST / MSG_GETNEXT
    // 將來源名稱轉為 jobjectArray (String[]) 回傳
}

JNIEXPORT jstring JNICALL Java_edu_ctgu_twain_JTwain_acquire
  (JNIEnv *env, jobject obj, jstring sourceName) {
    // 1. 開啟來源
    // 2. 協商能力 (ICAP_PIXELTYPE, ICAP_BITDEPTH, ICAP_XRESOLUTION 等)
    // 3. 啟用掃描 (DG_CONTROL / DAT_USERINTERFACE / MSG_ENABLEDS)
    // 4. 訊息迴圈處理 XFERDONE, CLOSEDSREQ 等事件
    // 5. 接收影像資料 (DG_IMAGE / DAT_IMAGENATIVEXFER / MSG_GET)
    // 6. 儲存為暫存檔 (JPEG/TIFF/PDF),回傳檔案路徑字串
}

關鍵技術挑戰與解決方案

挑戰解決方案
視窗控制代碼 (HWND) 取得AWT/Swing 無直接 API,需用 JNA WindowUtils.getHWND() 或 JNI 透過 JAWT_GetAWT 取得
訊息迴圈阻塞 EDT掃描流程在原生執行緒跑訊息泵,Java 端用 Future/CompletableFuture 非同步等待結果
大影像記憶體管理原生層分塊寫入暫存檔,避免一次性分配巨大記憶體;Java 端用 ImageIO 串流讀取
跨平台部署Windows: jtwain.dll;Linux: libjtwain.so (需 SANE backend);Mac: libjtwain.jnilib (需 TWAIN-SANE bridge)
例外處理傳遞原生層用 ThrowNew 拋出 Java 例外,保留堆疊追蹤

現代化替代方案

  • TWAIN Direct / TWAIN Local:新一代 RESTful 掃描標準,無需驅動安裝
  • Web TWAIN (Dynamsoft):瀏覽器端 WASM 實作,支援 Chrome/Edge/Firefox
  • SANE (Linux):sane-backends + JNA 直接呼叫,免寫 JNI
  • JavaFX + JNA:現代 UI 框架搭配 JNA 降低 JNI 複雜度

部署檢查清單

  1. 確認目標機器已安裝掃描儀 TWAIN 驅動程式
  2. 將 jtwain.dll 置於 java.library.path 路徑(或 -Djava.library.path 指定)
  3. 32/64 位元一致性:JVM、DLL、驅動程式必須同位元寬度
  4. 簽名與權限:非簽名 DLL 在高安全環境可能被阻擋
  5. 測試多廠牌掃描儀相容性(HP, Canon, Epson, Fujitsu 等)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved