2016-11-06 123 views
0

我试图从v3打印机驱动程序或端口监视器接收回调,但想从C#中执行此操作。 The docs say that we should be able to just pass一个实现IPrintAsyncNotifyCallbackRegisterForPrintAsyncNotifications的对象,并且在C++中有一个示例,但我找不到能够在C#中实现此接口的TLB。我该如何注册C#中的`IPrintAsyncNotifyCallback`#

另外,RegisterForPrintAsyncNotifications似乎不是从文档(Spoolss.dll)中指定的dll导出的。

如何在没有TLB的情况下实现IPrintAsyncNotifyCallback?我如何找到RegisterForPrintAsyncNotifications

回答

2

首先,请考虑改为编写v4打印驱动程序。尽管很快v3驱动程序支持不可能在任何时候下降,但v4驱动程序肯定是新开发的途径。他们也有一个用于回调的.Net API,它可以避免必须自己编写互操作。 See PrinterExtensionSample.

实现一个COM接口没有一个TLB

为了实现IPrintAsyncNotifyCallbackIPrintAsyncNotifyChannel,它refereces的IPrintAsyncNotifyDataObject接口,我们可以发现在prnasnot.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的顶部和方法排序和签名允许我们translate these to the appropriate ComImports

[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 */; 
    } 
}