Using WinFax.dll from C# .NET (NOT using FaxCom)

R

Robert Freas

Hi,

I have declared a bunch of WinFax.DLL exports as static externs in
..NET using the DllImportAttribute(). I am basing my code off the
Simple.c found in the Platform SDK in the Samples\netds\fax\SampleSend
directory. All the defined externs/structures/code is at the bottom
of this message.

As you can see below, the SendFax() method is based on the
SimpleSend.c sample in the Platform SDK. Everything works up until
the FaxSendDocument() API call. GetLastWin32Error returns 87--Invalid
parameter. The documentation suggests that I am missing a required
parameter--but, there are only 3:

jobParam.RecipientNumber = "5551212";
jobParam.ScheduleAction = (int) FAX_ENUM_JOB_SEND_ATTRIBUTES.JSA_NOW;
jobParam.CallHandle = new IntPtr(0);

....and I've set them all; NOTE: the documentation says that CALLHANDLE
must be NULL--so I set it to a "null" IntPtr.

Anyone have any ideas what is going on?

BTW, using the legacy WinFax.dll APIs are a requirement for me as I
need to leverage IOCompletionPorts in order to make sending a Fax a
synchronous operation; therefore, I cannot use the FaxCom COM objects.

Thanks for any help!

Rob

--

public abstract class Win32
{
public static readonly int INFINITE = -1;
public static readonly int INVALID_HANDLE_VALUE = -1;

[DllImport("kernel32")]
public static extern int CreateIoCompletionPort(int FileHandle, int
ExistingCompletionPort, int CompletionKey, int
NumberOfConcurrentThreads);

[DllImport("kernel32")]
public static extern int GetQueuedCompletionStatus(int
CompletionPort, ref int

lpNumberOfBytesTransferred, ref int lpCompletionKey, IntPtr
lpOverlapped, int dwMilliseconds);

[DllImport("kernel32")]
public static extern int CloseHandle(int hObject);
}

public abstract class WinFax
{
#region Enumerations

public enum FAX_PORT_OPEN_TYPE
{
PORT_OPEN_QUERY = 1,
PORT_OPEN_MODIFY = 2
}

public enum FAX_ENUM_JOB_SEND_ATTRIBUTES
{
JSA_NOW = 0, // Send now
JSA_SPECIFIC_TIME, // Send at specific time
JSA_DISCOUNT_PERIOD // Send at server configured
discount period
}

[FlagsAttribute()]
public enum WinFaxEvent
{
FEI_DIALING = 0x00000001,
FEI_SENDING = 0x00000002,
FEI_RECEIVING = 0x00000003,
FEI_COMPLETED = 0x00000004,
FEI_BUSY = 0x00000005,
FEI_NO_ANSWER = 0x00000006,
FEI_BAD_ADDRESS = 0x00000007,
FEI_NO_DIAL_TONE = 0x00000008,
FEI_DISCONNECTED = 0x00000009,
FEI_FATAL_ERROR = 0x0000000a,
FEI_NOT_FAX_CALL = 0x0000000b,
FEI_CALL_DELAYED = 0x0000000c,
FEI_CALL_BLACKLISTED = 0x0000000d,
FEI_RINGING = 0x0000000e,
FEI_ABORTING = 0x0000000f,
FEI_ROUTING = 0x00000010,
FEI_MODEM_POWERED_ON = 0x00000011,
FEI_MODEM_POWERED_OFF = 0x00000012,
FEI_IDLE = 0x00000013,
FEI_FAXSVC_ENDED = 0x00000014,
FEI_ANSWERED = 0x00000015,
FEI_JOB_QUEUED = 0x00000016,
FEI_DELETED = 0x00000017,
FEI_INITIALIZING = 0x00000018,
FEI_LINE_UNAVAILABLE = 0x00000019,
FEI_HANDLED = 0x0000001a,
FEI_FAXSVC_STARTED = 0x0000001b
}
#endregion

#region Structs

[StructLayout(LayoutKind.Sequential)]
public struct SYSTEMTIME
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}

