Use Internal Class in C# Harmony Patch - c#

I'm pretty new to c#, and I'm using Harmony patches to make a mod for a video game. The method I'm trying to patch is a private method which takes an internal class instance as a parameter. I've been able to use reflection to handle private methods in a few other patches, but when I try to add the internal parameter, I get a build error saying the class is inaccessible due to it's protection level.
I was trying to use the solution from this question, but I think I'm having some scope issues. Right now, I have something like
using System;
...
using System.Reflection;
using HarmonyLib;
using namespacesFromGame; // Including namespace where the internal is declared
...
namespace MyMod
{
[HarmonyPatch(typeof(GameClass))]
class MyPatch
{
Type MyInternal = typeof(GameClass).Assembly.GetType("GameInternal");
public static bool MethodPatch(GameClass__instance,..., MyInternal myInternal, ...)
{
...
}
}
}
When I try to do this, it tells me The type or namespace name 'MyInternal' cannot be found.
Where should I be putting my MyInternal declaration so it can be used as a parameter to MethodPatch, and so I will also be able to use the myInternal instance in the patch?

In C# you cannot declare the type of a property with another variable.
I see two solutions to this problem.
You can either do this :
using System;
...
using HarmonyLib;
using namespacesFromGame; // Including namespace where the internal is
namespace MyMod
{
[HarmonyPatch(typeof(GameClass))]
class MyPatch
{
public static bool MethodPatch(GameClass __instance,..., object myInternal, ...)
{
...
// do reflexion to access the method, the field and prop the object
}
}
}
This should work; but if you are new to C# the reflection may not necessarily be the easiest thing to do and it can quickly make your code unreadable.
Or do this :
You can publicise (make public) the dll you want to use. If you do this, you will have access to all classes, methods and ect... By doing this you will only have to use the desired type. But, you will have to compile your code in unstable.
For publicise, i found two github repo :
https://github.com/rwmt/Publicise
https://github.com/iRebbok/APublicizer
(you can also create your own but I think that to start it would be better to take one already made)
It will require republishing the assembly each time there is an update if your mod is outdated.
I also create modes on unity games. This is the solution I use and some FrameWork for modding uses to.
I don't know if there are performance impacts of using unstable code and calling private methods.
I advise you this solution, you would get cleaner code and you will have access to code more easily. But that is my personal opinion.

Related

Is it possible to export classes under a new namespace in C#?

I'm pretty new to C#, so I may just not know the terms to search for.
I'm trying to export a subset of classes available in a namespace to manage visibility.
High level - there's an assembly which provides the UnityEngine namespace. UnityEngine is huge. I'm writing various utility libraries which utilize a very small subset of the UnityEngine. A few points:
Including the "wrong" classes in the libraries results in code being untestable (and thus does not get tested).
The main purpose is it's clear to other developers when boundaries are being crossed.
I desire to use the UnityEngine types as opposed to my own so there's not a bunch of conversion. For example Vector2 is a type that should be used everywhere.
So given that, I'm trying to get something like the following to work, though other approaches are welcome too:
... Assembly UnityTypes.asmdef (my code)
... Depends on UnityEngine
namespace UnityTypes {
using Vector2 = UnityEngine.Vector2;
}
... Assembly PathFinding.asmdef (my code)
... Depends On UnityTypes (my code), NOT UnityEngine
namespace PathFinding {
using UnityTypes;
public class GraphBuilder {
public INode AddNode(Vector2 position, List<INode> edges);
}
}
... Assembly TheGame.asmdef (my code)
... Depends on Pathfinding and UnityEngine
namespace TheGame {
using UnityEngine;
public class PathFinder : MonoBehavior {
public void Awake() {
for (var waypoint : GetComponentsInChildren<IWaypoint>) {
_graph.AddChild(/* Vector2 */ waypoint.position, waypoint.destinations);
}
}
}
}
Are you trying to "change" the classes of an existing library?
I do not think that is possible or desirable.
You can inherit from those classes (given they are not marked as sealed) to implement your own behavior.
You can, however, not change existing classes.
It is a bit unclear to me what exactly you are trying to do here.
It sounds a bit like in your first script you want to do
namespace UnityTypes
{
using Vector2 = UnityEngine.Vector2;
}
and now you expect that wherever you do
using UnityTypes;
or even wrapping your new code in
namespace UnityTypes
{
}
the other static using is automatically applied.
This is not the case!
The usings in c# are per script.
An alternative sometimes might be using inheritance like e.g.
namespace UnityTypes
{
public struct GameObject : UnityEngine.GameObject { }
}
however, afaik most of the types you would be interested in will most probably be sealed or structs (like the Vector2).
=> Your users will simply have to use the correct usings and explicit types where necessary.
Now regarding the using: You only need it in those scripts where you explicitly use the type name.
So in your case GraphBuilder explicitly has a signature with Vector2 so here you either need the
using UnityEngine;
or have to explicitly write
UnityEngine.Vector2
and also need according assembly reference.
Then in your PathFinder you wouldn't need the
using UnityEngine;
when only accessing and passing the waypoint.position, but in your case you need the namespace anyway since MonoBehaviour etc are also part of it ;)
You still will need the assembly reference as soon as you access any member of a type that is defined in it, even if you only pass it on to somewhere else, you don't need to do that in case you only pass on the type which contain fields and properties etc where the type is used.

