通知通道

本部分包含有关 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 函数。 以下过程说明了调用此函数时每个输入参数的用途。 过程中的第一步适用于此函数中的第一个参数,第二个步骤适用于第二个参数,依此类而行。

若要创建通知通道,请指定以下项:

  1. 打印机或服务器的名称。

  2. 通知通道类型。 调用方可以指定要通过此通道发送的通知类型。

  3. 用户筛选器。 调用方可以指定要接收通知的用户,可以是通知发送方所在的用户,也可以是所有用户。

  4. 对话筛选器。 调用方必须指定这是单向通道还是双向通道。 若要将通道标记为单向通道,请将 CreatePrintAsyncNotifyChannel 类型为 IPrintAsyncNotifyChannel**) 的最后 一个 参数 (设置为 NULL

  5. 当通知从通道的另一端返回时,要调用的 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 回调函数已完成运行,则不得在通道上调用 CloseChannelRelease 或任何其他成员函数。 在这种情况下,通道已释放,因此任何进一步的调用都可能导致未定义的行为。 此限制可能需要前台线程和回调对象之间的协调。

    • 必须确保前台线程和回调对象协调对 CloseChannelRelease 的调用。 如果另一个线程即将调用或已完成调用 Release,则前台线程和回调对象无法开始对 CloseChannel 的调用。 可以使用 InterlockedCompareExchange 例程来实现此限制。 如果不使用 InterlockedCompareExchange,可能会导致未定义的行为。

  • 如果注册为通道上的侦听器,则可以调用 CloseChannel,然后在 IPrintAsyncNotifyCallback::OnEventNotify 回调函数中调用 Release 以结束双向通信。 但是,不得在 ChannelClosed 回调中调用 CloseChannelRelease

如果满足其中一个条件,则必须调用 Release。 如果不满足这些条件之一,则不得调用 Release

注意

在上述任一条件下调用 Release ,但第一个条件(在其中显式调用 AddRef )是常规 COM 编程模式的例外。 在这种情况下,IPrintAsyncNotifyChannel 不同于标准 COM 实践。