want to change project settings programatically there are many settings like this
EnvDTE80.DTE2 dte2 = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.12.0");
Project project = dte2.Solution.Projects.Item(1);
Configuration configuration = project.ConfigurationManager.ActiveConfiguration;
configuration.Properties.Item("StartAction").Value = VSLangProj.prjStartAction.prjStartActionProgram;
configuration.Properties.Item("StartProgram").Value = "your exe file";
configuration.Properties.Item("StartArguments").Value = "command line arguments";
bu i cant find any option to switch between Debug and Release :
Try (this was written for VS2019: check the GetActiveObject is right for you):
using EnvDTE80;
using System;
namespace SelectConfig
{
class Program
{
static void Main(string[] args)
{
ChangeConfiguration("Release", "Any CPU");
}
private static void ChangeConfiguration(string configurationName, string platformName)
{
DTE2 dte2 = (DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.16.0");
bool found = false;
foreach (SolutionConfiguration2 configuration in dte2.Solution.SolutionBuild.SolutionConfigurations)
{
if (configuration.Name == configurationName && configuration.PlatformName == platformName)
{
configuration.Activate();
found = true;
break;
}
}
if (found)
{
SolutionConfiguration2 finalConfig = (SolutionConfiguration2)dte2.Solution.SolutionBuild.ActiveConfiguration;
Console.WriteLine($"End Configuration: {finalConfig.Name}/{finalConfig.PlatformName}");
}
else
{
Console.WriteLine($"Requested configuration {configurationName}/{platformName} not found");
}
}
}
}
Related
I am developing a small app that accepts two different types of files (*.miz and *.5js). There are two functions that do parse those files, and they already work properly when triggered from button click events (see below). Also, the solution contains this project and a setup project, to create the installer of the app.
public void button1_Click(object sender, EventArgs e)
{
button1.BackColor = Color.Red;
openFileDialog1.InitialDirectory = "";
openFileDialog1.Title = "Load mission file";
openFileDialog1.ShowDialog();
textBox4.Text = openFileDialog1.FileName;
processMizFile(openFileDialog1.FileName);
button1.BackColor = SystemColors.Control;
}
and
private void button6_Click(object sender, EventArgs e)
{
button6.BackColor = Color.Red;
openFileDialog2.InitialDirectory = "";
openFileDialog2.Title = "Load standalone Datacards";
openFileDialog2.ShowDialog();
loadDatacard(openFileDialog2.FileName);
button6.BackColor = SystemColors.Control;
}
I am now trying to call those functions when I start the app by double clicking in one of those file types (associated to my app through Windows properties dialog). For that, I am running the following code:
public Form1()
{
LoadFont();
InitializeComponent();
string version = Assembly.GetExecutingAssembly().GetName().Version.ToString();//to display program version in the form
label29.Text += version;
string[] cmdl = Environment.GetCommandLineArgs();
using (StreamWriter sw = File.CreateText(Application.UserAppDataPath + #"\log.txt"))
{
if (cmdl.Length > 1)
{
sw.WriteLine("Argument 0 - Exe path: " + cmdl[0]);
sw.WriteLine("Argument 1: " + cmdl[1]);
sw.WriteLine("File type: " + Path.GetExtension(cmdl[1]).ToUpper());
}
else
{
sw.WriteLine("Argument 0 - Exe path: " + cmdl[0]);
sw.WriteLine("No further arguments provided");
}
}
if (cmdl.Length > 1)
{
if (Path.GetExtension(cmdl[1]).ToUpper() == ".MIZ")
{
processMizFile(cmdl[1]);
using (StreamWriter sw2 = File.CreateText(Application.UserAppDataPath + #"\log2.txt"))
{
sw2.WriteLine("MIZ file loaded!");
}
}
else if (Path.GetExtension(cmdl[1]).ToUpper() == ".5JS")
{
loadDatacard(cmdl[1]);
using (StreamWriter sw2 = File.CreateText(Application.UserAppDataPath + #"\log2.txt"))
{
sw2.WriteLine("5Js file loaded!");
}
}
}
}
The streamwriter parts are in place to have some traces, as I am not sure how can I debug this, otherwise.
Now, the problem is that it all works properly on the same laptop where I have Visual Studio installed. But, it does not work on a different computer:
Release Build
If I just double click one of the associated files(on the Desktop), the splash screen flashes quickly, and goes away. No log file is written
If I double click an associated file in the same path as the installed executable, it works fine
If I launch the app from command line, passing a path as argument, I get:
log.txt:
Argument 0 - Exe path: DatacardGenerator.exe
Argument 1: C:\Users\Username\OneDrive\Desktop\Datacards.5js
File type: .5JS
log3.txt: writes up to "Created path to extract dir: " and the right Appdata folder to temporary extract
using (StreamWriter sw3 = File.AppendText(Application.UserAppDataPath + #"\log3.txt"))
{
sw3.WriteLine("Created path to extract dir: " + extract5Js);
}
if (Directory.Exists(extract5Js))
{
System.IO.Directory.Delete(extract5Js, true);
}
using (StreamWriter sw3 = File.AppendText(Application.UserAppDataPath + #"\log3.txt"))
{
sw3.WriteLine("Proceeding to unzip...");
}
ZipFile.ExtractToDirectory(path5jscard, extract5Js);
So, how could I know what happens during the startup of my app when launched by an external file? I`ve set command line arguments in debug mode to test, but I am not sure if they come with full path or not, given the different results on the target machine (works if files in same directory as exe, does not work otherwise)
Thanks for the support!! :)
P.S: I do not process the command line arguments directly in Main() because I am using 2 different forms: Form2 is a splash screen, and Form1 is the main program. For that I am using an example I found time ago, in this way:
namespace WindowsFormsApp2{
static class Program{
static private List<PrivateFontCollection> _fontCollections;
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(true);
CultureInfo culture;
culture = CultureInfo.CreateSpecificCulture("en-US");
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
//Dispose all used PrivateFontCollections when exiting
Application.ApplicationExit += delegate {
if (_fontCollections != null)
{
foreach (var fc in _fontCollections) if (fc != null) fc.Dispose();
_fontCollections = null;
}
string appPath = Application.UserAppDataPath;
string parentPath = System.IO.Directory.GetParent(appPath).ToString();
System.IO.Directory.Delete(parentPath, true);
};
//Application.Run(new Form1());
new MyApp().Run(args);
}
}
public class MyApp : WindowsFormsApplicationBase
{
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new Form2();
}
protected override void OnCreateMainForm()
{
// Do your initialization here
//...
// Then create the main form, the splash screen will automatically close
this.MainForm = new Form1();
}
}
}
Solved!
I just had to declare a global var in Main to be accessed from my form, like:
public static string[] cmdlArgs;
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(true);
CultureInfo culture;
culture = CultureInfo.CreateSpecificCulture("en-US");
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
cmdlArgs = Environment.GetCommandLineArgs();
And then it can be accessed using the namespace :
Program.cmdlArgs
I have an issue with my (visual studio) pre-build action calling an executable, which requires a console input.
The code looks like:
using System;
using System.Diagnostics;
using System.Text;
namespace MyMinumumExample
{
internal class MinimumExample
{
private StringBuilder stdOutput = null;
private readonly string assemblyFilePath;
private readonly Process gitProcess = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = "git",
RedirectStandardOutput = true,
UseShellExecute = false,
},
};
public MinimumExample(string path)
{
assemblyFilePath = path;
}
private static int Main(string[] args)
{
string path = args[0];
var program = new MinimumExample(path);
if (program.CheckIfFileIsDirty(path))
{
Console.WriteLine("Changes will be discarded after compiling. Do you want to proceed?");
while (true)
{
Console.Write("[y/n]: ");
ConsoleKeyInfo key = Console.ReadKey();
if (key.KeyChar == 'y')
break;
if (key.KeyChar == 'n')
return 1;
Console.WriteLine();
}
}
return 0;
}
private bool CheckIfFileIsDirty(string path)
{
string gitCmdArgs = string.Format("diff --shortstat -- {0}", path);
stdOutput = new StringBuilder();
gitProcess.StartInfo.Arguments = gitCmdArgs;
gitProcess.Start();
gitProcess.BeginOutputReadLine();
gitProcess.OutputDataReceived += GitProcessOutputHandler;
gitProcess.WaitForExit();
gitProcess.OutputDataReceived -= GitProcessOutputHandler;
if (gitProcess.ExitCode != 0) throw new Exception(string.Format("Process 'git {0}' failed with code {1}", gitCmdArgs, gitProcess.ExitCode));
return !string.IsNullOrWhiteSpace(stdOutput.ToString());
}
private void GitProcessOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!string.IsNullOrWhiteSpace(outLine.Data))
{
stdOutput.Append(outLine.Data);
}
}
}
}
If I run this from cmd-shell or from a batch file everything works fine. If I put it into the pre build action, I get an exeption in line 38:
ConsoleKeyInfo key = Console.ReadKey();
The pre build action looks like:
call $(SolutionDir)MinimumExample.exe $(ProjectDir)dirtyExampleFile.cs
IF %ERRORLEVEL% GTR 0 (
EXIT 1
)
If I call the executable with start <exe> the example works, but I don't get the exit code of the exe (but probably of the new cmd shell).
Either, I can get the exit code of cmd shell to verify that the executable did exit cleanly, or I find a way to read keyboard input from the build event console.
Does anyone has an idea, how to solve this?
Best regards and thanks in advance!
I found the solution to get the exit code:
Using start with /wait option solved my problem.
PreBuildEvent is now:
start "Update version of $(ProjectName)" /wait $(SolutionDir)MinimumExample.exe $(ProjectDir)dirtyExampleFile.cs
I'm currently working on an editor script that will ease my transition between freemium and paid versions of my game.I would like to manually import the .unitypackage file that gets imported when I click the import button under Services -> In-App Purchasing.
I am aware of the function AssetDatabase.ImportAsset(path) but I need the path of the package first.
Thanks in advance!
When you enable IAP and click Import, the following will happen:
1.Unity will generate a random file name with
FileUtil.GetUniqueTempPathInProject()
2.A full path will be constructed like this:
<ProjectName>Temp\FileUtil.GetUniqueTempPathInProject()
3.Unity will then add .unitypackage to the end of that random file name.
Now, you have something like:
<ProjectName>Temp\FileUtil.GetUniqueTempPathInProject()+".unitypackage";
4.IAP package will then be downloaded and stored to path from #3.
Illustration of what it looks like:
5.The file is then copied to
<ProjectName>Temp\TarGZ
It is saved with the file name generated from #2. No .unitypackage at the end like #3.
UnityEngine.Cloud.Purchasing
6.After that, Unity imports it with AssetDatabase.ImportPackage not AssetDatabase.ImportAsset(path,false) as mentioned in your question.
Now you can see where Unity IAP package is located but there are just few problems in what you are trying to do:
1.For the IAP package to be present, the import button from the Services Tab must be clicked. I am sure you want this to be automated.
2.The file name is not static therefore making the path hard to retrieve. As you can see from the image above, there are other files in this folder too. We don't know which one is the AIP Package.
3.When you re-start Unity, the temp folder will be deleted. So the downloaded IAP package will be lost.
The best way to get the Unity IAP package is to download it directly from Unity's server then save it to your preferred location. The code below will download the IAP package and store it at: <ProjectName>/UnityEngine.Cloud.Purchasing.unitypackage
It will also import it and then, enable it. For AIP to work, Analytics must be enabled too. This code will also do that.
I tried to hide the AIP url so that it won't be abused and Unity won't have change it.
If you want to remake this into something else, the two most important functions to look at are downloadAndInstallAIP() and deleteAndDisableAIP().
AIPDownloader Code:
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using UnityEditor;
using UnityEditor.Analytics;
using UnityEditor.Purchasing;
using UnityEngine;
[ExecuteInEditMode]
public class AIPDownloader : MonoBehaviour
{
static string projectDirectory = Environment.CurrentDirectory;
static string aipFileName = "UnityEngine.Cloud.Purchasing.unitypackage";
static string etagName = "UnityEngine.Cloud.PurchasingETAG.text";
static string aipfullPath = "";
static string eTagfullPath = "";
static EditorApplication.CallbackFunction doneEvent;
[MenuItem("AIP/Enable AIP")]
public static void downloadAndInstallAIP()
{
//Make AIP fullpath
aipfullPath = null;
aipfullPath = Path.Combine(projectDirectory, aipFileName);
//Make AIP Etag fullpath
eTagfullPath = null;
eTagfullPath = Path.Combine(projectDirectory, etagName);
/*If the AIP File already exist at <ProjectName>/UnityEngine.Cloud.Purchasing.unitypackage,
* there is no need to re-download it.
Re-import the package
*/
if (File.Exists(aipfullPath))
{
Debug.Log("AIP Package already exist. There is no need to re-download it");
if (saveETag(null, true))
{
importAIP(aipfullPath);
return;
}
}
string[] uLink = {
"aHR0cHM=",
"Oi8vcHVibGljLWNkbg==",
"LmNsb3Vk",
"LnVuaXR5M2Q=",
"LmNvbQ==",
"L1VuaXR5RW5naW5l",
"LkNsb3Vk",
"LlB1cmNoYXNpbmc=",
"LnVuaXR5cGFja2FnZQ=="
};
prepare(uLink);
try
{
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(ValidateRemoteCertificate);
WebClient client = new WebClient();
client.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDoneDownloading);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);
client.DownloadFileAsync(new Uri(calc(uLink)), aipfullPath);
}
catch (Exception e)
{
Debug.LogError("Error: " + e.Message);
}
}
[MenuItem("AIP/Disable AIP")]
public static void deleteAndDisableAIP()
{
FileUtil.DeleteFileOrDirectory("Assets/Plugins/UnityPurchasing");
//Disable AIP
PurchasingSettings.enabled = false;
//Disable Analytics
AnalyticsSettings.enabled = false;
}
private static bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
{
return true;
}
public bool isAIPEnabled()
{
return PurchasingSettings.enabled;
}
private static bool saveETag(WebClient client, bool alreadyDownloadedAIP = false)
{
string contents = "";
if (alreadyDownloadedAIP)
{
//Load Etag from file
try
{
contents = File.ReadAllText(eTagfullPath);
return _saveEtag(contents, alreadyDownloadedAIP);
}
catch (Exception e)
{
Debug.LogWarning("File does not exist!: " + e.Message);
}
return false; //Failed
}
else
{
//Load Etag from downloaded WebClient
contents = client.ResponseHeaders.Get("ETag");
return _saveEtag(contents, alreadyDownloadedAIP);
}
}
static bool _saveEtag(string contents, bool alreadyDownloadedAIP = false)
{
if (contents != null)
{
try
{
//Save if not downloaded
if (!alreadyDownloadedAIP)
{
Directory.CreateDirectory(Path.GetDirectoryName(eTagfullPath));
File.WriteAllText(eTagfullPath, contents);
}
//Save to the etag to AIP directory
Directory.CreateDirectory(Path.GetDirectoryName("Assets/Plugins/UnityPurchasing/ETag"));
File.WriteAllText("Assets/Plugins/UnityPurchasing/ETag", contents);
return true;//Success
}
catch (Exception e)
{
Debug.LogWarning("Failed to write to file: " + e.Message);
return false; //Failed
}
}
else
{
return false; //Failed
}
}
public static void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
Debug.Log("Downloading: " + e.ProgressPercentage);
}
public static void OnDoneDownloading(object sender, AsyncCompletedEventArgs args)
{
WebClient wc = (WebClient)sender;
if (wc == null || args.Error != null)
{
Debug.Log("Failed to Download AIP!");
return;
}
Debug.Log("In Download Thread. Done Downloading");
saveETag(wc, false);
doneEvent = null;
doneEvent = new EditorApplication.CallbackFunction(AfterDownLoading);
//Add doneEvent function to call back!
EditorApplication.update = (EditorApplication.CallbackFunction)Delegate.Combine(EditorApplication.update, doneEvent);
}
static void AfterDownLoading()
{
//Remove doneEvent function from call back!
EditorApplication.update = (EditorApplication.CallbackFunction)Delegate.Remove(EditorApplication.update, doneEvent);
Debug.Log("Back to Main Thread. Done Downloading!");
importAIP(aipfullPath);
}
//Import or Install AIP
public static void importAIP(string path)
{
AssetDatabase.ImportPackage(path, false);
Debug.Log("Done Importing AIP package");
//Enable Analytics
AnalyticsSettings.enabled = true;
//Enable AIP
PurchasingSettings.enabled = true;
}
private static void prepare(string[] uLink)
{
for (int i = 5; i < uLink.Length; i++)
{
byte[] textAsBytes = System.Convert.FromBase64String(uLink[i]);
uLink[i] = Encoding.UTF8.GetString(textAsBytes);
}
for (int i = 0; i < uLink.Length; i++)
{
byte[] textAsBytes = System.Convert.FromBase64String(uLink[i]);
uLink[i] = Encoding.UTF8.GetString(textAsBytes);
if (i == 4)
{
break;
}
}
}
private static string calc(string[] uLink)
{
return string.Join("", uLink);
}
}
I made a simple compiler to learn about CodeDom. But it does not work, when I try to open my compiled file it does nothing.
When I run the code and slect an dir to save the exe file the exe file is generated, but when I click the exe file it does nothing.
The builder:
using System;
using System.IO;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Windows.Forms;
namespace TestBuilder
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
void build(string output, string title, string msg)
{
CompilerParameters p = new CompilerParameters();
p.GenerateExecutable = true;
p.ReferencedAssemblies.AddRange(new String[] { "System.dll"});
p.OutputAssembly = output;
p.CompilerOptions = "/t:winexe";
string source = File.ReadAllText(#"C:\Users\Gebruiker\Documents\visual studio 2015\Projects\TestCompiler\Test\Program.cs");
string errors = String.Empty;
source = source.Replace("[MESSAGE]", msg);
CompilerResults results = new CSharpCodeProvider().CompileAssemblyFromSource(p, source);
if (results.Errors.Count > 0)
{
foreach (CompilerError err in results.Errors)
{
errors += "Error: " + err.ToString() + "\r\n\r\n";
}
}
else errors = "Successfully built:\n" + output;
MessageBox.Show(errors, "Build", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void button1_Click(object sender, EventArgs e)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "EXE files (*.exe)|*.exe";
DialogResult result = sfd.ShowDialog();
if (result == DialogResult.OK)
{
build(sfd.FileName, textBox1.Text, textBox1.Text);
}
}
}
}
The program.cs file:
using System;
namespace Test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("[MESSAGE]");
Console.ReadLine();
}
}
}
How can I fix this problem, so when I compile a file and execute it, it shows the message I put in the textbox?
Change p.CompilerOptions = "/t:winexe"; to p.CompilerOptions = "/t:exe";.
After that the compiled program should output whatever you've put inside your TextBox when you run it.
Source
Use /target:exe to create a console application.
Why compiling this create an exe which :
open a console
launch the form ?
What can be done for the runtime compiled form open alone, without console ?
//LIST OF USING
using System;
using System.Windows.Forms;
using System.CodeDom.Compiler;
//CODE TO COMPILE
string oSource = #"
using System.Windows.Forms;
using System;
namespace fTest
{
public static class Program
{
public static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyForm());
}
}
public class MyForm:Form
{
public MyForm()
{
this.Text=""Generated exe"";
MessageBox.Show(""Generated exe says H3110 W0r1d"");
}
}
}";
string compiledOutput="Generated.exe";
//COMPILATION WORK
String [] referenceAssemblies={"System.dll","System.Drawing.dll","System.Windows.Forms.dll"};
CodeDomProvider _CodeCompiler = CodeDomProvider.CreateProvider("CSharp");
System.CodeDom.Compiler.CompilerParameters _CompilerParameters =
new System.CodeDom.Compiler.CompilerParameters(referenceAssemblies,"");
_CompilerParameters.OutputAssembly = compiledOutput;
_CompilerParameters.GenerateExecutable = true;
_CompilerParameters.GenerateInMemory = false;
_CompilerParameters.WarningLevel = 3;
_CompilerParameters.TreatWarningsAsErrors = true;
_CompilerParameters.CompilerOptions = "/optimize /target:winexe";//!! HERE IS THE SOLUTION !!
string _Errors = null;
try
{
// Invoke compilation
CompilerResults _CompilerResults = null;
_CompilerResults = _CodeCompiler.CompileAssemblyFromSource(_CompilerParameters, oSource);
if (_CompilerResults.Errors.Count > 0)
{
// Return compilation errors
_Errors = "";
foreach (System.CodeDom.Compiler.CompilerError CompErr in _CompilerResults.Errors)
{
_Errors += "Line number " + CompErr.Line +
", Error Number: " + CompErr.ErrorNumber +
", '" + CompErr.ErrorText + ";\r\n\r\n";
}
}
}catch (Exception _Exception)
{
// Error occurred when trying to compile the code
_Errors = _Exception.Message;
}
//AFTER WORK
if (_Errors==null)
{
// lets run the program
MessageBox.Show(compiledOutput+" Compiled !");
System.Diagnostics.Process.Start(compiledOutput);
}else
{
MessageBox.Show("Error occurred during compilation : \r\n" + _Errors);
}
By default, csc compiles console applications. You need to add /target:winexe to compiler options.
have you tried adding "target:winexe" to the command line parameters?