Quick recap of something I missed yesterday - when uploading content to my site, my tool will sometimes throw an exception and call it quits. Why?
I remembered vaguely that this was an issue, and one that I didn't care about. It had something to do with remote subdirectories. My first site had one top-level directory per year, and so it was a very rare thing to create a new one - presumably I did it by hand, and saved me the time to do handle properly..
Running in the debugger, I quickly found out that I was simply propagating errors from FtpFindFirstFile. It turns out that the path from lpszSearchFile was causing the problem by not existing - instead of an empty enumeration, I was getting an error code.
The proper way to handle this is to look for ERROR_INTERNET_EXTENDED_ERROR in GetLastError() after the call, and if found, invoke InternetGetLastResponseInfo.
That returns a second error code, but unfortunately that's not populated for FTP. Instead, I decided to check for the output text, which includes the FTP messages, as a way to sniff for it. Not foolproof, but I can always create the directory by hand, and it helps me with my own FTP server.
Anyway, if you're curious, here goes.
private bool FileExists(WinInet.HINTERNET connection, string path) {
WinInet.HINTERNET request; // Enumeration request.
WinInet.WIN32_FIND_DATA findFileData; // Data result.
request = WinInet.FtpFindFirstFile(connection, path, out findFileData, 0, IntPtr.Zero);
if (request.IsInvalid) {
int error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
if (error != 0 && error != WinInet.ERROR_NO_MORE_FILES) {
if (error == WinInet.ERROR_INTERNET_EXTENDED_ERROR) {
uint err;
uint len = 0;
WinInet.InternetGetLastResponseInfo(out err, null, ref len);
StringBuilder sb = new StringBuilder((int)len);
if (WinInet.InternetGetLastResponseInfo(out err, sb, ref len)) {
string errorString = sb.ToString();
if (errorString.Contains("No such file or directory")) {
return false;
}
throw new InvalidOperationException(sb.ToString());
}
throw new System.ComponentModel.Win32Exception(error);
} else {
throw new System.ComponentModel.Win32Exception(error);
}
}
return false;
} else {
WinInet.InternetCloseHandle(request);
return true;
}
}
I think I'll invest in a code formatter in the near future ...