"William DePalo [MVP VC++]" said:
Half of what you want to do is easy. Given a managed type (a.k.a class),
you can use reflection to determine the methods that it contains.
I cannot believe that I managed to figure out the answer to my own question
by trial and error, but I did.
The key requirements were that:
1. I be able to dynamically register a C# class that my COM object knows
nothing about other than the fact that it has an IDispatch interface. This
requirement precludes using a tool that exports an assembly to the registry
after the fact.
2. I be able to query (from within my COM object) whether or not a C# class
it has been handed as an interface pointer has a given method, by using a
string that is only known at run-time. This precludes using headers at
compile time.
3. That the COM object not in any way "depend" on the .NET framework being
installed. Although in this case I am using C# for testing purposes, the
mechanism was supposed to be generic enough that any COM object that exposes
IDispatch should be able to do what I wanted.
The solution is not exactly "pretty" but it works.
The reason I need this was so that a COM object implemented in unmanaged ATL
that has been imported into a .NET project via Interop could call-back into
the .NET side.
Here is a short snippet of the C#-side of the solution I came up with:
BEGIN
using System;
using METASCOMLib;
using MetaSLua;
using System.Runtime.InteropServices;
using System.Reflection;
namespace CSharpExample
{
[Guid("03911914-DCF0-4caf-A654-5897ED3DC060")]
public interface ITest
{
void b_event();
}
[ClassInterface(ClassInterfaceType.AutoDual)]
public class CSharpEventHost : ITest, IMetaXParserEventHost
{
public CSharpEventHost()
{
}
public Guid ClassGUID
{
get
{
return new Guid("03911914-DCF0-4caf-A654-5897ED3DC060");
}
}
[DispId(15)]
public void b_event()
{
Console.Out.Write("b_event fired\n");
}
}
class ApplicationClass
{
[STAThread]
static void Main(string[] args)
{
RegistrationServices rs = new RegistrationServices();
bool b = rs.RegisterAssembly(Assembly.GetExecutingAssembly( ),
0);
MetaXGrammar gmr = new MetaXGrammar();
gmr.LuaCallbacks = new LuaEvents(Console.Out, Console.Error);
gmr.SetLuaDebug(true, false, false);
gmr.EventHost = new CSharpEventHost();
gmr.Grammar =
"grammar X host Lua {" +
" S ::= a b c;" +
" a ::= '[0-9]+';" +
" b ::= '[a-z]+';" +
" c ::= '[0-9]+';" +
" @function_impls = :{ " +
" function c_event (N)\n" +
" if (N:MATCHED()) then\n" +
" print(N:LEXEME())\n" +
" end\n"+
" end" +
" }:;" +
"};";
if(gmr.IsGood)
{
IMetaXScanInfo r = gmr.Scan("12345 abc 67890 123abc456");
if(r.MatchLength > 0)
{
Console.Write("Lexeme = \"" + r.Lexeme + "\"\n");
Console.Write(gmr.Root.GetChildByPath("S/b").Lexeme + "\n");
}
}
else
{
Console.Write("Error: Grammar could not be compiled!\n");
}
rs.UnregisterAssembly(Assembly.GetExecutingAssembl y());
}
}
}
<< END
As you can see, "b" in the run-time compiled grammar is known only at
run-time, and therefore that "b_event" exists in CSharpEventHost must be
determined at run-time. The grammar could, after all, have been loaded from
a text file.
The tricky part is knowing how to query gmr.EventHost from within the ATL
code that implements METAXCOMLib. I figured it out by trial and error to be
(please pardon the hacky nature of the following C++ -- it was for
proof-of-concept. I'll clean it up later.)
STDMETHODIMP CMetaXGrammar::put_EventHost(IMetaXParserEventHost * newVal)
{
m_pEventHost = newVal;
if(m_pEventHost != NULL)
{
DISPID id = 0;
LCID lid = LOCALE_SYSTEM_DEFAULT;
UINT n = 0;
m_pEventHost->GetTypeInfoCount(&n);
if(n != 0)
{
GUID iid = {{0}};
m_pEventHost->get_ClassGUID(&iid);
IUnknown* p = NULL;
m_pEventHost->QueryInterface(iid, (void**)&p);
if(p != NULL)
{
IDispatch* pID = NULL;
p->QueryInterface(IID_IDispatch, (void**)&pID);
if(pID != NULL)
{
ITypeInfo* pTI = NULL;
HRESULT hr = pID->GetTypeInfo(n, lid, &pTI);
MEMBERID mid = 0;
CComBSTR sN("b_event"); // this could be any string
hr = pTI->GetIDsOfNames(&sN, 1, &mid);
if(hr == S_OK)
{
DISPPARAMS dp = {0};
hr = pID->Invoke(mid, IID_NULL, lid, DISPATCH_METHOD, &dp, NULL, NULL,
NULL);
}
pTI->Release();
pID->Release();
}
p->Release();
}
}
}
return S_OK;
}
--
Quinn