using System.Text.RegularExpressions; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; namespace mScriptableCS25; public class MainScriptingObject : IDisposable { SortedDictionary scrInp1 = new(StringComparer.OrdinalIgnoreCase); SortedDictionary scrInp2 = new(StringComparer.OrdinalIgnoreCase); //This will have the STRIPUNPRINTABLES applied to it string scriptSource = string.Empty; string tempPath = Path.GetTempPath(); Dictionary>? dctCache = null; Dictionary dctFileCache = new(StringComparer.OrdinalIgnoreCase); Random rn = new(); List lstFilesToRemove = new(); bool removedOldScripts = false; public void Dispose() { if (dctCache != null) dctCache.Clear(); } public bool HadErrors { get; set; } = false; public bool DebugMode { get; set; } = false; public bool UseCache { get; set; } = true; public bool AllowRemovalOfOldScripts { get; set; } = true; public string ScriptSource { get { return scriptSource; } set { scriptSource = CleanUserCode(value); dctCache = new(); } } public string TempPath { get { return tempPath; } set { string f = Path.Combine(value, "_test.txt"); if (!Directory.Exists(Path.GetDirectoryName(f))) Directory.CreateDirectory(Path.GetDirectoryName(f)); try { using (var sw = new StreamWriter(f, false)) { sw.WriteLine(); } if (File.Exists(f)) File.Delete(f); } catch { tempPath = Path.GetTempPath(); } } } public bool SetScriptFromFile(string VBScriptPath) { var rv = false; if (dctFileCache.ContainsKey(VBScriptPath)) { scriptSource = dctFileCache[VBScriptPath].VbsScriptSource; dctCache = dctFileCache[VBScriptPath].CacheDict; rv = true; } else { if (!File.Exists(VBScriptPath)) { scriptSource = string.Empty; dctCache = null; rv = false; } else { var z = new MdlVbsSources(); using (var sr = new StreamReader(VBScriptPath)) scriptSource = sr.ReadToEnd(); z.VbsScriptSource = scriptSource; dctCache = z.CacheDict; dctFileCache[VBScriptPath] = z; rv = true; } } return rv; } public void ClearScriptInputs() { scrInp1.Clear(); scrInp2.Clear(); } public void SetScriptInputs(Dictionary z) { foreach (var kvp in z) { scrInp1[kvp.Key] = kvp.Value; scrInp2[kvp.Key] = StripUnprintables(kvp.Value); } } public void AddScriptInput(string VariableName, string Value) { scrInp1[VariableName] = Value; scrInp2[VariableName] = StripUnprintables(Value); } public void RemoveOldScripts() { if (AllowRemovalOfOldScripts && !removedOldScripts) { removedOldScripts = true; foreach (var f0 in Directory.GetFiles(tempPath, "TmpVbsScript*.*")) { var fif = new FileInfo(f0); if (DateTime.UtcNow.Subtract(fif.LastWriteTimeUtc).TotalHours > 24) try { File.Delete(f0); } catch { } } } } public Dictionary RunScript() { var tStamp = string.Format("TmpVbsScript-{0:yyyyMMddHHmmssfff}-{1}", DateTime.Now, Guid.NewGuid().ToString("N").Substring(0, 12)); var outFile = Path.Combine(tempPath, tStamp + ".txt"); var scriptFile = Path.Combine(tempPath, tStamp + ".vbs"); var iKey = ToGuid(string.Join('\t', scrInp2.ToArray())); Dictionary toReturn = new(StringComparer.OrdinalIgnoreCase); var retVals = string.Empty; var varName = string.Empty; var varVal = string.Empty; HadErrors = false; lstFilesToRemove.Add(outFile); lstFilesToRemove.Add(scriptFile); if (UseCache && dctCache.ContainsKey(iKey)) toReturn = dctCache[iKey]; else { var genCodeHeader = new StringBuilder(); var genCodeFooter = new StringBuilder(); RemoveOldScripts(); genCodeHeader.AppendLine("'*************************************************"); genCodeHeader.AppendLine("'* VBScript generated by the mScript class *"); genCodeHeader.AppendLine("'* for dynamic scripting of .NET applications *"); genCodeHeader.AppendLine("'* this file can be safely delted. *"); genCodeHeader.AppendLine("'* *"); genCodeHeader.AppendLine("'* mScriptable was created by Don Smith *"); genCodeHeader.AppendLine("'* *"); genCodeHeader.AppendLine("'* You may use mScriptable however you like *"); genCodeHeader.AppendLine("'* as long as you give credit to me (Don) for *"); genCodeHeader.AppendLine("'* mScriptable. *"); genCodeHeader.AppendLine("'* Revised in 2015 by KeyMotive LLC (Rich Deck) *"); genCodeHeader.AppendLine("'*************************************************"); genCodeHeader.AppendLine("outFile = \"" + outFile + "\""); //get our file I/O initialized. This assumes we have write //access to the current directory. genCodeHeader.AppendLine("Set oFsObj = CreateObject(\"Scripting.FileSystemObject\")"); genCodeHeader.AppendLine("Set oFHnd = oFsObj.CreateTextFile(outFile, true)"); //a wrapper function with a shorter name to access the supplied inputs genCodeHeader.AppendLine("Function iv(variableName)"); genCodeHeader.AppendLine(" iv = inpVal(variableName)"); genCodeHeader.AppendLine("End Function ' iv()"); //a function to allow the user script to access supplied inputs genCodeHeader.AppendLine("Function inpVal(variableName)"); genCodeHeader.AppendLine(" Select Case UCase(variableName)"); /* This takes all of the script inputs and places them inside of a VBScript Select Case statement in the inpVal() function so the supplied VBScript can retrieve the values by name. */ foreach (var kvp in scrInp1) { if (!string.IsNullOrWhiteSpace(kvp.Key)) { if (decimal.TryParse(kvp.Value, out _)) varVal = kvp.Value.ToString(); else { if (kvp.Value == null || Convert.IsDBNull(kvp.Value)) varVal = string.Empty; else varVal = string.Format("\"{0}\"", kvp.Value.ToString().Replace("\"", "\"\"")); } varVal = Regex.Replace(varVal, "(\r\n|\n\r|\r|\n)", "\"\n\""); varName = "\"" + kvp.Key.ToUpper().Replace("\"", "\"\"") + "\""; genCodeHeader.AppendLine(" Case " + varName); genCodeHeader.AppendLine(" retVal = " + varVal); } } //finish the case/function genCodeHeader.AppendLine(" Case Else"); genCodeHeader.AppendLine(" retVal = \"\""); genCodeHeader.AppendLine(" End Select"); genCodeHeader.AppendLine(" inpVal = retVal"); genCodeHeader.AppendLine("End Function ' inpVal()\n"); //subroutine allowing the script to return a variable genCodeHeader.AppendLine("Sub return(varName, retVal)"); genCodeHeader.AppendLine(" While InStr(1, retVal, vbNewLine, vbTextCompare) >= 1"); genCodeHeader.AppendLine(" retVal = replace(retVal, vbNewLine, \"~NewLine~\")"); genCodeHeader.AppendLine(" Wend"); genCodeHeader.AppendLine(" oFHnd.WriteLine varName & vbTab & retVal"); genCodeHeader.AppendLine("End Sub ' return()\n"); //close our output file handle genCodeFooter.AppendLine("\n\noFHnd.Close"); //All code using (var sw = new StreamWriter(scriptFile, false)) sw.WriteLine(genCodeHeader.ToString() + scriptSource + genCodeFooter.ToString()); // Run the script! try { using (var px = new Process()) { var pInfo = new ProcessStartInfo() { FileName = "wscript.exe", Arguments = scriptFile }; px.StartInfo = pInfo; px.Start(); if (!px.HasExited) px.WaitForInputIdle(); px.WaitForExit(60000); if (!px.HasExited) { HadErrors = true; px.Kill(); } } if (File.Exists(outFile)) { using (var sr = new StreamReader(outFile)) retVals = sr.ReadToEnd(); } toReturn.Clear(); foreach (var f0 in Regex.Split(retVals, "(\r\n|\n\r|\r|\n)")) { if (f0.Length >= 3 && f0.IndexOf('\t') > 0) { var nvp = f0.Split('\t'); nvp[1] = Regex.Replace(nvp[1], "\x7eNewLine\x7e", "\n"); toReturn[nvp[0]] = nvp[1]; } } if (UseCache) dctCache[iKey] = toReturn; } catch (Exception ex) { using (var sw = new StreamWriter(outFile, true)) sw.WriteLine(ex.ToString()); } } return toReturn; } #region "Maint routines" string CleanUserCode(string ScriptCode) { string workCode = ScriptCode; Regex? re = null; string inpVal_re = @"Function\s+inpVal\s*?\([^)]+\)(.*?)End\s+Function"; string iv_re = @"Function\s+iv\s*?\([^)]+\)(.*?)End\s+Function"; string return_re = @"Sub\s+return\s*?\([^)]+\)(.*?)End\s+Sub"; //remove conflicting "inpVal" functions re = new Regex(inpVal_re, RegexOptions.Singleline | RegexOptions.IgnoreCase); if (re.IsMatch(workCode)) workCode = re.Replace(workCode, ""); //remove conflicting "iv" functions re = new Regex(iv_re, RegexOptions.Singleline | RegexOptions.IgnoreCase); if (re.IsMatch(workCode)) workCode = re.Replace(workCode, ""); //remove conflicting "return" subroutine re = new Regex(return_re, RegexOptions.Singleline | RegexOptions.IgnoreCase); if (re.IsMatch(workCode)) workCode = re.Replace(workCode, ""); return workCode; } string StripUnprintables(string inp) { var rv = Regex.Replace(inp, "[^\x20-\x7E]", " "); rv = Regex.Replace(rv, " +", " ").Trim(); return rv; } Guid ToGuid(string strToHash) { var gx = Guid.Empty; using (var mD = System.Security.Cryptography.MD5.Create()) { var stringBuilder = new StringBuilder(); byte[] array = mD.ComputeHash(Encoding.UTF8.GetBytes(strToHash)); for (int i = 0; i < array.Length; i++) stringBuilder.Append(array[i].ToString("X2")); gx = Guid.Parse(stringBuilder.ToString()); } return gx; } #endregion #region "Models and Such" internal class MdlVbsSources : IDisposable { public void Dispose() { CacheDict.Clear(); } public string VbsScriptSource { get; set; } = string.Empty; public Dictionary> CacheDict { get; set; } = new(); } #endregion }