Get GH_Component help html source

For our documentation I’d like to grab the html help from each of our components.
However, GH_Component.HtmlHelp_Source is private and can’t be accessed.

At the moment I’m trying to create the help dialog for each of our components and get the html from there.
This is the code:

foreach (var objectProxy in Grasshopper.Instances.ComponentServer.ObjectProxies)
{
	if (objectProxy.Desc.HasCategory && objectProxy.Desc.Category == "ELiSE")
	{
		var component = objectProxy.CreateInstance() as GH_Component;
		if (component != null)
   		{
			var foo = new Grasshopper.GUI.HTML.GH_HtmlHelpPopup();
			try
			{
				foo.LoadObject(component as GH_DocumentObject);
			}
			catch
			{ }
			//foo.Show();
			string htmlSource = foo.BrowserControl.DocumentText;
		}
	}
}

I’d expect htmlSource to be the actual source but it turns out to be "<HTML></HTML>\0" all the time.

Is this a valid approach or shoud I look into something else?

Thanks in advance!
Paul

1 Like

Try reflection to get at the private member HtmlHelp_Source

1 Like

You can access private and internal marked members. It works because of Reflection. Google for it. :slight_smile:

1 Like

Wow, instantaneous replies…
Works nicely and I finally know what reflection is good for :slight_smile:

using System.Reflection
MethodInfo htmlSource =
    component.GetType().GetMethod("HtmlHelp_Source", BindingFlags.NonPublic | BindingFlags.Instance);
string html = (string)htmlSource.Invoke(component, null); 

Thanks for the hints @menno and @TomTom!

1 Like

Here’s some Reflection code from Grasshopper2, handy if you want to make an api which is smart enough to inspect code and keep it all strongly-typed. In general I’m trying to stay away from Reflection as much as possible, but for this esoteric stuff (and indeed for accessing fields you’re not supposed to access) it’s the only solution.

/// <summary>
/// Reflect this instance and add all conversion delegates to the ConversionServer type.
/// </summary>
/// <returns>Number of delegates added.</returns>
internal int RegisterAllDelegates()
{
  int count = 0;
  MethodInfo[] methods = GetType().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
  foreach (MethodInfo method in methods)
    if (IsValidConversionMethod(method))
      if (RegisterConversionMethod(method))
        count++;

  return count;
}
private static bool IsValidConversionMethod(MethodInfo method)
{
  if (method.ReturnType != typeof(Merit))
    return false;

  ParameterInfo[] parameters = method.GetParameters();
  if (parameters.Length != 3)
    return false;

  // Note-to-self: since compilers are not required to decorate 'out' parameters
  // with any metadata, maybe I shouldn't rely on the following three checks...
  if (parameters[0].IsOut) return false;
  if (!parameters[1].IsOut) return false;
  if (!parameters[2].IsOut) return false;

  // Validate the third parameter is of type string&.
  if (parameters[2].ParameterType != _refStringType)
    return false;

  return true;
}
private static bool RegisterConversionMethod(MethodInfo method)
{
  ParameterInfo[] parameters = method.GetParameters();
  Type type0 = parameters[0].ParameterType;
  Type type1 = parameters[1].ParameterType;

  // Get the non-reference flavour of the second parameter type.
  if (type1.HasElementType)
    type1 = type1.GetElementType();

  if (type1 == null)
    return false;

  return ConversionServer.AddConversionDelegate(type0, type1, method);
}
private static readonly Type _refStringType = typeof(string).MakeByRefType();
1 Like

I could also think of using pointers or pointers to pointers in conjunction with some sort of memory scanner. Just in case you are running into a language without reflection mechanism. Although its much more difficult (gc etc)