[StructLayout(LayoutKind.Sequential)]
public struct FAX_DEVICE_STATUS
{
public int SizeOfStruct; // size

of this structure
public string CallerId; // caller id

string
public string Csid; // station

identifier
public int CurrentPage; //

current page
public int DeviceId; //

permanent line id
public string DeviceName; // device name
public string DocumentName; // document name
public int JobType; // send

or receive?
public string PhoneNumber; // sending phone

number
public string RoutingString; // routing

information
public string SenderName; // sender name
public string RecipientName; // recipient

name
public int Size; // size

in bytes of the document
public FILETIME StartTime; // starting
time of the fax

send/receive
public int Status; //

current status of the device, see FPS_??? masks
public string StatusString; // status string

if the Status field is zero. this may be NULL.
public FILETIME SubmittedTime; // time the

document was submitted
public int TotalPages; // total

number of pages in this job
public string Tsid; // transmitting

station identifier
public string UserName; // user that

submitted the active job
}

[StructLayout(LayoutKind.Sequential)]
public struct FAX_COVERPAGE_INFO
{
public int SizeOfStruct; // Size of
this structure
//
// general
//
public string CoverPageName; // coverpage
document name
public bool UseServerCoverPage; // coverpage
exists on the fax

server
//
// Recipient information
//
public string RecName; //
public string RecFaxNumber; //
public string RecCompany; //
public string RecStreetAddress; //
public string RecCity; //
public string RecState; //
public string RecZip; //
public string RecCountry; //
public string RecTitle; //
public string RecDepartment; //
public string RecOfficeLocation; //
public string RecHomePhone; //
public string RecOfficePhone; //
//
// Sender information
//
public string SdrName; //
public string SdrFaxNumber; //
public string SdrCompany; //
public string SdrAddress; //
public string SdrTitle; //
public string SdrDepartment; //
public string SdrOfficeLocation; //
public string SdrHomePhone; //
public string SdrOfficePhone; //
//
// Misc information
//
public string Note; // public

string Subject; //
public SYSTEMTIME TimeSent; // Time the fax
was

sent
public int PageCount; // Number of

pages
}

[StructLayout(LayoutKind.Sequential)]
public struct FAX_EVENT
{
public int SizeOfStruct; // Size of
this structure
public FILETIME TimeStamp; // Timestamp
for when the event

was generated
public int DeviceId; // Permanent
line id
public int EventId; // Current
event id
public int JobId; // Fax Job Id,
0xffffffff

indicates inactive job
}

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct FAX_JOB_PARAM
{
public int SizeOfStruct; // size

of this structure
public string RecipientNumber; // recipient fax

number
public string RecipientName; // recipient

name
public string Tsid; // transmitter's

id
public string SenderName; // sender name
public string SenderCompany; // sender

company
public string SenderDept; // sender

department
public string BillingCode; // billing code
public int ScheduleAction; // when

to schedule the fax, see JSA defines
public SYSTEMTIME ScheduleTime; // time to
send the fax when

JSA_SPECIFIC_TIME is used (must be local time)
public int DeliveryReportType; //

delivery report type, see DRT defines
public string DeliveryReportAddress; // email address

for delivery report (ndr or dr) thru MAPI / SMTP
public string DocumentName; // document name

(optional)
public IntPtr CallHandle; // optional call

handle

//[MarshalAs(UnmanagedType., SizeConst=3)]
public IntPtr[] Reserved; // reserved

for ms use only
}
#endregion

#region Externs

[DllImport("winfax.dll", SetLastError=true)]
public static extern int FaxConnectFaxServer(string MachineName,
[Out] out int FaxHandle);

[DllImport("winfax.dll", SetLastError=true)]
public static extern bool FaxInitializeEventQueue(int FaxHandle, int
CompletionPort, int

CompletionKey, int hWnd, int MessageStart);

[DllImport("winfax.dll", SetLastError=true)]
public static extern void FaxFreeBuffer(IntPtr Buffer);

[DllImport("winfax.dll", SetLastError=true)]
public static extern bool FaxClose(int FaxHandle);

[DllImport("winfax.dll", SetLastError=true)]
public static extern bool FaxCompleteJobParams([In, Out] ref
FAX_JOB_PARAM JobParams, [In,

Out] ref FAX_COVERPAGE_INFO CoverpageInfo);

