Sunday, January 23, 2011

Referencing Assemblies: T4 Gotcha

When I run T4 Templates through Visual Studio 2008, I don't have to give the full path to reference GAC assemblies:

<#@ assembly name="System.Data.dll" #>

But when I run the template in my own executable using Microsoft.VisualStudio.TextTemplating.Engine, I have problems.

If the Template code is in VB.NET, I get:

vbc : Command line (0,0) : error BC2006: Compiling transformation: option 'r' requires ': <file_list>'

If the Template code is in C#, I get:

c:\Users\jacks\AppData\Local\Temp\rnftuopt.0.cs(3,18) : error CS0234: Compiling transformation: The type or namespace name 'Data' does not exist in the namespace 'System' (are you missing an assembly reference?)

To fix this, I referenced the full path of the assembly.

<#@ assembly name="C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll" #>

NOTE: This doesn't seam to apply to non-GAC assemblies; I have noticed that I don't have give the full path of my own assemblies as long as they are in the same folder as my executable.

Friday, January 14, 2011

Fulfilled my contract with AT&T

Just over a two years ago, I bought an iPhone 3G (no S).

In Spokane, I haven't have any problems AT&T. But Spokane is the first place named in AT&T's Postcard Commercial in 2009, so AT&T knows that we get better than average service here in Spocompton.

If I was to stay in Spokane, I would probably be safe getting an upgrade from AT&T. The trouble is that I am working month to month as a temp and I don't know where I will be working if/when my current contract runs out.

I've hear horror stories of bad service in real cities like New York or Chicago. And Verizon will offer the iPhone 4 next month; some say that AT&T's problems are caused by the popularity of the iPhone and Verizon will degrade too.

I think I will stay with my antique phone for awhile.

Wednesday, January 05, 2011

Running FTP.EXE from C#

NOTE: Ultimately, I didn't go with this solution.

Since I need to log on to various FTP servers with various security configurations and FTP.EXE seams to figure out these configurations and the free FTP libraries I’ve played with can’t I have decided to wrap FTP.EXE with C#.

Telling it what to do

I wanted to avoid sending my commands to FTP.EXE via command files because I would have to dynamically write text files and make sure that they are secure. System.Diagnostics.Process allows me to send commands to its StandardInput stream, but I can’t figure out how to make it accept passwords. To solve these problems, I log in using command files (which I can secure and only allow myself and the program to see, and then write the rest of the commands to the StandardInput stream.

The Command File

The command file needs to reside in a directory with no spaces and the file needs to be saved using ANSI encoding. Here I open a server and login, the rest is up to the commands that I send to the stream.

open localhost
username
password

Sample Application

This simple console application demonstrates using the command file to log in and StandardInput to send a List command (and Quit).

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace DemoFtp
{
    class Program
    {
        private const string FTP_EXE_LOCATION = @"C:\Windows\System32\ftp.exe";
        private const string DEFAULT_COMMAND_FILE_LOCATION = @"C:\temp\localhost.txt";

        static void Main(string[] args)
        {
            try
            {
                string commandFileLocation = getCommandFileLocation(args);

                var commands = new List<string>();
                commands.Add("ls -la");
                commands.Add("quit");   // Don't forget to log out!
                string output = RunWithResponses(
                    FTP_EXE_LOCATION, 
                    string.Format("-s:{0}", commandFileLocation), 
                    commands);
                Console.WriteLine(output);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.ReadKey();
        }

        private static string getCommandFileLocation(string[] args)
        {
            string commandFileLocation;
            if (args.Length > 0)
                commandFileLocation = args[0];
            else
                commandFileLocation = DEFAULT_COMMAND_FILE_LOCATION;
            return commandFileLocation;
        }

        public static string RunWithResponses(string exeName, string argsLine,
            List<string> commands)
        {
            StreamReader outputStream = StreamReader.Null;
            StreamReader errorStream = StreamReader.Null;
            StreamWriter inputStream = StreamWriter.Null;
            string output = string.Empty;
            try
            {
                // Setup the process
                Process newProcess = new Process();
                newProcess.StartInfo.FileName = exeName;
                newProcess.StartInfo.Arguments = argsLine;
                newProcess.StartInfo.UseShellExecute = false;
                newProcess.StartInfo.CreateNoWindow = true;
                newProcess.StartInfo.RedirectStandardInput = true;
                newProcess.StartInfo.RedirectStandardOutput = true;
                newProcess.StartInfo.RedirectStandardError = true;
                newProcess.Start();
                inputStream = newProcess.StandardInput;
                outputStream = newProcess.StandardOutput;
                errorStream = newProcess.StandardError;
                inputStream.AutoFlush = true;

                // Issue My commands
                foreach (string command in commands)
                {
                    inputStream.WriteLine(command);
                }

                // Get the rsults and wait for it to end
                output = outputStream.ReadToEnd();
                newProcess.WaitForExit();
            }
            finally
            {
                // Clean up
                if (outputStream != StreamReader.Null)
                    outputStream.Close();
                if (errorStream != StreamReader.Null)
                    errorStream.Close();
                if (inputStream != StreamWriter.Null)
                    inputStream.Close();
            }
            return output;
        }
    }
}