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

OPOS秤驱动开发:DLL注册成功但无法正常调用问题求助

问题:.NET Framework 4.8开发OPOS秤驱动注册后测试工具报错

背景

基于.NET Framework 4.8开发OPOS秤驱动,实现代码如下:

using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using OPOSCONSTANTSLib;
using OposScale_CCO;

namespace ScaleOPOS
{
    [ComVisible(true)]
    [Guid("D3673432-393B-4CFF-B382-CED8F12F3388")]
    [ProgId("ScaleOPOS.WeightScale")]
    [ClassInterface(ClassInterfaceType.None)]

    public class WeightScale : IOPOSScale
    {
        [ComRegisterFunction]
        public static void DllRegisterServer(Type type)
        {
            try
            {
                string classKey = @"Software\Wow6432Node\OLEforRetail\ServiceOPOS\Scale";
                using (RegistryKey key = Registry.LocalMachine.CreateSubKey(classKey + @"\EmulatedScale"))
                {
                    key.SetValue("FriendlyName", "WeightScale");
                    key.SetValue("ProgID", "ScaleOPOS.WeightScale");
                    key.SetValue("Version", "1.0");
                }

                // Запись для ProgID
                string progIdKey = @"Software\Wow6432Node\Classes\ScaleOPOS.WeightScale";
                using (RegistryKey progId = Registry.LocalMachine.CreateSubKey(progIdKey))
                {
                    progId.SetValue("", "WeightScale");
                    progId.SetValue("CLSID", type.GUID.ToString("B").ToUpper());
                }

                // Запись для CLSID
                string clsidKey = $@"Software\Wow6432Node\Classes\CLSID\{type.GUID.ToString("B").ToUpper()}";
                using (RegistryKey clsid = Registry.LocalMachine.CreateSubKey(clsidKey))
                {
                    clsid.SetValue("", "WeightScale"); // Имя вашего COM-объекта

                    using (RegistryKey inprocServer = clsid.CreateSubKey("InprocServer32"))
                    {
                        inprocServer.SetValue("", @"C:\Users\Dmitriy\Desktop\ScaleOPOSDriver\bin\Debug\ScaleOPOSDriver.dll"); // Укажите путь к вашей DLL
                        inprocServer.SetValue("ThreadingModel", "Apartment");
                    }
                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("Ошибка регистрации: " + ex.Message, ex);
            }
        }

        [ComUnregisterFunction]
        public static void DllUnregisterServer(Type type)
        {
            try
            {
                string classKey = @"Software\Wow6432Node\OLEforRetail\ServiceOPOS\Scale";
                Registry.LocalMachine.DeleteSubKey(classKey + "\\EmulatedScale", false);

                string progIdKey = @"Software\Wow6432Node\Classes\ScaleOPOS.WeightScale";
                Registry.LocalMachine.DeleteSubKey(progIdKey, false);

                string clsidKey = $@"Software\Wow6432Node\Classes\CLSID\{type.GUID.ToString("B").ToUpper()}";
                Registry.LocalMachine.DeleteSubKey(clsidKey, false);
            }
            catch (Exception ex)
            {
                // Здесь можно записать лог или вывести сообщение об ошибке
                throw new InvalidOperationException("Ошибка регистрации: " + ex.Message, ex);
            }
        }


        public WeightScale() { }

        public void SOData(int Status)
        {
            throw new NotImplementedException();
        }

        public void SODirectIO(int EventNumber, ref int pData, ref string pString)
        {
            throw new NotImplementedException();
        }

        public void SOError(int ResultCode, int ResultCodeExtended, int ErrorLocus, ref int pErrorResponse)
        {
            throw new NotImplementedException();
        }

        public void SOOutputCompleteDummy(int OutputID)
        {
            throw new NotImplementedException();
        }

        public void SOStatusUpdate(int Data)
        {
            throw new NotImplementedException();
        }

        public int SOProcessID()
        {
            throw new NotImplementedException();
        }

        public int CheckHealth(int Level)
        {
            throw new NotImplementedException();
        }

        public int ClaimDevice(int Timeout)
        {
            throw new NotImplementedException();
        }

        public int ClearInput()
        {
            throw new NotImplementedException();
        }

        public int Close()
        {
            throw new NotImplementedException();
        }

        public int DirectIO(int Command, ref int pData, ref string pString)
        {
            throw new NotImplementedException();
        }

        public int Open(string DeviceName)
        {
            throw new NotImplementedException();
        }

        public int ReleaseDevice()
        {
            throw new NotImplementedException();
        }

        public int ReadWeight(out int pWeightData, int Timeout)
        {
            throw new NotImplementedException();
        }

        public int DisplayText(string Data)
        {
            throw new NotImplementedException();
        }

        public int ZeroScale()
        {
            throw new NotImplementedException();
        }

        public int ResetStatistics(string StatisticsBuffer)
        {
            throw new NotImplementedException();
        }

        public int RetrieveStatistics(ref string pStatisticsBuffer)
        {
            throw new NotImplementedException();
        }

        public int UpdateStatistics(string StatisticsBuffer)
        {
            throw new NotImplementedException();
        }

        public int CompareFirmwareVersion(string FirmwareFileName, out int pResult)
        {
            throw new NotImplementedException();
        }

        public int UpdateFirmware(string FirmwareFileName)
        {
            throw new NotImplementedException();
        }

        public int OpenResult => throw new NotImplementedException();

        public string CheckHealthText => throw new NotImplementedException();

        public bool Claimed => throw new NotImplementedException();

        public bool DeviceEnabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
        public bool FreezeEvents { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        public int ResultCode => throw new NotImplementedException();

        public int ResultCodeExtended => throw new NotImplementedException();

        public int State => throw new NotImplementedException();

        public string ControlObjectDescription => throw new NotImplementedException();

        public int ControlObjectVersion => throw new NotImplementedException();

        public string ServiceObjectDescription => throw new NotImplementedException();

        public int ServiceObjectVersion => throw new NotImplementedException();

        public string DeviceDescription => throw new NotImplementedException();

        public string DeviceName => throw new NotImplementedException();

        public int MaximumWeight => throw new NotImplementedException();

        public int WeightUnit => throw new NotImplementedException();

        public int WeightUnits => throw new NotImplementedException();

        public int BinaryConversion { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        public bool CapDisplay => throw new NotImplementedException();

        public bool AutoDisable { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        public int CapPowerReporting => throw new NotImplementedException();

        public int DataCount => throw new NotImplementedException();

        public bool DataEventEnabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
        public int PowerNotify { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        public int PowerState => throw new NotImplementedException();

        public bool AsyncMode { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        public bool CapDisplayText => throw new NotImplementedException();

        public bool CapPriceCalculating => throw new NotImplementedException();

        public bool CapTareWeight => throw new NotImplementedException();

        public bool CapZeroScale => throw new NotImplementedException();

        public int MaxDisplayTextChars => throw new NotImplementedException();

        public decimal SalesPrice => throw new NotImplementedException();

        public int TareWeight { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
        public decimal UnitPrice { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        public bool CapStatisticsReporting => throw new NotImplementedException();

        public bool CapUpdateStatistics => throw new NotImplementedException();

        public bool CapCompareFirmwareVersion => throw new NotImplementedException();

        public bool CapUpdateFirmware => throw new NotImplementedException();

        public bool CapStatusUpdate => throw new NotImplementedException();

        public int ScaleLiveWeight => throw new NotImplementedException();

        public int StatusNotify { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
        public bool ZeroValid { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
    }
}

已尝试操作

  • 使用Regasm name.dll /codebase命令注册DLL成功,第三方OPOS测试工具可在列表中找到该驱动,但选择后报错
  • 不指定注册表路径时,OPOS测试工具无法识别驱动
  • 使用regsvr32注册DLL时,提示“找不到DllRegisterServer入口点”

求助点

  1. 问题根源是什么?
  2. 注册表是否必须显式指定路径?

解决方案

问题根源分析

  1. regsvr32注册失败原因:你的DLL是.NET托管程序集,regsvr32仅支持注册非托管COM组件,因此必然提示找不到DllRegisterServer入口点,正确注册方式为使用Regasm。
  2. 测试工具报错核心原因:
    • 自定义注册逻辑中硬编码了DLL绝对路径,一旦DLL移动位置,注册信息直接失效;
    • IOPOSScale接口的所有方法均抛出NotImplementedException,测试工具调用核心方法时会直接触发异常;
    • 手动创建的注册表项不符合OPOS规范,缺少必要字段或路径适配逻辑。

注册表相关说明

OPOS确实需要在特定注册表路径注册才能被测试工具识别,但不需要手动硬编码COM相关路径:

  • Regasm会自动处理CLSID、ProgID等COM基础注册表项,无需手动创建Wow6432Node\Classes下的内容,它会根据系统位数自动适配;
  • 仅需手动创建OPOS专用的Service Object注册表项(Software\OLEforRetail\ServiceOPOS\Scale\EmulatedScale)即可。

修复步骤

  1. 简化注册逻辑:删除自定义注册/注销方法中手动创建CLSID、ProgID的代码,仅保留OPOS专用注册表项的处理:
[ComRegisterFunction]
public static void DllRegisterServer(Type type)
{
    try
    {
        // 仅创建OPOS Service Object专属注册项
        string oposKeyPath = @"Software\OLEforRetail\ServiceOPOS\Scale\EmulatedScale";
        using (RegistryKey key = Registry.LocalMachine.CreateSubKey(oposKeyPath))
        {
            key.SetValue("FriendlyName", "WeightScale");
            key.SetValue("ProgID", "ScaleOPOS.WeightScale");
            key.SetValue("Version", "1.0");
            // 可选:添加OPOS规范要求的其他字段,如"DeviceCategory"
        }
    }
    catch (Exception ex)
    {
        throw new InvalidOperationException("注册失败: " + ex.Message, ex);
    }
}

[ComUnregisterFunction]
public static void DllUnregisterServer(Type type)
{
    try
    {
        string oposKeyPath = @"Software\OLEforRetail\ServiceOPOS\Scale\EmulatedScale";
        Registry.LocalMachine.DeleteSubKey(oposKeyPath, false);
    }
    catch (Exception ex)
    {
        throw new InvalidOperationException("注销失败: " + ex.Message, ex);
    }
}
  1. 实现核心接口方法:至少完成Open、ClaimDevice、ReadWeight等核心方法的实现,避免抛出NotImplementedException,示例:
public int Open(string DeviceName)
{
    // 初始化逻辑:如模拟硬件连接、初始化状态
    return OPOSCONSTANTS.SUCCESS;
}

public int ClaimDevice(int Timeout)
{
    Claimed = true;
    return OPOSCONSTANTS.SUCCESS;
}

public int ReadWeight(out int pWeightData, int Timeout)
{
    // 模拟返回重量数据,单位为克
    pWeightData = 100;
    return OPOSCONSTANTS.SUCCESS;
}
  1. 重新注册DLL:以管理员权限打开命令提示符,执行以下命令:
Regasm ScaleOPOSDriver.dll /codebase /tlb

(/tlb参数会生成类型库,帮助OPOS测试工具正确识别接口定义)

内容的提问来源于stack exchange,提问作者Dmitriy Bobrov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 05:23:09