[DllImport("winfax.dll", SetLastError=true)]
public static extern bool FaxSendDocument(int FaxHandle, string
FileName, [In] ref

FAX_JOB_PARAM JobParams, [In] ref FAX_COVERPAGE_INFO CoverpageInfo,
[Out] out int FaxJobId);

[DllImport("winfax.dll", SetLastError=true)]
public static extern bool FaxSendDocument(int FaxHandle, string
FileName, [In] ref

FAX_JOB_PARAM JobParams, [In] ref IntPtr CoverpageInfo, [Out] out int
FaxJobId);

[DllImport("winfax.dll", SetLastError=true)]
public static extern bool FaxOpenPort(int FaxHandle, int DeviceId,
int Flags, [Out] int

FaxPortHandle);

[DllImport("winfax.dll", SetLastError=true)]
public static extern bool FaxGetDeviceStatus(int FaxPortHandle,
[Out] out FAX_DEVICE_STATUS

DeviceStatus);

#endregion

public static bool SendFax(string faxDocument)
{
bool bTerminate = false;
int hCompletionPort = 0;
int completionKey = 0;
int bytesSent = 0;
int hFax = 0;
//IntPtr hFax = new IntPtr(0);
//IntPtr hPort = new IntPtr(0);
int hPort = 0;
int faxJobID = 0;
FAX_DEVICE_STATUS deviceStatus;
FAX_EVENT faxEvent = new FAX_EVENT();
FAX_JOB_PARAM jobParam = new FAX_JOB_PARAM();
FAX_COVERPAGE_INFO coverPageInfo = new FAX_COVERPAGE_INFO();

faxEvent.SizeOfStruct = Marshal.SizeOf(typeof(FAX_EVENT));
IntPtr pFaxEvent = Marshal.AllocHGlobal(faxEvent.SizeOfStruct);
Marshal.StructureToPtr(faxEvent, pFaxEvent, true);

jobParam.SizeOfStruct = Marshal.SizeOf(typeof(FAX_JOB_PARAM));
IntPtr pJobParam = Marshal.AllocHGlobal(jobParam.SizeOfStruct);
Marshal.StructureToPtr(jobParam, pJobParam, true);

coverPageInfo.SizeOfStruct =
Marshal.SizeOf(typeof(FAX_COVERPAGE_INFO));
IntPtr pCoverPageInfo =
Marshal.AllocHGlobal(coverPageInfo.SizeOfStruct);
Marshal.StructureToPtr(coverPageInfo, pCoverPageInfo, true);

// connect to fax service
if(FaxConnectFaxServer(null, out hFax) == 0)
{
Console.WriteLine("GetLastWin32Error: " +

Marshal.GetLastWin32Error().ToString());

//_tprintf( TEXT("FaxConnectFaxServer failed, ec =
%d\n"),GetLastError() );
return false;
}

// create completion port to poll against while waiting for fax
completion
hCompletionPort =
Win32.CreateIoCompletionPort(Win32.INVALID_HANDLE_VALUE, 0, 0, 0);

if(hCompletionPort == 0)
{
Console.WriteLine("GetLastWin32Error: " +

Marshal.GetLastWin32Error().ToString());

//_tprintf( TEXT("CreateIoCompletionPort failed, ec = %d\n"),
GetLastError()

);
FaxClose(hFax);
return false;
}

if (!FaxInitializeEventQueue(
hFax,
hCompletionPort,
0,
0,
0))
{
Console.WriteLine("GetLastWin32Error: " +

Marshal.GetLastWin32Error().ToString());

//_tprintf( TEXT("FaxInitializeEventQueue failed, ec = %d\n"),

GetLastError() );
FaxClose(hFax);
return false;
}

FaxCompleteJobParams(ref jobParam, ref coverPageInfo);

jobParam.RecipientNumber = "5551212";
jobParam.ScheduleAction = (int)
FAX_ENUM_JOB_SEND_ATTRIBUTES.JSA_NOW;
jobParam.CallHandle = new IntPtr(0);

