Visual Studio 2017 multi-line Debug environment variables - c#

Is VS 2017 (or even earlier versions), when you navigate to Debug -> <ProjectName> Properties -> Debug tab, you can add environment variables that your program can then use via Environment.GetEnvironmentVariable("var_name").
This works great when using a single line value for the variable, but has a character limit.
I have some programs which rely on multi-line variables that are defined on the system they run on when the application is deployed, such as:
string rsaPrivateKey = Environment.GetEnvironmentVariable("PRIVATE_KEY");
However, this is an RSA private keys, and those are multi-line. I want to be able to debug the application on my workstation, and have those environment variables declared in my VS 2017, and not hard-coded so that the logic that uses these variables can actually succeed.
Has anyone ran into this, and can you provide an alternative for declaring multi-line environment variables for Debugging purposes which will not be committed to source code?

Use:
string rsaPrivateKey;
string rsaPrivateKeyPath = Environment.GetEnvironmentVariable("PRIVATE_KEY_Path");
using (StreamReader sr = File.OpenText(rsaPrivateKeyPath ))
{
rsaPrivateKey = sr.ReadToEnd();
}

Related

Visual Studio 2022 System.Environment.GetEnvironmentVariable not working

I know this is still in preview, but I just want to make sure I am not doing anything wrong as I have done things like this in the past. I have my Environment variables set in properties:
And I am trying to set up my tests:
[TestInitialize]
public void Initialize()
{
var test = Environment.GetEnvironmentVariables();
// test enumerates all the Env variables, don't see it there
var connectionString = Environment.GetEnvironmentVariable("CONNECTION_STRING");
if (string.IsNullOrWhiteSpace(connectionString)) // so this is obviously null
throw new ArgumentNullException("CONNECTION_STRING");
_ConnectionString = connectionString;
}
As you can see by my comments, the environment variables are not found/loaded.
What am I missing? Thank you.
I'm assuming that you are using Visual Studio 2022 because you are using .NET 6 and the minimal host (i.e., no Startup.cs)?
The general preference is not to store information in the Environment Variables given that this information is often uploaded to GitHub and can be trawled and used against you.
For a local development secret, the preference is to store these using the secrets.json file. There is information on how to do this at Safe Storage of Secrets, as well as details on accessing Configuration files at Accessing Configuration File Information.
For the TL;DR: Crowd the steps below might help (this is what I did in my Blazor app with .NET 6):
In Visual Studio 2022, right click on the project in question and select 'Manage User Secrets'. This will create a local secrets.json file and open it. It will also add a GUID in your project tile for UserSecretsId.
Create your secrets as JSON key value pairs in this file as you would for environment variables.
Go to 'Connected Services' in your project and configure the Secrets.json service.
Add the 'User Secrets' to your configuration file; this will depend on exactly where this is happening.
Inject the IConfiguration into your controller and save this to a field.
Call the data you want using:
{yourConfigurationFieldName}.GetValue<string>({yourJsonKey})
In Visual Studio 2022, you can access environment variables in development by modifying the following section of launchSettings.json file.
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"VaultUri": "https://xxx.vault.azure.net/",
"AZURE_USERNAME": "xxx#user.com"
}
where VaultUri is the name of your environment variable.

Antlr4 C# build error

