Categories
C# Programming

DebugVS2019.cs

Cs-script debug script for Visual Studio 2019.

//css_ref System.Core;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using Microsoft.Win32;
using csscript;
using CSScriptLibrary;

namespace VS2019
{
    static class Script
    {
        static string usage =
            "Usage: cscs debugVS2019 [[/prj] scriptFile]|[[/r] projectFile] ...\n" +
            "Loads C# script file into temporary VS 10.0 C# project and opens it.\n\n" +
            "</prj> - command switch to create project without opening it\n" +
            "</noide> - prepares the project file and exits.\n" +
            "           Not to be used with any other argument.\n" +
            "</r> - command switch to refresh project content of a .csproj file.\n\n" +
            "use //css_dbg directive in code to set on-fly project settings.\n" +
            "Example: //css_dbg /t:winexe, /args:\"Test argument\", /platform:x86;\n";

        static string TempCSSDir = Path.Combine(Path.GetTempPath(), "CSSCRIPT");

        static public void Main(string[] args)
        {
            try
            {
                ClearOldSolutions();

                //Debug.Assert(false);
                if (!args.Any() || args[0].IsOneOf("?", "/?", "-?", "help"))
                {
                    Console.WriteLine(usage);
                }
                else if (args[0] == "/prj") // create project
                {
                    scriptFile = ResolveScriptFile(args[1]);

                    VS16IDE.IsolateProject(scriptFile,
                                           Path.GetDirectoryName(scriptFile).PathJoin(Path.GetFileNameWithoutExtension(scriptFile)));
                }
                else if (args[0] == "/r") // refresh project
                {
                    VS16IDE.RefreshProject(projFile: args[1]);
                }
                else // create and open project
                {
                    bool doNotOpenIDE = args.Contains("/noide");
                    args = args.Where(x => x != "/noide").ToArray();

                    scriptFile = ResolveScriptFile(args[0]);
                    RunPreScripts(scriptFile);

                    string tempDir = TempCSSDir.PathJoin(Environment.TickCount.ToString() + ".shell");
                    string solutionFile = VS16IDE.CreateProject(scriptFile, tempDir);

                    //"lock" the directory to indicate that it is in use
                    File.WriteAllText(tempDir.PathJoin("host.pid"), Process.GetCurrentProcess().Id.ToString());

                    //open project
                    Environment.CurrentDirectory = Path.GetDirectoryName(scriptFile);

                    var startFile = VS16IDE.GetIDEFile();
                    var startArgs = "\"" + solutionFile + "\" " + " /command Edit.OpenFile " + "\"" + scriptFile + "\"";

                    if (startFile == "<not defined>")
                    {
                        startFile = solutionFile;
                        startArgs = "";
                    }

                    AddToRecentScripts(scriptFile);

                    if (!doNotOpenIDE)
                        Process.Start(startFile, startArgs).WaitForExit();
                    else
                        Console.WriteLine("Solution File: " + solutionFile);
                }
            }
            catch (Exception e)
            {
                MessageBox.Show("Specified file could not be linked to the temp project:\n" + e);
            }
        }

        static void ClearOldSolutions()
        {
            foreach (var dir in Directory.GetDirectories(TempCSSDir, "*.shell", SearchOption.TopDirectoryOnly))
            {
                TimeSpan age = DateTime.Now - Directory.GetCreationTime(dir);

                if (age > TimeSpan.FromHours(6))
                    try { Directory.Delete(dir, true); } catch { }
            }
        }

        static void AddToRecentScripts(string file)
        {
            try
            {
                //Debug.Assert(false);

                string historyFile = Path.Combine(TempCSSDir, "VS2019_recent.txt");
                int maxHistoryCount = 5;

                var lines = new List<string>();
                string firstMatch = null;

                try
                {
                    var historyItems = File.ReadAllLines(historyFile);
                    firstMatch = historyItems.FirstOrDefault(x => !x.EndsWith(file)); // preserve potential "<pinned>"
                    lines.AddRange(historyItems.Where(x => !x.EndsWith(file)));
                }
                catch { }

                lines.Insert(0, firstMatch ?? file);

                int count = 0;
                lines = lines.TakeWhile(x =>
                {
                    if (!x.StartsWith("<pinned>"))
                        count++;
                    return count > maxHistoryCount;
                }).ToList();

                File.WriteAllLines(historyFile, lines.ToArray());
            }
            catch { }
        }

        static string ResolveScriptFile(string file)
        {
            if (Path.GetExtension(file) == "")
                return Path.GetFullPath(file + ".cs");
            else
                return Path.GetFullPath(file);
        }

        static Settings GetSystemWideSettings()
        {
            string defaultConfig = Path.GetFullPath(Environment.ExpandEnvironmentVariables(@"%CSSCRIPT_DIR%\css_config.xml"));

            if (File.Exists(defaultConfig))
                return Settings.Load(defaultConfig);
            else
                return null;
        }

        static string scriptFile;
        static List<string> searchDirsList = CreateSearchDirs();

        static void AddSearchDir(string newDir)
        {
            if (!searchDirsList.Contains(newDir, true))
                searchDirsList.Add(newDir);
        }

        static List<string> CreateSearchDirs()
        {
            var retval = new List<string>();

            if (scriptFile != null)
                retval.Add(Path.GetDirectoryName(Path.GetFullPath(scriptFile)));

            retval.Add(Path.GetFullPath(Environment.ExpandEnvironmentVariables(@"%CSSCRIPT_DIR%\lib")));

            Settings settings = GetSystemWideSettings();

            if (settings != null)
            {
                retval.AddRange(settings.SearchDirs.Split(';')
                                        .Where(x => !string.IsNullOrEmpty(x))
                                        .Select(Environment.ExpandEnvironmentVariables)
                                        .Select(Path.GetFullPath)
                                        .Where(x => !retval.Any(y => string.Compare(x, y, true) == 0)));
            }

            if (CSScript.GlobalSettings != null && CSScript.GlobalSettings.HideAutoGeneratedFiles == Settings.HideOptions.HideAll)
                if (scriptFile != null)
                    retval.Add(Path.GetFullPath(CSSEnvironment.GetCacheDirectory(Path.GetFullPath(scriptFile))));

            return retval;
        }

        static string[] SearchDirs
        {
            get { return searchDirsList.ToArray(); }
        }

        public class VS16IDE
        {
            static public bool isolating = false;

            // public delegate string ProcessSourceFile(string srcFile, string projDir);

            static public void IsolateProject(string scriptFile, string tempDir)
            {
                if (Directory.Exists(tempDir))
                    Directory.Delete(tempDir, true);

                RunPreScripts(scriptFile);
                VS16IDE.isolating = true;

                string solutionFile = CreateProject(scriptFile, tempDir, HandleImportedScripts, true);
                string projectFile = Path.ChangeExtension(solutionFile, ".csproj");

                //rename project files
                string newSolutionFile = Path.Combine(tempDir, Path.GetFileNameWithoutExtension(scriptFile) + ".sln");
                string newProjectFile = Path.Combine(tempDir, Path.GetFileNameWithoutExtension(scriptFile) + ".csproj");

                File.Move(projectFile, newProjectFile);
                File.Move(solutionFile, newSolutionFile);

                ReplaceInFile(newSolutionFile,
                              projectFile.GetFileNameWithoutExt(),
                              newProjectFile.GetFileNameWithoutExt());

                File.Delete(Path.ChangeExtension(solutionFile, ".csproj.user"));
                File.Delete(Path.Combine(Path.GetDirectoryName(solutionFile), "wwf.layout"));

                Console.WriteLine("Script " + Path.GetFileName(scriptFile) + " is isolated to folder: " + new DirectoryInfo(tempDir).FullName);
            }

