You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

控制台正常,Windows服务无法识别Digital Persona U.are.U 4500指纹采集器

问题描述

我尝试使用Topshelf将Digital Persona U.are.U 4500指纹采集器集成到Windows服务中,该程序在控制台模式下运行正常,但安装为Windows服务后,无法识别设备,也无法采集并保存指纹图像。当前使用的是U.are.U 3.2.0.89版本驱动,相关代码如下:

Program.cs

using System;
using System.Drawing.Imaging;
using System.Drawing;
using DPUruNet;
using DPXUru;
using System.Threading;
using Topshelf;
using DigitalPersonaConsole;
using log4net;
// 使用4500 U.are.U 3.2.0.89驱动
namespace FingerprintReaderApp 
{
    class Program 
    {
        private static readonly ILog _log = LogManager.GetLogger(typeof(Program));
  
        static void Main(string[] args)
        {
            HostFactory.Run(x =>
            {
                // x.DependsOn("DpHost");
                x.Service<MyService>(s =>
                {
                    s.ConstructUsing(service => new MyService());
                    s.WhenStarted(service => service.Start());
                    s.WhenStopped(service => service.Stop());
                });
                x.RunAsLocalSystem();
                x.SetServiceName("DigitalPersonaService");
                x.SetDisplayName("Digital Persona Service");
                x.SetDescription("This is Digital persona service test");
            });
        }
    }
}

MyService.cs

using DPUruNet;
using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FingerprintReaderApp;
using log4net;

namespace DigitalPersonaConsole
{
    public class MyService
    {
        private readonly ILog _log = LogManager.GetLogger(typeof(MyService));

        public Thread thread = null;
        public Reader currentReader;
        private ManualResetEventSlim fingerprintCapturedEvent = new ManualResetEventSlim(false);

        public MyService() {
            thread = new Thread(new ThreadStart(initService));
        }

        public void Start()
        {
            _log.Info("Start service....");
            thread.Start();
        }

        public void Stop()
        {
            _log.Info("Stop service....");
            thread.Join();
        }

        public void initService()
        {
            // 指纹阅读器索引
            int selectedReader = 1; 
            // 初始化阅读器
            var readers = ReaderCollection.GetReaders();
            _log.Info($"No of connceted readers: {readers.Count}");
            currentReader = readers[selectedReader];
            
            foreach (Reader reader in readers)
            {
                _log.Info(reader.Description.SerialNumber);
            }
            
            if (!OpenReader(currentReader))
            {
                //this.Close();
            }

            if (!StartCaptureAsync(OnCaptured))
            {
                //this.Close();
            }
            fingerprintCapturedEvent.Wait();
        }

        public bool OpenReader(Reader currentReader)
        {
            Constants.ResultCode result = Constants.ResultCode.DP_DEVICE_FAILURE;
            // 打开阅读器
            result = currentReader.Open(Constants.CapturePriority.DP_PRIORITY_EXCLUSIVE);
            if (result != Constants.ResultCode.DP_SUCCESS)
            {
                _log.Info("Error:  " + result);
                return false;
            }
            return true;
        }

        public void OnCaptured(CaptureResult captureResult)
        {
            try
            {
                // 检查采集质量,不合格则返回
                if (!CheckCaptureResult(captureResult)) return;

                // 创建位图
                foreach (Fid.Fiv fiv in captureResult.Data.Views)
                {
                    _log.Info($"Finger print captured:::::");
                    string outputPath = "fingerprint.bmp";
                    Bitmap bmp = CreateBitmap(fiv.RawImage, fiv.Width, fiv.Height);
                    bmp.Save(outputPath, ImageFormat.Bmp);
                }
            }
            catch (Exception ex)
            {
                _log.Info("Error:  " + ex.Message);
            }
        }

        public bool CheckCaptureResult(CaptureResult captureResult)
        {
            if (captureResult.Data == null || captureResult.ResultCode != Constants.ResultCode.DP_SUCCESS)
            {
                if (captureResult.ResultCode != Constants.ResultCode.DP_SUCCESS)
                {
                    throw new Exception(captureResult.ResultCode.ToString());
                }

                // 如果质量检测到假指纹则抛出错误
                if ((captureResult.Quality != Constants.CaptureQuality.DP_QUALITY_CANCELED))
                {
                    throw new Exception("Quality - " + captureResult.Quality);
                }
                return false;
            }
            return true;
        }

        public bool CaptureFingerAsync()
        {
            try
            {
                GetStatus();

                Constants.ResultCode captureResult = currentReader.CaptureAsync(Constants.Formats.Fid.ANSI, Constants.CaptureProcessing.DP_IMG_PROC_DEFAULT, currentReader.Capabilities.Resolutions[0]);
                if (captureResult != Constants.ResultCode.DP_SUCCESS)
                {
                    throw new Exception("" + captureResult);
                }
                return true;
            }
            catch (Exception ex)
            {
                _log.Info("Error:  " + ex.Message);
                return false;
            }
        }

