Как я могу использовать COM-объекты WinFax Pro из приложения.NET?

Я знаю, что WinFax Pro - это 1998 год.

(примечание: это не WinFax.dll, которая, по-видимому, является частью Windows. Это WinFax Pro, отдельное коммерческое приложение-надстройка, первоначально выпущенное Delrina, а позднее приобретенное Symantec).

Я работаю в офисе, который все еще использует WinFax Pro в качестве операционной системы. У них есть номера факсов клиентов, которые хранятся в "Телефонной книге" WinFax Pro, и используют их для уведомления клиентов о посещениях службы. Теперь это работает так: кто-то смотрит на (распечатанное) расписание, сгенерированное из Календаря Mac, затем нажимает на все соответствующие записи в телефонной книге WinFax, чтобы отправить уведомление по факсу.

Это похоже на то, что мы привыкли называть интеграцией "вращающееся кресло", но это относится к двум экранам. Это даже не 2 экрана - это один лист бумаги и один экран.

Во всяком случае, я пытаюсь автоматизировать это и возникают проблемы.

Хорошие новости:
- WinFax Pro предоставляет свои функции в виде COM-объектов: WinFax.SDKSend для механизма отправки факсов; WinFax.SDKPhoneBook для адресной книги и тд.
- WinFax Pro поставляет библиотеку типов, wfxctl32.tlb, которая описывает эти различные COM-объекты. - Я могу успешно использовать объект WinFax.SDKSend из.NET (C#) через обертки, сгенерированные из tlbimport. (Я использую.NET 3.5, не могу сделать.NET 4.0) .

Плохие новости:
Я не смог вызвать ни один из методов в WinFax COM-объектах, кроме WinFax.SDKSend. Подписи выглядят не сложнее, чем в WinFax.SDKSend, но я продолжаю получать исключения.

Код C#:

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

Исключение:

Исключение: System.InvalidCastException: невозможно преобразовать COM-объект типа 'wfxctl32.CSDKPhoneBookClass' в интерфейс типа 'wfxctl32.ISDKPhoneBook'. Эта операция завершилась неудачно, поскольку вызов QueryInterface для компонента COM для интерфейса с IID '{A67FCC81-9949-11D0-961E-444553540000}' завершился неудачно из-за следующей ошибки: такой интерфейс не поддерживается (Исключение из HRESULT: 0x80004002 (E_NOINTERFACE)),

at System.RuntimeType.InvokeDispMethod(имя строки, BindingFlags invokeAttr, цель объекта, аргументы объекта [], логическое значение [] byrefModifiers, культура Int32, строка [] namedParameters)
в System.RuntimeType.InvokeMember(имя строки, BindingFlags bindingFlags, связыватель связывания, объектная цель, Object[] provideArgs, модификаторы ParameterModifier[], CultureInfo culture, String[] namedParams)
в System.RuntimeType.ForwardCallToInvokeMember(String memberName, флаги BindingFlags, объектная цель, Int32[] aWrapperTypes, MessageData& msgData)
в wfxctl32.CSDKPhoneBookClass.GetFolderListFirst(стандартная папка Int16, папка StringID)

3 ответа

Решение

Я открыл инструмент OleView, часть Windows SDK, и я вижу, что интерфейс COM для WinFax.SDKPhoneBook отсутствует на объекте. Хм, это сюрприз. Интерфейс описывается библиотекой типов, и tlbimport.exe создает для нее оболочку. Также интерфейс документирован в PDF-документе WinFax Pro SDK. Но я не смог найти ни одного примера того, кто бы успешно использовал интерфейс с ранней привязкой для 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? говорит мне, как связаться с интерфейсами IDispatch из C#. Используя это, я могу сделать что-то вроде этого:

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-клиента.

Я бы посчитал это решением для наихудшего случая, но вы всегда можете написать свою собственную обертку для COM-объектов Winfax на C++ (или, возможно, даже на VB6) и заставить этот код выполнять реальную работу, а затем вызывать эти обертки из вашего.Net-кода., Таким образом, вам не нужно слишком беспокоиться о том, как выяснить, как преобразовать типы COM-интерфейсов в.Net-совместимые типы и т. Д., Так что это может немного облегчить задачу.

Я не могу помочь вам с вашей конкретной проблемой, но помните, что COM-взаимодействие работает только с comll'ами с той же "битностью", и.NET по умолчанию будет генерировать приложение, которое является родным для компьютера, на котором он работает (т.е. оно создаст 64-битное приложение на 64-битном компьютере).

Почти все COM DLL являются 32-битными, особенно коммерческими, поэтому вам придется принудительно заставлять приложение всегда собирать 32-битные, установив тип процессора в настройках проекта от "Any" до "32 bit".

Другие вопросы по тегам