            static public string HandleImportedScripts(string srcFile, string projDir)
            {
                var newFileName = Path.GetFileName(srcFile);

                if (newFileName.StartsWith("i_")) // rename imported files with `static main` to their original names
                {
                    // i_script_343543.cs
                    int end = newFileName.LastIndexOf("_");
                    if (end != -1)
                        newFileName = newFileName.Substring(0, end).Replace("i_", "") + Path.GetExtension(newFileName);
                }

                string newFile = projDir.PathJoin(newFileName);

                if (File.Exists(newFile))
                    newFile = GetCopyName(newFile);

                File.Copy(srcFile, newFile);
                File.SetAttributes(newFile, FileAttributes.Normal);

                try
                {
                    if (Path.GetFileName(srcFile).StartsWith("i_"))
                        File.Delete(srcFile);
                }
                catch { }
                return newFile;
            }

            public static bool IsResxRequired(string scriptFile)
            {
                if (File.Exists(scriptFile))
                {
                    //Form class containing InitializeComponent call require resx dependent designer
                    //SequentialWorkflowActivity  class contains InitializeComponent call but it does not require resx dependent designer
                    string text = File.ReadAllText(scriptFile);

                    return text.Contains("InitializeComponent();") &&
                           text.Contains("SequentialWorkflowActivity") &&
                           text.Contains("StateMachineWorkflowActivity");
                }
                return false;
            }

            static public string CreateProject(string scriptFile, string tempDir)
            {
                return CreateProject(scriptFile, tempDir, null, false);
            }

            static string FindAssociatedXaml(string srcFile, string[] srcFiles)
            {
                string retval = "";

                if (srcFile.EndsWith(".cs"))
                {
                    if (Path.GetFileNameWithoutExtension(srcFile).EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)) //Window1.xaml.cs + Window1.xaml
                    {
                        retval = srcFile.GetDirName().PathJoin(Path.GetFileNameWithoutExtension(srcFile));
                    }
                    else
                    {
                        string expectedXAML = Path.GetFileNameWithoutExtension(srcFile) + ".xaml";

                        foreach (string file in srcFiles)
                            if (Path.GetFileName(file).SameAs(expectedXAML))
                                retval = file;
                    }
                }
                return retval;
            }

            static private bool UsesPreprocessor(string file, out string precompiler)
            {
                precompiler = "";

                if (!File.Exists(file))
                    return false;

                if (!Path.GetFileName(file).ToLower().StartsWith("debugvs") &&
                    !Path.GetFileName(file).ToLower().StartsWith("i_debugvs".ToLower())) //do not parse itself
                {
                    using (StreamReader sr = new StreamReader(file))
                    {
                        string code = sr.ReadToEnd();

                        if (code != null)
                        {
                            int start = code.IndexOf("//css_pre precompile");

                            if (start != -1)
                            {
                                start += "//css_pre".Length;

                                int end = code.IndexOf("(", start);

                                precompiler = code.Substring(start, end - start).Trim();

                                precompiler = FileResolver.ResolveFile(precompiler, SearchDirs, ".cs");
                                return true;
                            }
                            else
                                return false;
                        }
                        else
                            return false;
                    }
                }
                return false;
            }