How do properly set up my common C# code so I can access them with a using directive?

I want to be able organize my most common code elements so that they can be "included" with a using statement similar to
using System;
using System.IO;
I am able to do this to a certain extent, but because of C# rules about namespaces and classes, I am unable to access my stuff directly, I always have to add it to a class and then access it by .item.
For example if I make a C# file named MyStuff.cs as follows:
namespace MyStuff
{
public static class MyClass
{
public static int MyCode(...)
{
...
}
}
}
I have to use it in my other code like this:
using MyStuff
...
int result = MyClass.MyCode(...);
...
I would like to be able to just access it like this (without the MyClass):
using MyStuff
...
int result = MyCode(...);
...
But I can't get that to work. I read somewhere that adding the "static" to the class they are in then the dot notation would not be needed, but that does not appear to be true.
Can anybody explain how to do this, or am I stuck with the extra layer?
Thanks!
I don't think it can be done.
In order to something to be accessed directly from within a namespace, it has to be an object. Functions cannot be directly put inside a namespace.
You can achieve what you want by making use of extension method by writing an extension method for instances of the 'Object' class (which is more of a hack). But you will still have to invoke it this way, this.MyCode().
Another solution is to have a superclass where you have this function MyCode() and inherit the place where you are going to use MyCode() from there but I am pretty sure that you have already considered something which is as basic as that.

Attempt to Add Instance of Class Fails

