.Net Reflection: find the parent 2 generations up the chain
I'm new to System.Reflection and I've stumbled on the following.
I am using a third party executable, A, that has a custom interpreter engine class B that allows me to build simple 'tasks' that run on a mobile device. 'A' also has a class C that, when using A's default interface, shows a list of 'tasks' that are running within the interpreted runtime environment.
'A' also allows me to write custom functions embedded in .Net assemblies that can be l开发者_开发知识库oaded at runtime to extend the capabilities of interpreter B. Let's call my assembly D.
In execution, 'A' invokes 'B' to process my task that uses 'D'. Due to some issues with this runtime system, I sometimes get multiple copies of my task running that causes all sorts of problems.
I'd like to somehow get access to C from inside my assembly D so that I can find out what tasks are running and take some action based on that. For example, if I've already got a certain task running, I don't need to start another one, I want to resume the existing one.
The vendor is unhelpful in explaining (as in, not forthcoming at all) as to how I might approach this, so I figured I could probably figure it out myself. I've gotten as far as being able to get a reference to the Parent assembly (System.Reflection.Assembly.GetCallingAssembly()), but that only gets me to B. In order to get a reference to C, I need to back up one more level in the chain to find a reference to A, such that I can instantiate an instance of C to get at the list of running tasks.
This sounds an awful lot like an operating system scheduler, and in fact it's startlingly similar, however, the application is a database centric mobile application that allows non-programmers to quickly and easily deploy mobile apps. This gives rise, I'm sure, to the vendor being reluctant to share with me how this might be done, support issues and all that.
Does this make any sense at all?
This makes sense; but I'm wondering if a simpler approach is possible. It sounds like you don't want two instances of your custom task extension, D, to run concurrently. Is it possible to abort your task in the definition of D if you detect that one is already running? I'm a little surprised that the API for A doesn't provide a handle for you to get the currently running tasks; but you could use a thread-safe static class or some other mechanism specific to D in order to track it when it starts and finishes its execution. Does this help any?
Too much for a comment. :)
I wasn't very precise in my original question in one respect. It's not that I don't want two copies of D to be running (although that certainly is true that I do not), but rather that I have the capability of 'suspending' D (or one of several other tasks) and subsequently resuming it.
Our application written on top of the vendor's product allows farmworkers (vineyard workers, actually) to take 'measurements' about grapes. This task is called Measurements. We have a second task that allows them to take GPS readings for geographical entities - we call this Map Features. Our use model is such that our users can switch between these two tasks throughout the day by using the 'put task on hold' feature of the platform.
In theory, and at least partly in practice, is that I can 'put on hold' the measurements task and 'resume' the map features task as needed. Since when each task is first started I can find out what the task ID is, the vendor has recommended (in absence of an API to query their task list) that I keep my own, parallel, list of TaskIDs that are started in a db table.
This, of course, is a recipe for ugly since if something should happen and their list and mine gets out of sync, I may try to resume a task that doesn't exist (i.e., my list has a task ID for X that is different from the task ID for X that they have.) In fact, this is precisely the problem that we're experiencing.
I could solve this problem if attempting to resume a task ID of '123' returned an error. However, there is no mechanism in this environment to do such a thing - it fails silently.
One might argue that this is a fundamental flaw - failures should seldom if ever be silent. However, given that their target audience is not programmers but line-of-business people, they'd say that I'm pushing the envelope and my use is not supported. :(
I've found the class using RedGate's reflector in which their list is stored, and it seems to me that if I could only get a reference to that instance of C, I could then find out what is really running and what is not. I have no idea whether that is possible, and hoped that reflection would give me a solution.
If using the dotNet reflector you can find either a proprety or field that contains the object you want to view, you can try the following where widget is an instance of class that contains the link to C and name is the field name:
///<summary>Get a field value</summary>
///<param name="widget">The widget to search</param>
///<param name="name">The name of the field</param>
///<returns>The value assigned to the field</returns>
public static object GetField( object widget, string name ) {
FieldInfo pi = widget.GetType().GetField( name );
if ( pi == null )
return null;
return pi.GetValue( widget );
}
///<summary>Get a property value</summary>
///<param name="widget">The widget to search</param>
///<param name="name">The name of the property</param>
///<returns>The value assigned to the property</returns>
public static object GetProperty( string name ) {
PropertyInfo pi = widget.GetType().GetProperty( name );
if ( pi == null )
return null;
return pi.GetValue( widget, null );
}
In retrieving the PropertyInfo or FieldInfo, you should also look at changing some of the System.Reflection.BindingFlags to see if your result will show up.
GetProperty( name, BindingFlags.NonPublic| BindingFlags.Instance | BindingFlags.DeclaredOnly );
You might try BindingFlags.Public or others to see if it will show up. But it really comes down to finding exactly what object you are trying to recover and how it is referenced.
精彩评论