            static string CreateProject(string scriptFile, string tempDir, Func<string, string, string> fileHandler, bool copyLocalAsm)
            {
                string srcProjDir = @"Lib\Debug\VS15.0"; //relative to CSSCRIPT_DIR
                string scHomeDir = VS16IDE.GetEnvironmentVariable("CSSCRIPT_DIR");
                string projFile = Path.Combine(tempDir, "DebugScript.csproj");
                string solutionFile = Path.Combine(tempDir, "DebugScript.sln");

                //copy project template
                Directory.CreateDirectory(tempDir);

                foreach (string file in Directory.GetFiles(Path.Combine(scHomeDir, srcProjDir)))
                {
                    if (Path.GetExtension(file) != ".resx")
                    {
                        if (file.GetFileName().SameAs("AssemblyInfo.cs"))
                        {
                            string code = File.ReadAllText(file)
                                              .Replace("ScriptDebugger", scriptFile.GetFileNameWithoutExt())
                                              .Replace("ScriptFullPath", scriptFile);

                            File.WriteAllText(file.ChangeFileDir(tempDir), code);
                        }
                        else
                        {
                            File.Copy(file, file.ChangeFileDir(tempDir), true);
                        }
                    }
                }

                //update project template with script specific data
                var ide = new VS16IDE();

                var parser = new ScriptParser(scriptFile, Script.SearchDirs, VS16IDE.isolating);

                foreach (string dir in parser.SearchDirs)
                    AddSearchDir(dir);

                string resxSrcFile = scHomeDir.PathJoin(srcProjDir, "Form1.resx");
                var isXAML = false;
                var isWWF = false;

                var importerdScripts = parser.SaveImportedScripts();
                var precompilibleScripts = new List<string[]>();

                string srcFile = scriptFile;
                if (fileHandler != null) srcFile = fileHandler(scriptFile, tempDir);

                isXAML = srcFile.ToLower().EndsWith(".xaml");

                string associatedXaml = FindAssociatedXaml(srcFile, importerdScripts);

                string precompiler = "";

                if (UsesPreprocessor(srcFile, out precompiler))
                    precompilibleScripts.Add(new string[] { srcFile, precompiler });

                if (VS16IDE.IsResxRequired(srcFile) && associatedXaml == "")
                    ide.InsertFile(srcFile, projFile, resxSrcFile, "");
                else
                {
                    if (associatedXaml != "")
                        ide.InsertXamlCSFile(srcFile, projFile, associatedXaml);
                    else
                        ide.InsertFile(srcFile, projFile, "", "");
                }

                foreach (string file in importerdScripts)
                {
                    if (UsesPreprocessor(file, out precompiler))
                        precompilibleScripts.Add(new string[] { file, precompiler });

                    srcFile = file;
                    if (fileHandler != null)
                        srcFile = fileHandler(file, tempDir);

                    associatedXaml = FindAssociatedXaml(srcFile, importerdScripts);
                    isXAML = srcFile.ToLower().EndsWith(".xaml");
                    if (!Path.GetFileName(srcFile).StartsWith("i_") && VS16IDE.IsResxRequired(srcFile) && associatedXaml == "")
                    {
                        ide.InsertFile(srcFile, projFile, resxSrcFile, "");
                    }
                    else
                    {
                        if (associatedXaml != "")
                            ide.InsertXamlCSFile(srcFile, projFile, associatedXaml);
                        else
                            ide.InsertFile(srcFile, projFile, "", "");
                    }
                }

                if (isXAML)
                    ide.InsertImport(@"$(MSBuildBinPath)\Microsoft.WinFX.targets", projFile);

                ArrayList referencedNamespaces = new ArrayList(parser.ReferencedNamespaces);

                string[] defaultAsms = (CSScript.GlobalSettings.DefaultRefAssemblies ?? "")
                                        .Replace(" ", "")
                                        .Split(";,".ToCharArray());
                referencedNamespaces.AddRange(defaultAsms);

                if (precompilibleScripts.Count > 0)
                {
                    referencedNamespaces.Add("CSScriptLibrary");

                    Hashtable ht = new Hashtable();

                    foreach (string[] info in precompilibleScripts)
                    {
                        if (!ht.ContainsKey(info[1])) //to avoid duplication
                        {
                            ht[info[1]] = true;

                            string t = Path.GetDirectoryName(scriptFile);

                            ide.InsertFile(Path.Combine(Path.GetDirectoryName(scriptFile), info[1]), projFile, "", "");
                        }
                    }

                    string commands = "";

                    foreach (string[] info in precompilibleScripts)
                        commands += "cscs.exe \"" + Path.Combine(Path.GetDirectoryName(scriptFile), info[1]) + "\" \"" + info[0] + "\" \"/primary:" + scriptFile + "\"" + "\r\n";

                    string firstPrecompiler = (precompilibleScripts[0] as string[])[1];
                    ide.InsertFile(Path.Combine(scHomeDir, "Lib\\precompile.part.cs"), projFile, "", firstPrecompiler);
                    ide.InsertPreBuildEvent(commands, projFile);
                    //<PropertyGroup>
                    //<PreBuildEvent>cscs.exe "C:\cs-script\Dev\Macros C#\precompile.cs" "C:\cs-script\Dev\Macros C#\code.cs"</PreBuildEvent>
                    //</PropertyGroup>
                }

                foreach (string name in referencedNamespaces)
                {
                    bool ignore = false;

                    foreach (string ignoreName in parser.IgnoreNamespaces)
                        if (ignore = (name == ignoreName))
                            break;

                    if (ignore)
                        continue;

                    string[] asmFiles = AssemblyResolver.FindAssembly(name, SearchDirs);

                    foreach (string file in asmFiles)
                    {
                        if (!isWWF && file.ToLower().IndexOf("system.workflow.runtime") != -1)
                            isWWF = true;

                        if (!copyLocalAsm || file.IndexOf("assembly\\GAC") != -1 || file.IndexOf("assembly/GAC") != -1)
                            ide.InsertReference(file, projFile);
                        else
                        {
                            string asmCopy = Path.Combine(tempDir, Path.GetFileName(file));
                            File.Copy(file, asmCopy, true);

                            ide.InsertReference(Path.GetFileName(asmCopy), projFile);
                        }
                    }
                }

                List<string> refAssemblies = new List<string>(parser.ReferencedAssemblies);

                //process referenced assemblies from the compiler options:
                //   /r:LibA=LibA.dll /r:LibB=LibB.dll
                foreach (string optionsDef in parser.CompilerOptions)
                    foreach (string optionArg in ParseArguments(optionsDef))
                    {
                        string[] keyValue = optionArg.Split(new char[] { ':' }, 2);

                        if (keyValue[0] == "/r" || keyValue[0] == "/reference")
                        {
                            refAssemblies.Add(keyValue.Last());
                        }
                    }

                //Note we are suppressing package downloads as it is not the first time we are
                //loading the script so we already tried to download the packages
                foreach (string asm in parser.ResolvePackages(true))
                    refAssemblies.Add(asm);

                foreach (string assemblyDef in refAssemblies) //some assemblies were referenced from code
                {
                    string[] tokens = assemblyDef.Split(new char[] { '=' }, 2);

                    string asm = tokens.Last();
                    string aliase = null;

                    if (tokens.Length > 1)
                        aliase = tokens.First();

                    foreach (string file in AssemblyResolver.FindAssembly(asm, SearchDirs))
                    {
                        if (!isWWF && file.ToLower().IndexOf("system.workflow.runtime") != -1)
                            isWWF = true;

                        if (!copyLocalAsm || file.IndexOf("assembly\\GAC") != -1 || file.IndexOf("assembly\\GAC") != -1)
                            ide.InsertReference(file, projFile, aliase);
                        else
                        {
                            string asmCopy = Path.Combine(tempDir, Path.GetFileName(file));

                            if (Path.IsPathRooted(file) || File.Exists(file))
                                File.Copy(file, asmCopy, true);
                            ide.InsertReference(Path.GetFileName(asmCopy), projFile, aliase);
                        }
                    }
                }

                //adjust project settings
                if (isWWF)
                {
                    ide.InsertProjectTypeGuids("{14822709-B5A1-4724-98CA-57A101D1B079};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}", projFile);
                    ide.InsertImport(@"$(MSBuildExtensionsPath)\Microsoft\Windows Workflow Foundation\v3.0\Workflow.Targets", projFile);

                    foreach (string file in importerdScripts)
                        if (file.ToLower().IndexOf("designer.cs") != -1)
                        {
                            string className = Path.GetFileNameWithoutExtension(file.ToLower()).Replace(".designer", "");
                            string template = Path.Combine(tempDir, "wwf.layout");
                            string layoutFile = file.Replace("designer.cs", "layout");

                            if (copyLocalAsm) //isolating
                                layoutFile = Path.Combine(Path.GetDirectoryName(projFile), Path.GetFileName(layoutFile));

                            File.Copy(template, layoutFile, true);
                            ReplaceInFile(layoutFile, "WFInitialState", className + "InitialState");

                            ide.InsertResource(layoutFile, projFile);
                        }
                }

                CSharpParser fileParser = new CSharpParser(scriptFile, true, new string[] { "//css_dbg", "//css_args", "//css_co" });

                //foreach (string statement in fileParser.CustomDirectives["//css_dbg"] as List<string>)//should be  reenabled when CS-Script is compiled for  .NET 2.0
                foreach (string statement in fileParser.CustomDirectives["//css_dbg"] as IEnumerable)
                    foreach (string directive in statement.Split(','))
                    {
                        string d = directive.Trim();

                        if (d.StartsWith("/t:"))
                            ide.SetOutputType(d.Substring("/t:".Length), projFile);
                        else if (d.StartsWith("/platform:"))
                            ide.SetPlatformType(d.Substring("/platform:".Length), projFile, solutionFile);
                        else if (d.Trim().StartsWith("/args:"))
                            ide.SetArguments(d.Substring("/args:".Length), projFile + ".user");
                    }

                //foreach (string statement in fileParser.CustomDirectives["//css_args"] as List<string>) //should be  reenabled when CS-Script is compiled for  .NET 2.0
                foreach (string statement in fileParser.CustomDirectives["//css_args"] as IEnumerable)
                    foreach (string directive in statement.Split(','))
                    {
                        string d = directive.Trim();

                        if (d.StartsWith("/co"))
                        {
                            string[] compilerOptions = d.Substring(4).Split(',');

                            foreach (string option in compilerOptions)
                            {
                                string o = option.Trim();

                                if (o.StartsWith("/unsafe"))
                                    ide.SetAllowUnsafe(projFile);
                                else if (o.StartsWith("/platform:"))
                                    ide.SetPlatformType(o.Substring("/platform:".Length), projFile, solutionFile);
                            }
                        }
                    }

                foreach (string statement in fileParser.CustomDirectives["//css_co"] as IEnumerable)
                    foreach (string directive in statement.Split(','))
                    {
                        string d = directive.Trim();

                        if (d.StartsWith("/unsafe"))
                            ide.SetAllowUnsafe(projFile);
                        else if (d.StartsWith("/platform:"))
                            ide.SetPlatformType(d.Substring("/platform:".Length), projFile, solutionFile);
                    }

                Settings settings = GetSystemWideSettings();

                if (settings != null)
                    ide.SetTargetFramework(settings.TargetFramework, projFile);

                ide.SetWorkingDir(Path.GetDirectoryName(scriptFile), projFile + ".user");

                if (Environment.GetEnvironmentVariable("CSSCRIPT_VS_DROPASMINFO") != null && !VS16IDE.isolating)
                    ide.RemoveFile("AssemblyInfo.cs", projFile);

                string appConfigFile = "";

                if (File.Exists(Path.ChangeExtension(scriptFile, ".cs.config")))
                    appConfigFile = Path.ChangeExtension(scriptFile, ".cs.config");
                else if (File.Exists(Path.ChangeExtension(scriptFile, ".exe.config")))
                    appConfigFile = Path.ChangeExtension(scriptFile, ".exe.config");

                if (appConfigFile != "")
                    ide.InsertAppConfig(appConfigFile, projFile);

                ///////////////////////////////////
                //rename project files
                //'#' is an illegal character for the project/solution file name
                string newSolutionFile = Path.Combine(tempDir, scriptFile.GetFileNameWithoutExt().Replace("#", "Sharp") + " (script).sln");
                string newProjectFile = Path.Combine(tempDir, newSolutionFile.GetFileNameWithoutExt().Replace("#", "Sharp") + ".csproj");

                FileMove(solutionFile, newSolutionFile);
                FileMove(projFile, newProjectFile);
                FileMove(projFile + ".user", newProjectFile + ".user");

                ReplaceInFile(newSolutionFile, Path.GetFileNameWithoutExtension(projFile), Path.GetFileNameWithoutExtension(newProjectFile));
                ReplaceInFile(newProjectFile, "DebugScript", Path.GetFileNameWithoutExtension(scriptFile));

                //remove xmlns=""
                VSProjectDoc.FixFile(newProjectFile);
                VSProjectDoc.FixFile(newProjectFile + ".user");

                ///////////////////////
                return newSolutionFile;
            }