IntPtr coverPageNull = new IntPtr(0);
if(!FaxSendDocument(hFax, faxDocument, ref jobParam, ref
coverPageNull, out

faxJobID))
{
Console.WriteLine("GetLastWin32Error: " +

Marshal.GetLastWin32Error().ToString());

//jobParam = (FAX_JOB_PARAM) Marshal.PtrToStructure(pJobParam,

typeof(FAX_JOB_PARAM));
//coverPageInfo = (FAX_COVERPAGE_INFO)

Marshal.PtrToStructure(pCoverPageInfo, typeof(FAX_COVERPAGE_INFO));

//_tprintf( TEXT("FaxSendDocument failed, ec = %d \n"),
GetLastError() );

FaxClose(hFax);
Win32.CloseHandle(hCompletionPort);
FaxFreeBuffer(pJobParam);
FaxFreeBuffer(pCoverPageInfo);
return false;
}

//_tprintf(TEXT("Queued document %s for transmition to %s, JobID =
%d\n"), Document,

Number, JobId);

FaxFreeBuffer(pJobParam);
FaxFreeBuffer(pCoverPageInfo);

Marshal.StructureToPtr(faxEvent, pFaxEvent, true);

while (!bTerminate &&
(Win32.GetQueuedCompletionStatus(
hCompletionPort,
ref bytesSent,
ref completionKey,
pFaxEvent,
Win32.INFINITE) != 0))
{

faxEvent = (FAX_EVENT) Marshal.PtrToStructure(pFaxEvent,
typeof(FAX_EVENT));

//_tprintf( TEXT("Received event 0x%x\n"),FaxEvent->EventId);

switch ((WinFaxEvent) faxEvent.EventId)
{
case WinFaxEvent.FEI_IDLE:
case WinFaxEvent.FEI_COMPLETED:
case WinFaxEvent.FEI_MODEM_POWERED_ON:
case WinFaxEvent.FEI_MODEM_POWERED_OFF:
case WinFaxEvent.FEI_FAXSVC_ENDED:
bTerminate = true;
break;

case WinFaxEvent.FEI_JOB_QUEUED:
//_tprintf( TEXT("JobId 0x%x queued\n"),FaxEvent->JobId );
break;

case WinFaxEvent.FEI_DIALING:
case WinFaxEvent.FEI_SENDING:
case WinFaxEvent.FEI_RECEIVING:
case WinFaxEvent.FEI_BUSY:
case WinFaxEvent.FEI_NO_ANSWER:
case WinFaxEvent.FEI_BAD_ADDRESS:
case WinFaxEvent.FEI_NO_DIAL_TONE:
case WinFaxEvent.FEI_DISCONNECTED:
case WinFaxEvent.FEI_FATAL_ERROR:
if (FaxOpenPort(hFax, faxEvent.DeviceId, (int)

FAX_PORT_OPEN_TYPE.PORT_OPEN_QUERY, hPort) )
{
if (FaxGetDeviceStatus(hPort, out deviceStatus) )
{
IntPtr pDeviceStatus = new IntPtr();
deviceStatus.SizeOfStruct =

Marshal.SizeOf(typeof(FAX_DEVICE_STATUS));
Marshal.StructureToPtr(deviceStatus,

pDeviceStatus, true);

//PrintDeviceStatus(deviceStatus);
FaxFreeBuffer(pDeviceStatus);
}
FaxClose(hPort);
}
break;
}
}

FaxClose(hFax);
Win32.CloseHandle(hCompletionPort);
return true;
}

}
 
Joined
Dec 6, 2006
Messages
1
Reaction score
0
Hi,

I am using the WinFax.Dll for Faxing; If you have the solution please update me.
I want to send TIFF file through Fax. I am using Faxcomlib.dll but it is not able to send the TIFF file and giving error "Invalid data". I decided to go by your way but the program is not working. Please update me.

Thanks in advance.
 
Joined
Aug 31, 2008
Messages
1
Reaction score
0
Using a native DLL (or C++ DLL) in your .Net applications

Please check out this article:
http://blog.rednael.com/2008/08/29/MarshallingUsingNativeDLLsInNET.aspx

It's an in depth article about importing native DLLs into .NET code. It has some C# examples. The article shows which types are interoperable, how to import a DLL, how to pass strings and how to de-reference/unreference pointers.

It answers most of your questions
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top