//-----------------------------------------------------------------------
// <copyright file="LoggingIn.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <disclaimer>
// THIS CODE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESSED
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK OF
// USE OR RESULTS IN CONNECTION WITH THE USE OF THIS CODE REMAINS WITH THE USER.
// </disclaimer>
//-----------------------------------------------------------------------
using System;
using System.Collections;
using System.Data;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using Microsoft.BusinessSolutions.SmallBusinessAccounting;
using Microsoft.Win32;
namespace LoggingIn
{
/// <summary>
/// This class contains the body of the sample.
/// </summary>
class LoggingIn : IDisposable
{
private Utilities utilities = null;
/// <summary>
/// Only constructor to use
/// </summary>
/// <param name="serverName"></param>
/// <param name="databaseName"></param>
public LoggingIn(string serverName, string databaseName )
{
try
{
string sbaFullBusinessLogicAssemblyPath = Utilities.GetSbaFullBusinessLogicAssemblyPath();
utilities = new Utilities(sbaFullBusinessLogicAssemblyPath);
// Logging in or out is an expensive event in terms of resources, so it
// is best to design solutions to minimize the number of these events
Debug.WriteLine(Environment.NewLine + "About to log in..." + Environment.NewLine + Environment.NewLine);
utilities.LogOn(serverName, databaseName);
}
catch (ApplicationException ex)
{
if (ex is ISmallBusinessException)
{
utilities.LogOff();
}
throw;
}
}
private LoggingIn(){}
/// <summary>
/// To manage release of resource handles
/// </summary>
public void Dispose()
{
utilities.LogOff(); // release handles. This utility contains a null check
GC.SuppressFinalize(this);
}
}
/// <summary>
/// The main class to start the sample, and handle some exceptions
/// </summary>
class CMain
{
[STAThread]
static void Main(string[] args)
{
string serverName = null;
string databaseName = null;
try
{
// Add a listener so that output of sample text will always go to the command-line
// environment as well as the Output window in debug sessions through VS.NET (which
// is the default listener for that setting)
TextWriterTraceListener commandLineWriter = new TextWriterTraceListener(System.Console.Out);
Debug.Listeners.Add(commandLineWriter);
// Begin by parsing the arguments
if (Parse(args, ref databaseName, ref serverName) )
{
using (LoggingIn sample = new LoggingIn(serverName, databaseName) )
{
Debug.WriteLine(Environment.NewLine + "The sample has now instantiated a SmallBusinessInstance, and used it to log in to the specified database." + Environment.NewLine + Environment.NewLine);
}
Debug.WriteLine(Environment.NewLine + "The sample has now logged out through disposal of the LoggingIn class." + Environment.NewLine + Environment.NewLine);
}
else
{
serverName = null;
UsageMessage();
}
}
catch(ApplicationException ex)
{
Utilities.OutputToConsoleFromException(ex);
}
}
/// <summary>
/// Parse the command-line arguments
/// </summary>
/// <param name="args">the arguments array</param>
/// <param name="server">server name</param>
/// <param name="database">database name</param>
/// <returns></returns>
static bool Parse(string[] args, ref string database, ref string server)
{
ParseArgs parseArgs = new ParseArgs(args);
database = parseArgs.UseSwitch('d', "sampleproductcompany");
server = parseArgs.UseSwitch('s', "(local)\\MICROSOFTSMLBIZ");
return parseArgs.ParametersCorrect();
}
/// <summary>
/// Usage suggestions output to console
/// </summary>
static void UsageMessage()
{
Debug.WriteLine("Usage:" + Environment.NewLine +
" LoggingIn [-d DatabaseName] [-s ServerName]" + Environment.NewLine + Environment.NewLine +
"Where:" + Environment.NewLine +
" DatabaseName = name of the database the sample uses (default: sampleproductcompany)" + Environment.NewLine +
" ServerName=machineName\\MSDE_InstanceName (default: (local)\\MICROSOFTSMLBIZ)" + Environment.NewLine + Environment.NewLine +
"Examples:" + Environment.NewLine +
" LoggingIn -d sampleproductcompany -s DEVMACHINE\\MSDE_InstanceOnMachine" + Environment.NewLine + Environment.NewLine +
" LoggingIn ? (to show this arguments help)" + Environment.NewLine + Environment.NewLine +
"Quotes are required around arguments that contain spaces." + Environment.NewLine +
"The argument names are case-insensitive." + Environment.NewLine +
"All arguments must be used." + Environment.NewLine +
"The order of arguments is not important." + Environment.NewLine +
"This sample does not read from or write to the database." + Environment.NewLine + Environment.NewLine);
}
}
/// <summary>
/// Class to parse command-line input arguments to strings
/// </summary>
sealed class ParseArgs
{
private string[] argsTypedIn = null;
private bool[] argsMatched = null;
private bool allRequiredArgsFoundSoFar = true;
private bool requestForHelp = false;
private ParseArgs() {}
/// <summary>
/// Only available constructor
/// </summary>
/// <param name="arguments">arguments from the command line</param>
public ParseArgs(string[] arguments)
{
argsTypedIn = arguments;
argsMatched = new bool[arguments.Length];
if ( (arguments != null) && (arguments.Length > 0) )
{
string firstArg = arguments[0].ToLower();
if ( (firstArg == "?") ||
(firstArg == "-?") ||
(firstArg == "/?") ||
(firstArg == "h") ||
(firstArg == "help") ||
(firstArg == "commands") )
{
allRequiredArgsFoundSoFar = false;
requestForHelp = true;
}
}
}
/// <summary>
/// Look for the required argument by the switch
/// </summary>
/// <param name="letterSwitch"></param>
/// <returns>The value of the argument from the command line, if available</returns>
public string UseSwitch(char letterSwitch)
{
if (requestForHelp)
{
return null;
}
else
{
return UseSwitch(letterSwitch, true);
}
}
/// <summary>
/// Look for the non-required argument by the switch, with default value supplied
/// </summary>
/// <param name="letterSwitch"></param>
/// <param name="defaultValue"></param>
/// <returns>The value of the argument from the command line, or default if not available</returns>
public string UseSwitch(char letterSwitch, string defaultValue)
{
if (requestForHelp)
{
return null;
}
else
{
string argResult = UseSwitch(letterSwitch, false);
if (argResult == null)
{
argResult = defaultValue;
}
return argResult;
}
}
/// <summary>
/// Last argument check for sample
/// </summary>
/// <returns>whether parameters have been correctly matched up with what is required for this sample</returns>
public bool ParametersCorrect()
{
if (requestForHelp)
{
return false;
}
else
{
bool returnBool = allRequiredArgsFoundSoFar;
if (allRequiredArgsFoundSoFar == false)
{
Debug.WriteLine("Sample is missing one or more arguments.");
}
for (int counter = 0; counter < argsMatched.Length; counter++)
{
returnBool = returnBool && argsMatched[counter];
if (argsMatched[counter] == false)
{
Debug.WriteLine(string.Format("Sample doesn't know how to parse argument \"{0}\".", argsTypedIn[counter]) );
}
}
return returnBool;
}
}
private string UseSwitch(char letterSwitch, bool requiredArgument)
{
Debug.Assert(char.IsLetter(letterSwitch), "all hyphenated switches must be letters");
string hyphenatedSwitch = "-" + letterSwitch;
string returnString = null;
for (int argIndex = 0; argIndex < (argsTypedIn.Length - 1 /*The -1 here is because a hyphenated switch as last input argument is not useful*/); argIndex++)
{
if ( (string.Compare(hyphenatedSwitch, argsTypedIn[argIndex], true) == 0) && (argsMatched[argIndex] == false) && (argsMatched[argIndex + 1] == false) )
{
returnString = argsTypedIn[argIndex + 1];
argsMatched[argIndex] = argsMatched[argIndex + 1] = true;
break;
}
}
if (requiredArgument && (returnString == null) )
{
allRequiredArgsFoundSoFar = false;
}
return returnString;
}
}
/// <summary>
/// This class is used as a simple wrapper for a set of utilities to be used for the samples.
/// </summary>
sealed class Utilities
{
private ISmallBusinessInstance smallBusinessInstance = null;
private Utilities() {}
/// <summary>
/// The only public constructor
/// </summary>
/// <param name="pathToImplementationAssembly"></param>
public Utilities(string pathToImplementationAssembly)
{
// Since this sample does not reference SBAAPI.DLL directly, it has
// no compile-time knowledge of that assembly. Therefore the sample
// has to instantiate the SBA instance through reflection
//
// It would be more performant to just reference SBAAPI.DLL directly
// and instantiate the SBA instance that way, but the technique
// used here is neater and more configurable because we're only
// referencing the interface at compile time.
Assembly businessLogicAssembly = Assembly.LoadFrom(pathToImplementationAssembly);
smallBusinessInstance = (ISmallBusinessInstance) businessLogicAssembly.CreateInstance("Microsoft.BusinessSolutions.SmallBusinessAccounting.SmallBusinessInstance");
}
/// <summary>
/// Central accessor for the SmallBusinessInstance created with LogOn method
/// in this class object
/// </summary>
public ISmallBusinessInstance SbaInstance
{
get
{
return smallBusinessInstance;
}
}
/// <summary>
/// Central utility to log on
/// </summary>
/// <param name="serverName"></param>
/// <param name="database"></param>
public void LogOn(string serverName, string database)
{
if ( (serverName != null) && (database != null) )
{
if (!smallBusinessInstance.IsConnected)
{
Debug.WriteLine(string.Format("Logging on with databaseName \"{0}\", serverName \"{1}\"" + Environment.NewLine + Environment.NewLine, database, serverName) );
smallBusinessInstance.LogOn(database, serverName);
}
}
}
/// <summary>
/// Checks for null, and logs off the SBA instance.
/// </summary>
public void LogOff()
{
if (smallBusinessInstance != null)
{
if (smallBusinessInstance.IsConnected)
{
smallBusinessInstance.LogOff();
}
smallBusinessInstance = null;
}
}
/// <summary>
/// Outputs message and InnerException message if available
/// </summary>
/// <param name="ex">exception for which info will be output to console</param>
public static void OutputToConsoleFromException(ApplicationException ex)
{
if (ex != null)
{
Debug.WriteLine(string.Format("ApplicationException was thrown with Message: {0}", ex.Message) );
if ( (ex.InnerException != null) && (ex.InnerException.Message != null) && (ex.InnerException.Message.Length > 0) )
{
Debug.WriteLine(string.Format(" InnerException Message: {0}", ex.InnerException.Message) );
}
}
}
public static string GetSbaFullBusinessLogicAssemblyPath()
{
// If setup was run, then the install path will be in the registry
const string MagellanInstallPathRegKey = @"SOFTWARE\Microsoft\Business Solutions eCRM\2.0\";
const string InstallPathRegValue = "AccountingAPI";
const string BusinessLogicAssemblyName = "SBAAPI.DLL";
const string DefaultPath = @"C:\Program Files\Microsoft Small Business\Small Business Accounting\";
StringBuilder installPath = new StringBuilder();
// Check for a installation
RegistryKey regkey = Registry.LocalMachine.OpenSubKey(MagellanInstallPathRegKey);
if (regkey != null)
{
Object temp = regkey.GetValue(InstallPathRegValue);
if (temp != null)
{
installPath.Append(temp.ToString());
}
}
if (regkey != null)
{
regkey.Close();
}
if (installPath.Length == 0)
{
// Default value
installPath.Append(DefaultPath);
installPath.Append(BusinessLogicAssemblyName);
}
return installPath.ToString();
}
}
}