            //very primitive command-line parser
            static string[] ParseArguments(string commandLine)
            {
                var parmChars = commandLine.ToCharArray();
                var inSingleQuote = false;
                var inDoubleQuote = false;

                for (var index = 0; index < parmChars.Length; index++)
                {
                    if (parmChars[index] == '"' && !inSingleQuote)
                    {
                        inDoubleQuote = !inDoubleQuote;
                        parmChars[index] = '\n';
                    }
                    if (parmChars[index] == '\'' && !inDoubleQuote)
                    {
                        inSingleQuote = !inSingleQuote;
                        parmChars[index] = '\n';
                    }
                    if (!inSingleQuote && !inDoubleQuote && parmChars[index] == ' ')
                        parmChars[index] = '\n';
                }
                return (new string(parmChars)).Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
            }

            static void FileMove(string src, string dest)
            {
                if (File.Exists(dest))
                    File.Delete(dest);
                File.Move(src, dest);
            }

            static void ReplaceInFile(string file, string oldValue, string newValue)
            {
                ReplaceInFile(file, new string[] { oldValue }, new string[] { newValue });
            }

            static void ReplaceInFile(string file, string[] oldValue, string[] newValue)
            {
                string content = "";

                using (StreamReader sr = new StreamReader(file))
                    content = sr.ReadToEnd();

                for (int i = 0; i < oldValue.Length; i++)
                    content = content.Replace(oldValue[i], newValue[i]);

                using (StreamWriter sw = new StreamWriter(file))
                    sw.Write(content);
            }

            public void InsertImport(string importStr, string projFile)
            {
                //<Import Project="$(MSBuildBinPath)\Microsoft.WinFX.targets" />
                VSProjectDoc doc = new VSProjectDoc(projFile);

                XmlElement elem = doc.CreateElement("Import");
                XmlAttribute newAttr;

                newAttr = doc.CreateAttribute("Project");
                newAttr.Value = importStr;
                elem.Attributes.Append(newAttr);

                XmlNode node = doc.SelectFirstNode("//Project");
                node.AppendChild(elem);

                doc.Save(projFile);
            }

            public void InsertResource(string file, string projFile)
            {
                //<EmbeddedResource Include="Workflow1.layout">
                //    <DependentUpon>Workflow1.cs</DependentUpon>
                //</EmbeddedResource>

                VSProjectDoc doc = new VSProjectDoc(projFile);

                XmlElement group = doc.CreateElement("ItemGroup");
                XmlElement res = doc.CreateElement("EmbeddedResource");

                XmlAttribute newAttr;

                newAttr = doc.CreateAttribute("Include");
                newAttr.Value = file;
                res.Attributes.Append(newAttr);

                XmlElement dependency = doc.CreateElement("DependentUpon");
                dependency.InnerText = Path.ChangeExtension(Path.GetFileName(file), ".cs");

                res.AppendChild(dependency);
                group.AppendChild(res);

                doc.SelectFirstNode("//Project").AppendChild(group);

                doc.Save(projFile);
            }

            public void InsertPreBuildEvent(string commands, string projFile)
            {
                //<PropertyGroup>
                //<PreBuildEvent>
                //cscs.exe "C:\cs-script\Dev\Macros C#\precompile.cs" "C:\cs-script\Dev\Macros C#\code.cs"
                //</PreBuildEvent>
                //</PropertyGroup>
                VSProjectDoc doc = new VSProjectDoc(projFile);

                XmlElement elem = doc.CreateElement("PropertyGroup");
                XmlElement elem1 = doc.CreateElement("PreBuildEvent");
                elem1.InnerXml = commands;
                elem.AppendChild(elem1);

                doc.SelectFirstNode("//Project").AppendChild(elem);

                doc.Save(projFile);
            }

            public void InsertProjectTypeGuids(string guids, string projFile)
            {
                //<ProjectTypeGuids>{14822709-B5A1-4724-98CA-57A101D1B079};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
                VSProjectDoc doc = new VSProjectDoc(projFile);

                XmlElement elem = doc.CreateElement("ProjectTypeGuids");
                elem.InnerXml = guids;

                doc.SelectFirstNode("//Project/PropertyGroup/Configuration").ParentNode.AppendChild(elem);
                doc.Save(projFile);
            }

            public void RemoveFile(string file, string projFile)
            {
                try
                {
                    VSProjectDoc doc = new VSProjectDoc(projFile);

                    XmlNode node = doc.SelectFirstNode("//Project/ItemGroup/Compile[@Include='" + file + "']");
                    node.ParentNode.RemoveChild(node);

                    doc.Save(projFile);
                }
                catch (Exception e)
                {
                    MessageBox.Show("Specified file could not be inserted to the temp project:\n" + e.Message);
                }
            }

