PosKeyboard 服务对象从 POS 键盘读取键。 POS 键盘可以是辅助键盘,也可能是包含系统键盘上部分或全部键的虚拟键盘。 在 .NET 版的 Microsoft 销售点(POS for .NET)中,POS 键盘 基类为 PosKeyboardBase。
PosKeyboard 服务对象遵循常规输入设备模型:
- 从 POS 键盘接收到输入时,会将 DataEvent 排入队列。
- 如果 AutoDisable 属性 为 true,则当 DataEvent 事件排队时,设备会自动禁用自身。 这由 PosKeyboardBase 类自动完成。
- 当 DataEvent 事件已排入队列,并且 DataEventEnabled 属性为 true 且满足其他事件传递要求时,该事件将传递到应用程序。 PosKeyboardBase 类将自动管理事件传递。
- 当收集或处理输入时出错,ErrorEvent 事件会排入队列。当 DataEventEnabled 为true 并且满足其他事件传递要求时,该事件将被传递到应用程序。
- DataCount 属性由 PosKeyboardBase 类维护,可以读取它以获取排队事件数。
- 所有排队输入都可以通过调用 ClearInput()来删除。
POS 键盘是独占使用的设备:
- 应用程序在启用设备之前必须声明该设备。
- 应用程序必须在设备开始读取输入之前声明并启用设备。
本部分包含一个 示例 PosKeyboard 服务对象,该对象生成使用 DataEvents 发送到应用程序的模拟击键。 此示例依赖于 服务对象读取器线程介绍中介绍的线程助手对象。 若要编译此示例,需要包含该主题中的代码。
编写服务对象
为 Microsoft.PointofService、Microsoft.PointOfService.BaseServiceObjects 和 System.Threading 添加 using 指令。
添加全局属性 PosAssembly。
为项目选择适当的命名空间名称。
创建派生自 PosKeyboardBase 的服务对象类。
将
ServiceObject属性添加到 Service Object 类,使用 DeviceType.PosKeyboard 值作为设备类型。
向示例键盘服务对象添加功能
创建一个线程帮助程序类 KeyboardThreadingObject,派生自 ServiceObjectThreadHelper 的 Service 对象读取线程 部分。
在 KeyboardThreadingObject 类中实现 ServiceObjectThreadProcedure 方法。 这是将在单独的线程上运行的过程。 在下面的示例代码中,此方法模拟键盘输入。
此示例类实现名为 SendKey 的方法和另一个名为 ChangePowerState 的方法。 这些方法是受保护成员周围的包装器。 由于它们受到保护,因此无法直接从线程处理对象调用它们。
重写 PosCommon.Open 方法以指定此服务对象支持的功能并创建新的线程帮助程序对象。
专门重写 PosCommon.DeviceEnable 以 打开 和 关闭 线程帮助程序。
从 PosCommon 实现抽象虚拟方法,提供最低功能。
运行应用程序
此示例服务对象可与 POS for .NET 软件开发工具包(SDK)提供的测试应用程序一起运行。
测试服务对象
启动测试应用程序,然后从键盘下拉列表中选择 SamplePosKeyboard。
打开 并 声明 设备,然后选择带有 DeviceEnabled 复选框的设备以启用该设备。
选中 DataEventEnabled 框将允许服务对象向应用程序发送单个模拟密钥。 当调用 KeyDown 时,DataEvent 会由 PosKeyboardBase 类自动排队。
选择“ 自动启用数据事件 ”框后,服务对象可以继续传递字符(两秒)。
服务对象将发送字符“a”到“z”的模拟键笔划。 之后,将发送 StatusUpdateEvent 事件,指示设备现在处于脱机状态。 当 Properties.PowerState 发生更改时,PosKeyboardBase 类会自动发送此事件。
Example
此示例演示如何开发简单的 PosKeyboard 服务对象。 它支持单独的读取器线程以异步方式将 DataEvents 发送到应用程序。 编译后,可以将服务对象与 POS for .NET SDK 附带的测试应用程序一起执行。
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Microsoft.PointOfService;
using Microsoft.PointOfService.BaseServiceObjects;
[assembly: PosAssembly("Service Object Contractors, Inc.")]
namespace SOSamples.Keyboard
{
[ServiceObject(
DeviceType.PosKeyboard,
"SamplePosKeyboard",
"Sample PosKeyboard Service Object",
1,
9)]
public class SampleKeyboard : PosKeyboardBase
{
KeyboardThreadingObject ReadThread = null;
public SampleKeyboard()
{
// DevicePath must be set before Open() is called.
// In the case of Play and Plug hardware, the
// POS for .NET Base class will set this value.
DevicePath = "Sample Keyboard";
// NOTE: You can test the power notification events
// sent from this Service Object by selecting the
// "Power Notify" check box.
// Let the application know advanced power
// reporting is supported.
Properties.CapPowerReporting = PowerReporting.Advanced;
Properties.CapKeyUp = false;
}
~SampleKeyboard()
{
// Code added from previous sections to terminate
// the read thread started by the thread-helper
// object.
if (ReadThread != null)
{
ReadThread.CloseThread();
}
Dispose(false);
}
// Expose the protected KeyDown() method so that it can be
// called from our threading helper.
public void SendKey(int key)
{
KeyDown(key);
}
// Expose the protected PowerState property so it can be
// changed from the threading helper.
public void ChangePowerState(PowerState state)
{
Properties.PowerState = state;
}
#region Override Virtual PosCommon Members
public override void Open()
{
base.Open();
// Create the reader-thread object.
ReadThread = new KeyboardThreadingObject(this);
}
public override bool DeviceEnabled
{
get
{
return base.DeviceEnabled;
}
set
{
if (value != base.DeviceEnabled)
{
base.DeviceEnabled = value;
if (value == false)
{
// Stop the reader thread when the device
// is disabled.
ReadThread.CloseThread();
}
else
{
try
{
// By enabling the device, start the
// reader thread.
ReadThread.OpenThread();
}
catch (Exception e)
{
base.DeviceEnabled = false;
if (e is PosControlException)
throw;
throw new PosControlException(
"Unable to Enable Device",
ErrorCode.Failure, e);
}
}
}
}
}
#endregion Override Virtual PosCommon Members
#region Implement Abstract PosCommon Members
private string MyHealthText = "";
// PosCommon.CheckHealthText.
public override string CheckHealthText
{
get
{
// VerifyState(mustBeClaimed,
// mustBeEnabled).
VerifyState(false, false);
return MyHealthText;
}
}
// PosCommon.CheckHealth.
public override string CheckHealth(
HealthCheckLevel level)
{
// Verify that device is open, claimed and enabled.
VerifyState(true, true);
// Your code here:
// Check the health of the device and return a
// descriptive string.
// Cache result in the CheckHealthText property.
MyHealthText = "Ok";
return MyHealthText;
}
// PosCommon.DirectIOData.
public override DirectIOData DirectIO(
int command,
int data,
object obj)
{
// Verify that the device is open.
VerifyState(false, false);
return new DirectIOData(data, obj);
}
#endregion Implement Abstract PosCommon Members
}
#region Thread Helper Class
public class KeyboardThreadingObject :
ServiceObjectThreadHelper, IDisposable
{
// This is a helper class which will depend on
// being able to call back into the actual Service
// Object to pass along data. However, you cannot
// keep a strong reference to the Service Object,
// since that may prevent clean disposal, leaving
// hardware resources unavailable to other processes.
// Therefore, you create a weak reference. From this
// reference, you can get a temporary strong reference,
// which you can act on and then release.
WeakReference ServiceObjectReference;
// The name of the Service Object.
string ObjectName;
public KeyboardThreadingObject(SampleKeyboard so)
{
ObjectName = GetType().Name;
ServiceObjectReference = new WeakReference(so);
}
// This method will be called during initialization.
public override void ServiceObjectThreadOpen()
{
Logger.Info(ObjectName, "Keyboard Thread Open");
}
// This method will be called curing shutdown.
public override void ServiceObjectThreadClose()
{
Logger.Info(ObjectName, "Keyboard Thread Open");
}
// Your code used to monitor your device for input should
// go here. The implementation below generates simulated
// input as an example.
public override void ServiceObjectThreadProcedure(
AutoResetEvent ThreadStopEvent)
{
Logger.Info(ObjectName,
"Keyboard Thread Procedure Entered");
int KeyValue = (int)'a';
while (true)
{
// When this method is called by the
// ServiceObjectThreadHelper, it is obligated to
// exit when the event ThreadStopEvent has been
// set.
if (ThreadStopEvent.WaitOne(2000, false))
{
break;
}
if (KeyValue <= (int) 'z')
{
Logger.Info(ObjectName, "Reader Thread cycling");
// Try to get a strong reference to the Service
// Object using the weak reference saved when
// this helper object was created.
SampleKeyboard Keyboard =
ServiceObjectReference.Target
as SampleKeyboard;
// If this fails, that means the Service Object
// has already been disposed of - exit the thread.
if (Keyboard == null)
{
break;
}
if (Keyboard.DataEventEnabled == true)
{
// Call a method implemented in our Keyboard
// class to queue the key stroke.
Keyboard.SendKey(KeyValue);
// Simulate input by moving through the
// alphabet, sending one character at a time.
KeyValue++;
if (KeyValue >= (int)'z')
{
// Once you run out of input, simulate a
// power state change. Setting the SO's
// PowerState property to
// PowerState.Offline will cause a
// StatusUpdateEvent to be sent to the
// application.
Keyboard.ChangePowerState(
PowerState.Offline);
// Release the strong reference.
Keyboard = null;
// There is no more work, so exit the
// loop.
break;
}
}
// Release the strong reference.
Keyboard = null;
}
}
}
}
#endregion Thread Helper Class
}
编译代码
- 此示例要求包含 “服务对象读取器线程” 部分中的代码。
- 必须引用程序集 Microsoft.PointOfService 和 Microsoft.PointOfService.BaseServiceObjects 。