        public Bitmap CreateBitmap(byte[] bytes, int width, int height)
        {
            byte[] rgbBytes = new byte[bytes.Length * 3];

            for (int i = 0; i <= bytes.Length - 1; i++)
            {
                rgbBytes[(i * 3)] = bytes[i];
                rgbBytes[(i * 3) + 1] = bytes[i];
                rgbBytes[(i * 3) + 2] = bytes[i];
            }
            Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);

            BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);

            for (int i = 0; i <= bmp.Height - 1; i++)
            {
                IntPtr p = new IntPtr(data.Scan0.ToInt64() + data.Stride * i);
                System.Runtime.InteropServices.Marshal.Copy(rgbBytes, i * bmp.Width * 3, p, bmp.Width * 3);
            }

            bmp.UnlockBits(data);
            return bmp;
        }

        public bool StartCaptureAsync(Reader.CaptureCallback OnCaptured)
        {
            // 激活采集回调
            currentReader.On_Captured += new Reader.CaptureCallback(OnCaptured);

            // 调用采集方法
            if (!CaptureFingerAsync())
            {
                return false;
            }
            return true;
        }

        public void GetStatus()
        {
            Constants.ResultCode result = currentReader.GetStatus();

            if ((result != Constants.ResultCode.DP_SUCCESS))
            {
                throw new Exception("" + result);
            }

            if ((currentReader.Status.Status == Constants.ReaderStatuses.DP_STATUS_BUSY))
            {
                Thread.Sleep(50);
            }
            else if ((currentReader.Status.Status == Constants.ReaderStatuses.DP_STATUS_NEED_CALIBRATION))
            {
                currentReader.Calibrate();
            }
            else if ((currentReader.Status.Status != Constants.ReaderStatuses.DP_STATUS_READY))
            {
                throw new Exception("Reader Status - " + currentReader.Status.Status);
            }
        }

        public void CancelCaptureAndCloseReader(Reader.CaptureCallback OnCaptured)
        {
            if (currentReader != null)
            {
                currentReader.CancelCapture();
                // 释放阅读器句柄并解绑事件
                currentReader.Dispose();
            }
        }
    }
}
解决方案

这个问题核心是Windows服务默认运行在Session 0隔离环境,而USB设备通常绑定到用户会话(如Session 1),Digital Persona驱动无法在Session 0访问用户会话的设备。结合代码问题,以下是具体修复步骤:

1. 解决Session隔离问题

临时测试方案:允许服务与桌面交互

在Topshelf配置中添加交互权限:

x.RunAsLocalSystem();
x.EnableInteractive(); // 新增该行

安装服务后,在服务属性的「登录」选项卡勾选「允许服务与桌面交互」。此方法能临时解决设备访问问题,但存在安全风险,不适合生产环境。

生产环境方案:让服务运行在用户会话

  • 修改Topshelf运行身份,使用当前登录用户(需有设备访问权限):
// 直接指定用户
x.RunAs(@"DOMAIN\Username", "Password");
// 或让用户在安装时输入凭证
x.RunAsPrompt();
  • 设置服务延迟启动,确保用户登录后设备已加载到会话:
x.StartAutomaticallyDelayed();

2. 修复代码中的潜在问题

问题1:硬编码阅读器索引

代码中selectedReader = 1硬编码索引,若只有一个设备会导致空引用。修改为:

int selectedReader = 0; 
var readers = ReaderCollection.GetReaders();
_log.Info($"No of connected readers: {readers.Count}");
if(readers.Count == 0)
{
    _log.Error("未找到指纹阅读器!");
    return;
}
currentReader = readers[selectedReader];

问题2:未处理设备重连

服务运行中设备可能断开,添加设备变化监听:

// 在initService方法开头添加
ReaderCollection.OnReaderAdded += ReaderCollection_OnReaderAdded;
ReaderCollection.OnReaderRemoved += ReaderCollection_OnReaderRemoved;

// 实现事件处理方法
private void ReaderCollection_OnReaderRemoved(object sender, ReaderEventArgs e)
{
    _log.Info($"阅读器已移除: {e.Reader.Description.SerialNumber}");
    if(currentReader == e.Reader)
    {
        CancelCaptureAndCloseReader(OnCaptured);
        currentReader = null;
    }
}

private void ReaderCollection_OnReaderAdded(object sender, ReaderEventArgs e)
{
    _log.Info($"阅读器已添加: {e.Reader.Description.SerialNumber}");
    if(currentReader == null)
    {
        currentReader = e.Reader;
        OpenReader(currentReader);
        StartCaptureAsync(OnCaptured);
    }
}

问题3:指纹保存路径错误

服务工作目录不是控制台程序目录,"fingerprint.bmp"会保存到C:\Windows\System32,需指定绝对路径:

// 先添加引用using System.IO;
string outputPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "fingerprint.bmp");

3. 验证驱动模式

确保Digital Persona驱动安装为服务模式而非仅用户模式。打开设备管理器,查看U.are.U 4500的驱动属性,确认支持Session 0访问;若不支持,重新安装驱动时选择「服务安装」选项。

内容的提问来源于stack exchange,提问作者Walulya francis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 08:32:33