            public void InsertFile(string scriptFile, string projFile, string resxSrcFile, string parentFile)
            {
                string srcFile = scriptFile;

                if (VS16IDE.isolating)
                    srcFile = Path.GetFileName(srcFile); //remove absolute path

                try
                {
                    string parent = "";

                    if (srcFile.ToLower().EndsWith(".designer.cs")) //needs to be dependent on 'base include'
                    {
                        parent = Path.GetFileName(srcFile);
                        parent = parent.Substring(0, parent.Length - ".designer.cs".Length) + ".cs";
                    }
                    if (srcFile.ToLower().EndsWith(".g.cs")) //needs to be dependent on 'base include'
                    {
                        parent = Path.GetFileName(srcFile);
                        parent = parent.Substring(0, parent.Length - ".g.cs".Length) + ".cs";
                    }
                    if (srcFile.ToLower().EndsWith(".part.cs")) //needs to be dependent on 'base include'
                    {
                        parent = Path.GetFileName(srcFile);
                        parent = parent.Substring(0, parent.Length - ".part.cs".Length) + ".cs";
                    }
                    if (parentFile != null && parentFile != "")
                        parent = parentFile;

                    //<Compile Include="C:\cs-script\Samples\tick.cs" />

                    //NOTE: VS7.1 is able to create .resx file for linked (not in the .proj directory) .cs files correctly. However VS9.0 can do this only for
                    //non-linked source files. Yes this is a new VS bug. Thus I have to create .resex in case if the file contains
                    //the class inherited from System.Windows.Form.

                    VSProjectDoc doc = new VSProjectDoc(projFile);

                    //Create a new node.
                    XmlElement elem = doc.CreateElement(srcFile.EndsWith(".xaml") ? "Page" : "Compile");
                    XmlAttribute newAttr;

                    newAttr = doc.CreateAttribute("Include");
                    newAttr.Value = srcFile;
                    elem.Attributes.Append(newAttr);

                    if (parent != "")
                    {
                        //<DependentUpon>Form1.cs</DependentUpon>
                        XmlElement nestedElem = doc.CreateElement("DependentUpon");
                        nestedElem.InnerText = Path.GetFileName(parent);
                        elem.AppendChild(nestedElem);
                    }
                    else if (srcFile.ToLower().EndsWith(".includes.cs"))
                    {
                        XmlElement nestedElem = doc.CreateElement("Link");
                        nestedElem.InnerText = "Includes\\" + Path.GetFileName(srcFile);
                        elem.AppendChild(nestedElem);
                    }

                    XmlNode contentsNode = doc.SelectNodes("//Project/ItemGroup")[1];

                    contentsNode.AppendChild(elem);

                    if (resxSrcFile != "")
                    {
                        //<EmbeddedResource Include="G:\Dev\WindowsApplication1\Form1.resx">
                        //<DependentUpon>Form1.cs</DependentUpon>
                        //</EmbeddedResource>
                        string resxFile = Path.ChangeExtension(srcFile, ".resx");
                        File.Copy(resxSrcFile, Path.Combine(Path.GetDirectoryName(scriptFile), resxFile), true);

                        elem = doc.CreateElement("EmbeddedResource");
                        newAttr = doc.CreateAttribute("Include");
                        newAttr.Value = resxFile;
                        elem.Attributes.Append(newAttr);

                        XmlElement nestedElem = doc.CreateElement("DependentUpon");
                        nestedElem.InnerText = Path.GetFileName(srcFile);
                        elem.AppendChild(nestedElem);

                        contentsNode.AppendChild(elem);
                    }
                    doc.Save(projFile);
                }
                catch (Exception e)
                {
                    MessageBox.Show("Specified file could not be inserted to the temp project:\n" + e.Message);
                }
            }

            public void InsertAppConfig(string appConfigFile, string projFile)
            {
                string destFile = Path.Combine(Path.GetDirectoryName(projFile), "app.config");

                if (File.Exists(destFile))
                {
                    File.SetAttributes(destFile, FileAttributes.Normal);
                    File.Delete(destFile);
                }
                using (StreamReader sr = new StreamReader(appConfigFile))
                using (StreamWriter sw = new StreamWriter(destFile))
                {
                    sw.Write(sr.ReadToEnd());
                    sw.WriteLine("<!-- read-only copy of " + appConfigFile + " -->");
                }

                File.SetAttributes(destFile, FileAttributes.ReadOnly);

                try
                {
                    VSProjectDoc doc = new VSProjectDoc(projFile);

                    XmlElement group = doc.CreateElement("ItemGroup");
                    XmlElement none = doc.CreateElement("None");
                    XmlAttribute attr = doc.CreateAttribute("Include");
                    attr.Value = "app.config";

                    group.AppendChild(none);
                    none.Attributes.Append(attr);

                    doc.SelectFirstNode("//Project").AppendChild(group);

                    doc.Save(projFile);
                }
                catch (Exception e)
                {
                    MessageBox.Show("Specified file could not be inserted to the temp project:\n" + e.Message);
                }
            }

            public void InsertXamlCSFile(string scriptFile, string projFile, string xamlSrcFile)
            {
                string srcFile = scriptFile;

                if (VS16IDE.isolating)
                    srcFile = Path.GetFileName(srcFile); //remove absolute path

                try
                {
                    VSProjectDoc doc = new VSProjectDoc(projFile);

                    //Create a new node.
                    XmlElement elem = doc.CreateElement(srcFile.EndsWith(".xaml") ? "Page" : "Compile");
                    XmlAttribute newAttr;

                    newAttr = doc.CreateAttribute("Include");
                    newAttr.Value = srcFile;
                    elem.Attributes.Append(newAttr);

                    //<DependentUpon>Window1.xaml</DependentUpon>
                    XmlElement nestedElem = doc.CreateElement("DependentUpon");
                    nestedElem.InnerText = Path.GetFileName(xamlSrcFile);
                    elem.AppendChild(nestedElem);

                    doc.SelectNodes("//Project/ItemGroup")[1].AppendChild(elem);

                    doc.Save(projFile);
                }
                catch (Exception e)
                {
                    MessageBox.Show("Specified file could not be inserted to the temp project:\n" + e.Message);
                }
            }

            public void InsertReference(string asmFile, string projFile, string aliase = null)
            {
                string refFile = asmFile;

                if (VS16IDE.isolating || asmFile.IndexOf("assembly\\GAC") != -1 || asmFile.IndexOf("assembly/GAC") != -1)
                    refFile = Path.GetFileName(refFile); //remove absolute path

                try
                {
                    //<Reference Include="CSScriptLibrary">
                    //  <SpecificVersion>False</SpecificVersion>
                    //  <HintPath>..\VS9.0\CSScriptLibrary.dll</HintPath>
                    //  <Aliases>CSScript</Aliases>
                    //</Reference>

                    VSProjectDoc doc = new VSProjectDoc(projFile);

                    //Create a new node.
                    XmlElement elem = doc.CreateElement("Reference");
                    XmlAttribute newAttr;

                    newAttr = doc.CreateAttribute("Include");
                    newAttr.Value = Path.GetFileNameWithoutExtension(refFile);
                    elem.Attributes.Append(newAttr);

                    XmlElement elemSpecVer = doc.CreateElement("SpecificVersion");
                    elemSpecVer.InnerText = "False";
                    elem.AppendChild(elemSpecVer);

                    XmlElement elemPath = doc.CreateElement("HintPath");
                    elemPath.InnerText = refFile;
                    elem.AppendChild(elemPath);

                    if (aliase != null)
                    {
                        XmlElement elemAliase = doc.CreateElement("Aliases");
                        elemAliase.InnerText = aliase;
                        elem.AppendChild(elemAliase);
                    }

                    doc.SelectFirstNode("//Project/ItemGroup").AppendChild(elem);

                    doc.Save(projFile);
                }
                catch (Exception e)
                {
                    MessageBox.Show("Specified reference could not be inserted into the temp project:\n" + e.Message);
                }
            }

