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
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
            };
        }
    }