I'm trying to work with a C# USB HID library (Mike O'Brien's open-source HIDLibrary) from my VB.NET application. I was able to make calls to methods in one of the classes easily. However, another method I'd like to call into in a different class isn't working. I can't access it directly by invoking the namespace and class name like I did for the other class, as VS complains "that a reference to a non-shared member requires an object reference". The class is public, so I think I *should" be able to call into it, but OK, so I try to add an object reference in my app, and it complains "Overload resolution failed because no "New" is accessible". The two classes (one of which "works", the other of which doesn't) appear to be set up virtually identically: they're both declared public, as are both of the methods I'm trying to call into.
I'm still a bit new to C#, and OOP in general, so pardon the newb question.
The full classes are pretty big, so I'm going to attempt to only include the "relevant" parts, but if I'm leaving something out, please let me know.
How the class I'd like to access is defined:
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace HidLibrary
{
public class HidDevice : IHidDevice
{ ...
And the method itself I'm attempting to call:
public bool ReadFeatureData(out byte[] data, byte reportId = 0)
The way I've set up my VB.NET app to call into this dll:
Imports HidLibrary
Public Class DeviceIDTest
Dim HIDDeviceObj As New HidLibrary.HidDevice
And finally my attempt at a method call:
If HidDeviceObj.ReadFeatureData(InBuff, reportID) Then
Return InBuff
Else
Return False
End If
The constructor for HidDevice is internal, so it can only be called from other classes in the HidLibrary assembly, which is why you get the "Overload resolution failed because no "New" is accessible" error. Since the class is public, that probably implies that there is some other way to create instances. I have never used this library, but from a quick glance it looks like maybe you are supposed to use one of the static methods on HidDevices, i.e. GetDevice or one of the Enumerate methods, to access instances of HidDevice.
For example (completely untested code!):
Dim data() As Byte
For Each device In HidDevices.Enumerate()
If device.ReadFeatureData(data) Then
' Do something with data?
End If
Next

using MyProj; at the user class definition or MyProj.MyClass at the specific method(s)?

Ok, let me explain:
Whenever you start a class in .Net and let's say you have another project you've developed referenced which has non static class(es) you need to use...
You have 2 options (That I know of)
Place a using at the top of your class meaning you won't need to explicitly name the whole project each time you need it's classes
using FooBarProj;
public FooBar MyMethod()
{
FooBar fb = new FooBar();
//Do stuff
return fb;
}
Or do explicitly implement those:
public FooBarProj.FooBar MyMethod()
{
FooBarProj.FooBar fb = new FooBarProj.FooBar();
//Do stuff
return fb;
}
Does this make an effective difference at all? either efficiency at execution, compiling, whatever? or its simply a dev convenience issue?
It makes no difference after compiling the code.
Either using an using statement, or using the whole class namespace and name, is the same thing after all.
When compiling to CIL code, references to types are a single thing, each reference contains all the information, regardless of how you coded that reference.
Referencing a class with a different name
You can even rename a class if you whant, doing this:
using TheString = System.String;
Now you can refer to System.String using the name TheString in your code.
All of these produces the same compiled code
string
System.String
String (if you place using System; at the beginning)
TheString (if you place using TheString = System.String; at the beginning)
Conclusion: All using statements are just convenience.
I think it is a dev convenience issue. The first version is better in all circumstances other than there is a similar class name in different namespaces. The explicit namespace declaration before the class is needed when there are same class names in different namespaces like. Dog23.class1 and dog45.class1. Always the first version if it works. It is shorter and readable.

C# functions in common class not recognized in code-behind

Converting vb web project to c# using vwd express 2010.
Development system is a 64bit windows 7.
I have a commonly used function declared in an external cs file.
Contents of file "clsCommon.cs" =
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Mail;
namespace Project_Website
{
public class clsCommon
{
public void testA()
{
}
} // clsCommon
} // namespace
In code behind I try to access the function testA() as follows:
testA();
Also tried:
clsCommon.testA();
and
Project_Website.clsCommon.testA();
The actual example I want to use is more complicated, but this is the simplest example that manifests the problem(s) I enumerate below:
The As I type, Intellisense recognizes clsCommon, but doesn't think testA() is a method in it. Intellisense only sees two methods: Equals() and ReferenceEquals().
I ignore Intellisense and compile anyway, which produces the following error message:
Error 1 An object reference is required for the non-static field, method, or property 'Project_Website.clsCommon.testA()'
What is the root cause of this problem?
You either need to make that method static, or create an instance of the class clsCommon to use it, i.e.:
public class clsCommon
{
public static void testA()
{
}
} // clsCommon
Note that above method signature screams "side effects" since you don't return anything and are not passing anything in. If you are modifying other members or properties of the class, you should create an instance instead and use a member method:
var common = new clsCommon();
common.testA();
You should only make the method static if you do not need access to any other properties or methods with the clsCommon class. If that is the case for all of your methods, and clsCommon is just holding a bunch of utility methods, you should make the clsCommon class static as well.

Categories