Jump to content

C# Reflection Question


Recommended Posts

I'm trying to write a reflection wrapper for Mechjeb, to use with kRPC...  and it's my first foray into reflection.  I've got methods working fine...  But I'm having trouble with properties?   In the wrapper I'm doing this:  


   

    public static bool ExecStatusGet()
        {
            System.Type ExecutorType = AssemblyLoader.loadedAssemblies
                  .Select(a => a.assembly.GetExportedTypes())
                  .SelectMany(t => t)
                  .FirstOrDefault(t => t.FullName == "MechJebModuleNodeExecutor");

            PropertyInfo pi = ExecutorType.GetProperty("enabled");
            return (bool)pi.GetValue(null, null);
             
        }

        public static void ExecStatusSet(bool stat)
        {
            System.Type ExecutorType = AssemblyLoader.loadedAssemblies
                  .Select(a => a.assembly.GetExportedTypes())
                  .SelectMany(t => t)
                  .FirstOrDefault(t => t.FullName == "MechJebModuleNodeExecutor");

            PropertyInfo pi = ExecutorType.GetProperty("enabled");
            pi.SetValue(null, stat, null);
        }

Then over in the actual service class that's accessible in the game doing one of these...
     

  [KRPCProperty]
        public static bool ExecutorStatus
        {
            get
            {
                return MJWrapper.ExecStatusGet();
            }
            set
            {
                MJWrapper.ExecStatusSet(value);
            }

        }

But I'm getting an "Object reference not set to an instance of an object" error at the end of it.  I'm sure I'm missing something...  but it's basically the same way I'm  getting access to methods -  the following code is working:

 

 public static void hohmann()
        {
            System.Type OrbitalManeuverCalculatorType = AssemblyLoader.loadedAssemblies
                   .Select(a => a.assembly.GetExportedTypes())
                   .SelectMany(t => t)
                   .FirstOrDefault(t => t.FullName == "MuMech.OrbitalManeuverCalculator");
            MethodInfo ReflectedMethod = OrbitalManeuverCalculatorType.GetMethod("DeltaVAndTimeForHohmannTransfer", BindingFlags.Public | BindingFlags.Static);
            object[] parameters = { vessel.orbit, target.GetOrbit(), Planetarium.GetUniversalTime(), null };
            Vector3d deltav = (Vector3d)ReflectedMethod.Invoke(null, parameters);
            double time = (double)parameters[3];
            vessel.PlaceManeuverNode(vessel.orbit, deltav, time);
        }

Thanks for any help you come up with!

Link to comment
Share on other sites

(bool)pi.GetValue(null, null);

You need an instance of the module to replace that first null. So you would have to do in reflection 

MechJebCore mechjebCore = vessel.getMasterMechjeb();

MechJebModuleNodeExecutor nodeExecutor = mechjebCore.node;

And then do your 

(bool)pi.GetValue(nodeExecutor , null);

(same thing for set)

 

I am quite sure someone already do something similar but I can't remembrer where.

 

 

 

Link to comment
Share on other sites

I've messed around with reflection some and I'm not going to try posting code on my mobile (it would be wrong), I'm pretty sure I know what's going on and agree with sarbian, I'm just going to be more verbose about it as I got stuck at the same place and found it quite counter-intuitive myself as well.

What I'm pretty sure is tripping you up is this line:

PropertyInfo pi = ExecutorType.GetProperty("enabled");

This line returns 'pi' as a PropertyInfo object which described the "enabled" property. It does not include a reference to a specific object, it just describes "enabled".

Therefore:

return (bool)pi.GetValue(null, null);

fails because pi has no object reference, it merely described the "enabled" field. You need to pass a reference to a specific "MechJebNodeExectuor" object inside the GetValue method to tell it which actual object you are searching for the "enabled" property in, as described by the 'pi' object.

Hopefully that's clear enough on what's happening.

D.

Link to comment
Share on other sites

That... does make sense.  I'm treating the computer module like it's static, when it's not.   I think I know where to start now...  maybe...  will post working code when I get it.  Or... the next disaster when I have it. :P

