FTP Clients - Part 12: BitKinex

For this installment in my series about FTP clients, I want to take a look at BitKinex 3, which is an FTP client from Barad-Dur, LLC. For this blog I used BitKinex 3.2.3, and it is available from the following URL:

http://www.bitkinex.com/

At the time of this blog post, BitKinex 3 is available for free, and it contains a bunch of features that make it an appealing FTP and WebDAV client.

Fig. 1 - The Help/About dialog in BitKinex 3.

BitKinex 3 Overview

When you open BitKinex 3, it shows four connection types (which it refers to as Data Sources): FTP, HTTP/WebDAV, SFTP/SSH, and My Computer. The main interface is analogous to what you would expect in a Site Manager with other FTP clients - you can define new data sources (connections) to FTP sites and websites:

Fig. 2 - The main BitKinex 3 window.

Creating an FTP data source is pretty straight-forward, and there are a fair number of options that you can specify. What's more, data sources can have individual options specified, or they can inherit from a parent note.

Fig. 3 - Creating a new FTP data source.
Fig. 4 - Specifying the options for an FTP data source.

Once a data source has connected, a child window will open and display the folder trees for your local and remote content. (Note: there are several options for customizing how each data source can be displayed.)

Fig. 5 - An open FTP data source.

BitKinex 3 has support for command-line automation, which is pretty handy if you do a lot of scripting like I do. Documentation about automating BitKinex 3 from the command line is available on the BitKinex website at the following URL:

BitKinex Command Line Interface

That being said, the documentation is a bit sparse and there are few examples, so I didn't attempt anything ambitious from a command line during my testing.

Using BitKinex 3 with FTP over SSL (FTPS)

BitKinex 3 has built-in support for FTP over SSL (FTPS) supports both Explicit and Implicit FTPS. To specify the FTPS mode, you need to choose the correct mode from the Security drop-down menu for your FTP data source.

Fig. 6 - Specifying the FTPS mode.

Once you have established an FTPS connection through BitKinex 3, the user experience is the same as it is for a standard FTP connection.

Using Using BitKinex 3 with True FTP Hosts

True FTP hosts are not supported natively, and even though BitKinex 3 allows you to send a custom command after a data source has been opened, I could not find a way to send a custom command before sending user credentials, so true FTP hosts cannot be used.

Using Using BitKinex 3 with Virtual FTP Hosts

BitKinex 3's login settings allow you to specify the virtual host name as part of the user credentials by using syntax like "ftp.example.com|username" or "ftp.example.com\username", so you can use virtual FTP hosts with BitKinex 3.

Fig. 7 - Specifying an FTP virtual host.

Scorecard for BitKinex 3

This concludes my quick look at a few of the FTP features that are available with BitKinex 3, and here are the scorecard results:

Client
Name
Directory
Browsing
Explicit
FTPS
Implicit
FTPS
Virtual
Hosts
True
HOSTs
Site
Manager
Extensibility
BitKinex 3.2.3 Rich Y Y Y N Y N/A
Note: I could not find anyway to extend the functionality of BitKinex 3; but as I
mentioned earlier, it does support command-line automation.

That wraps it up this blog - BitKinex 3 is pretty cool FTP client with a lot of options, and I think that my next plan of action is to try out the WebDAV features that are available in BitKinex 3. ;-)

Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

Restarting the FTP Service Orphans a DLLHOST.EXE Process

I was recently creating a new authentication provider using FTP extensibility, and I ran into a weird behavior that I had seen before. With that in mind, I thought my situation would make a great blog subject because someone else may run into it.

Here are the details of the situation: let's say that you are developing a new FTP provider for IIS, and your code changes never seem to take effect. Your provider appears to be working, it's just that any new functionality is not reflected in your provider's behavior. You restart the FTP service as a troubleshooting step, but that does not appear to make any difference.

I'll bypass mentioning any other troubleshooting tasks and cut to the chase - if you read my Changing the Identity of the FTP 7 Extensibility Process blog post a year ago, you will recall that I mentioned that all custom FTP extensibility providers are executed through COM+ in a DLLHOST.exe process. When you restart the FTP service, that should clean up the DLLHOST.EXE process that is being used for FTP extensibility. However, if you are developing custom FTP providers and the DLLHOST.EXE process is not terminated by the FTP service, you may find yourself in a situation where you have a DLLHOST.EXE process in memory that contains an older copy of your provider, which will not be removed from memory until the DLLHOST.EXE process for FTP extensibility has been forcibly terminated.

