Как я могу использовать 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".