I am trying to install Antlr4 on a new machine but I am getting the following build error:
C:\antlrtest\antlrtest\Reference\Antlr4\Antlr4.targets(129,5): error AC1000: Unknown build error: Object reference not set to an instance of an object.
This is the offending code:
<Antlr4ClassGenerationTask
ToolPath="$(Antlr4ToolLocation)"
BuildTaskPath="$(Antlr4BuildTaskLocation)"
OutputPath="$(IntermediateOutputPath)"
TargetLanguage="%(Antlr4.TargetLanguage)"
TargetFrameworkVersion="$(TargetFrameworkVersion)"
TargetNamespace="%(Antlr4.CustomToolNamespace)"
SourceCodeFiles="#(Antlr4)"
ContinueOnError="$(_IntellisenseOnlyCompile)"
TokensFiles="#(Antlr4Tokens)"
AbstractGrammarFiles="#(Antlr4AbstractGrammar)"
LanguageSourceExtensions="$(DefaultLanguageSourceExtension)"
GenerateListener="%(Antlr4.Listener)"
GenerateVisitor="%(Antlr4.Visitor)"
ForceAtn="%(Antlr4.ForceAtn)"
AbstractGrammar="%(Antlr4.Abstract)">
<Output ItemName="Antlr4GeneratedCodeFiles" TaskParameter="GeneratedCodeFiles" />
</Antlr4ClassGenerationTask>
I think I followed the correct procedure:
I installed the extension from the VS 2012 website for Antlr.
Then I edited the project file to point to the References.
Then I added a reference to the Runtime DLL.
Any help would be appreciated.
So I ran into this today and was pulling my hair out over it for quite some time. The solution for me was to install both the 32-bit and 64-bit versions of the Java SDK.
Initially I only had the 64-bit version installed. It immediately started working after I installed the 32-bit as well.
Hope that helps someone.
This error is caused by Antlr4 failing to find an appropriate version of Java. On a Windows machine, Antlr4 checks the registry first before it checks your environment variables to determine the suitable location of Java.
Shortly before I experienced the same problem, I had been consolidating the many instances of the Java Runtime Environment I had on my computer. I had many and I trimmed them down to only 1 instance of a 64b JRE and 32b JRE each. I removed the rest by just deleting their folders. Subsequently I checked that my user and system environment variables (specifically JAVA_HOME) still pointed to a valid Java directory.
But I failed to realise that there are registry entries that have to be updated too. Possibly a reinstall of Java would fix the registry entries, but I reverted to just fixing them manually.
So the appropriate place in the Antlr4 code is at https://github.com/tunnelvisionlabs/antlr4cs/blob/master/runtime/CSharp/Antlr4BuildTasks/Antlr4ClassGenerationTaskInternal.cs#L144 .
private string JavaHome
{
get
{
#if !NETSTANDARD
string javaHome;
if (TryGetJavaHome(RegistryView.Default, JavaVendor, JavaInstallation, out javaHome))
return javaHome;
if (TryGetJavaHome(RegistryView.Registry64, JavaVendor, JavaInstallation, out javaHome))
return javaHome;
if (TryGetJavaHome(RegistryView.Registry32, JavaVendor, JavaInstallation, out javaHome))
return javaHome;
#endif
if (Directory.Exists(Environment.GetEnvironmentVariable("JAVA_HOME")))
return Environment.GetEnvironmentVariable("JAVA_HOME");
throw new NotSupportedException("Could not locate a Java installation.");
}
}
#if !NETSTANDARD
private static bool TryGetJavaHome(RegistryView registryView, string vendor, string installation, out string javaHome)
{
javaHome = null;
string javaKeyName = "SOFTWARE\\" + vendor + "\\" + installation;
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, registryView))
{
using (RegistryKey javaKey = baseKey.OpenSubKey(javaKeyName))
{
if (javaKey == null)
return false;
object currentVersion = javaKey.GetValue("CurrentVersion");
if (currentVersion == null)
return false;
using (var homeKey = javaKey.OpenSubKey(currentVersion.ToString()))
{
if (homeKey == null || homeKey.GetValue("JavaHome") == null)
return false;
javaHome = homeKey.GetValue("JavaHome").ToString();
return !string.IsNullOrEmpty(javaHome);
}
}
}
}
#endif
Your .\packages\Antlr4.CodeGenerator.4.6.3\build\Antlr4.CodeGenerator.targets file (adjust for correct version number) has lines to set the JavaVendor and JavaInstallation variables:
<Antlr4JavaVendor Condition="'$(Antlr4JavaVendor)'==''">JavaSoft</Antlr4JavaVendor>
<Antlr4JavaInstallation Condition="'$(Antlr4JavaInstallation)'==''">Java Runtime Environment</Antlr4JavaInstallation>
So, in my case at least, the appropriate registry settings were located in HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment
To fix the incorrect registry:
Ensure that CurrentVersion's value (in my case 1.8.0_91) has a corresponding key
Ensure that the JavaHome key in HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.8.0_91 (replace with your version) has a directory path to a valid Java installation.
If you're running a 64b Windows, ensure this for both the 64b and 32b versions of the registry (see https://support.microsoft.com/fr-dz/help/305097/how-to-view-the-system-registry-by-using-64-bit-versions-of-windows for how to view the 32b registry on a 64b machine).
Keep in mind, that Visual Studio is a 32b process and that, by default, it will run Antlr4 by looking for a 32b version of the JRE. If, for some reason, you're building the solution outside of Visual Studio with 64b processes, the 64b version of the registry is used.
I am using Visual Studio 2022 and I had to compile a project that used Antlr4.4.5.3. The grammar file was configured to generate a Visitor but it did not work. I had the same error as you.
In my case I had an Open JDK in my JAVA_HOME environment variable. I also had an Oracle Java JDK installed. So, I deduced that Antl4 did not like my JAVA_HOME pointing to the Open JDK.
So I deleted the JDK and JRE folders (that pointed to the Open JDK) at HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft in the registry allowing me to compile my project successfully.
I deduce that Antlr then used the HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment folder where the configuration is set when you install an Oracle Java JDK version.