If you have read some of my earlier blog posts or walkthroughs on IIS.NET, you may have noticed that I generally like to use a few pre-build and post-build commands in my FTP projects; usually I add these commands in order to to automatically register/unregister my FTP providers in the Global Assembly Cache (GAC).

With a little modification and some command-line wizardry, you can automate the termination of any orphaned DLLHOST.EXE processes that are being used for FTP extensibility. With that in mind, here are some example pre-build/post-build commands that will unregister/reregister your provider in the GAC, restart the FTP service, and terminate any orphaned FTP extensibility DLLHOST.EXE processes.

Note: The following syntax was written using Visual Studio 2010; you would need to change "%VS100COMNTOOLS%" to "%VS90COMNTOOLS%" for Visual Studio 2008 or "%VS110COMNTOOLS%" for Visual Studio 2012.

Pre-build Commands:

net stop ftpsvc

call "%VS100COMNTOOLS%\vsvars32.bat">nul

cd /d "$(TargetDir)"

gacutil.exe /uf "$(TargetName)"

for /f "usebackq tokens=1,2* delims=," %%a in (`tasklist /fi "MODULES eq Microsoft.Web.FtpServer.*" /fi "IMAGENAME eq DLLHOST.EXE" /fo csv ^| find /i "dllhost.exe"`) do taskkill /f /pid %%b

Post-build Commands:

call "%VS100COMNTOOLS%\vsvars32.bat">nul

gacutil.exe /if "$(TargetPath)"

net start ftpsvc

The syntax is a little tricky for the FOR statement, so be carefully when typing or copying/pasting that into your projects. For example, you need to make sure that all of the code from the FOR statement through the TASKKILL command are on the same line in your project's properties.

When you compile your provider, Visual Studio should display something like the following:

------ Rebuild All started: Project: FtpBlogEngineNetAuthentication, Configuration: Release Any CPU ------
The Microsoft FTP Service service is stopping.
The Microsoft FTP Service service was stopped successfully.

Microsoft (R) .NET Global Assembly Cache Utility. Version 4.0.30319.1
Copyright (c) Microsoft Corporation. All rights reserved.

Assembly: FtpBlogEngineNetAuthentication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=426f62526f636b73, processorArchitecture=MSIL
Uninstalled: FtpBlogEngineNetAuthentication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=426f62526f636b73, processorArchitecture=MSIL
Number of assemblies uninstalled = 1
Number of failures = 0
SUCCESS: The process with PID 12656 has been terminated.
FtpBlogEngineNetAuthentication -> C:\Users\dude\Documents\Visual Studio 2010\Projects\FtpBlogEngineNetAuthentication\FtpBlogEngineNetAuthentication\bin\Release\FtpBlogEngineNetAuthentication.dll
Microsoft (R) .NET Global Assembly Cache Utility. Version 4.0.30319.1
Copyright (c) Microsoft Corporation. All rights reserved.

Assembly successfully added to the cache
The Microsoft FTP Service service is starting.
The Microsoft FTP Service service was started successfully.

========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

If you analyze the output from the build process, you will see that the commands in my earlier samples stopped the FTP service, removed the existing assembly from the GAC, terminated any orphaned DLLHOST.EXE processes, registered the newly-built DLL in the GAC, and then restarted the FTP service.

By utilizing these pre-build/post-build commands, I have been able to work around situations where a DLLHOST.EXE process is being orphaned and caching old assemblies in memory.

Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

The Road Less Travelled

Perhaps it's because the media is going through yet another season of what seems like a never-ending parade of Hollywood awards programs, but I was thinking the other day about all of the awards that I will never win. For example, I will never win a Golden Globe. I will never win a People's Choice Award. I will never win an Oscar, or a Tony, or an Emmy, or any award that is named after some person who might not be real. And despite a lifetime of playing music, I will never win a Grammy or any other award that the music industry is giving out these days. This may be my reality, but to be perfectly honest, I am never saddened by this, nor do I generally give this concept a second thought.

That being said, the most-recent awards show made me think about the reasons why we even care about those kinds of awards. I can't name who won Best Actor or Actress from any of the awards shows that have taken place in the last several years, and that's really not an issue for me; I'll never meet any of the people who win those awards anyway. What's more, I'm not sure if I would want to meet most of the people who actually win those awards, seeing as how the evening news and morning talk shows are always spinning stories of their latest transgressions. I think the part that gets me the most is how - after throwing their lives away on one selfish pursuit after another - the world eventually calls them "artists," and everyone waxes poetic about how these artists have suffered for their cause; as if they woke up one day and consciously chose to take the road less travelled in Robert Frost's famous poem. When I was younger, I think I bought into that illusion, too. But the older I get, the less I am impressed by their actions - and perhaps I should explain what I mean by that.

