Here is what I have done:
Create a new standard .Net C# console project
static void Main(string[] args)
{
foreach (var arg in args)
Console.WriteLine(arg);
}
Rightclick on the project and select Add->Docker Support
Edit the project's dockerfile and change its ENTRYPOINT:
ENTRYPOINT ["C:\app\ConsoleApp1.exe", "Hello", "World"]
Set a breakpoint and Run
args is empty.
docker-compose did emit my entrypoint during the build:
1>Step 5/5 : ENTRYPOINT ["C:\app\ConsoleApp1.exe", "Hello", "World"]
What am I missing?
You can get the command line arguments this way:
Environment.GetCommandLineArgs();
This works also in a Docker container.
Related
Standard template for command
dotnet new console
contains #if for the preprocessor:
#if (csharpFeature_TopLevelProgram)
// See https://aka.ms/new-console-template for more information
#endif
#if (!csharpFeature_ImplicitUsings)
using System;
#endif
#if (csharpFeature_TopLevelProgram)
Console.WriteLine("Hello, World!");
#else
namespace Company.ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}
#endif
What command line switches should I specify to dotnet new so that the variable "csharpFeature_TopLevelProgram" is not defined and so that when I execute dotnet new console the file is generated not for Net6 but without TopLevelProgram as before in the good old Net5?
If you use the -h command line option, it tells you how to use the template and that you can specify the --use-program-main parameter:
For example:
dotnet new console --use-program-main true
Note: You may need to update to a newer version of .NET 6 for this command line option to exist. This was tested with v6.0.401
This is in continuation to this Question here, I have a PowerShell command which I have created and am able to call the command in a PowerShell window, but when trying to call from C# method, I am getting error as the cmdlet is not recognized, I tried with other existing commands and get same error, so I suspect issue in Importing the Module, though I don't get that error in streams. Error. The only error I get is "Get-RowAndPartitionKey is not a recognized cmndlt, please check the spelling.....".
Would like to know if there is any other way, I should try it or if I can debug more here to see if my Module fetches all command or not. right now I am clueless how to fix this.
public string RunScript( string contentScript, Dictionary<string, EntityProperty> parameters )
{
List<string> parameterList = new List<string>();
foreach( var item in parameters )
{
parameterList.Add( item.Value.ToString() );
}
using( PowerShell ps = PowerShell.Create() )
{
IAsyncResult async =
ps.AddCommand( "Import-Module" ).AddArgument( #"C:\Users\...\.D.PowerShell.dll" )
.AddStatement()
.AddCommand( "Get-RowAndPartitionKey" ).AddParameter( "Properties", "test" )
.BeginInvoke();
StringBuilder stringBuilder = new StringBuilder();
foreach( PSObject result in ps.EndInvoke( async ) )
{
stringBuilder.AppendLine( result.ToString() );
}
return stringBuilder.ToString();
}
}
}
Below method do not return any error in Streams.Error or Verbose but no output also:
public async Task<IEnumerable<object>> RunScript( string scriptContents, List<string> scriptParameters )
{
// create a new hosted PowerShell instance using the default runspace.
// wrap in a using statement to ensure resources are cleaned up.
using( PowerShell ps = PowerShell.Create() )
{
// specify the script code to run.
ps.AddScript( scriptContents );
// specify the parameters to pass into the script.
ps.AddParameter( "Properties" ,"test") ;
// execute the script and await the result.
var pipelineObjects = await ps.InvokeAsync().ConfigureAwait( false );
return pipelineObjects;
}
}
scriptContent
"\"$path = 'C:\\Users...\\.TabularData.PowerShell.dll'\\r\\nImport-Module $path\\r\\nGet-RowAndPartitionKeys\""
The following is self-contained PowerShell sample code that uses on-demand compilation of C# code:
It shows that the approach works in principle, as described in this answer to your original question.
Prerequisites: The PowerShell SDK package and .NET runtime used in the C# project that calls your custom Get-RowAndPartitionKey" cmdlet must be compatible with the PowerShell SDK and .NET runtime that you used to compile the assembly DLL that houses that cmdlet, to be imported via Import-Module.
The sample code below ensures that implicitly, by running directly from PowerShell, using the Add-Type cmdlet to compile C# code on demand - it works in Windows PowerShell as well as in PowerShell (Core) 7+.
In practice I've found that a .NET Framework-compiled DLL (from Windows PowerShell) also works in PowerShell (Core) (.NET (Core) 5.0), but not vice versa.
It shows troubleshooting techniques, namely:
Adding the -Verbose switch to the Import-Module call to produce verbose output that lists the commands being imported from the given module (DLL).
Printing these verbose messages (look for // --- TROUBLESHOOTING CODE)
Printing any non-terminating PowerShell errors that occurred (as opposed to exceptions that you'd have to handle in C# code).
# Create a (temporary) assembly containing cmdlet "Get-RowAndPartitionKey".
# This assembly can directly be imported as a module from PowerShell.
# The cmdlet simply outputs "Hi from Get-RowAndPartitionKey" and
# echoes the elements of the list passed to -Properties, one by one.
$tempModuleDll = Join-Path ([IO.Path]::GetTempPath()) "TempModule_$PID.dll"
Remove-Item -ErrorAction Ignore $tempModuleDll
Add-Type #'
using System.Management.Automation;
using System.Collections.Generic;
[Cmdlet("Get", "RowAndPartitionKey")]
public class GetRowAndPartitionKeyCmdlet : PSCmdlet {
[Parameter] public List<string> Properties { get; set; }
protected override void ProcessRecord() {
WriteObject("Hi from Get-RowAndPartitionKey: ");
WriteObject(Properties, true);
}
}
'# -ErrorAction Stop -OutputAssembly $tempModuleDll
# Compile a C# class ad hoc to simulate your project, and call its static
# method, which imports the module and effectively calls
# Get-RowAndPartitionKey -Properties "foo", "bar"
(Add-Type #"
using System;
using System.Management.Automation;
using System.Collections.Generic;
using System.Text;
public static class Foo {
public static string RunScript(List<string> parameterList)
{
using (System.Management.Automation.PowerShell ps = PowerShell.Create())
{
IAsyncResult async =
// Add -Verbose to the Import-Module call, so that the list of
// commands being imported is written to the verbose output stream.
ps.AddCommand("Import-Module").AddArgument(#"$tempModuleDll").AddParameter("Verbose", true)
.AddStatement()
.AddCommand("Get-RowAndPartitionKey").AddParameter("Properties", parameterList)
.BeginInvoke();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject result in ps.EndInvoke(async))
{
stringBuilder.AppendLine(result.ToString());
}
// --- TROUBLESHOOTING CODE
// Print verbose output from the Import-Module call
foreach (var v in ps.Streams.Verbose) { Console.WriteLine("VERBOSE: " + v.ToString()); }
// Print any errors.
foreach (var e in ps.Streams.Error) { Console.WriteLine("ERROR: " + e.ToString()); }
// ---
return stringBuilder.ToString();
}
}
}
"# -ErrorAction Stop -PassThru)::RunScript(("foo", "bar"))
# Clean-up instructions:
if ($env:OS -eq 'Windows_NT') {
Write-Verbose -vb "NOTE: Re-running this code requires you to start a NEW SESSION."
Write-Verbose -vb "After exiting this session, you can delete the temporary module DLL(s) with:`n`n Remove-Item $($tempModuleDll -replace '_.+', '_*.dll')`n "
} else {
Write-Verbose -vb "NOTE: Re-running this code after modifying the embedded C# code requires you to start a NEW SESSION."
Remove-Item $tempModuleDll
}
On my Windows 10 machine, both from PowerShell (Core) 7.0.5 and Windows PowerShell 5.1, the above yields (clean-up instructions omitted) the following, showing that everything worked as intended:
VERBOSE: Loading module from path 'C:\Users\jdoe\AppData\Local\Temp\TempModule_11876.dll'.
VERBOSE: Importing cmdlet 'Get-RowAndPartitionKey'.
Hi from Get-RowAndPartitionKey:
foo
bar
Specifically, line VERBOSE: Importing cmdlet 'Get-RowAndPartitionKey'. indicates that the custom cmdlet was successfully imported into the session.
Given the program:
using System;
using System.IO;
namespace fsw_bug_poc
{
class Program
{
private static FileSystemWatcher _fileSystemWatcher;
static void Main(string[] args)
{
_fileSystemWatcher = new FileSystemWatcher("Watched", "*.*");
_fileSystemWatcher.Changed += Notify;
_fileSystemWatcher.Created += Notify;
_fileSystemWatcher.Deleted += Notify;
_fileSystemWatcher.Renamed += Notify;
_fileSystemWatcher.IncludeSubdirectories = true;
_fileSystemWatcher.EnableRaisingEvents = true;
Console.ReadKey(false);
}
private static void Notify(object sender, FileSystemEventArgs e)
{
Console.WriteLine($"{e.FullPath} {e.ChangeType}");
}
}
}
The Dockerfile:
FROM mcr.microsoft.com/dotnet/core/runtime:2.2-stretch-slim AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build
WORKDIR /src
COPY ["fsw-bug-poc.csproj", ""]
RUN dotnet restore "fsw-bug-poc.csproj"
COPY . .
WORKDIR "/src/"
RUN dotnet build "fsw-bug-poc.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "fsw-bug-poc.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENV DOTNET_USE_POLLING_FILE_WATCHER=true
RUN mkdir -p /app/Watched
VOLUME /app/Watched
ENTRYPOINT ["dotnet", "fsw-bug-poc.dll"]
According to this link adding ENV DOTNET_USE_POLLING_FILE_WATCHER=true to the Dockerfile fixes the FileSystemWatcher not working inside the container.
Even with this fix, FileSystemWatcher will not work when running a Linux container on Windows and mounting a shared driver to a volume:
docker build -t fsw-bug-poc .
docker run -it --rm -v C:\Shared:/app/Watched fsw-bug-poc
Modifying a file inside the container:
Modifying files in the shared volume folder:
Nothing happens!!
Can someone explain what is going on? The FileSystemWatcher is using a polling strategy, so it should work the same way, shouldn't it?
Switching to PhysicalFileProvider did the job. It seems to be a more portable implementation for file system watching strategies.
The current implementation of PhysicalFileProvider supports the DOTNET_USE_POLLING_FILE_WATCHER environment variable. I couldn't find any reference of it in FileSystemWatcher implementation.
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
using System;
using System.IO;
namespace fsw_bug_poc
{
class Program
{
private static PhysicalFileProvider _fileProvider;
private static IChangeToken _fileChangeToken;
static void Main(string[] args)
{
_fileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "."));
WatchForFileChanges();
Console.ReadKey(false);
}
private static void WatchForFileChanges()
{
_fileChangeToken = _fileProvider.Watch("*.*");
_fileChangeToken.RegisterChangeCallback(Notify, default);
}
private static void Notify(object state)
{
Console.WriteLine("File change detected");
WatchForFileChanges();
}
}
}
How do I pass command line parameters from my C# application to IronPython 2.x? Google is only returning results about how to do it with Iron Python 1.x.
static void Main(string[] args)
{
ScriptRuntime scriptRuntime = IronPython.Hosting.Python.CreateRuntime();
// Pass in script file to execute but how to pass in other arguments in args?
ScriptScope scope = scriptRuntime.ExecuteFile(args[0]);
}
You can either set the sys.argv via the following C# code:
static void Main(string[] args)
{
var scriptRuntime = Python.CreateRuntime();
var argv = new List();
args.ToList().ForEach(a => argv.Add(a));
scriptRuntime.GetSysModule().SetVariable("argv", argv);
scriptRuntime.ExecuteFile(args[0]);
}
having the following python script
import sys
for arg in sys.argv:
print arg
and calling the exe like
Test.exe SomeScript.py foo bar
gives you the output
SomeScript.py
foo
bar
Another option would be passing the prepared options to Python.CreateRuntime as explained in this answer
I have what seems to be a simple problem that I can't solve myself. I have a WinForm app, with main method modified to accept command line arguments like this:
[STAThread]
static void Main(String[] args)
{
int argCount = args.Length;
}
This code works fine and argCount is equals to 2 when compiled in debug mode with the following execution line: program.exe -file test.txt.
However as soon as I compile the program in release mode, argCount is now 1 with the same command line arguments. The only argument contains "-file test.txt". More than that it only happens if I run the compiled executable from obj/Release folder, but not from bin/Release. Unfortunately setup project takes executables from obj/Release so I can't change that.
Is this a known issue and is there a way around this problem?
The command line processing should be the same, therefore something else is going on. When I try this:
class Program {
[STAThread]
static void Main(String[] args) {
Console.WriteLine("Have {0} arguments", args.Length);
for (int i = 0; i < args.Length; ++i) {
Console.WriteLine("{0}: {1}", i, args[i]);
}
}
}
and then it from the various locations I get 100% consistent results, the only way of getting arguments "merged" is to enclose them in quotes on the command line (which is specifically there to allow you do have arguments containing a space, see the last example below):
PS C:\...\bin\Debug> .\ConsoleApplication1.exe one two three
Have 3 arguments
0: one
1: two
2: three
PS C:\...\bin\Debug> pushd ..\release
PS C:\...\bin\Release> .\ConsoleApplication1.exe one two three
Have 3 arguments
0: one
1: two
2: three
PS C:\...\bin\Release> pushd ..\..\obj\debug
PS C:\...\obj\Debug> .\ConsoleApplication1.exe one two three
Have 3 arguments
0: one
1: two
2: three
PS C:\...\obj\Debug> pushd ..\release
PS C:\...\obj\Release> .\ConsoleApplication1.exe one two three
Have 3 arguments
0: one
1: two
2: three
PS C:\...\obj\Release> .\ConsoleApplication1.exe -file test.txt
Have 2 arguments
0: -file
1: test.txt
PS C:\...\obj\Release> .\ConsoleApplication1.exe "-file test.txt"
Have 1 arguments
0: -file test.txt
Additional While launching from a command prompt makes it easy to see what is being passed it can be hard to check when another application launches yours. However tools like Process Explorer will show the command line used to start a program (double click on a process and look at the image tab).
This works for me from bin/Debug, bin/Release, obj/Debug and obj/Release:
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args) {
FormMain.Args = args;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FormMain());
}
}
public partial class FormMain: Form {
public static string[] Args;
public FormMain() {
InitializeComponent();
}
private void FormMain_Shown(object sender, EventArgs e) {
foreach (string s in Args) {
MessageBox.Show(s);
}
}
}
Have you tried Environment.GetCommandLineArgs()?
Your problem (as Richard points out within his code) is that your arguments when you are running the release version are all enclosed in one set of quotes. Remove the quotes and it will work.