Hi Matt,
I've done some research and managed to make the callback to work in your
code:
public class MasterFtp : IDisposable
{
[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern IntPtr InternetOpen(
string lpszAgent,
int dwAccessType,
string lpszProxyName,
string lpszProxyBypass,
int dwFlags);
[DllImport("wininet.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool InternetCloseHandle(IntPtr hInternet);
[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern IntPtr InternetConnect(
IntPtr hInternet,
string lpszServerName,
short nServerPort,
string lpszUsername,
string lpszPassword,
int dwService,
int dwFlags,
IntPtr dwContext);
[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern bool FtpGetCurrentDirectory(
IntPtr hConnect,
StringBuilder directory,
ref int bufferLength);
[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern bool FtpSetCurrentDirectory(
IntPtr hFtpConnection,
string lpszDirectory);
[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern IntPtr FtpOpenFile(
IntPtr hConnect,
string lpszFileName,
int dwAccess,
int dwFlags,
IntPtr dwContext);
[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern uint FtpGetFileSize(
IntPtr hFile,
out ulong lpdwFileSizeHigh);
[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool FtpGetFile(
IntPtr hConnect,
string remoteFile,
string newFile,
[MarshalAs(UnmanagedType.Bool)] bool failIfExists,
int flagsAndAttributes,
int flags,
IntPtr context);
[DllImport("wininet.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool InternetGetLastResponseInfo(
out int errorCode,
StringBuilder buffer,
ref int bufferLength);
[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern IntPtr InternetSetStatusCallback(
IntPtr hInternet,
INTERNET_STATUS_CALLBACK lpfnInternetCallback);
private delegate void INTERNET_STATUS_CALLBACK(
IntPtr hInternet,
IntPtr dwContext,
InternetStatus dwInternetStatus,
IntPtr lpvStatusInformation,
int dwStatusInformationLength);
private enum InternetStatus
{
ResolvingName = 10,
NameResolved = 11,
ConnectingToServer = 20,
ConnectedToServer = 21,
SendingRequest = 30,
RequestSent = 31,
ReceivingResponse = 40,
ResponseReceived = 41,
CtlResponseReceived = 42,
Prefetch = 43,
ClosingConnection = 50,
ConnectionClosed = 51,
HandleCreated = 60,
HandleClosing = 70,
RequestComplete = 100,
Redirect = 110,
IntermediateResponse = 120,
StateChange = 200
}
private enum InternetState
{
Connected = 1,
Disconnected = 2,
DisconnectedByUser = 16,
Idle = 256,
Busy = 512
}
private enum ConnectionState
{
Internet,
Connection,
Directory,
Size,
Download
}
const int ERROR_SUCCESS = 0;
const int INTERNET_OPEN_TYPE_PRECONFIG = 0;
const int INTERNET_SERVICE_FTP = 1;
const int INTERNET_FLAG_PASSIVE = 0x8000000;
const int FTP_TRANSFER_BINARY = 0x2;
const int FTP_TRANSFER_ASCII = 0x1;
const int GENERIC_READ = unchecked((int)0x80000000);
const int INTERNET_INVALID_STATUS_CALLBACK = -1;
int MAX_PATH = 260;
private IntPtr hOpen;
private IntPtr hConnection;
private IntPtr hContext;
public MasterFtp(string ftpHost, short port, string user, string
password, bool usePasv)
{
hContext = Marshal.AllocHGlobal(1);
hOpen = InternetOpen("C# FtpDownloader",
INTERNET_OPEN_TYPE_PRECONFIG, null, null, 0);
if (hOpen == IntPtr.Zero)
GetError(ConnectionState.Internet);
hConnection = InternetConnect(hOpen, ftpHost, port, user,
password, INTERNET_SERVICE_FTP, usePasv ? INTERNET_FLAG_PASSIVE : 0,
hContext);
if (hConnection == IntPtr.Zero)
GetError(ConnectionState.Connection);
}
public void SetDirectory(string directory)
{
StringBuilder dir = new StringBuilder(MAX_PATH);
foreach (string currentChange in directory.Split('\\'))
{
MAX_PATH = 260;
dir = new StringBuilder(MAX_PATH);
if (!FtpGetCurrentDirectory(hConnection, dir, ref MAX_PATH))
GetError(ConnectionState.Directory);
Trace.WriteLine(dir, "Current Directory");
Trace.WriteLine(currentChange, "Change");
if (!FtpSetCurrentDirectory(hConnection, currentChange))
GetError(ConnectionState.Directory);
Trace.WriteLine("Changed");
MAX_PATH = 260;
dir = new StringBuilder(MAX_PATH);
if (!FtpGetCurrentDirectory(hConnection, dir, ref MAX_PATH))
GetError(ConnectionState.Directory);
Trace.WriteLine(dir, "New Directory");
}
}
public uint GetFileSize(string remoteFile)
{
IntPtr hFile = FtpOpenFile(hConnection, remoteFile,
GENERIC_READ, FTP_TRANSFER_BINARY, new IntPtr());
if (hFile == IntPtr.Zero)
GetError(ConnectionState.Size);
ulong sizeHigh = 0;
return FtpGetFileSize(hFile, out sizeHigh);
}
INTERNET_STATUS_CALLBACK callback;
public void DownloadFile(string remoteFile, string localFile, bool
bFailIfExists)
{
callback = new INTERNET_STATUS_CALLBACK(InternetStatusCallback);
IntPtr callbackPtr = InternetSetStatusCallback(hConnection,
callback);
if (!FtpGetFile(hConnection, remoteFile, localFile,
bFailIfExists, (int)FileAttributes.Normal, FTP_TRANSFER_BINARY, hContext))
GetError(ConnectionState.Download);
}
private void InternetStatusCallback(IntPtr hInternet,
IntPtr dwContext,
InternetStatus dwInternetStatus,
IntPtr lpvStatusInformation,
int dwStatusInformationLength)
{
Trace.WriteLine(dwInternetStatus + " - " + lpvStatusInformation
+ " - " + dwStatusInformationLength);
}
private void GetError(ConnectionState state)
{
int errorCode = Marshal.GetLastWin32Error();
Trace.WriteLine("Ftp Error: " + errorCode);
}
#region IDisposable Members
public void Dispose()
{
if (hConnection != IntPtr.Zero)
{
InternetCloseHandle(hConnection);
hConnection = IntPtr.Zero;
}
if (hOpen != IntPtr.Zero)
{
InternetCloseHandle(hOpen);
hOpen = IntPtr.Zero;
}
if (hContext != IntPtr.Zero)
{
Marshal.FreeHGlobal(hContext);
hContext = IntPtr.Zero;
}
}
#endregion
}
Some changes:
1) The dwContext passed to InternetConnect and FtpGetFile should be
consistent, therefore I created a member variable hContext to track it,
it's initalized with Marshal.AllocHGlobal with 1 byte.
2) The delegate instance needs to stay at member scope, it must be kept
alive during the lifetime of the class.
3) The handle passed to InternetSetStatusCallback should be the handle
returned from InternetConnect instead of InternetOpen.
Hope this helps.
Regards,
Walter Wang (
[email protected], remove 'online.')
Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.