Notification Channel

This section contains information about the CreatePrintAsyncNotifyChannel function and the IPrintAsyncNotifyChannel interface.

HRESULT
 CreatePrintAsyncNotifyChannel(
    IN LPCWSTR,
    IN PrintAsyncNotificationType*,
    IN PrintAsyncNotifyUserFilter,
    IN PrintAsyncNotifyConversationStyle,
    IN IPrintAsyncNotifyCallback*,
 OUT IPrintAsyncNotifyChannel**
    );

Printing components call the CreatePrintAsyncNotifyChannel function to create a notification channel. 通道可以是每打印机或每服务器。

仅当后台处理程序加载组件时,打印组件才能打开通知通道。 如果调用方在应用程序内部运行,而不是在后台处理程序服务中运行,Winspool.drv 将禁用此功能。 For example, when the application loads the driver to perform rendering, a call to CreatePrintAsyncNotifyChannel fails. 但是,如果后台处理程序服务加载驱动程序,则相同的调用会成功。

Spoolss.lib 提供此功能,以便端口监视器可以打开通道。 Components that run inside the spooler and that are linked to Spoolss.lib can call the CreatePrintAsyncNotifyChannel function. 以下过程说明了对此函数的调用中每个输入参数的用途。 过程中的第一步适用于此函数中的第一个参数,第二步适用于第二个参数,依此类说。

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

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

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

  3. 用户筛选器。 调用方可以指定接收通知的用户、与通知发送方相同的用户或所有用户。

  4. 聊天筛选器。 调用方必须指定这是单向通道还是双向通道。 To mark the channel as unidirectional, set the last parameter (of type IPrintAsyncNotifyChannel**) of CreatePrintAsyncNotifyChannel to NULL.

  5. The IPrintAsyncNotifyCallback interface to be called when a notification comes back from the other end of the channel. This can be NULL, if the caller is not interested in receiving responses.

When CreatePrintAsyncNotifyChannel returns, the sixth parameter (of type IPrintAsyncNotifyChannel**) points to a memory location that contains the address of an IPrintAsyncNotifyChannel object. 此对象标识通道,用于发送通知和关闭通道。

IPrintAsyncNotifyChannel Interface

The IPrintAsyncNotifyChannel interface identifies a channel and is used to send notifications and to close the channel. When a printing component calls the CreatePrintAsyncNotifyChannel function to create a notification channel, the spooler service responds by providing an object that exposes the IPrintAsyncNotifyChannel interface.

This interface inherits from the IUnknown interface so that the clients of the spooler notification mechanism can implement either a COM or a C++ object. 以下代码示例中的接口声明显示此继承:

#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;
};

To send a notification, the sender calls the IPrintAsyncNotifyChannel::SendNotification method. 发送方可以是打开通道的打印组件,并在必须响应通知时发送通知或侦听客户端。 此方法的行为异步。 当该方法返回成功代码时,后台处理程序会尝试将通知发送到侦听器。 但不能保证任何侦听器都会收到通知。

To close the channel, the sender or a listener can call the IPrintAsyncNotifyChannel::CloseChannel method. The caller can pass in a notification that gives the reason for closing the channel or can pass a NULL pointer. 关闭通道后,将丢弃所有排队通知。

You must be careful in calling Release on a channel object, because it does not follow all the general COM programming invariants. You should call Release on IPrintAsyncNotifyChannel only if the following conditions occur:

  • If you called AddRef explicitly, and you must match it with a call to Release.

  • If you created the channel as unidirectional, and you must call Release one time on the pointer that you received as an output parameter. You should call Release after you have sent the desired notifications and closed the channel.

  • If you created the channel as bidirectional, you might have to call Release one time on the pointer that you received as an output parameter. You should call Release only if you do one or more of the following:

    • Before you call Release for a bidirectional channel, you must always call CloseChannel and receive a success result. You must not call Release if the call to CloseChannel fails, because the channel might have already been released on your behalf.

    • You must not call Release while entering the ChannelClosed event. To avoid this situation, check for a call to CloseChannel that has failed with the error CHANNEL_ALREADY_CLOSED. You do not have to call Release in this case, because the channel has already been released on your behalf.

    • You must not call CloseChannel, Release, or any other member function on the channel if your ChannelClosed callback function has finished running. 在这种情况下,通道已发布,因此任何进一步调用都可能导致未定义的行为。 此限制可能需要在前台线程和回调对象之间进行协调。

    • You must make sure that your foreground thread and callback object coordinate the call to CloseChannel and Release. Your foreground thread and your callback object cannot begin a call to CloseChannel if the other is about to call or has completed calling Release. You can implement this restriction by using the InterlockedCompareExchange routine. If you do not use InterlockedCompareExchange, you might cause undefined behavior.

  • If you registered as a listener on the channel, you can call CloseChannel and then call Release in your IPrintAsyncNotifyCallback::OnEventNotify callback function to end the bidirectional communication. However, you must not call CloseChannel or Release in your ChannelClosed callback.

If you meet one of these conditions, you must call Release. If you do not meet one of these conditions, you must not call Release.

Note

Calling Release under any of the preceding conditions but the first, in which you call AddRef explicitly, is an exception to general COM programming patterns. IPrintAsyncNotifyChannel differs from standard COM practice in this situation.