Different configuaration string for specific build configuration

how can i configure my Visual Studio 2010 C# solution/project so
that when i select a Debug configuration - ConnectionString#1 would be used
Release - Connection string #2
and
"Myconfiguarion1" (which was copied from debug) -> Connection string #3
I got to it work with debug in such a way:
if (ConfigurationManager.ConnectionStrings["ConnectionString1"] != null)
{
winApplication.ConnectionString = ConfigurationManager.ConnectionStrings["ConnectionString1"].ConnectionString;
}
#if DEBUG
if(ConfigurationManager.ConnectionStrings["ConnectionString2"] != null)
{
winApplication.ConnectionString = ConfigurationManager.ConnectionStrings["ConnectionString2"].ConnectionString;
}
#endif
but this doesn't work with "MybuildConfiguration"
If you're trying to do this for the web.config file of an ASP.NET project in Visual Studio 2010, it's built-in via XML Transformations for web.config.
Web Deployment: Web.Config Transformations
If you're trying to do this for an app.config file, you can use the same transformations but working with them is a bit of a hack:
Visual Studio App.config XML Transformations
Both boil down to actually using separate config files for the different environments you are going to be running your app in. That allows you to supply different values for any of the keys based on what environment you're running in.
I think you can use conditional compilation constants. To define them,
you have to open project property window, select compilation tab, and define a name in the conditional constants field, e.g. CONN1.
This constants get defined only for your active build configuration, so you can define CONN1 for Debug configuration,CONN2 for Release configuration,CONN3 for your custom configuration etc.
then, in your code, you can use:
#ifdef CONN1
//use connection 1
#else
#ifdef CONN2
//use connection 2
#else
//use connection 3
#endif
#endif

Detecting if a program was run by Visual Studio, as opposed to run from Windows Explorer

