3

我知道,WinFax Pro 是 1998 年的。

(注意:这不是 WinFax.dll,它显然是 Windows 的一部分。这是 WinFax Pro,一个单独的商业附加应用程序,最初来自 Delrina,后来被赛门铁克收购)。

我在仍然使用 WinFax Pro 作为操作系统的办公室工作。他们将客户传真号码存储在 WinFax Pro“电话簿”中,并使用它来通知客户服务访问。它现在的工作方式是,某人查看从 Mac 日历生成的(打印的)时间表,然后单击 WinFax 电话簿中的所有适当条目,以发送通知传真。

这就像我们过去所说的“转椅”集成,但它指的是 2 个屏幕。这甚至不是 2 个屏幕——它是一张纸和一个屏幕。

无论如何,我正在尝试使其自动化并遇到麻烦。

好消息:
- WinFax Pro 将其功能公开为 COM 对象:用于传真发送引擎的 WinFax.SDKSend;WinFax.SDKPhoneBook 为通讯录等。
- WinFax Pro 提供了一个类型库 wfxctl32.tlb,它描述了这些不同的 COM 对象。- 我能够通过从 tlbimport 生成的包装器成功使用 .NET (C#) 中的 WinFax.SDKSend 对象。(我使用的是 .NET 3.5,不能使用 .NET 4.0)。

坏消息:
除了 WinFax.SDKSend,我无法调用 WinFax COM 对象上的任何方法。签名看起来并不比 WinFax.SDKSend 中的签名复杂,但我不断收到异常。

C# 代码:

public void Run()
{
    var pb = new wfxctl32.CSDKPhoneBook();
    string id = pb.GetFolderListFirst(1, "");
}

例外:

异常:System.InvalidCastException:无法将类型为“wfxctl32.CSDKPhoneBookClass”的 COM 对象转换为接口类型“wfxctl32.ISDKPhoneBook”。此操作失败,因为 IID 为“{A67FCC81-9949-11D0-961E-444553540000}”的接口的 COM 组件上的 QueryInterface 调用因以下错误而失败:不支持此类接口(来自 HRESULT 的异常:0x80004002 (E_NOINTERFACE)) .

在 System.RuntimeType.InvokeDispMethod(字符串名称,BindingFlags invokeAttr,对象目标,Object[] args,Boolean[] byrefModifiers,Int32 文化,String[] namedParameters)
在 System.RuntimeType.InvokeMember(字符串名称,BindingFlags bindingFlags,Binder binder,在 wfxctl32.CSDKPhoneBookClass
的 System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData)中的对象目标、Object[] providedArgs、ParameterModifier[] 修饰符、CultureInfo 文化、String[] namedParams)
。 GetFolderListFirst(Int16 standardFolder, String folderID)

4

3 回答 3

1

我打开了OleView 工具,它是 Windows SDK 的一部分,我可以看到对象上没有 WinFax.SDKPhoneBook 的 COM 接口。嗯,这是一个惊喜。接口由类型库描述,tlbimport.exe 为其生成一个包装器。该接口也记录在 WinFax Pro SDK PDF 文档中。但是我找不到任何人成功使用 WinFax.SDKPhoneBook 的早期绑定接口的示例。

当我尝试使用 javascript 调用 COM 对象时,它工作得很好。

function say(x){ WScript.Echo(x); }

var Folder = function(id) {
    this.Id = id;
    this.DisplayName = null;
    this.Parent = null;
};

Folder.prototype.GetFolderName = function() {
    if (this.DisplayName === null) {
        this.DisplayName = comObject.GetFolderDisplayName(this.Id);
    }
    return (this.Parent === null) ? this.DisplayName
        : this.Parent.GetFolderName() + "/" + this.DisplayName;
};

var comObject = new ActiveXObject("WinFax.SDKPhoneBook");

var GetPbFoldersForId = function(firstId) {
    // stage 1 - do searches for folders
    var list = [];
    var id = firstId;
    do {
        list.push(new Folder(id));
        id = comObject.GetFolderListNext();
    } while (id != "");

    // stage 2 - get subfolders, if any, for each folder
    var subs =[];
    for (var i=0; i<list.length; i++) {
        id = comObject.GetFolderListFirst(0,list[i].Id);
        if (id != "") {
            var a = GetPbFoldersForId(id);  // recurse
            for (var j=0; j < a.length; j++) {
                if (a[j].Parent === null) {a[j].Parent = list[i];}
                subs.push(a[j]);
            }
        }
    }

    for (var k=0; k<subs.length; k++) {
        list.push(subs[k]);
    }

    return list; // a list of folders
};

var id = comObject.GetFolderListFirst(1, "");
Folders = GetPbFoldersForId(id);

for (var k=0; k<Folders.length; k++) {
    say(Folders[k].GetFolderName());
}

这使我得出结论,WinFax Pro COM 接口不是双接口 - 它们只是 IDispatch(后期绑定),并且可以从 VB6、VBScript、Javascript、Perl、Python 和其他后期绑定语言自然访问,但不能直接访问来自 .NET 语言,如 C# 或 VB.NET。


C# .NET 是否支持 IDispatch 后期绑定?告诉我如何从 C# 连接 IDispatch 接口。使用它,我可以做这样的事情:

public sealed class PhoneBook // a singleton
{
    Object comObject;
    Type type;

    private readonly static PhoneBook _instance = new PhoneBook();
    public static PhoneBook Instance  { get { return _instance; } }

    private PhoneBook()
    {
        var t = Type.GetTypeFromProgID("WinFax.SDKPhoneBook");
        if (t == null)
            throw new ArgumentException("WinFax Pro is not installed.");
        comObject = Activator.CreateInstance(t);
        type = comObject.GetType();
    }

    public string GetUserGroupFirst(int flavor, string id)
    {
        var parameters = new Object[2];
        parameters[0] = flavor;
        parameters[1] = id;
        string s = type.InvokeMember("GetUserGroupFirst",
                                     BindingFlags.InvokeMethod,
                                     null,
                                     comObject,
                                     parameters) as String;
        return s;
    }
....

PhoneBook 类是 IDispatch 接口的包装器。我为 typelib 中的每个方法和属性编写了一个包装方法。我认为这有点像 .NET 4.0 会自动为我做的事情。无论如何,这对我来说效果很好。

我在这里发布此问答只是为了让其他与 WinFax Pro 打交道的人可能有这些信息。我搜索了所有的中间管,找不到任何好的信息,所以我把它放在那里。


编辑- 这个 .NET 代码现在在 ASPNET MVC 应用程序中运行,允许用户从网页或 REST 客户端查找条目和发送传真。

于 2011-01-25T15:36:30.657 回答
0

我将其视为最坏情况的解决方案,但您始终可以为 C++(甚至可能是 VB6)中的 Winfax COM 对象编写自己的包装器,并让该代码完成实际工作,然后从您的 .Net 代码中调用这些包装器. 这样您就不必太担心如何将 COM 接口类型转换为 .Net 兼容类型等,因此它可能会变得容易一些。

于 2011-01-25T15:35:30.023 回答
0

我无法帮助您解决您的特定问题,但请注意,COM 互操作仅适用于具有相同“位数”的 com dll,并且 .NET 将默认生成一个应用程序,该应用程序是它所运行的计算机的本机应用程序(即它将在 64 位计算机上生成 64 位应用程序)。

几乎所有的 COM dll 都是 32 位的,尤其是商业的,所以你必须强制应用程序始终构建 32 位,方法是将项目设置中的 CPU 类型从“任何”设置为“32 位”。

于 2011-01-25T15:41:02.817 回答