the C# harmony documentation: https://github.com/pardeike/Harmony/wiki/Prioritiy-annotations
my question is that not able to run the C# harmony example successfully
the postfix annotation didn't get injection log printed as expected after Class and method get patched that i didn't see "injection logs" get printed.
c# code example below. Can someone help me find the issue
you may paste into https://dotnetfiddle.net/ to debug it.
using System;
using System.Collections.Generic;
using System.Reflection;
using Harmony;
public class Program
{
public static void Main()
{
var harmony = HarmonyInstance.Create("net.example.plugin");
harmony.PatchAll(Assembly.GetExecutingAssembly());
Program p = new Program();
p.Bar();
}
[HarmonyPostfix]
[HarmonyPatch(typeof(Program), "Bar")]
public static void Postfix_Bar(){
Console.WriteLine("postfix Bar log"); // this never gets printed as expected.
}
[HarmonyPostfix]
[HarmonyPatch(typeof(Program), "Foo")]
public static void Postfix_Foo(ref string res){ //however, this gets error res could not be found. https://github.com/pardeike/Harmony/wiki/Prioritiy-annotations
Console.WriteLine("postfix Foo log");
res = "new value";
}
public void Bar() {
Console.WriteLine("Hello World !!! ");
}
static string Foo()
{
return "secret";
}
}
The main problem is that PatchAll() looks for classes that have at least on [HarmonyPatch] annotation. Your patches are in the class Program which does not have that annotations. This is the main problem in your example.
Solution: either annotate your Program class like this:
[HarmonyPatch]
public class Program
{
}
or create a new class that has that annotation.
The second problem I can see is that Postfix_Foo(ref string res) uses res which does not follow the documented standard for naming patch arguments. It can either be the name of an argument that the original method has (it has no arguments) or refer to the result, which requires it to be named __result.
The comment about priority annotations is misplaced too because they only apply to multiple patches to the same original method.
Finally, you call Bar() after patching which means that Foo is never called - not sure if that’s intended.
Related
My understanding is that it is similar to write code directly into the old "static void Main(string[] args)" without the need to display what's above.
However, I used to declare my variables in the class Program to have them accessible from other classes (apologies if not best practice, I learnt C# by myself and as long as it works, I'm happy with my code).
See example below:
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
namespace myNameSpace
{
class Program
{
//variables declaration
public static string abc = "abc";
public static int xyz = 1;
static void Main(string[] args)
{
//code goes here
}
}
}
With C# 9, it seems I can only declare variables in the Main section, so how can I declare them to be able to access them from other classes?
I don't think the currently-accepted answer is correct, if you toss a partial class signature in Program.cs you can most certainly add things like static-scoped fields and attributes:
var customAttributes = (CustomAttribute[])typeof(Program).GetCustomAttributes(typeof(CustomAttribute), true);
Console.WriteLine(customAttributes[0].SomePropery);
Console.WriteLine(MyField);
[Custom(SomePropery = "hello world")]
public partial class Program
{
private const string MyField = "value";
}
class CustomAttribute : Attribute
{
public string SomePropery { get; set; }
}
The above code and nothing else in Program.cs will output:
/home/dongus/bazinga/bin/Debug/net6.0/bazinga
hello world
value
Process finished with exit code 0.
I use this method to apply the [ExcludeFromCodeCoverage] attribute to my projects' Program.cs files
For .Net 5:
When you use the Top-Level Program feature of C# 9, you give up the ability to put anything outside the Main method scope. Fields, properties, attributes on the Main method or Program class, setting the namespace, changing the class name, etc are all no longer available (the only exception is "importing" namespaces with using lines).
If that limitation doesn't work for you, don't use the feature.
For .Net 6 and higher, use dongus's answer.
My understanding is that it is similar to write code directly into the old "static void Main(string[] args)" without the need to display what's above.
However, I used to declare my variables in the class Program to have them accessible from other classes (apologies if not best practice, I learnt C# by myself and as long as it works, I'm happy with my code).
See example below:
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
namespace myNameSpace
{
class Program
{
//variables declaration
public static string abc = "abc";
public static int xyz = 1;
static void Main(string[] args)
{
//code goes here
}
}
}
With C# 9, it seems I can only declare variables in the Main section, so how can I declare them to be able to access them from other classes?
I don't think the currently-accepted answer is correct, if you toss a partial class signature in Program.cs you can most certainly add things like static-scoped fields and attributes:
var customAttributes = (CustomAttribute[])typeof(Program).GetCustomAttributes(typeof(CustomAttribute), true);
Console.WriteLine(customAttributes[0].SomePropery);
Console.WriteLine(MyField);
[Custom(SomePropery = "hello world")]
public partial class Program
{
private const string MyField = "value";
}
class CustomAttribute : Attribute
{
public string SomePropery { get; set; }
}
The above code and nothing else in Program.cs will output:
/home/dongus/bazinga/bin/Debug/net6.0/bazinga
hello world
value
Process finished with exit code 0.
I use this method to apply the [ExcludeFromCodeCoverage] attribute to my projects' Program.cs files
For .Net 5:
When you use the Top-Level Program feature of C# 9, you give up the ability to put anything outside the Main method scope. Fields, properties, attributes on the Main method or Program class, setting the namespace, changing the class name, etc are all no longer available (the only exception is "importing" namespaces with using lines).
If that limitation doesn't work for you, don't use the feature.
For .Net 6 and higher, use dongus's answer.
I read the book "CLR via C#" by Jeffrey Richter. In chapter 20, there is a code example demonstrating usage of Constrained Execution Regions (CERs):
private static void Demo2() {
// Force the code in the finally to be eagerly prepared
RuntimeHelpers.PrepareConstrainedRegions(); // System.Runtime.CompilerServices namespace
try {
Console.WriteLine("In try");
}
finally {
// Type2’s static constructor is implicitly called in here
Type2.M();
}
}
public class Type2 {
static Type2() {
Console.WriteLine("Type2's static ctor called");
}
// Use this attribute defined in the System.Runtime.ConstrainedExecution namespace
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static void M() { }
}
And the following text:
Now, when I run this version of the code, I get the following output.
Type2's static ctor called
In try
But when I run this code I get the following output no matter if I use CERs or not:
In try
Type2's static ctor called
So, Type2 constructor is getting called after try block (breaking the meaning of CERs usages, as I understand it).
What can be possible reason of this?
EDIT:
I'm using .Net Core 3.1 and VS Enterprise 2019 Preview
Version 16.6.0 Preview 3.0, my code is:
using System;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
public sealed class Program
{
public static void Main()
{
Demo2();
}
private static void Demo2()
{
RuntimeHelpers.PrepareConstrainedRegions();
try
{
Console.WriteLine("In try");
}
finally
{
Type2.M();
}
}
}
public class Type2
{
static Type2()
{
Console.WriteLine("Type2's static ctor called");
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static void M() { }
}
By the way, Type2 class can be inside the Program, it does not change the output.
(This is not the full answer to what is actually going on here, but it might help you)
Your problem is most likely that you run your process from within Visual Studio (using CTRL+F5). It is then launched via VsDebugConsole.exe, which then starts your process (also see this).
For whatever reason - that would need to be researched - it looks as if CERs are not honored when running in this way.
What you could do to validate, is attempt to run your program directly from the command prompt / console.
Here is a quote from Microsoft's article about Constrained Execution Regions:
CER is only supported in .NET Framework. This article doesn't apply to
.NET Core or .NET 5 and above.
I have just started doing the tasks on exercism.io and the first one is to make Hello World print out. When I have done it in the past, I just put Console.WriteLine("Hello, World!"); into the curly brackets. This time though I get an error saying not all code paths return a value.
The template for my code is different this time, it has
using System;
public static class HelloWorld
{
public static string Hello()
What do these two lines actually mean and how comes Console.WriteLine doesn't play nice with them?
What's the difference between that one and
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
How comes the template doesn't have a namespace?
using System;
public static class HelloWorld
{
public static string Hello()
{
return "Hello, World!";
}
}
is this the best way to implement "Hello, World" with this template?
What do these two lines actually mean and how comes Console.WriteLine doesn't play nice with them?
Console.WriteLine doesn't return any data. It has the return data type of void.
Your first method signature is
public static string Hello()
Which means that the method does not take any parameters and returns a string. If your full method looks like this:
public static string Hello()
{
Console.WriteLine("Something");
}
You'll get your "Not all code paths return a value" error. This is because the method is not returning a value, even though you've told the compiler it should be.
What's the difference between that one and [one which uses void]
Again, look at the signatures of the methods:
public static string Hello()
static void Main(string[] args)
The main difference you're interested in is one returns string, and the other returns void.
How comes the template doesn't have a namespace?
That's a great question. It's up to how the website architectures their code process. My guess it that it wraps your code file in a namespace behind the scenes when you submit it.
In Windows 8 it worked fine.
When I upgraded it to Windows 8.1 I got an error:
Error 1 The call is ambiguous between the following methods or properties: 'System.IO.WindowsRuntimeStreamExtensions.AsRandomAccessStream(System.IO.Stream)' and 'EventHandler.UI.Extensions.StreamExtensions.AsRandomAccessStream(System.IO.Stream)'...
Here is my Method that gets the error:
public BitmapImage ConvertByteArrayToBitMapImage(byte[] imageByteArray)
{
BitmapImage bitmapImg = new BitmapImage();
MemoryStream memStream = new MemoryStream(imageByteArray);
var randomAccessStream = memStream.AsRandomAccessStream(); //This line has error.
bitmapImg.SetSourceAsync(randomAccessStream);
return bitmapImg;
}
Can someone help me?
Thanks.
You can fix your problem by using the full namespace:
var randomAccessStream =
System.IO.WindowsRuntimeStreamExtensions.AsRandomAccessStream(memStream);
As it's an extension method, you can call it the way the code shows.
What is going on is that AsRandomAccessStream exists in more than one namespace being in scope . The compiler can't know which one you are referring to. You have two options:
Remove the namespace that you do not need that also contains AsRandomAccessStream
Specify the complete path to AsRandomAccessStream like System.IO.WindowsRuntimeStreamExtensions.AsRandomAccessStream
My guess is that EventHandler.UI.Extensions.StreamExtensions.AsRandomAccessStream was possibly added by the update and System.IO.WindowsRuntimeStreamExtensions.AsRandomAccessStream is the one you were using already.
AsRandomAccessStream is an extension method, and you can't cast a method to some namespace. So you can't do something like object.ExtensionMethod() from MyNameSpace.ExtensionMethods or so, for as far I know... If it is actually possible, I would like to know myself! So you can only call this extension method like any other regular static class method.
Little example code never hurts:
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Example NewExample = new Example();
//NewExample.DoSomething(); //Ambiguous error
ExtensionClass1.DoSomething(NewExample); //OK
}
}
public class Example
{
}
public static class ExtensionClass1
{
public static void DoSomething(this Example A)
{
}
}
public static class ExtensionClass2
{
public static void DoSomething(this Example A)
{
}
}
}