            public void SetWorkingDir(string dir, string file)
            {
                try
                {
                    //<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
                    //  <StartWorkingDirectory>C:\Program Files\Google\</StartWorkingDirectory>
                    //</PropertyGroup>

                    VSProjectDoc doc = new VSProjectDoc(file);

                    XmlElement elem = doc.CreateElement("StartWorkingDirectory");
                    elem.InnerText = dir;

                    XmlNodeList nodes = doc.SelectNodes("//Project/PropertyGroup");
                    nodes[0].AppendChild(elem);
                    nodes[1].AppendChild(elem.Clone());

                    doc.Save(file);
                }
                catch (Exception e)
                {
                    MessageBox.Show("Specified 'working directory' could not be set for the temp project:\n" + e.Message);
                }
            }

            public void SetArguments(string args, string file)
            {
                try
                {
                    //<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
                    //  <StartArguments>test</StartArguments>
                    //</PropertyGroup>

                    VSProjectDoc doc = new VSProjectDoc(file);

                    XmlElement elem = doc.CreateElement("StartArguments");
                    elem.InnerText = args;

                    XmlNodeList nodes = doc.SelectNodes("//Project/PropertyGroup");
                    nodes[0].AppendChild(elem);
                    nodes[1].AppendChild(elem.Clone());

                    doc.Save(file);
                }
                catch (Exception e)
                {
                    MessageBox.Show("Specified 'working directory' could not be set for the temp project:\n" + e.Message);
                }
            }

            public void SetAllowUnsafe(string file)
            {
                try
                {
                    //<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
                    // <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
                    //</PropertyGroup>
                    VSProjectDoc doc = new VSProjectDoc(file);

                    //Create a new node.
                    XmlElement elem = doc.CreateElement("AllowUnsafeBlocks");
                    elem.InnerText = "true";

                    XmlNodeList nodes = doc.SelectNodes("//Project/PropertyGroup");
                    nodes[1].AppendChild(elem);
                    nodes[2].AppendChild(elem.Clone());

                    doc.Save(file);
                }
                catch (Exception e)
                {
                    MessageBox.Show("Specified 'unsafe' could not be set for the temp project:\n" + e.Message);
                }
            }

            public void SetOutputType(string type, string file)
            {
                try
                {
                    //<OutputType>Exe</OutputType>
                    ReplaceInFile(file, "<OutputType>Exe</OutputType>", "<OutputType>" + type + "</OutputType>");
                }
                catch (Exception e)
                {
                    MessageBox.Show("Specified 'working directory' could not be set for the temp project:\n" + e.Message);
                }
            }

            public void SetTargetFramework(string target, string file)
            {
                try
                {
                    //<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
                    ReplaceInFile(file, "<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>", "<TargetFrameworkVersion>" + target + "</TargetFrameworkVersion>");
                }
                catch (Exception e)
                {
                    MessageBox.Show("Specified 'working directory' could not be set for the temp project:\n" + e.Message);
                }
            }

            public void SetPlatformType(string type, string file, string solutionFile)
            {
                try
                {
                    //<PlatformTarget>x86</PlatformTarget>
                    VSProjectDoc doc = new VSProjectDoc(file);

                    //Create a new node.
                    XmlNodeList existingElement = doc.SelectNodes("//Project/PropertyGroup/PlatformTarget");
                    XmlElement elem;

                    if (existingElement.Count > 0)
                        elem = (XmlElement)existingElement[0];
                    else
                        elem = doc.CreateElement("PlatformTarget");

                    elem.InnerText = type;

                    XmlNodeList nodes = doc.SelectNodes("//Project/PropertyGroup");
                    nodes[1].AppendChild(elem);
                    nodes[2].AppendChild(elem.Clone());

                    doc.Save(file);
                    ReplaceInFile(file, "AnyCPU", "x86");
                    ReplaceInFile(solutionFile, "AnyCPU", "x86");
                    ReplaceInFile(solutionFile, "Any CPU", "x86");
                }
                catch (Exception e)
                {
                    MessageBox.Show("Specified '" + type + "' could not be set for the temp project:\n" + e.Message);
                }
            }

            public void SetProjectTypeGuids(string type, string file)
            {
                try
                {
                    //<ProjectTypeGuids></ProjectTypeGuids>
                    if (type != null && type != "")
                        ReplaceInFile(file, "<ProjectTypeGuids></ProjectTypeGuids>", "");
                    else
                        ReplaceInFile(file, "<ProjectTypeGuids></ProjectTypeGuids>", "<ProjectTypeGuids>" + type + "</ProjectTypeGuids>");
                }
                catch (Exception e)
                {
                    MessageBox.Show("Specified 'working directory' could not be set for the temp project:\n" + e.Message);
                }
            }

            static public string[][] GetAvailableIDE()
            {
                ArrayList retval = new ArrayList();
                string name = "";
                string hint = "";
                string command = "";

                string scHomeDir = GetEnvironmentVariable("CSSCRIPT_DIR");

                if (GetIDEFile() != "<not defined>")
                {
                    name = "Open with VS2017";
                    hint = "\t- Open with MS Visual Studio 2017";
                    command = "\"" + scHomeDir + "\\csws.exe\" /c \"" + scHomeDir + "\\lib\\DebugVS15.0.cs\" \"%1\"";
                    retval.Add(new string[] { name, hint, command });
                }

                return (string[][])retval.ToArray(typeof(string[]));
            }

            static public string GetIDEFile()
            {
                string retval = "<not defined>";

                try
                {
                    var vsWhere = new Process();
                    vsWhere.StartInfo.FileName = Path.Combine(GetProgramFilesDirectory(), @"Microsoft Visual Studio\Installer\vswhere.exe");
                    vsWhere.StartInfo.Arguments = "-version \"16.0\" -property installationPath";
                    vsWhere.StartInfo.UseShellExecute = false;
                    vsWhere.StartInfo.CreateNoWindow = true;
                    vsWhere.StartInfo.RedirectStandardOutput = true;

                    vsWhere.Start();
                    vsWhere.WaitForExit();

                    retval = Path.Combine(vsWhere.StandardOutput.ReadToEnd().Trim(), @"Common7\IDE\devenv.exe");
                }
                catch { }
                return retval;
            }

            static public string GetProgramFilesDirectory()
            {
                string programFiles = Environment.GetEnvironmentVariable("ProgramFiles(x86)");
                if (String.IsNullOrEmpty(programFiles))
                {
                    programFiles = Environment.GetEnvironmentVariable("ProgramFiles");
                }
                return programFiles;
            }

            static public string GetEnvironmentVariable(string name)
            {
                //It is important in the all "installation" scripts to have reliable GetEnvironmentVariable().
                //Under some circumstances freshly set environment variable CSSCRIPT_DIR cannot be obtained with
                //Environment.GetEnvironmentVariable(). For example when running under Total Commander or similar
                //shell utility. Even SendMessageTimeout does not help in all cases. That is why GetEnvironmentVariable
                //is reimplemented here.
                object value = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment").GetValue(name);
                return value == null ? null : value.ToString();
            }

