using System; using System.ComponentModel; // for Win32Exception using System.Diagnostics; // for ProcessWindowStyle using System.IO; // for File class using System.Reflection; // for GetExecutingAssembly using System.Windows.Forms; // for MessageBox using System.Xml; // for XmlDocument /// /// Utility-type code that can be included in other projects with /// no external dependencies. /// public class Util { #region Directory functions /// /// Get the folder where the application is stored on disk. /// /// /// The full path of the folder ending in "\" of where the application /// is stored. /// public static string DirGetStartup() { string strDrive = ""; string strDir = ""; string strFname = ""; string strExt = ""; Splitpath(Application.ExecutablePath, ref strDrive, ref strDir, ref strFname, ref strExt); return strDrive + strDir; } /// /// Count the number of files matching a given search path. /// /// /// String containing the file path and the search pattern used. /// For example "c:\\mydir\\*.txt" counts the number of text files /// in the "mydir" folder. /// /// /// The number of matching files. /// public static int DirCountTotal(string strPattern) { string[] arrFiles = FileGetList(strPattern); return (arrFiles.Length); } #endregion #region Error functions /// /// Show an error summary in a message box. /// /// /// The exception being shown. /// public void ErrorShow(Exception e) { string strLastStackLine = e.StackTrace.Substring(e.StackTrace.LastIndexOf("\\")+1); string strError = "Error = " + e.Message + "\r\n" + "Location = " + strLastStackLine + "\r\n"; MessageBox.Show(strError, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } private static string cls_strError = ""; private static int cls_iMaxError = 1; /// /// Add a line number and an error description to the error list. /// /// /// Line number in the input of where the error occurred. /// /// /// Text of the error. If this is null or an empty string, the error is not added. /// public static void ErrorAdd(int iLineNumber, string strError) { if (strError != null && strError.Length > 0 && ErrorGetCount() < cls_iMaxError) { cls_strError = cls_strError + iLineNumber.ToString() + " " + strError + "\r\n"; } } /// /// Return the number of errors in the error list. /// /// /// The number of errors so far. /// public static int ErrorGetCount() { int iReturn = 0; for (int i=0; i /// Initialize the error list and reset the error count. /// /// /// The maximum number of errors that will be reported. Any errors /// in excess of this limit added with ErrorAdd are ignored. /// public static void ErrorInit(int iMaxError) { cls_strError = ""; cls_iMaxError = iMaxError; } /// /// Return the errors in the error list. /// /// /// The list of errors, formatted as a string. /// public static string ErrorGetList() { return cls_strError; } #endregion #region File functions /// /// Read in a text file. /// /// /// File name of the text file being read in. /// /// /// The contents of the text file, or an empty string if the text file does not exist. /// public static string FileReadText(string strFilename) { string strFileText = ""; string strInput = ""; if (strFilename != null && strFilename != "" && File.Exists(strFilename)) { StreamReader sr = File.OpenText(strFilename); while ((strInput = sr.ReadLine()) != null) strFileText = strFileText + strInput + "\n"; sr.Close(); } return strFileText; } /// /// Write a text file to disk. /// /// /// File name of the text file being written. Any existing file of /// the same name is overwritten. /// /// /// String that gets written to the text file. If this is an empty /// string, an empty text file is created. /// public static void FileWriteText(string strFilename, string strFileText) { if (strFilename != null && strFilename != "") { StreamWriter sw = File.CreateText(strFilename); if (strFileText != null) sw.Write(strFileText); sw.Close(); } } /// /// Append some text to a text file. /// /// /// File name of the text file being appended to. This file is created /// if it does not exist. /// /// /// String that gets appended to the text file. /// /// /// This routine is useful for error logging, as it can be called /// repeatedly to append text strings to the log file. /// public static void FileAppendText(string strFilename, string strText) { if (strFilename != null && strFilename != "" && strText != null && strText != "") { if (!File.Exists(strFilename)) { StreamWriter f = new StreamWriter(strFilename); f.Write(strText); f.Close(); } else { StreamWriter f = new StreamWriter(strFilename, true); f.Write(strText); f.Close(); } } } /// /// Count the number of lines in a text file. /// /// /// File name of the text file having its lines counted. /// /// /// The number of lines in the file. Lines are defined as being /// terminated with the "\n" character. If the last line in the /// file does not end with "\n" it is not counted. /// public static int FileCountLines(string strFilename) { char[] buffer = new char[32 * 1024]; int total = 0; StreamReader sr = null; int charCount = 0; if (strFilename != null && strFilename != "" && File.Exists(strFilename)) { sr = new StreamReader(strFilename); while ((charCount = sr.Read(buffer, 0, buffer.Length)) > 0) { for (int i = 0; i < charCount; i++) if (buffer[i] == '\n') total++; } sr.Close(); } return total; } /// /// Get the list of file names matching a given search path. /// /// /// String containing the file path and the search pattern used. /// For example "c:\\mydir\\*.txt" returns the names of the text files /// in the "mydir" folder. /// /// /// A string array containing the full paths of the files that match. /// If there are no matching files, this returns an empty array. /// It does not return null. /// public static string[] FileGetList(string strPattern) { string[] arrFiles = null; string strDriveDir = FileGetdrive(strPattern) + FileGetdir(strPattern); string strFnameExt = FileGetfname(strPattern) + FileGetext(strPattern); if (Directory.Exists(strDriveDir)) arrFiles = Directory.GetFiles(strDriveDir, strFnameExt); if (arrFiles != null) return (arrFiles); else return (new string[] { }); } /// /// Delete one or more files in a single folder, matching a given pattern. /// /// /// String containing the file path and the search pattern used. /// For example "c:\\mydir\\*.txt" deletes the text files in the "mydir" folder. /// /// /// The number of files deleted. /// public static int FileDelete(string strPattern) { string[] arrFiles = FileGetList(strPattern); for (int i = 0; i < arrFiles.Length; i++) { File.Delete(arrFiles[i]); } return (arrFiles.Length); } /// /// Return the Drive element of a file path. /// /// /// A partial or complete file path. The path is processed as a string /// and the drives, folders and files in the path do not have to exist. /// /// /// The Drive element of the path, for example "C:". /// An empty string is returned if this element does not exist. /// public static string FileGetdrive(string path) { string drive = ""; string dir = ""; string fname = ""; string ext = ""; Splitpath(path, ref drive, ref dir, ref fname, ref ext); return (drive); } /// /// Return the Directory element of a file path. /// /// /// A partial or complete file path. The path is processed as a string /// and the drives, folders and files in the path do not have to exist. /// /// /// The Directory element of the path, for example "\mydir\myfiles\". /// If the Directory element exists, it always ends in "\" or "/". /// An empty string is returned if this element does not exist. /// public static string FileGetdir(string path) { string drive = ""; string dir = ""; string fname = ""; string ext = ""; Splitpath(path, ref drive, ref dir, ref fname, ref ext); return (dir); } /// /// Return the Filename element of a file path. /// /// /// A partial or complete file path. The path is processed as a string /// and the drives, folders and files in the path do not have to exist. /// /// /// The Filename element of the path, for example "myfile". /// If the Filename element exists, it never contains "\", "/" or ".". /// An empty string is returned if this element does not exist. /// public static string FileGetfname(string path) { string drive = ""; string dir = ""; string fname = ""; string ext = ""; Splitpath(path, ref drive, ref dir, ref fname, ref ext); return (fname); } /// /// Return the Extension element of a file path. /// /// /// A partial or complete file path. The path is processed as a string /// and the drives, folders and files in the path do not have to exist. /// /// /// The Extension element of the path, for example ".ext". /// If the Extension element exists, it always starts with /// "." and never contains "\" or "/". /// An empty string is returned if this element does not exist. /// public static string FileGetext(string path) { string drive = ""; string dir = ""; string fname = ""; string ext = ""; Splitpath(path, ref drive, ref dir, ref fname, ref ext); return (ext); } /// /// Split a file path up into Drive, Directory, Filename and Extension elements. /// /// /// A partial or complete file path. The path is processed as a string /// and the drives, folders and files in the path do not have to exist. /// /// /// This returns the Drive element of the path, for example "C:". /// An empty string is returned if this element does not exist. /// /// /// The Directory element of the path, for example "\mydir\myfiles\". /// If the Directory element exists, it always ends in "\" or "/". /// An empty string is returned if this element does not exist. /// /// /// The Filename element of the path, for example "myfile". /// If the Filename element exists, it never contains "\", "/" or ".". /// An empty string is returned if this element does not exist. /// /// /// The Extension element of the path, for example ".ext". /// If the Extension element exists, it always starts with /// "." and never contains "\" or "/". /// An empty string is returned if this element does not exist. /// public static void Splitpath(string path, ref string drive, ref string dir, ref string fname, ref string ext) { int idxDrive, lenDrive; int idxDir, lenDir; int idxFname, lenFname; int idxExt, lenExt; // Initialize return values, this is OK even if the inouts are null. drive = dir = fname = ext = ""; if (path != null && path.Length > 0) { // Get the drive. idxDrive = lenDrive = 0; if (path.Length >= 2 && Char.IsLetter(path[0]) && path[1] == ':') { idxDrive = 0; lenDrive = 2; } // Get the ext. idxExt = path.Length - 1; lenExt = 0; while (idxExt >= lenDrive && path[idxExt] != '.' && path[idxExt] != '\\' && path[idxExt] != '/') { idxExt--; lenExt++; } if (idxExt >= lenDrive && path[idxExt] == '.') lenExt++; else idxExt = lenExt = 0; // Get the fname. if (lenExt > 0) idxFname = idxExt - 1; else idxFname = path.Length - 1; lenFname = 0; while (idxFname >= lenDrive && path[idxFname] != '\\' && path[idxFname] != '/') { idxFname--; lenFname++; } if (lenFname > 0) idxFname++; else idxFname = lenFname = 0; // Get the dir. idxDir = lenDrive; if (lenFname > 0) lenDir = idxFname - idxDir; else if (lenExt > 0) lenDir = idxExt - idxDir; else lenDir = path.Length - idxDir; if (lenDir == 0) idxDir = lenDir = 0; // Return values. drive = path.Substring(idxDrive, lenDrive); dir = path.Substring(idxDir, lenDir); fname = path.Substring(idxFname, lenFname); ext = path.Substring(idxExt, lenExt); } } /// /// Load the content of named elements from an XML formatted initialization /// file. The file should exist in the application startup folder. /// /// /// String array with the names of the XML elements being loaded. /// /// /// Returns a string array of values obtained from the XML elements /// requested. This parameter will contain the same number of elements /// as the element paths array, and elements that were not found return /// an empty string. If the initialization file was not found, this /// returns an empty array. It does not return null. /// /// /// The XML formatted initialization file is assumed to have the same /// name as the application and should exist in the application startup /// directory. For example the initialization file for "c:\mydir\myapp.exe" /// should be "c:\mydir\myapp.xml". The XML should be in the following format: /// /// <root> /// <inner1>Value1</inner1> /// <inner2>Value2</inner2> /// ... /// </root> /// public static void LoadIniFile(string[] aElementPaths, ref string[] aElementValues) { if (aElementPaths != null && aElementPaths.Length > 0) { aElementValues = new string[aElementPaths.Length]; XmlDocument xmldoc = new XmlDocument(); string strExeFile = Application.ExecutablePath; string strXmlIniFile = strExeFile.Substring(0, strExeFile.LastIndexOf('.')) + ".ini"; if (File.Exists(strXmlIniFile)) { xmldoc.Load(strXmlIniFile); for (int i = 0; i < aElementPaths.Length; i++) { if (xmldoc.SelectSingleNode(aElementPaths[i]) != null) aElementValues[i] = xmldoc.SelectSingleNode(aElementPaths[i]).InnerText; else aElementValues[i] = ""; } } else { for (int i = 0; i < aElementValues.Length; i++) aElementValues[i] = ""; } } else { aElementValues = new string[] { }; } } /// /// Save the content of named elements back to an XML formatted initialization file. /// The file is created in the application startup folder, and any pre-existing /// XML file of the same name is overwritten. /// /// /// String array with names of XML elements to create. /// /// /// String array with values of the XML elements. This array should have /// the same number of elements as the Paths array. /// public static void SaveIniFile(string[] aElementPaths, string[] aElementValues) { if (aElementPaths != null && aElementPaths.Length > 0 && aElementValues != null && aElementValues.Length > 0) { // Create an empty XmlDocument with the outer elements only. XmlDocument xmldoc = new XmlDocument(); string strRootNode = FileGetdir(aElementPaths[0]).Replace("/", "").Replace("\\", ""); string strOuterXml = "<" + strRootNode + ">" + "\r\n" + "" + "\r\n"; xmldoc.LoadXml(strOuterXml); // Add nested elements for all elements in aElementPaths[]. XmlElement newElement = null; for (int i = 0; i < aElementPaths.Length; i++) { newElement = xmldoc.CreateElement(FileGetfname(aElementPaths[i])); newElement.InnerText = ((i < aElementValues.Length) ? aElementValues[i] : ""); xmldoc.DocumentElement.AppendChild(newElement); } // Save the XmlDocument back to the ini file. string strExeFile = Application.ExecutablePath; string strXmlIniFile = strExeFile.Substring(0, strExeFile.LastIndexOf('.')) + ".ini"; xmldoc.Save(strXmlIniFile); } } #endregion #region Information functions /// /// Count the number of computers on the local network on which the /// specified path is available [not finished yet!]. /// /// /// A directory path checked for availability on the available /// networked computers. A networked computer is counted as being /// available if this folder exists with read/write access. /// /// /// Currently this routine returns the value 1, indicating the /// local computer is the only one available. /// /// /// This routine helps multi processing on a network. If a program /// is installed onto multiple networked computers, it can create /// a shared folder on each networked computer and can run itself /// on startup. It can periodically monitor the shared folder for /// work items and put the completed work back in the same folder. /// A master program is responsible for creating the work items, /// copying them to the shared folders on the networked computers, /// and collecting the results. /// public static int CountNetworkedComputers(string strTestPath) { return 1; } /// /// Return a program version string. /// /// /// The program version in the form X.Y (yyyy.mm.dd) where X and Y are /// the major and minor version number, and yyyy, mm and dd are the /// date the EXE file was built. /// public string GetProgramVersion() { // v1.2.3.4 as entered in application properties. int iVersionMajor = Assembly.GetExecutingAssembly().GetName().Version.Major; int iVersionMinor = Assembly.GetExecutingAssembly().GetName().Version.Minor; int iBuildMajor = Assembly.GetExecutingAssembly().GetName().Version.Build; int iBuildMinor = Assembly.GetExecutingAssembly().GetName().Version.Revision; DateTime dt = GetLinkerTimestamp(); return (iVersionMajor.ToString() + "." + iVersionMinor.ToString() + " (" + dt.Year.ToString() + "." + dt.Month.ToString() + "." + dt.Day.ToString() + ")"); } /// /// Return some useful information about the running program. /// /// /// A multi line string containing program information. /// public string GetProgramInfo() { return "Build date = " + GetLinkerTimestamp() + CRLF + "Computer name = " + Environment.MachineName + CRLF + "Current folder = " + Environment.CurrentDirectory + CRLF + "Domain name = " + Environment.UserDomainName + CRLF + "Framework version = " + Environment.Version + CRLF + "User name = " + Environment.UserName + CRLF + "Windows version = " + Environment.OSVersion + CRLF; } private DateTime GetLinkerTimestamp() { string strFilepath = System.Reflection.Assembly.GetCallingAssembly().Location; const int c_PeHeaderOffset = 60; const int c_LinkerTimestampOffset = 8; byte[] b = new byte[2048]; Stream s = null; try { s = new FileStream(strFilepath, FileMode.Open, FileAccess.Read); s.Read(b, 0, 2048); } finally { if (s != null) s.Close(); } int i = BitConverter.ToInt32(b, c_PeHeaderOffset); int iSecsSince1970 = BitConverter.ToInt32(b, i + c_LinkerTimestampOffset); DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0); dt = dt.AddSeconds(iSecsSince1970); dt = dt.AddHours(TimeZone.CurrentTimeZone.GetUtcOffset(dt).Hours); return dt; } #endregion #region Process control functions // Some common error codes, from "winerror.h" and "winnt.h". private const int ERROR_FILE_NOT_FOUND = 2; private const int ERROR_ACCESS_DENIED = 5; /// Control+C was pressed in a command prompt window. public const uint STATUS_CONTROL_C_EXIT = 0xC000013A; private const string CRLF = "\r\n"; /// /// Run a program, optionally waiting for it to finish. /// /// /// Name of the EXE file to run. /// /// /// Optional arguments passed to the EXE file. /// /// /// True to wait for the EXE file to finish running, False otherwise. /// /// /// Values such as Normal, Hidden, Minimized, Maximized. /// /// /// If the EXE file was run using the Wait option, this parameter is /// the EXE file's return code. Otherwise this returns zero. /// public static void Exec(string strPath, string strArgs, bool fWait, ProcessWindowStyle eStyle, ref int iReturnCode) { Process objProcess = new Process(); iReturnCode = 0; try { objProcess.StartInfo.FileName = strPath; objProcess.StartInfo.Arguments = strArgs; objProcess.StartInfo.WindowStyle = eStyle; objProcess.Start(); if (fWait) { objProcess.WaitForExit(); iReturnCode = objProcess.ExitCode; } } catch (Win32Exception e) { if (e.NativeErrorCode == ERROR_FILE_NOT_FOUND) MessageBox.Show(e.Message + ". Check the path.\r\n", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); else if (e.NativeErrorCode == ERROR_ACCESS_DENIED) MessageBox.Show(e.Message + ". No permission to run file.\r\n", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); else MessageBox.Show(e.Message + ".\r\n", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } #endregion #region String functions /// /// Test if a character is alphanumeric. /// /// /// The character being tested which should be: A-Z, a-z, 0-9 /// /// /// True if the character passed the test, False otherwise. /// public static bool IsAlnumChar(char ch) { if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) return true; else return false; } /// /// Test if a character is alphabetic. /// /// /// The character being tested which should be: A-Z, a-z /// /// /// True if the character passed the test, False otherwise. /// public static bool IsAlphaChar(char ch) { if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' || ch <= 'z')) return true; else return false; } /// /// Test if a character is an ASCII control character. /// /// /// The character being tested which should be: 0x00-0x1F, 0x7F. /// /// /// True if the character passed the test, False otherwise. /// public static bool IsCntrlChar(char ch) { if ((ch >= 0x00 && ch <= 0x1F) || (ch == 0x7F)) return true; else return false; } /// /// Test if a character can occur in an identifier other than as the first character. /// /// /// The character being tested which should be: A-Z, a-z, 0-9, _ /// /// /// True if the character passed the test, False otherwise. /// public static bool IsCsymChar(char ch) { if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '_')) return true; else return false; } /// /// Test if a character can occur as the first character of an identifier. /// /// /// The character being tested which should be: A-Z, a-z, _ /// /// /// True if the character passed the test, False otherwise. /// public static bool IsCsymfChar(char ch) { if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch == '_')) return true; else return false; } /// /// Test if a character is a numeric digit. /// /// /// The character being tested which should be: 0-9 /// /// /// True if the character passed the test, False otherwise. /// public static bool IsDigitChar(char ch) { if (ch >= '0' && ch <= '9') return true; else return false; } /// /// Test if a character is printable ASCII (a space to a tilde). /// /// /// The character being tested which should be: 0x20-0x7e. /// /// /// True if the character passed the test, False otherwise. /// public static bool IsPrintChar(char ch) { if (ch >= 0x20 && ch <= 0x7e) return true; else return false; } /// /// Test if a character is a space. /// /// /// The character being tested which should be: 0x09-0x0D, 0x20 /// 0x09 is a Tab, 0x0A is LF, 0x0B is a Vertical tab, 0x0C is a page feed, /// 0x0D is CR, 0x20 is a space. Only the normal tab, LF, CR and space are /// common in text files. /// /// /// True if the character passed the test, False otherwise. /// public static bool IsSpaceChar(char ch) { if ((ch >= 0x09 && ch <= 0x0D) || (ch == 0x20)) return true; else return false; } /// /// Test if a character is a tab or a space. /// /// /// The character being tested which should be: 0x09, 0x20 /// /// /// True if the character passed the test, False otherwise. /// public static bool IsWhitespaceChar(char ch) { if (ch == 0x09 || ch == 0x20) return true; else return false; } /// /// Test if a character is hexadecimal. /// /// /// The character being tested which should be: A-F, a-f, 0-9 /// /// /// True if the character passed the test, False otherwise. /// public static bool IsXdigitChar(char ch) { if ((ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f') || (ch >= '0' && ch <= '9')) return true; else return false; } /// /// Test if a character can occur in an XML identifier other than as the first character. /// /// /// The character being tested which should be: A-Z, a-z, 0-9, _, :, -, . /// /// /// True if the character passed the test, False otherwise. /// public static bool IsXmlsymChar(char ch) { if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '_') || (ch == ':') || (ch == '-') || (ch == '.')) return true; else return false; } /// /// Test if a character can occur as the first character of an XML identifier. /// /// /// The character being tested which should be: A-Z, a-z, _, : /// /// /// True if the character passed the test, False otherwise. /// public static bool IsXmlsymfChar(char ch) { if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch == '_') || (ch == ':')) return true; else return false; } /// /// Test if a string is a valid identifier. /// /// /// The string being tested which should be in the form: [A-Za-z_] [A-Za-z_0-9]* /// /// /// True if the string is a valid identifier, False otherwise. /// public static bool IsCsymString(string s) { bool fReturn = false; if (s != null && s != "") { fReturn = IsCsymfChar(s[0]); for (int i=1; i /// If a string contains identifiers enclosed with the specified characters, /// replace any spaces in the identifier with underscores. /// /// /// This parameter may contain fragments of program text, such as identifiers /// enclosed in brackets containing spaces, and internal strings. /// /// /// Left bracket character, usually this will be '[' or similar. /// /// /// Right bracket character, usually this will be ']' or similar. /// /// /// The input text with underscores instead of spaces in any bracketed /// identifiers. For example: /// "[my id] = 'array a[1 2]' + b c" /// becomes /// "[my_id] = 'array a[1 2]' + b c" /// Note the contents of internal strings in the input text are unchanged. /// public static string StringFixDelimitedId(string strIn, char chLeft, char chRight) { char[] aOut = null; bool fInSqString = false; bool fInDqString = false; bool fInIdent = false; if (strIn != null && strIn.Length > 0) { aOut = new char[strIn.Length]; for (int i=0; i /// Unwrap a string from any enclosing brackets. /// /// /// String being tested. /// /// /// This is compared to the string's leftmost character. /// /// /// This is compared to the string's rightmost character. /// /// /// If a string starts and ends with the specified characters, /// return it without these characters. Otherwise return the /// string unchanged. For example "[my id]" is returned as "my id". /// public static string StringUnwrap(string strIn, char chLeft, char chRight) { string strOut = ""; if (strIn != null && strIn.Length > 0) { int len = strIn.Length; if (len >= 2 && strIn[0] == chLeft && strIn[len-1] == chRight) strOut = strIn.Substring(1, len-2); else strOut = strIn; } return strOut; } /// /// Convert a two dimensional array into a string representation. /// /// /// A two dimensional array of integers, indexed as a[rows,cols]. The array /// is converted by using each integer as an index in aDescriptions, and building /// up the returned string from the descriptions. /// /// /// String array containing the descriptions used for the conversion. /// If the integers in aIn[] are within the bounds of the descriptions array, /// they index the description from the descriptions array. If the integers /// are outside the bounds, the description and separator are not added. /// /// /// String array containing separators used after applying the descriptions. /// /// /// A multi line string produced by converting aIn[]. Each row in aIn[] /// produces a line in the returned string. /// public static string ArrayToString(int[,] aIn, string[] aDescriptions, string[] aSeparators) { string strReturn = ""; string strDescription = ""; if (aIn != null && aDescriptions != null && aSeparators != null) { for (int r=aIn.GetLowerBound(0); r<=aIn.GetUpperBound(0); r++) { for (int c=aIn.GetLowerBound(1); c<=aIn.GetUpperBound(1); c++) { if (aIn[r,c] >= aDescriptions.GetLowerBound(0) && aIn[r,c] <= aDescriptions.GetUpperBound(0)) { strDescription = aDescriptions[aIn[r,c]]; strReturn = strReturn + strDescription; if (c >= aSeparators.GetLowerBound(0) && c <= aSeparators.GetUpperBound(0)) strReturn = strReturn + aSeparators[c]; else if (aSeparators.GetUpperBound(0) >= aSeparators.GetLowerBound(0)) strReturn = strReturn + aSeparators[aSeparators.GetUpperBound(0)]; } } strReturn = strReturn.TrimEnd() + "\r\n"; } } return strReturn; } #endregion }