C# 如何从.NET应用程序使用WinFax Pro COM对象?
我知道,WinFax Pro是1998年的事 (注意:这不是WinFax.dll,它显然是Windows的一部分。这是WinFax Pro,一个独立的商业附加应用程序,最初来自Delrina,后来被Symantec收购) 我现在的办公室仍然使用WinFax Pro作为操作系统。他们将客户传真号码存储在WinFax Pro“电话簿”中,并使用它通知客户服务访问。它现在的工作方式是,有人查看由Mac日历生成的(打印的)时间表,然后单击WinFax电话簿中的所有适当条目,以发送通知传真 这就像我们过去称之为“转椅”集成的东西,但它指的是两个屏幕。这甚至不是两个屏幕,而是一张纸和一个屏幕 不管怎样,我正在尝试自动化它,但遇到了麻烦 好消息:C# 如何从.NET应用程序使用WinFax Pro COM对象?,c#,.net,com,interop,winfax,C#,.net,Com,Interop,Winfax,我知道,WinFax Pro是1998年的事 (注意:这不是WinFax.dll,它显然是Windows的一部分。这是WinFax Pro,一个独立的商业附加应用程序,最初来自Delrina,后来被Symantec收购) 我现在的办公室仍然使用WinFax Pro作为操作系统。他们将客户传真号码存储在WinFax Pro“电话簿”中,并使用它通知客户服务访问。它现在的工作方式是,有人查看由Mac日历生成的(打印的)时间表,然后单击WinFax电话簿中的所有适当条目,以发送通知传真 这就像我们过去
-WinFax Pro将其功能公开为COM对象:用于传真引擎的WinFax.SDKSend;用于通讯簿的WinFax.SDKPhoneBook等。
-WinFax Pro提供了一个类型库wfxctl32.tlb,用于描述这些不同的COM对象。 -我能够通过tlbimport生成的包装器从.NET(C#)成功使用WinFax.SDKSend对象。(我正在使用.NET3.5,无法使用.NET4.0) 坏消息:
除了WinFax.SDKSend之外,我无法在WinFax COM对象上调用任何方法。签名看起来并不比WinFax.SDKSend中的签名更复杂,但我不断遇到例外情况 C#代码: 例外情况: 异常:System.InvalidCastException:无法强制转换的COM对象 将“wfxctl32.CSDKPhoneBookClass”键入接口类型 “wfxctl32.ISDKPhoneBook”。此操作失败,因为 对具有IID的接口的COM组件的QueryInterface调用 由于以下原因,{A67FCC81-9949-11D0-961E-444553540000}失败 错误:不支持此类接口(HRESULT的异常:0x80004002 (E_NOINTERFACE)) 在System.RuntimeType.InvokeDispMethod(字符串名称、BindingFlags invokeAttr、对象目标、对象[]参数、布尔[]byrefModifiers、Int32区域性、字符串[]namedParameters)
位于System.RuntimeType.InvokeMember(字符串名称、BindingFlags BindingFlags、绑定器绑定器、对象目标、对象[]提供的参数、参数修改器[]修饰符、CultureInfo区域性、字符串[]namedParams)
在System.RuntimeType.ForwardCallToInvokeMember(字符串memberName、BindingFlags标志、对象目标、Int32[]aWrapperTypes、MessageData和msgData)
位于wfxctl32.CSDKPhoneBookClass.GetFolderListFirst(Int16 standardFolder,字符串folderID)
<>我将它视为最坏的解决方案,但您可以在C++(甚至可能是VB6)中为WiFaScript COM对象编写自己的包装器,并使代码执行实际工作,然后从.NET代码中调用这些包装器。这样,您就不必太担心如何将COM接口类型转换为与.Net兼容的类型等,因此这可能会使转换更加容易 我打开了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());
}
PhoneBook类是IDispatch接口上的包装器。我为typelib中的每个方法和属性编写了一个包装器方法。我想这是.NET4.0自动为我做的。无论如何,这对我来说很有效 我在这里发布此问答,只是为了让其他与WinFax Pro打交道的人能够了解这些信息。我搜索了所有的管道,没有找到任何好的信息,所以我把它放在那里
编辑-此.NET代码现在正在ASPNET MVC应用程序中运行,允许用户从网页或REST客户端查找条目和发送传真 我无法帮助您解决您的特定问题,但请注意,COM interop仅适用于具有相同“位”的COM dll,并且.NET将默认为运行它的计算机的本机应用程序(即,它将在64位计算机上生成64位应用程序)
几乎所有的COM dll都是32位的,尤其是商用的,因此您必须通过将项目设置中的CPU类型从“Any”设置为“32位”来强制应用程序始终构建32位。您可能已经厌倦了它,但这不是真正的原因。Tlbimp在类型库中找到双接口没有问题,服务器没有实际实现它是前所未闻的。这种事故最常见的原因是版本控制问题,即类型库与安装的版本不匹配。很普通,更新需要新的guid。@Hans-你可能是对的;我不是COM专家。但是,当我在讨论中的ProgId(
WinFax.SDKPhoneBook
)上使用OleView时,我看到了IDispatch、IUnknown、ITypeInfo和其他一些(刚才不是在机器上),但没有看到WinFax Pro定义的应用程序接口。这让我感到困惑,因为我认为如果.tlb拥有它,那么COM对象会将其作为早期绑定接口公开。我研究了版本和guid——这似乎不是问题所在。看起来赛门铁克从未实现过早期绑定接口。你认为那是不可能的吗?(严肃的问)哦,赛门铁克。我突然不再那么感兴趣了。你有事情要做,应该为你工作。祝你好运
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());
}
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;
}
....