PosKeyboard 服务对象从 POS 键盘读取键。 POS 键盘可以是辅助键盘,也可以是虚拟键盘,由系统键盘上的部分或全部键组成。 在 Microsoft Point of Service for .NET (POS for .NET) 中,POS 键盘基类为 PosKeyboardBase。
PosKeyboard 服务对象遵循常规输入设备模型:
- 从 POS 键盘接收输入时,DataEvent 将排队。
- 如果 AutoDisable 属性为 true,则当 DataEvent 事件排队时,设备会自动禁用自身。 此操作由 PosKeyboardBase 类自动完成。
- 当 DataEventEnabled 属性为 true 且满足其他事件传递要求时,将排队的 DataEvent 事件传递到应用程序。 PosKeyboardBase 类将自动管理事件传递。
- 如果收集或处理输入时出错,则 ErrorEvent 事件将排队,并在 DataEventEnabled 为 true 且满足其他事件传递要求时传递到应用程序。
- 可以读取由 PosKeyboardBase 类维护的 DataCount 属性,以获取排队事件数。
- 可以通过调用 ClearInput() 删除所有排队的输入。
POS 键盘是专用设备:
- 应用程序必须先声明设备,然后才能启用设备。
- 应用程序必须在设备开始读取输入之前认领并启用设备。
本部分包含一个示例 PosKeyboard 服务对象,该对象生成使用 DataEvents 发送到应用程序的模拟击键。 此示例依赖于服务对象读取器线程简介中提供的线程帮助程序对象。 若要编译此示例,需要包含该主题中的代码。
编写服务对象
为 Microsoft.PointofService、Microsoft.PointOfService.BaseServiceObjects 和 System.Threading 添加 using 指令。
添加全局属性 PosAssembly。
为项目选择一个适当的命名空间名称。
创建派生自 PosKeyboardBase 的服务对象类。
将 ServiceObject 属性添加到服务对象类,使用 DeviceType.PosKeyboard 值作为设备类型。
向示例键盘服务对象添加功能
从“服务对象读取线程”部分创建派生自 ServiceObjectThreadHelper 的线程帮助程序类 KeyboardThreadingObject。
在 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 类会自动发送此事件。
示例
此示例演示如何开发简单的 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。