I have an unusual situation here.
Problem
I'm using Visual studio (VS) to write scripts to use in-game in the game Space Engineers.
The problem is that you only use a portion of the code from the file in-game. (I.E, Ctrl+A wont do). So selecting the correct portion is tedious.
I want to streamline the process of copying the desired code in VS and pasting it in Space Engineers.
The idea is to trim all unnecessary white space (there's a character limit) and copy to clipboard when pressing run in VS.
Where I'm at
I've found that you can make your own build configuration and use the "Pre-build event command line" to run something custom. The idea is to make a simple console application that does what I described above. But I don't know how to get the correct file to send to said application.
Am I on the right track? How do I send the desired file to the trimming application? Is there a better way?
Edit:
This is what I had in mind when I said "simple console application".
It does everything I needed it to do (trimming white-space and adding a portion of the code to clipboard). Only thing missing is that I have to specify the file name I want it to use. Which isn't important, it would just be nice.
using System;
using System.Windows.Forms;
namespace TrimFileToClipboard
{
class Program
{
[STAThread()]
static void Main(string[] args)
{
string startString = (args.Length > 1) ? "#region " + args[1] : "#region in-game";
string line;
string trimmed = "";
bool read = false;
int depth = 0;
System.IO.StreamReader file = new System.IO.StreamReader(args[0]);
while ((line = file.ReadLine()) != null)
{
if (!read && line.Contains(startString)) read = true;
else if (read && line.Contains("#region")) depth++;
else if (read && line.Contains("#endregion"))
{
if (depth == 0) break;
else if (depth < 0)
{
Console.WriteLine("There's something wrong with your #regions. Please edit the file.");
Console.ReadLine();
Environment.Exit(0);
}
else depth--;
}
else if (read) trimmed += line.Trim() + "\n";
}
file.Close();
Clipboard.SetText(trimmed);
}
}
}
It can be used by adding
"<path>\TrimFileToClipboard.exe" "$(ProjectDir)<classname>.cs"
to Pre-build event command line, in the project properties/Build events. Where <path> is the path to the application above and <classname> is the file you want to process.
Maybe I should post this part as an answer but I don't know if it's a decent approach or an ugly hack.
Instead of coping the code to the clipboard, I save it directly inside the game as saved workshop script with this simple C# console application.
The SE script I edit using VS has the comments \\script-begin and \\script-end to tell the application where to look for the actual code that needs to be in the programmable block.
After the execution the script will be available at the local workshop. It makes it very easy to work with the SE scripts, whenever I make a change using VS, I run the console application again and the script will be updated inside the game.
internal class Program
{
private static void Main(string[] args)
{
String[] InputLines, outputLines;
Int32 scriptBegin = 0, scriptEnd = 0;
String scriptName = args[0];
String inputPath = "C:\\Users\\hfand\\source\\repos\\se-scripts\\" + scriptName + ".cs";
if (File.Exists(inputPath))
{
InputLines = File.ReadAllLines(inputPath);
for (int i = 0; i < InputLines.Length; i++)
{
if (InputLines[i].Contains("script-begin"))
{
scriptBegin = i + 1;
}
if (InputLines[i].Contains("script-end"))
{
scriptEnd = i - 1;
}
}
outputLines = new List<string>(InputLines).GetRange(scriptBegin, scriptEnd - scriptBegin + 1).ToArray();
for (int i = 0; i < outputLines.Length; i++)
{
if (outputLines[i].Length >= 8)
{
outputLines[i] = outputLines[i].Substring(8);
}
}
String outputPath = "C:\\Users\\hfand\\AppData\\Roaming\\SpaceEngineers\\IngameScripts\\local\\" + scriptName;
if (Directory.Exists(outputPath))
{
File.WriteAllLines(outputPath + "\\Script.cs", outputLines);
}
else
{
Directory.CreateDirectory(outputPath);
File.WriteAllLines(outputPath + "\\Script.cs", outputLines);
}
Console.WriteLine(scriptName + " sincronizado");
}
else
{
Console.WriteLine("Arquivo \"" + inputPath + "\" não encontrado");
}
}
}
Here is an example of how the code in VS should look like
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using VRageMath;
using VRage.Game;
using Sandbox.ModAPI.Interfaces;
using Sandbox.ModAPI.Ingame;
using Sandbox.Game.EntityComponents;
using VRage.Game.Components;
using VRage.Collections;
using VRage.Game.ObjectBuilders.Definitions;
using VRage.Game.ModAPI.Ingame;
using SpaceEngineers.Game.ModAPI.Ingame;
namespace BlankScript
{
public class Program : MyGridProgram
{
//script-begin
public Program()
{
}
public void Save()
{
}
public void Main(string argument, UpdateType updateSource)
{
}
//script-end
}
}
You can write a C# command with my Visual Commander extension that gets active file path in Visual Studio as DTE.ActiveWindow.Document.FullName and then runs your file.ReadLine() loop over it and calls Clipboard.SetText(trimmed) at the end. See for example Copy current file, line, method sample code.
Related
I have the following c# Console app I would run this in ssis but i am using a couple of PDF manipulating librarys. so i am going to call an exe from my ssis package while passing in a file path.
But i am getting the following error when trying to run via the exe.
Unhandled Exception: System.IndexOutOfRangeException: Index was
outside the bounds of the array. at ConsoleApp.program.Main(String[]
args) line 87
BUT if i run in debug it works fine. Once i get it working on its own via the exe, i want to pass the filepath as a parameter in ssis.
see c# below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using org.apache.pdfbox.pdmodel;
using org.apache.pdfbox.util;
using System.IO;
namespace PDF_Read_ConsoleApp
{
class Program
{
public static void FilePath(string path)
{
//Console.WriteLine("Please enter full pdf path \n\n ");
//path = Console.ReadLine();
string fp;
fp = #path;
string[] files = Directory.GetFiles(path, "*.pdf");
foreach (string s in files)
{
string txtOutput = s.Replace(".pdf", ".txt");
if (File.Exists(txtOutput))
{
File.Delete(txtOutput);
}
string output;
PDDocument doc = null;
try
{
doc = PDDocument.load(s);
PDFTextStripper stripper = new PDFTextStripper();
stripper.getText(doc);
output = stripper.getText(doc);
StreamWriter NewFile;
NewFile = new StreamWriter(txtOutput);
//NewFile.Write(output.ToString());
NewFile.Write(output.ToString());
NewFile.Close();
}
finally
{
//if (doc != null)
//{
doc.close();
// Console.WriteLine("\n\n File saveed - ({0} ", txtOutput);
//}
}
}
}
static void Main(string[] args)
{
args[0] = #"C:\SSIS_Packages\PDF_Import\PDF_Import\PO_pdfs"; //// TESTING FILE PATH1
FilePath(args[0]);
}
}
}
Kind Regards
Rob
I have managed to get it working, I need to enter an argument within the debug screen, see information in URL below
Console app arguments, how arguments are passed to Main method
THank you for everyone's comments
I have a windows forms tool in VB that i have been working on for a while now.
Now i would like to be able to access all of the controls and return values through the command prompt so that i am able to play with it through Azure and basically make the application a black box.
So this is how i went about thinking i should do it.
1 - In my project, i created a second solution, a C# Windows Command line framework.
2 - i than added the following script to that second project in order to run the win forms
using EnabledTest;
using System;
using System.Windows.Forms;
namespace Command_lineStartup
{
internal class Program
{
private static frmMain MainForm;
[STAThread]
private static void Main(string[] args)
{
if (args.Length > 0)
{
// Command line given, display console
}
else
{
AllocConsole();
ConsoleMain(args);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(MainForm = new frmMain());
GUI();
}
}
private static void ConsoleMain(string[] args)
{
Console.WriteLine("Command line = {0}", Environment.CommandLine);
for (int ix = 0; ix < args.Length; ++ix)
Console.WriteLine("Argument{0} = {1}", ix + 1, args[ix]);
// Console.ReadLine();
}
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AllocConsole();
public static void GUI()
{
Console.WriteLine("Testing version 1 :");
Console.WriteLine("Enter Project File path to open, Project must be a .mmp file");
string Path = Console.ReadLine();
MainForm.LoadProject(Path);
}
}
}
However, I do not think this is the right way. When i run the console application through CMD using C:\TFS\Enabled Test\Command-lineStartup\bin\Debug\Command-lineStartup.exe"
I does not behave how i want it to work.
So my question is.
Am i going about this the right way? if so what am i doing wrong here
is there an easier way?
So i found a way in the end. When running the application through CMD, the arguments that are supplied can be accessed using
Dim cla As String() = Environment.GetCommandLineArgs()
then for each of the arguments provided u can do something with it.
For example,
If cla.Length > 1 Then
'cla(0) is the executable path.
'cla(1) is the Path to the project
If Not IsIDE() Then WCLicenseIsLicensed("Application", True)
Me.Text = Application.ProductName
mblnLoaded = True
LoadProject(cla(1))
TreeVieuwSystem.Nodes(cla(2)).Expand()
TreeVieuwSystem.SelectedNode = TreeVieuwSystem.Nodes(cla(2)).Nodes.Find(cla(2) & "\" & cla(3), True).First
NodeSelected()
If cla(2) = "Test Plans" Then
TheWindowThatAllowsYouToEditTheObject.RunTestPlan()
ElseIf cla(2) = "Tests" Then
TheWindowThatAllowsYouToEditTheObject.RunTest(False)
End If
Else
I want to add a Product Version to a Form.[assembly: AssemblyVersion("1.0.*")]
string version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
This is the solution I found, and which will work. But the Version will be like "1.0.6262.26540".
Can I change the Rule or can I get the Publish Version which Visual Studio generates programmatically?
You could use ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString(). However, this will only work if you are running a version of your program that was installed by the ClickOnce publisher installer (ApplicationDeployment.IsNetworkDeployed returns true).
When you start the compiled assembly directly (e.g. during debugging), you will get an InvalidDeploymentException when trying to access the CurrentDeployment property. To safeguard against this, you can use something like this:
string CurrentVersion
{
get
{
return ApplicationDeployment.IsNetworkDeployed
? ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString()
: "1.0.0.0"; // Fallback version string, or retrieve from assembly as in your question
}
}
If you are not using the ClickOnce Publish function to distribute your software I am not sure that you can expect to access the "Publish Version".
using System;
using System.IO;
using System.Linq;
namespace MyAssemblyInfoPatcher
{
internal class Program
{
static void Main(string[] args)
{
if (args.Length > 0)
{
string path = args[0].ToString();
Console.WriteLine(string.Format("Current App version is set to: {0}", path));
string now_date = DateTime.Now.ToString("yyyy.MM.dd.HHmm");
if (File.Exists(path))
{
string _AssemblyVersion = string.Empty;
string _AssemblyFileVersion = string.Empty;
var lines = File.ReadLines(string.Format(path));
for (int i = 0; i < lines.Count(); i++)
{
if (lines.ElementAt(i).ToString().StartsWith("[assembly: AssemblyVersion"))
{
_AssemblyVersion = lines.ElementAt(i).ToString();
}
else if (lines.ElementAt(i).ToString().StartsWith("[assembly: AssemblyFileVersion"))
{
_AssemblyFileVersion = lines.ElementAt(i).ToString();
}
}
string _replace_assembly = File.ReadAllText(path);
if (_AssemblyVersion != string.Empty)
{
_replace_assembly = _replace_assembly.Replace(_AssemblyVersion, string.Format("[assembly: AssemblyVersion(\"{0}\")]", now_date));
}
if (_AssemblyFileVersion != string.Empty)
{
_replace_assembly = _replace_assembly.Replace(_AssemblyFileVersion, string.Format("[assembly: AssemblyFileVersion(\"{0}\")]", now_date));
}
File.WriteAllText(path, _replace_assembly);
}
}
}
}
}
Above the programs code, you can create a console application and in Project Properties > Build Events, add a "Pre-build event command line" like this: "D:\SomePath\MyAssemblyInfoPatcher.exe" "$(ProjectDir)Properties\AssemblyInfo.cs"
i just started to develop applications for AutoCAD 2016. I want to load my dLLs into a separate AppDomain, so that i don't have to restart ACAD all the time.
After a lot of research and trying i ended up with a pipeline solution
using System.Addin and System.Addin.Contract.
I use only interfaces and standardclasses for the Views Contract and Adapters like in this example here.
This is my addin containing one methode to write Hello into Acad's Editor and a second methode for drawing a line.
using System.AddIn;
using CADAddinView;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
namespace CADAddIn
{
[AddIn("cadAddIn", Version = "1.0.0.0")]
public class CADAddIn : ICADAddinView
{
public void drawLine()
{
Document acDoc = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument;
Database acCurDb = acDoc.Database;
using (DocumentLock acLckDoc = acDoc.LockDocument())
{
using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
{
DBObject blkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead);
BlockTable acBlkTbl = blkTbl as BlockTable;
BlockTableRecord acBlkTblRec = (BlockTableRecord)acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
Polyline acPoly = new Polyline();
acPoly.SetDatabaseDefaults();
acPoly.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0);
acPoly.AddVertexAt(0, new Point2d(100, 100), 0, 0, 0);
acBlkTblRec.AppendEntity(acPoly);
acTrans.AddNewlyCreatedDBObject(acPoly, true);
acTrans.Commit();
}
}
}
public void sayHello()
{
Editor ed = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument.Editor;
ed.WriteMessage("Hello");
}
}
}
this is my HostApplication:
using System.AddIn.Hosting;
using System.Windows.Forms;
using CADHostView;
using System;
using System.Collections.ObjectModel;
using Autodesk.AutoCAD.Runtime;
namespace CADHost
{
public class CADHost
{
[CommandMethod("sayHello")]
public static void sayHello()
{
string addInPath = Environment.CurrentDirectory + "\\Pipeline";
string[] warnings = AddInStore.Update(addInPath);
foreach (string warning in warnings)
{
MessageBox.Show(warning);
}
Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(ICADHostView), addInPath);
if (tokens.Count == 0)
{
MessageBox.Show("No AddIn found.");
}
else
{
AddInToken cadToken = tokens[0];
ICADHostView cadApp = cadToken.Activate<ICADHostView>(AddInSecurityLevel.Host);
cadApp.sayHello();
}
}
[CommandMethod("drawLine")]
public static void drawLine()
{
string addInPath = Environment.CurrentDirectory + "\\Pipeline";
string[] warnings = AddInStore.Update(addInPath);
foreach (string warning in warnings)
{
MessageBox.Show(warning);
}
Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(ICADHostView), addInPath);
if (tokens.Count == 0)
{
MessageBox.Show("No AddIn found.");
}
else
{
AddInToken cadToken = tokens[0];
ICADHostView cadApp = cadToken.Activate<ICADHostView>(AddInSecurityLevel.Host);
cadApp.drawLine();
}
}
}
}
Both of the two applications reference to three standard-Dlls from Acad:
accoremgd.dll, acdbmgd.dll, acmgd.dll.
In both projects these dlls have the option local copy false.
If i start then i get an Exception, where the programm cannot find the file "accoremgd.dll" and Acad crashes.
So i tried to set the Option local copy true only for the Addin.
Now it works for the "sayHello"-Methode.
but i get an invalide cast exception when acBlkTbl is initialised.
Would be great if someone has the last steps for me to make this work.
Also great would be a working example must not be made with the Addinsystem
i only want to make this work for not restarting acad all the time^^
Thank you for your help
matthias
I don't believe a separate AppDomain will work, when you call AutoCAD object types it will go to the main AppDomain and get messed up...
As just want to edit your code and don't restart, you'll be better with Edit & Continue feature (available since VC2013 on AutoCAD 2015, I believe).
This is not supported. AutoCAD is a very old and complex program and most of the AutoCAD API objects cannot be used in remote fashion.
Please read:
http://through-the-interface.typepad.com/through_the_interface/2008/09/tired-of-not-be.html
http://forums.autodesk.com/t5/net/netload-is-there-a-net-unload-command/td-p/2404002
https://www.theswamp.org/index.php?topic=38675.0
In the #3, you can see that the AutoCAD development team confirmed that there are some global variables which will prevent working this way.
I gave up my tries to solve this problem. My current "best" solution is to load dlls at the start of AutoCAD. At least i don't have to netload every dll.
If someone has a better solution feel free to tell me^^ Thanks to all that answered. matthias
I googled and found the solution at MSDN.
// Compose a string that consists of three lines.
string lines = "First line.\r\nSecond line.\r\nThird line.";
// Write the string to a file.
System.IO.StreamWriter file = new System.IO.StreamWriter("c:\\test.txt");
file.WriteLine(lines);
file.Close();
How to extend the lines to complex content which including some natural C# code lines.
eg. I want to write the information below to my test.cs file.
Why?
I am parsing a XML schema with C# Console Application. And i want to generate the Console Result to a .cs file during the compiler time.
using System;
using System.Collections.Generic;
using System.Text;
namespace CommonDef
{
public class CCODEData
{
public int iCodeId;
public string sCode;
public CODEDType cType;
public int iOccures;
}
[Description("CodeType for XML schema.")]
public enum CODEDType
{
cString = 1,
cInt = 2,
cBoolean = 3,
}
thank you.
If your source code is hardcoded as in your sample, you could use a C# literal string:
string lines =
#"using System;
using System.Collections.Generic;
using System.Text;
namespace CommonDef
..."
Anyway in such cases it is a better idea (more readable and maintainable) to have the whole text contents into a text file as an embedded resource in your assembly, then read it using GetManifestResourceStream.
(I'm assuming you're trying to build up the result programmatically - if you genuinely have hard-coded data, you could use Konamiman's approach; I agree that using an embedded resource file would be better than a huge verbatim string literal.)
In your case I would suggest not trying to build up the whole file into a single string. Instead, use WriteLine repeatedly:
using (TextWriter writer = File.CreateText("foo.cs"))
{
foreach (string usingDirective in usingDirectives)
{
writer.WriteLine("using {0};", usingDirective);
}
writer.WriteLine();
writer.WriteLine("namespace {0}", targetNamespace);
// etc
}
You may wish to write a helper type to allow simple indentation etc.
If these suggestions don't help, please give more details of your situation.
I know an answer has already been accepted but why not use an XSLT applied to the XML instead? this would mean that you could easily generate c#, vb.net, .net without having to recompile the app.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace FileHandling
{
class Class1
{
static void Main()
{
Console.WriteLine("Enter data");
ConsoleKeyInfo k;
//Console.WriteLine(k.KeyChar + ", " + k.Key + ", " + k.Modifiers );
string str="";
char ch;
while (true)
{
k = Console.ReadKey();
if ((k.Modifiers == ConsoleModifiers.Control) && (k.KeyChar == 23))
{
Console.WriteLine("\b");
break;
}
if (k.Key == ConsoleKey.Enter)
{
Console.WriteLine("");
str += "\n";
}
ch = Convert.ToChar(k.KeyChar);
str += ch.ToString();
}
Console.WriteLine(str);
Console.Read();
}
}
}