If a man whom you knew personally walked out on his wife and family, in most cases you would probably think he was acting like a selfish pig. But if it was a famous actor from Hollywood or a legendary singer from Nashville, you might think to yourself, "Gee, that's too bad...," as if their fame has excused their adverse behavior for some inexplicable reason. You might even go so far as to feel sorry for said person; after all, it's just so sad that their family doesn't understand how hard an artist's life must be.

But why do we feel this way? Why do we put these people on some sort of undeserved pedestal? Is it because they're artists? The more I think about it, I don't believe that they've chosen the road less travelled - I think they've chosen the easy path; they've chosen the path that's all about them. Perhaps that's why they need so many awards shows; they need the constant reassurance that all of the suffering they cause is for a noble purpose. But I just can't bring myself to see it that way.

Let me briefly tell you a true story about my life, and this is difficult for me because it is always dangerous when you open up your life to public scrutiny; you never know what people are going to think. When I was much younger, I faced one of those situations where it seemed like two roads were diverging before me and I had to pick which path I would travel.

I had just celebrated my 19th birthday, and my rock band was starting to do really well. We weren't great by any means, but we were just coming off a series of really great gigs when my fiancé told me that she was pregnant with our child. I had a lot of options before me: we could get married, we could put the baby up for adoption, etc. (My girlfriend had additional concerns: what if I suddenly became some sort of jerk and told her that it was her problem and left her to face this on her own.) Once the news began to work its way through the grapevine to all our friends and family, I heard a lot of advice from a lot well-meaning people - all of whom listed off suggestions that were much like the choices that I just mentioned.

But I didn't take anyone's advice. Instead, against everyone else's counsel, I married my girlfriend. We had a baby girl, who is now almost ten years older than I was when I made my choice to keep her. But this decision on my part didn't come without cost; my days of playing long-haired lead guitar for a rock band were over. In fact, my entire youth ended almost overnight - it was time to put aside my personal ambitions and accept the responsibilities that lay before me. My wife and I spent many years in abject poverty as we fought side-by-side to build a home together and raise our children as best we could. Despite the difficult times, my wife and I recently celebrated our 28th anniversary, and we raised three great kids along the way.

However, my life might not have been this way; I could have chosen the other path when I was given the opportunity to do so. I could have chosen something selfish that I wanted just for me, and I could have left my girlfriend to deal with it on her own. Some years later, I could have written a heart-wrenching song about the hard choices that I had to make. Perhaps that could have become a hit, and I could have sold that song to untold scores of fans. Maybe I could have written a book about my life and my admirers might have said, "That's so sad - look at everything he gave up to become who he is."

Every year people walk out on their responsibilities in the hopes that the scenario which I just described will happen to them; they hope they'll be successful despite the pain that they cause to others. What is worse, however, is that popular culture applauds such actions. Songs like Bruce Springsteen's Hungry Heart attempt to spin public opinion in support of egocentric behavior by unapologetically suggesting that a deadbeat dad was simply "following his heart."

Yet in my personal situation this delusion would have been far from the truth; I would have been a selfish punk who left his unwed 18-year-old girlfriend to face the world alone with a newborn baby girl. Perhaps I might have become a successful 'artist' and sent generous child support payments to take care of my daughter's every need, but that's just not the same. Children need parents; they need both a father and a mother to be there to love and raise them.

There is no way that I can say this so it won't sound overly-judgmental, but I think it makes someone a coward when they choose their own selfish desires over their family and their responsibilities. When I chose to become a father, I gave up everything that I wanted for myself; I gave up my personal hopes, dreams, and desires for my life. I sacrificed everything so my daughter would grow up with both a mom and dad. My choice was much harder to live with than I ever could have imagined, but my daughter's life was worth the cost.

So in the end, when I finally shrug off this mortal coil, I will not have won any awards for what I have accomplished in my life, and I'll have no golden statuettes to adorn the shelves in my study. I am sure that I will never win father of the year, but my three children will have had better lives because I chose to be their father. I did not choose the easy path for my life - I chose the road less travelled, and I pray that for my family it has made all the difference.