Is there a way to detect if your program was loaded through Visual Studio vs. whether it was started as a standalone executable?
Our software has a bug reporting feature to handle unhandled exceptions -- we need to be able to distribute debug builds to our beta testers, but we don't want the bug report to go off when we are in the middle of development, because the Exceptions are a lot more useful if VS catches them with a full stack trace, etc.
Right now, I'm disabling the bug report if Application.ExecutablePath includes bin\Debug or bin\Release, but I figure there is probably a more robust way of detecting whether the program was loaded through VS.
Obviously, we could set up a different build with some preprocessor macros, but for the sake of the question, assume that isn't a possibility -- I don't mind adding code, but I'm trying to make the fewest modifications to the build process, which is why command-line options are kind of a last resort as well.
If it matters, I'm using VS2003/.NET 1.1.
If you're doing this to determine if it is in any debugger (clarified by #JaredPar), you can use Debugger.IsAttached in the exception handler.
try
{
// ...
}
catch(Exception ex)
{
if (!Debugger.IsAttached)
{
ExceptionHandler.Frob(ex);
}
else
{
throw;
}
}
Alternatively:
public static void Frob(Exception ex)
{
if (Debugger.IsAttached)
{
Debugger.Break();
}
}
I don't do .net development, but in java I have done this by passing a flag into the startup options of the application. So you could pass a debug flag into the app from the IDE, and then check for that, when the app is run as an executable the flag would not be present. I would be surprised if .net didn't have something similar.
I know this is old but the provided solutions are not very satisfying.
I used the following class instead:
using System.IO;
using System.Reflection;
public static class Program
{
public static string ExecutablePath
{
get;
private set;
}
static Program()
{
var assemblyPath = Assembly.GetEntryAssembly().Location;
var assemblyDirectory = Path.GetDirectoryName(assemblyPath);
if (assemblyDirectory.EndsWith(#"\Debug") || assemblyDirectory.EndsWith(#"\Release"))
{
string projectFile = Path.GetFileNameWithoutExtension(assemblyPath) + ".csproj";
var root = new DirectoryInfo(assemblyDirectory);
while (root.Parent != null)
{
if (File.Exists(Path.Combine(root.FullName, projectFile)))
break;
root = root.Parent;
if (root.Parent == null) // we could not find it (should not happen)
ExecutablePath = assemblyDirectory;
}
ExecutablePath = root.FullName;
}
else
{
ExecutablePath = assemblyDirectory;
}
}
}
Then you can just use Program.ExecutablePath. If you already have a class named Program you can just extend it by those properties and methods.
If running from Visual Studio it will give you the project path where the csproj-file resides. This is the executable path without the "bin\*\Debug" or "bin\*\Release" stuff.
If not running from Visual Studio it will give you the path where the executable resides.
The solution is independent of debug settings, other attached debuggers or build configurations. The only important thing is, that your configurations are named "Release" and "Debug".
Note: As Troy Gizzi mentioned in the comments, this solution only works if you run the executable from another directory than the output directory. For my use case (simulate the deployment directory structure with the project directory as the root directory), this is a suitable solution. In general I copy my executable later to the deployment directory and expect the same behavior as if I run my program from within Visual Studio. Content and other dependencies are located relative to the project directory in my case.
Have you considered command line arguments? Run the program from Visual Studio with a --no-exception-handling flag (or whatever sounds appropriate), and don't handle exceptions if that argument is passed in. When you start the program elsewhere, without this argument, it'll behave normally.
Instead of tracking by process tree, I would add a configuration flag that enables the reporting feature. The flag can always default to "true" unless you are in your DEV environment then you set it to "false".
Sometimes the application is started outside the debugger and the debugger gets attached later. (Doubleclick on a file where the application is assigned to ...) I use this code to wait for the debugger attach.
using System.Diagnostics;
Process[] procName = Process.GetProcessesByName("devenv");
if(procName.Length > 0)
MessageBox.Show("Wait for debugger attach");

Debugging C# Custom Installer Classes

I have written an installation class that extends Installer and overrides afterInstall, but I'm getting a null pointer exception. How can I go about debugging my class?
Something that is handy for hard to debug sections of code is
System.Diagnostics.Debugger.Break()
Will throw a breakpoint caught by any installed debugger (VStudio, WinDbg, Remote debugger etc...).
Use it to debug really tricky areas where regular F5+Go or "Attach to Process" is difficult or impossible to perform, some examples include:
short-lived processes
time-sensitive processes
breaking into spawned sub-processes
installers
service stop/start
distributed systems
The best way I've found is to write a unit test, and new up and initialize your installer class from your unit test:
[TestClass] public class InstallerTest {
[TestMethod]
public void InstallTest() {
// substitute with your installer component here
DataWarehouseInstall installer = new DataWarehouseInstall();
string assemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string installLogFilePath = Path.Combine(assemblyDirectory, "install.log");
installer.Context = new System.Configuration.Install.InstallContext(installLogFilePath, null);
// Refactor to set any parameters for your installer here
installer.Context.Parameters.Add("Server", ".");
//installer.Context.Parameters.Add("User", "");
//installer.Context.Parameters.Add("Password", "");
installer.Context.Parameters.Add("DatabaseName", "MyDatabaseInstallMsiTest");
//installer.Context.Parameters.Add("DatabasePath", "");
// Our test isn't injecting any save state so we give a default instance for the stateSaver
installer.Install(new Hashtable());
} }
At least then it takes advantage of the IDE tooling better. This is especially helpful for very large installers with LOTS of components. Then you can also create ordered unit tests and run them in sequence to mimic your installer during debug or your automated builds.
Another tip would be general SOLID/GRASS software principles...develop in neat/thin layers, keeping your actual "custom action" installer logic very simple and instead call into any reusable API stuff you have that is specific to your installer(s), just as we are used to with UI development. (The installer is just another UI anyway.) This is especially key if your goal is to have a certain UI experience shared across all installers of your products.
Surprised no one has actually answered. Put a MessageBox.Show("hello") into your custom action's Install() member. Build the deployment in debug config. Install. When the MessageBox appears, go into VS IDE, Debug, Attach Process and look for the instance of msiexec that is labeled "Managed". Attach the debugger to that instance of msiexec. Now go back to the source of your custom action and place a breakpoint right after the call to MessageBox.Show(). Close the MessageBox and your breakpoint will be hit, and you're debugging in the IDE!
attach the installer process to Visual studio in Debug->Processes->Attach or CTRL + ALT + P
set the breakpoint and you should be able to go
In your installer method add Debugger.Launch() statement which will launch "Visual Studio just in time debugger" where you can attach an instance of visual studio and debug your installer class (MSI). This should work in Visual Studio 2010 as well. But you need to have administrative rights to do this. If you don't have administrative rights, you might have issues. So, log in as administrator for debugging MSI. For example:
public override void Install(System.Collections.IDictionary stateSaver)
{
Debugger.Launch();
base.Install(stateSaver);
}
In visual studio 2005, even Debugger.Break() use to work but somehow this does not work with Visual Studio 2010.
This is what actually worked for me.
System.Diagnostics.Debugger.Launch();
Then right click on the Installer Project and press "Install"
None of above worked for me. This is what actually worked. Note that you need to put insert "both" lines.
using System.Diagnostics;
MessageBox.Show("Test is about to begin");
Debugger.Launch();
I use EventLog.WriteEntry("source", "message"), and check the EventLog when installing. Maybe not optimal, but works for me :)
I use the following class to write a simple log into the target directory. In my opinion, it's easier than trying to use the Visual Studio debugger.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace MyCompany.Deployment
{
/// <summary>
/// Enables a quick and easy method of debugging custom actions.
/// </summary>
class LogFile
{
const string FileName = "MyCompany.Deployment.log";
readonly string _filePath;
public LogFile(string primaryOutputPath)
{
var dir = Path.GetDirectoryName(primaryOutputPath);
_filePath = Path.Combine(dir, FileName);
}
public void Print(Exception ex)
{
File.AppendAllText(_filePath, "Error: " + ex.Message + Environment.NewLine +
"Stack Trace: " + Environment.NewLine + ex.StackTrace + Environment.NewLine);
}
public void Print(string format, params object[] args)
{
var text = String.Format(format, args) + Environment.NewLine;
File.AppendAllText(_filePath, text);
}
public void PrintLine() { Print(""); }
}
}
For logging purposes (in 3.5) what about using:
Context.LogMessage("My message");
Write the following code in the beginning of the method that you want to debug
#if DEBUG
MessageBox.Show(Process.GetCurrentProcess().Id.ToString());
#endif
So when your method is called, the above code will be hit and you can then attach the debugger to the process(ctrl+alt+p) using the above process ID. You may have to start VS with elevated permissions.
build a VM, install Visual studio, make a copy of it (or create a differencing Virtual HDD) and run the installer under the debugger under the VM.
That is what I would do (but I'm no expert).
You can also use the installUtil.exe utility to test your installer component.
In case you created a c# class assembly with your Installer class, Change your debug settings to start the external program 'C:\Windows\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe'
and enter your commandline arguments accordingly (e.g. /Args=myargument "path to the assembly")
As last set your breakpoints, press f5 and you're set to debug your code.
--paralax
You might automate debugging of installer projects by adding following section to either .csproj or .csproj.user file:
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<StartAction>Program</StartAction>
<StartProgram>$(MSBuildBinPath)\installutil.exe</StartProgram>
<StartArguments>$(AssemblyName).dll</StartArguments>
</PropertyGroup>
Use project file if you want other developers benefit from this change and .user file if you want to use it by yourself.

Categories