使用 C# 中的传真调制解调器发送传真






4.86/5 (9投票s)
使用传真调制解调器和电话线发送传真(C#)
引言
本文将详细介绍如何使用 C# 通过传真调制解调器和电话线发送传真。该代码已在 Windows 7/ 8 / 10 上的“U.S. Robotics V.92 USB Voice Modem”上进行了 100% 测试。这是我第一次尝试作为文章分享内容,请忽略任何错误/遗漏/等。
背景
最近,我们的一位客户要求我对从 Windows 7、8 和 10 通过传真调制解调器和电话线发送传真进行研发。在研发过程中,我发现“FAXCOMLib”和“FAXCOMEXLib”可用于从 Windows 发送传真。因此,我开始使用博客和文章中提供的现有代码,但没有一个代码能正常工作。在大多数情况下,某些事件没有正确触发。我们还需要跟踪状态,例如——“正在拨号”、“正在传输”、“完成”等。
使用代码前的任务
在开始编写代码之前,您可能需要确认一些任务——
- 确认已安装“Windows 传真和扫描”服务
- 确认已连接调制解调器并安装必要的驱动程序(如果需要)
- 确认调制解调器已检测到并在设备管理器中显示
- 通过“诊断”选项卡中的“查询调制解调器”确认调制解调器工作正常
- 在“电话和调制解调器”窗口中设置位置(国家和区号)
使用代码
我使用了一个 C# 控制台应用程序来发送传真。首先创建一个控制台应用程序,随意命名——我命名为 FaxTest。请注意,我使用了“FAXCOMEXLib”(扩展库)。您可以参考“https://msdn.microsoft.com/en-us/library/windows/desktop/ms693384(v=vs.85).aspx”
现在,要使用“FAXCOMEXLib”,我们需要在“引用”中添加“Microsoft Fax Service Extended COM Type Library”,如下图所示。
添加完引用后,我们就可以开始编码了——
我们在项目中添加了一个名为“FaxSender
”的类。然后包含 FAXCOMEXLib 命名空间——
using FAXCOMEXLib;
在类构造函数中,我们实例化了 FaxServer
,通过提供 MachineName 连接到 FaxServer(您也可以手动提供字符串名称,该名称可以在系统属性中找到),并绑定/注册 FaxServer 事件——
注意:我们使用了“FaxServer”接口而不是“FaxServerClass”,因为我们遇到了一些错误消息;具体原因记不清了,但可能需要将程序集的 Embed Interop Types 设置为 True!
private static FaxServer faxServer;
public FaxSender()
{
try
{
faxServer = new FaxServer();
faxServer.Connect(Environment.MachineName);
RegisterFaxServerEvents();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
我们使用“IFaxServerNotify2_Event
”接口设置事件处理程序和要监听的几个事件;它们组合使用 OR (|),注意:网上的许多示例使用 (+) 符号,但那样的做法并不能正确工作。
private void RegisterFaxServerEvents()
{
faxServer.OnOutgoingJobAdded +=
new IFaxServerNotify2_OnOutgoingJobAddedEventHandler(faxServer_OnOutgoingJobAdded);
faxServer.OnOutgoingJobChanged +=
new IFaxServerNotify2_OnOutgoingJobChangedEventHandler(faxServer_OnOutgoingJobChanged);
faxServer.OnOutgoingJobRemoved +=
new IFaxServerNotify2_OnOutgoingJobRemovedEventHandler(faxServer_OnOutgoingJobRemoved);
var eventsToListen =
FAX_SERVER_EVENTS_TYPE_ENUM.fsetFXSSVC_ENDED | FAX_SERVER_EVENTS_TYPE_ENUM.fsetOUT_QUEUE
| FAX_SERVER_EVENTS_TYPE_ENUM.fsetOUT_ARCHIVE | FAX_SERVER_EVENTS_TYPE_ENUM.fsetQUEUE_STATE
| FAX_SERVER_EVENTS_TYPE_ENUM.fsetACTIVITY | FAX_SERVER_EVENTS_TYPE_ENUM.fsetDEVICE_STATUS;
faxServer.ListenToServerEvents(eventsToListen);
}
在事件处理程序/监听器中,我们打印一些基本通知。“OnOutgoingJobAdded
”和“OnOutgoingJobRemoved
”将触发一次。但“OnOutgoingJobChanged
”会在每次作业发生变化时触发多次,我们可以据此跟踪“FaxJobStatus”。例如,如果我们想知道何时开始拨打某个号码,我们需要检查“FaxJobStatus.ExtendedStatusCode”是否为“fjesDIALING”,或者,如果我们想知道何时开始发送文件,我们需要检查“ExtendedStatusCode”是否为“fjesTRANSMITTING”,等等。
您可以探索“FAX_JOB_EXTENDED_STATUS_ENUM”来了解不同的状态。有关详细信息,您可以参考“https://msdn.microsoft.com/en-us/library/windows/desktop/ms688631(v=vs.85).aspx”
注意:如果您仔细查看代码,您会发现使用了“OutgoingQueue.Refresh()”来从传真服务器刷新 FaxOutgoingQueue 对象的信息。您可以参考“https://msdn.microsoft.com/en-us/library/windows/desktop/ms693456(v=vs.85).aspx”以了解更多信息。
#region Event Handlers/Listeners
private static void faxServer_OnOutgoingJobAdded(FaxServerpFaxServer, string bstrJobId)
{
Console.WriteLine("OnOutgoingJobAdded event fired. A fax is added to the outgoing queue.");
}
private static void faxServer_OnOutgoingJobChanged(FaxServerpFaxServer, string bstrJobId, FaxJobStatuspJobStatus)
{
Console.WriteLine("OnOutgoingJobChanged event fired. A fax is changed to the outgoing queue.");
pFaxServer.Folders.OutgoingQueue.Refresh();
PrintFaxStatus(pJobStatus);
}
private static void faxServer_OnOutgoingJobRemoved(FaxServerpFaxServer, string bstrJobId)
{
Console.WriteLine("OnOutgoingJobRemoved event fired. Fax job is removed to outbound queue.");
}
#endregion
我们使用“PrintFaxStatus”方法来跟踪一些状态。另请注意,通过检查 2 个条件“faxJobStatus.Status” && “faxJobStatus.ExtendedStatusCode”来确认传真发送成功。因为在我调试时,我发现“OnOutgoingJobChanged
”事件触发时,“ExtendedStatusCode”属性最初显示为“fjesCALL_COMPLETED
”,而“Status”属性仍然是“fjsINPROGRESS
”。在下一次事件触发后,“Status”属性被设置为“fjsCOMPLETED
”。这就是为什么我检查两者都完成后。
private static void PrintFaxStatus(FaxJobStatusfaxJobStatus)
{
if (faxJobStatus.ExtendedStatusCode == FAX_JOB_EXTENDED_STATUS_ENUM.fjesDIALING)
{
Console.WriteLine("Dialing...");
}
if (faxJobStatus.ExtendedStatusCode == FAX_JOB_EXTENDED_STATUS_ENUM.fjesTRANSMITTING)
{
Console.WriteLine("Sending Fax...");
}
if (faxJobStatus.Status == FAX_JOB_STATUS_ENUM.fjsCOMPLETED
&& faxJobStatus.ExtendedStatusCode == FAX_JOB_EXTENDED_STATUS_ENUM.fjesCALL_COMPLETED)
{
Console.WriteLine("Fax is sent successfully.");
}
}
在我们的“SendFax”方法中,我们设置要发送的文档,为此我们使用“FaxDocument
”。设置好文档后,我们调用“Submit”方法,该方法启动传真发送过程。
public void SendFax()
{
try
{
FaxDocumentSetup();
objectsubmitReturnValue = faxDoc.Submit(faxServer.ServerName);
faxDoc = null;
}
catch (System.Runtime.InteropServices.COMExceptioncomException)
{
Console.WriteLine("Error connecting to fax server. Error Message: " + comException.Message);
Console.WriteLine("StackTrace: " + comException.StackTrace);
}
}
“FaxDocumentSetup”方法很简单,为必需的字段分配一些值(作为测试,我使用了硬编码的值!您可以使用配置文件和/或参数)。您必须将测试传真号码“12345678912”更改为真实的传真号码,并设置您想要传真的文件。
注意:请注意,在发送 PDF 时,机器上应安装 PDF 阅读器(例如 Adobe Reader)。另外,在尝试附加和发送文件时,请检查文件未被占用。您可以使用一个简单的方法来检查它,使用 File.OpenRead 并带有一个 try catch 块,并在 finally 块中处理 IOException 后关闭/释放 FileStream。
private void FaxDocumentSetup()
{
faxDoc = new FaxDocument();
faxDoc.Priority = FAX_PRIORITY_TYPE_ENUM.fptHIGH;
faxDoc.ReceiptType = FAX_RECEIPT_TYPE_ENUM.frtNONE;
faxDoc.AttachFaxToReceipt = true;
faxDoc.Sender.Name = "Md. Faroque Hossain";
faxDoc.Sender.Company = "Aprosoft, Bangladesh";
faxDoc.Body = @"E:\Aprosoft\WPF\Codes\FaxTest\FaxTest\TestPDFFilewithMultiPage.pdf";
faxDoc.Subject = "Send Test Fax from Windows";
faxDoc.DocumentName = "TestPDFFilewithMultiPage";
faxDoc.Recipients.Add("12345678912", "TestReceipent-001");
}
最后,要发送传真,请使用以下代码——
class Program
{
static void Main(string[] args)
{
FaxSender fs = new FaxSender();
fs.SendFax();
Console.ReadLine();
}
}
希望这篇博文能对某人有所帮助!欢迎任何评论/建议。
参考文献
- 从 Google 的一些帖子中获取了帮助,其中一些 MSDN 链接内联提及,但此时记不起其他的了。感谢那些其他的帖子!!
关注点
- 通过检查“faxJobStatus.Status” && faxJobStatus.ExtendedStatusCode”来确认成功发送传真。
- 发送 PDF 时,机器上应安装 PDF 阅读器(例如 Adobe Reader)。
- 在尝试附加和发送文件时,请检查文件未被占用。