0

我正在尝试从 v3 打印机驱动程序或端口监视器接收回调,但想从 C# 接收回调。 文档说我们应该能够只传递一个实现IPrintAsyncNotifyCallbackto的对象RegisterForPrintAsyncNotifications,并且在 C++ 中有一个示例,但是我找不到能够在 C# 中实现这个接口的 TLB。

此外,RegisterForPrintAsyncNotifications似乎没有从文档 ( Spoolss.dll) 中命名的 dll 导出。

IPrintAsyncNotifyCallback如果没有 TLB ,我该如何实现?如何定位RegisterForPrintAsyncNotifications

4

1 回答 1

3

首先,考虑编写一个 v4 打印驱动程序。虽然 v3 驱动程序支持不太可能很快被放弃,但 v4 驱动程序无疑是新开发的方式。他们还有一个用于回调的 .Net API,避免了自己编写互操作。 PrinterExtensionSample

在没有 TLB 的情况下实现 COM 接口

为了实现IPrintAsyncNotifyCallback和它所引用IPrintAsyncNotifyChannel的接口,我们可以在下面找到它们的定义。IPrintAsyncNotifyDataObjectprnasnot.h

// ... snip ...
DEFINE_GUID(IID_IPrintAsyncNotifyChannel,        0x4a5031b1, 0x1f3f, 0x4db0, 0xa4, 0x62, 0x45, 0x30, 0xed, 0x8b, 0x04, 0x51);
DEFINE_GUID(IID_IPrintAsyncNotifyCallback,       0x7def34c1, 0x9d92, 0x4c99, 0xb3, 0xb3, 0xdb, 0x94, 0xa9, 0xd4, 0x19, 0x1b);
DEFINE_GUID(IID_IPrintAsyncNotifyDataObject,     0x77cf513e, 0x5d49, 0x4789, 0x9f, 0x30, 0xd0, 0x82, 0x2b, 0x33, 0x5c, 0x0d);
// ... snip ...
DECLARE_INTERFACE_(IPrintAsyncNotifyDataObject, IUnknown)
{
    // ... snip ...
};
// ... snip ...
DECLARE_INTERFACE_(IPrintAsyncNotifyChannel, IUnknown)
{
    // ... snip ...
};
// ... snip ...
DECLARE_INTERFACE_(IPrintAsyncNotifyCallback, IUnknown)
{
    STDMETHOD(QueryInterface)(
        THIS_
        _In_        REFIID riid,
        _Outptr_ void   **ppvObj
        ) PURE;

    STDMETHOD_(ULONG, AddRef)(
        THIS
        ) PURE;

    STDMETHOD_(ULONG, Release)(
        THIS
        ) PURE;

    STDMETHOD(OnEventNotify)(
         THIS_
         _In_ IPrintAsyncNotifyChannel    *pChannel,
         _In_ IPrintAsyncNotifyDataObject *pData
         ) PURE;

    STDMETHOD(ChannelClosed)(
         THIS_
         _In_ IPrintAsyncNotifyChannel    *pChannel,
         _In_ IPrintAsyncNotifyDataObject *pData
         ) PURE;
};

IID顶部的s 以及方法排序和签名允许我们将这些转换为适当ComImport的 s

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("77cf513e-5d49-4789-9f30-d0822b335c0d")]
public interface IPrintAsyncNotifyDataObject
{
    void AcquireData(out IntPtr data, out uint cbData, out IntPtr schema);
    void ReleaseData();
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("4a5031b1-1f3f-4db0-a462-4530ed8b0451")]
public interface IPrintAsyncNotifyChannel
{
    void SendNotification(IPrintAsyncNotifyDataObject data);
    void CloseChannel(IPrintAsyncNotifyDataObject data);
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("7def34c1-9d92-4c99-b3b3-db94a9d4191b")]
public interface IPrintAsyncNotifyCallback
{
    void OnEventNotify(IPrintAsyncNotifyChannel channel, IPrintAsyncNotifyDataObject data);
    void ChannelClosed(IPrintAsyncNotifyChannel channel, IPrintAsyncNotifyDataObject data);
}

public enum PrintAsyncNotifyUserFilter : uint
{
    kPerUser = 0,
    kAllUsers = 1
}

public enum PrintAsyncNotifyConversationStyle : uint
{
    kBiDirectional = 0,
    kUniDirectional = 1
}

查找 RegisterForPrintAsyncNotifications

由于 C++ 示例按原样工作,并且RegisterForPrintAsyncNotifications是一个导入 - 而不是宏 - 链接器将查看WinSpool.lib文档中指定的文件以找到适当的 dll。我们可以使用dumpbin.

c:\Drop>dumpbin -headers "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10586.0\um\x64\WinSpool.Lib" > out.txt
// ... snip ...
  Version      : 0
  Machine      : 8664 (x64)
  TimeDateStamp: 56F9F510 Mon Mar 28 22:22:56 2016
  SizeOfData   : 00000030
  DLL name     : WINSPOOL.DRV
  Symbol name  : RegisterForPrintAsyncNotifications
  Type         : code
  Name type    : name
  Hint         : 162
  Name         : RegisterForPrintAsyncNotifications
// ... snip ...

这表明它RegisterForPrintAsyncNotifications实际上是从WINSPOOL.DRV.

[DllImport("WINSPOOL.DRV", PreserveSig = false, ExactSpelling = true)]
public static extern void RegisterForPrintAsyncNotifications(
    [MarshalAs(UnmanagedType.LPWStr)] string name,
    [MarshalAs(UnmanagedType.LPStruct)] Guid notificationType, PrintAsyncNotifyUserFilter filter,
    PrintAsyncNotifyConversationStyle converstationStyle,
    IPrintAsyncNotifyCallback callback, out PrintAsyncNotificationSafeHandle handle);

[DllImport("WINSPOOL.DRV", PreserveSig = true, ExactSpelling = true)]
public static extern int UnRegisterForPrintAsyncNotifications(IntPtr handle);

public sealed class PrintAsyncNotificationSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
{
    public PrintAsyncNotificationSafeHandle()
        : base(true)
    {
    }

    protected override bool ReleaseHandle()
    {
        return UnRegisterForPrintAsyncNotifications(handle) == 0 /* S_OK */;
    }
}
于 2016-11-06T22:10:59.467 回答