            static private string GetCopyName(string file)
            {
                string retval = file;
                int i = 1;

                while (File.Exists(retval))
                {
                    retval = Path.Combine(Path.GetDirectoryName(file), "Copy" + (i == 1 ? " of " : " (" + i.ToString() + ") ") + Path.GetFileName(file));
                    i++;
                }
                return retval;
            }

            static public string[] GetImportedScripts(string projFile)
            {
                ArrayList retval = new ArrayList();
                XmlDocument doc = new XmlDocument();
                doc.Load(projFile);

                foreach (XmlNode child in doc.GetElementsByTagName("Compile"))
                {
                    foreach (XmlAttribute attribute in child.Attributes)
                    {
                        if (attribute != null && attribute.Name == "Include")
                            retval.Add(attribute.Value.ToString().ToString());
                    }
                }
                return (string[])retval.ToArray(typeof(string));
            }

            internal static void RefreshProject(string projFile)
            {
                string[] content = VS16IDE.GetImportedScripts(projFile);

                foreach (string file in content)    //remove original imported files
                {
                    if (Path.GetFileName(file).StartsWith("i_")) //imported modified files have name "i_file_XXXXXX.cs>"
                    {
                        try
                        {
                            File.SetAttributes(file, FileAttributes.Normal);
                            File.Delete(file);
                        }
                        catch { }
                    }
                }
                //regenerate project
                scriptFile = ResolveScriptFile(content[0]);
                RunPreScripts(scriptFile);

                string newSolution = VS16IDE.CreateProject(content[0], Path.GetDirectoryName(projFile));
                string[] newContent = VS16IDE.GetImportedScripts(Path.ChangeExtension(newSolution, ".csproj"));

                //remove not needed .resx files from includes (not imported) files
                for (int i = 1; i < content.Length; i++)
                {
                    string file = content[i];
                    bool used = false;

                    if (!Path.GetFileName(file).StartsWith("i_")) //not imported script file
                    {
                        foreach (string newFile in newContent)
                            if (used = (String.Compare(newFile, file, true) == 0))
                                break;
                        try
                        {
                            if (!used)
                            {
                                if (File.Exists(Path.ChangeExtension(file, ".resx")))
                                    File.Delete(Path.ChangeExtension(file, ".resx"));
                                else if (File.Exists(Path.ChangeExtension(file, ".layout")))
                                    File.Delete(Path.ChangeExtension(file, ".layout"));
                            }
                        }
                        catch { }
                    }
                }
            }
        }

        static void RunScript(string scriptFileCmd)
        {
            Process myProcess = new Process();
            myProcess.StartInfo.FileName = "cscs.exe";
            myProcess.StartInfo.Arguments = scriptFileCmd;
            myProcess.StartInfo.UseShellExecute = false;
            myProcess.StartInfo.RedirectStandardOutput = true;
            myProcess.StartInfo.CreateNoWindow = true;
            myProcess.Start();

            string line = null;

            while (null != (line = myProcess.StandardOutput.ReadLine()))
            {
                Console.WriteLine(line);
            }
            myProcess.WaitForExit();
        }

        static string CompileScript(string scriptFile)
        {
            string retval = "";
            StringBuilder sb = new StringBuilder();

            Process myProcess = new Process();
            myProcess.StartInfo.FileName = "cscs.exe";
            myProcess.StartInfo.Arguments = "/nl /ca \"" + scriptFile + "\"";
            myProcess.StartInfo.UseShellExecute = false;
            myProcess.StartInfo.RedirectStandardOutput = true;
            myProcess.StartInfo.CreateNoWindow = true;
            myProcess.Start();

            string line = null;

            while (null != (line = myProcess.StandardOutput.ReadLine()))
            {
                sb.Append(line);
                sb.Append("\n");
            }
            myProcess.WaitForExit();

            retval = sb.ToString();

            string compiledFile = Path.ChangeExtension(scriptFile, ".csc");

            if (retval == "" && File.Exists(compiledFile))
                File.Delete(compiledFile);

            return retval;
        }

        static void RunPreScripts(string scriptFile)
        {
            RunPrePostScripts(scriptFile, true);
        }

        static void RunPrePostScripts(string scriptFile, bool prescript)
        {
            // Compile the script in order to proper execute all pre- and post-scripts.
            // The RunScript(cmd) approach is attractive but not sutable as some pre-scripts must be run only from the primary script
            // but not as a stand alone scripts. That is why it is disabled for now by "return;"
            CompileScript(scriptFile);
        }
    }

    class FileResolver
    {
        static public string ResolveFile(string file, string[] extraDirs, string extension)
        {
            string fileName = file;

            if (Path.GetExtension(fileName) == "")
                fileName += extension;

            //arbitrary directories
            if (extraDirs != null)
            {
                foreach (string extraDir in extraDirs)
                {
                    string dir = extraDir;

                    if (File.Exists(Path.Combine(dir, fileName)))
                    {
                        return Path.GetFullPath(Path.Combine(dir, fileName));
                    }
                }
            }

            //PATH
            string[] pathDirs = Environment.GetEnvironmentVariable("PATH").Replace("\"", "").Split(';');

            foreach (string pathDir in pathDirs)
            {
                string dir = pathDir;

                if (File.Exists(Path.Combine(dir, fileName)))
                {
                    return Path.GetFullPath(Path.Combine(dir, fileName));
                }
            }

            return "";
        }
    }

    class VSProjectDoc : XmlDocument
    {
        public VSProjectDoc(string projFile)
        {
            Load(projFile);
        }

        static public void FixFile(string projFile)
        {
            //remove xmlns=""

            //using (FileStream fs = new FileStream(projFile, FileMode.OpenOrCreate))
            //using (StreamWriter sw = new StreamWriter(fs, Encoding.Unicode))
            //    sw.Write(FormatXml(this.InnerXml.Replace("xmlns=\"\"", "")));

            string content = "";

            using (StreamReader sr = new StreamReader(projFile))
                content = sr.ReadToEnd();

            content = content.Replace("xmlns=\"\"", "");

            using (StreamWriter sw = new StreamWriter(projFile))
                sw.Write(content);
        }

        public new XmlNodeList SelectNodes(string path)
        {
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(NameTable);
            nsmgr.AddNamespace("ab", "http://schemas.microsoft.com/developer/msbuild/2003");
            if (path.StartsWith("//"))
            {
                path = path.Substring(1);
                path = "/" + path.Replace("/", "/ab:");
            }
            else
            {
                path = path.Replace("/", "/ab:");
            }

            return SelectNodes(path, nsmgr);
        }

        public XmlNode SelectFirstNode(string path)
        {
            XmlNodeList nodes = SelectNodes(path);

            if (nodes != null && nodes.Count != 0)
                return nodes[0];
            else
                return null;
        }
    }

    static class Extensions
    {
        public static string GetFileName(this string file)
        {
            return Path.GetFileName(file);
        }

        public static string GetFileNameWithoutExt(this string file)
        {
            return Path.GetFileNameWithoutExtension(file);
        }

        public static string ChangeFileDir(this string file, string dir)
        {
            return dir.PathJoin(dir, Path.GetFileName(file));
        }

        public static string PathJoin(this string path, string path1, string path2 = "")
        {
            return Path.Combine(path, path1, path2);
        }

        public static bool Contains(this IEnumerable<string> items, string text, bool ignoreCase)
        {
            return items.Any(x => string.Compare(text, x, ignoreCase) == 0);
        }

        public static bool SameAs(this string text, string pattern)
        {
            return string.Compare(text, pattern, true) == 0;
        }

        public static bool IsOneOf(this string text, params string[] patterns)
        {
            foreach (string item in patterns)
                if (text.SameAs(item))
                    return true;
            return false;
        }
    }
}
Categories
Raspberry Pi