Link to comment
Share on other sites

On 3/8/2016 at 2:08 PM, artwhaley said:

That... does make sense.  I'm treating the computer module like it's static, when it's not.   I think I know where to start now...  maybe...  will post working code when I get it.  Or... the next disaster when I have it. :P

You can also take a look at a few mods that implement reflection apis such as DeepFreeze, ShipManifest,  These should give you examples of how to do it.

Link to comment
Share on other sites

On 9/3/2016 at 7:08 AM, artwhaley said:

That... does make sense.  I'm treating the computer module like it's static, when it's not.   I think I know where to start now...  maybe...  will post working code when I get it.  Or... the next disaster when I have it. :P

Like Papa_Joe suggested...
I have written several wrappers already in DeepFreeze, Ship Manifest, Roster Manager, and AmpYear mods.
Look here for some examples: https://github.com/JPLRepo/DeepFreeze/tree/master/Source/APIs
First:

PropertyInfo pi = ExecutorType.GetProperty("enabled");
            return (bool)pi.GetValue(null, null);

 

To get the property you need to also specify the bindingflags on your getproperty.
And like others have said, to get the value you need the actual instance of the class. which depends on what kind of class it is.. singleton or not...
Usually if it's not, there needs to be a static "Instance" property which is actually set to the instance of the class that you can retrieve.
See Sarbian's response above for your specific problem with MJ.


If this is not clear, or from the examples drop me an PM or reply here.

Edited by JPLRepo
Link to comment
Share on other sites

  • 1 year later...

I'm going to violate the laws of the internet and necro a year old post... but...  I've necro'ed the project so it seems only fitting.   

I am just ramming my head into the wall with my attempt to move my KRPC >>  Mechjeb interface service from hard linked to soft linked using reflection.  I KNOW it's not a complicated concept... but...  every implementation I can find of it is too 'elegant' for me to be able to take apart and understand - utilizing delegates and such and...  blah.  I'm frustrated.  

 

I've cut my code down to the bare minimum in a test project.  I have accessing a static method in the orbital maneuver calculator module working just fine.  But everything I've tried to grab the right instance of a dynamic method or property hasn't worked, so I've deleted all of that code.  

Here's what I THINK I'm missing -

  1. In the .GetCore() method of the wrapper I need to somehow get my hands on either the core's MasterMechJeb property, or the active vessels GetMasterMechJeb() method (from the mechjeb vessel extensions...so reflected.  
  2. The body of the ExecuteOne() method - which I think should call GetCore() to get the active mechjeb core, then reflect that core's instance of MechJebModuleNodeExecutor then grab the method info and invoke the ExecuteOneNode method.
  3. And the body of the get for the ExecStatus property... which I think needs to do largely the same thing - GetCore to grab the active core, then grab the instance of the Node Executor module, then grab the instanced property info for the .enabled property.

 

What I've got working to get the static method working is the approach from 'gravity turn.'  And like I said, I think it's skipping the step to find a master mechjeb module... and I think that's important on ships with more than one mechjeb part.   I've TRIED to digest the Mechjeb Wrappers in Persistent Rotation and RasterPropManager...   but... they're just over my head.  I feel like I've been CLOSE.... but...  I can't figure out what I'm doing wrong.   I'm just in over my head trying to learn 3 things at once and not sure which I'm screwing up.  I hate to dump a DO THIS FOR ME PLEASE post here...  but I'm hoping by cutting this down to just the three methods it might be easy enough for someone to type up or point me towards a simple enough example or c# tutorial that will explain it in clear enough terms I can finally get it.   

 

I've posted just the mechjeb wrapper to Gist...  here- https://gist.github.com/artwhaley/f5bb453b8ed18fa89b53e47ffde46407

AND the whole cut down project here - this includes the wrapper but also the addon code, the krpc service, and the orbital extensions from MJ I've borrowed to make it work temporarily here - https://gist.github.com/artwhaley/b5d1770fd6a0389920620a6a17bd2166

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...