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入口点”
求助点
- 问题根源是什么?
- 注册表是否必须显式指定路径?
解决方案
问题根源分析
- regsvr32注册失败原因:你的DLL是.NET托管程序集,regsvr32仅支持注册非托管COM组件,因此必然提示找不到
DllRegisterServer入口点,正确注册方式为使用Regasm。 - 测试工具报错核心原因:
- 自定义注册逻辑中硬编码了DLL绝对路径,一旦DLL移动位置,注册信息直接失效;
IOPOSScale接口的所有方法均抛出NotImplementedException,测试工具调用核心方法时会直接触发异常;- 手动创建的注册表项不符合OPOS规范,缺少必要字段或路径适配逻辑。
注册表相关说明
OPOS确实需要在特定注册表路径注册才能被测试工具识别,但不需要手动硬编码COM相关路径:
Regasm会自动处理CLSID、ProgID等COM基础注册表项,无需手动创建Wow6432Node\Classes下的内容,它会根据系统位数自动适配;- 仅需手动创建OPOS专用的Service Object注册表项(
Software\OLEforRetail\ServiceOPOS\Scale\EmulatedScale)即可。
修复步骤
- 简化注册逻辑:删除自定义注册/注销方法中手动创建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); } }
- 实现核心接口方法:至少完成
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; }
- 重新注册DLL:以管理员权限打开命令提示符,执行以下命令:
Regasm ScaleOPOSDriver.dll /codebase /tlb
(/tlb参数会生成类型库,帮助OPOS测试工具正确识别接口定义)
内容的提问来源于stack exchange,提问作者Dmitriy Bobrov
相关产品推荐
相关产品推荐