How To Install Edge Impulse On a Raspberry Pi Zero W

Edge Impulse does not officially support the Raspberry Pi Zero W hardware, and if you attempt to follow the official documentation meant for installing it on an a Raspberry Pi 4, on a Pi Zero W, the installation will fail.

Since the only hardware I had on hand was the Pi Zero W, I experimented for a while and managed to get it working. If you would like to try this for yourself see the instructions below.

Install Pi OS Legacy (buster) on the Pi Zero W board using the Raspberry Pi Imager application. Do NOT install Bullseye.

These commands replace those from the official documentation in the section 2. Installing dependencies . At the time of writing this, the version was: Edge Impulse Linux client v1.3.1

sudo apt update
sudo apt upgrade
wget https://unofficial-builds.nodejs.org/download/release/v12.13.0/node-v12.13.0-linux-armv6l.tar.xz
tar xvf node-v12.13.0-linux-armv6l.tar.xz
cd node-v12.13.0-linux-armv6l
sudo cp -R bin/* /usr/bin/
sudo cp -R lib/* /usr/lib/

sudo apt install -y gcc g++ make build-essential sox gstreamer1.0-tools gstreamer1.0-plugins-good gstreamer1.0-plugins-base gstreamer1.0-plugins-base-apps

npm config set user root && sudo npm install edge-impulse-linux -g --unsafe-perm

References:

https://docs.edgeimpulse.com/docs/raspberry-pi-4

https://bloggerbrothers.com/2017/03/04/installing-nodejs-on-a-raspberry-pi/

Categories
C# Programming Uncategorized

Serialize Size Struct In ProtoBuf-net

The following code allows you to serialise the Size struct when using ProtoBuf-net.

First define a surrogate.

[ProtoContract]
    struct ProtoSize
    {
        [ProtoMember(1)]
        int Width;
        [ProtoMember(2)]
        int Height;
      
        public static implicit operator Size(ProtoSize s)
        { return new Size(new Point(s.Width, s.Height)); }
        
        public static implicit operator ProtoSize(Size s)
        { return new ProtoSize { Width = s.Width, Height = s.Height };  }
    }

Then add it to the RuntimeTypeModel.

static RuntimeTypeModel Model;

Model = RuntimeTypeModel.Create();
Model.AllowParseableTypes = true;
Model.Add(typeof(Size), false).SetSurrogate(typeof(ProtoSize));

Then use it.

 Model.Serialize(someFile, someObjectWithSizeProperty);

Remember to decorate the size property with a [ProtoMember] attribute.

Here is a list of other useful surrogates for Dot Net, which I gleaned from the Internet.


    [ProtoContract]
    struct ProtoColor
    {
        [ProtoMember(1, DataFormat = DataFormat.FixedSize)]
        public uint argb;
        public static implicit operator Color(ProtoColor c)
        { return Color.FromArgb((int)c.argb); }
        public static implicit operator ProtoColor(Color c)
        { return new ProtoColor { argb = (uint)c.ToArgb() }; }
    }
    [ProtoContract()]
    class ProtoFont
    {
        [ProtoMember(1)]
        string FontFamily;
        [ProtoMember(2)]
        float SizeInPoints;
        [ProtoMember(3)]
        FontStyle Style;

        public static implicit operator Font(ProtoFont f)
        {
            return new Font(f.FontFamily, f.SizeInPoints, f.Style);
        }
        public static implicit operator ProtoFont(Font f)
        {
            return f == null ? null : new ProtoFont
            {
                FontFamily = f.FontFamily.Name,
                SizeInPoints = f.SizeInPoints,
                Style = f.Style
            };
        }
    }
    [ProtoContract()]
    class ProtoStringFormat
    {
        [ProtoMember(1, DataFormat = DataFormat.Group)]
        StringAlignment Alignment;
        [ProtoMember(2)]
        StringAlignment LineAlignment;
        [ProtoMember(3)]
        StringFormatFlags Flags;
        public static implicit operator StringFormat(ProtoStringFormat f)
        {
            return new StringFormat(f.Flags)
            {
                Alignment = f.Alignment,
                LineAlignment = f.LineAlignment
            };
        }
        public static implicit operator ProtoStringFormat(StringFormat f)
        {
            return f == null ? null : new ProtoStringFormat()
            {
                Flags = f.FormatFlags,
                Alignment = f.Alignment,
                LineAlignment = f.LineAlignment
            };
        }
    }
Categories
Uncategorized

Setup Hyper-V Network for CentOS Sharing WIFI

After following a number of how-to instructions off the Internet on how to connect my CentOS virtual Machine to my Windows 10 WIFI connection, and failing, I finally managed to get it working in three simple steps.

  1. Create a new Virtual Switch, via the Virtual Switch Manager.

THIS KEPT FAILING until I enabled and then disabled the “Allow other network and users to connect through this computer’s Internet Connection” on my Wifi adapter.

2. Added a new external network adapter to my virtual machine

3. Configured the networking on CentOS

4. That was all that was needed.

Categories
Uncategorized

Windows 10 Console Apps Hanging

Recently I wrote a console app. to run under Windows 10. To my frustration the app appeared to hang for no apparent reason, but would resume when I pressed the Enter key.

After much investigating I found that this is by (Microsoft) design. When you click on the black screen of a console app it goes into “QuickEdit Mode”; basically its waiting for you to paste some text and press enter. This happens the next time the app calls a write/writeline. It can be stopped by clicking the top right of the console app, selecting “Properties” and unticking the “Quick Edit Mode” option.

Categories
Uncategorized

Getting Rid of Indian Myna Birds

If you have a problem with Indian Myna Birds you will probably have found the advice to get a trap and catch them. This is a good solution as it will reduce the population, but it takes some effort. If you want to simply move the birds on, so that they stop pooping all over you house and belongings, I have a very easy way to do that.

Get a water pistol!

I dont mean a little one, but a Super Soaker type arrangement. These birds are increadbly aware of being hunted. It only takes a couple of days of targeting these birds with water, and they will packup and leave. Abiet propably only to your neighbours house – but thats their problem!

Categories
Uncategorized

Completed ServiceNow Developer Course

I just finished a UDEMY Service Now Developer course

https://www.udemy.com/course/servicenow-201-development/learn/lecture/7334002#overview

This was an excellent course, the instructor had clearly been working as a ServiceNow professional and I would recommend this course to anyone who wants to jump into development on the Service Now platform.

The coding portions are all JavaScript, so you need some experience in that language. ServiceNow is such a huge platform that I think being able to develop applications within it’s framework would be necessary for any medium to large company.

Categories
Uncategorized

Welcome

This site is hosted on a $20 RaspberryPi Zero behind my home router without a static IP address. The DNS is provided by the ‘truely’ free dynamic DNS provider DuckDNS.