通知通道
本部分包含有关 CreatePrintAsyncNotifyChannel 函数和 IPrintAsyncNotifyChannel 接口的信息。
HRESULT
CreatePrintAsyncNotifyChannel(
IN LPCWSTR,
IN PrintAsyncNotificationType*,
IN PrintAsyncNotifyUserFilter,
IN PrintAsyncNotifyConversationStyle,
IN IPrintAsyncNotifyCallback*,
OUT IPrintAsyncNotifyChannel**
);
打印组件调用 CreatePrintAsyncNotifyChannel 函数来创建通知通道。 通道可以是每个打印机或每个服务器。
仅当后台处理程序加载组件时,打印组件才能打开通知通道。 如果调用方在应用程序内部而不是后台处理程序服务中运行,Winspool.drv 将禁用此功能。 例如,当应用程序加载驱动程序以执行呈现时,对 CreatePrintAsyncNotifyChannel 的调用将失败。 但是,如果后台处理程序服务加载了驱动程序,则相同的调用会成功。
Spoolss.lib 提供此功能,以便端口监视器可以打开通道。 在后台处理程序中运行的和链接到 Spoolss.lib 的组件可以调用 CreatePrintAsyncNotifyChannel 函数。 以下过程说明了调用此函数时每个输入参数的用途。 过程中的第一步适用于此函数中的第一个参数,第二个步骤适用于第二个参数,依此类而行。
若要创建通知通道,请指定以下项:
打印机或服务器的名称。
通知通道类型。 调用方可以指定要通过此通道发送的通知类型。
用户筛选器。 调用方可以指定要接收通知的用户,可以是通知发送方所在的用户,也可以是所有用户。
对话筛选器。 调用方必须指定这是单向通道还是双向通道。 若要将通道标记为单向通道,请将 CreatePrintAsyncNotifyChannel 类型为 IPrintAsyncNotifyChannel**) 的最后 一个 参数 (设置为 NULL。
当通知从通道的另一端返回时,要调用的 IPrintAsyncNotifyCallback 接口。 如果调用方对接收响应不感兴趣,则可以为 NULL。
当 CreatePrintAsyncNotifyChannel 返回时,第六个参数 (类型为 IPrintAsyncNotifyChannel**) 指向包含 IPrintAsyncNotifyChannel 对象的地址的内存位置。 此对象标识通道,并用于发送通知和关闭通道。
IPrintAsyncNotifyChannel 接口
IPrintAsyncNotifyChannel 接口标识通道,用于发送通知和关闭通道。 当打印组件调用 CreatePrintAsyncNotifyChannel 函数来创建通知通道时,后台处理程序服务将通过提供公开 IPrintAsyncNotifyChannel 接口的对象进行响应。
此接口继承自 IUnknown 接口,以便后台处理程序通知机制的客户端可以实现 COM 或 C++ 对象。 以下代码示例中的接口声明显示了此继承:
#define INTERFACE IPrintAsyncNotifyChannel
DECLARE_INTERFACE_(IPrintAsyncNotifyChannel, IUnknown)
{
STDMETHOD(QueryInterface)(
THIS_
REFIID riid,
void** ppvObj
) PURE;
STDMETHOD_(ULONG, AddRef)(
THIS
) PURE;
STDMETHOD_(ULONG, Release)(
THIS
) PURE;
STDMETHOD(SendNotification)(
THIS_
IN IPrintAsyncNotifyDataObject*
) PURE;
STDMETHOD(CloseChannel)(
THIS_
IN IPrintAsyncNotifyDataObject*
) PURE;
};
若要发送通知,发送方调用 IPrintAsyncNotifyChannel::SendNotification 方法。 发送方可以是打开通道并发送通知的打印组件,也可以是必须响应通知的侦听客户端。 此方法以异步方式运行。 当方法返回成功代码时,后台处理程序会尝试将通知发送给侦听器。 但不能保证任何侦听器都会收到通知。
若要关闭通道,发送方或侦听器可以调用 IPrintAsyncNotifyChannel::CloseChannel 方法。 调用方可以传入通知,该通知提供关闭通道的原因,也可以传递 NULL 指针。 通道关闭后,将放弃所有排队通知。
在通道对象上调用 Release 时必须小心,因为它不遵循所有常规 COM 编程固定项。 仅当出现以下情况时,才应在 IPrintAsyncNotifyChannel 上调用 Release:
如果显式调用 AddRef ,则必须将其与 对 Release 的调用匹配。
如果将通道创建为单向通道,并且必须在作为输出参数收到的指针上调用 Release 一次。 发送所需通知并关闭通道后,应调用 Release 。
如果将通道创建为双向通道,则可能需要对作为输出参数收到的指针调用 Release 一次。 仅当执行以下一项或多项操作时,才应调用 Release :
在为双向通道调用 Release 之前,必须始终调用 CloseChannel 并接收成功结果。 如果对 CloseChannel 的调用失败,则不得调用 Release,因为通道可能已代表你发布。
在进入 ChannelClosed 事件时,不得调用 Release。 若要避免这种情况,检查调用 CloseChannel 但失败并出现错误CHANNEL_ALREADY_CLOSED。 在这种情况下,无需调用 Release ,因为通道已代表你发布。
如果 ChannelClosed 回调函数已完成运行,则不得在通道上调用 CloseChannel、Release 或任何其他成员函数。 在这种情况下,通道已释放,因此任何进一步的调用都可能导致未定义的行为。 此限制可能需要前台线程和回调对象之间的协调。
必须确保前台线程和回调对象协调对 CloseChannel 和 Release 的调用。 如果另一个线程即将调用或已完成调用 Release,则前台线程和回调对象无法开始对 CloseChannel 的调用。 可以使用 InterlockedCompareExchange 例程来实现此限制。 如果不使用 InterlockedCompareExchange,可能会导致未定义的行为。
如果注册为通道上的侦听器,则可以调用 CloseChannel,然后在 IPrintAsyncNotifyCallback::OnEventNotify 回调函数中调用 Release 以结束双向通信。 但是,不得在 ChannelClosed 回调中调用 CloseChannel 或 Release。
如果满足其中一个条件,则必须调用 Release。 如果不满足这些条件之一,则不得调用 Release。
注意
在上述任一条件下调用 Release ,但第一个条件(在其中显式调用 AddRef )是常规 COM 编程模式的例外。 在这种情况下,IPrintAsyncNotifyChannel 不同于标准 COM 实践。