I have a stand-alone application which exposes COM Interfaces/CoClasses/Enums, etc. One of those interfaces exports the following function:
procedure FindTask(const TaskId: WideString; var Task: OleVariant); safecall;
Note: Task is also exposed as a CoClass.
Note: Task is [in, out]
So far, my legacy application, which is also written in Delphi, had been able to communicate with the stand-alone application. Consider that stand-alone app as an out-of-proc COM server and consider this legacy app as a client app.
As I am rewriting the old Delphi app (client) in C#, I have to use COM Interop in C# to communicate with that application. Therefore, I made use of tlbimp.exe to translate those COM interfaces to C# interfaces.
That method was translated as following:
void FindTask(string, TaskId, ref object Task);
How can I call FindTask and receive the result of an already existent task with this signature?
I've tried this: (Task is the CoClass type)
ScTask target = new CsTask();
scheduler.FindTask("A GUID value", ref target);
But the compiler yelled about the type, since there is no overload with that class type (the translated method receives an object)
Could I do this, instead?
object target = new object();
scheduler.FindTask("A GUID value", ref target);
ScTask translated = (ScTask) target;
How can I call a method that expects an var (in, out) Variant *, therefore translated as object, but that actually have a more precise type?
It rather looks to me as though the parameter is really an out parameter. You don't actually appear to be attempting to pass anything in. The method appears to be returning a different object. So in the C# it ought to be:
void FindTask(string TaskId, out object Task);
You'd then call it like this:
You'd need to change the type library to specify out semantics.
If it really is in/out then you'd keep the use of ref and call the method like this:
object target = null;
scheduler.FindTask("A GUID value", ref target);
ScTask translated = (ScTask) target;
Or possibly pass in an actual object with meaningful state if that's how it is designed to work.
Related
I have a C# assembly that does some work and sends the results of the work back to a C++ core. I am trying to use Reflection to pass it back since the C# assembly runs on a different thread than the one it was initialized by from the C++ core. I have tried using the COM interface as the parameter type.
IDL:
HRESULT SendEvent([in] IEventData *pEventData);
C#:
WECOInspectionCoreIDL.IEventData eventData = new EventData() as WECOInspectionCoreIDL.IEventData;
var parameters = new object[1];
parameters[0] = eventData;
_piInspectionCore.GetType().InvokeMember("SendEvent", BindingFlags.InvokeMethod, null, _piInspectionCore, parameters);
This gets the error "0x80020005 Type mismatch" in the atlcom.h hRes = m_pInfo->Invoke(...) call which apparently eventually gets converted to "Unable to cast COM object of type 'System.__ComObject' ... No such interface supported.
I've also tried making the parameter an IDispatch* and then the call goes through to C++, but it doesn't seem to be the real object.
IDL:
HRESULT SendEvent([in] IDispatch *pEventData);
C++:
STDMETHODIMP CInspectionCore::SendEvent(IDispatch *pEventData)
{
IEventData *pIEventData = (IEventData *)pEventData;
Even calling pIEventData->GetIDsOfNames() fails.
Is there a way to pass a COM object created in C# to C++ when the C# object is called from a different C++ thread?
With COM, you should never cast a COM interface into another COM interface like this:
STDMETHODIMP CInspectionCore::SendEvent(IDispatch *pEventData)
{
IEventData *pIEventData = (IEventData *)pEventData; // wrong!
}
Instead you must use QueryInterface, this works fine:
STDMETHODIMP CInspectionCore::SendEvent(IDispatch *pEventData)
{
IEventData* pIEventData;
HRESULT hr = pEventData->QueryInterface(&pIEventData);
if (FAILED(hr)) // etc.
}
In some cases (in fact often), raw casting may work which can give the false impression that it's ok.
In your case, it doesn't work because you use different threads which creates implicit proxies (COM apartments, etc.). You can see that if you breakpoint in SendEvent have a look at the call stack when it's called, it's all COM marshaling stuff.
In C# I can do this:
delegate void myFunctionDelegate<T>(T arg);
In C++, I understand that I need to use an alias to a template for a function pointer, but the syntax is so bizaare that all of the examples I find just confuse me more.
The following is wrong; how can I correct it?
template<typename T>
using myFunctionDelegate = void (*)(T arg);
I want to use it like so:
template<class T> class Foo
{
...
void someOtherFunction(myFunctionDelegate callback)
{
...
callback(someValue);
}
}
and then:
myClassInstance.someOtherFunction([&](T arg) {
// do something with the callback argument
});
What you have almost works syntactically; the use of myFunctionDelegate simply needs a type argument:
void someOtherFunction(myFunctionDelegate<T> callback)
^^^
And the alias parameter names are optional if you aren't getting any particular benefit from them:
template<typename T>
using myFunctionDelegate = void(*)(T);
However, there is a larger problem: function pointers don't handle state. The lambda used in your sample call uses state by the capturing it does. Thus, a capturing lambda cannot be converted to a function pointer. When it's so handy to pass in such a lambda, function arguments should support that.
There are two common ways of doing so. The first is to forget about forcing a specific return and parameter type. Instead, let the caller pass any object (lambda, function pointer, functor, the result of std::bind) that can be called the way your function calls it:
template<typename Callable>
void someOtherFunction(Callable callback) {
...
callback(someValue);
}
If the call doesn't work, the code will fail to compile1 (with an error that unfortunately isn't too helpful, but the future Concepts additions can easily help there).
On the other hand, you might want to explicitly specify the function type. C++ has a general-purpose type to store any callable object (see the above list). That type is std::function. It's a bit more heavyweight than a simple template parameter, but useful when you need it.
template<typename T>
using myFunctionDelegate = std::function<void(T)>;
void someOtherFunction(const myFunctionDelegate<T> &callback) {...}
[1]: This isn't always true (see SFINAE), but it probably will be as far as you're concerned.
std::function<void(T)> myFunctionDelegate is the (very) rough equivalent of delegate void myFunctionDelegate<T>(T arg)
std::function<void(T)> follows value semantics (it behaves more like an int than a C# object reference) which makes things different.
A lambda closure ([](T t){/*code*/}) whose lifetime (or copies of it) outlives the local scope should not use & based capture. Instead use = based capture (which may require extra work). If the code you are calling does not store a copy of the delegate beyond the lifetime of the call, [&] is optimal. In C++ the lifetime of data is something you need concern yourself with.
This is not intended as a full tutorial on how lambdas and std::function work, but just to point you in the right direction.
I'm writing a C# COM dll that will be used by both Managed C# as well as Delphi and C++ programs and javascript. The COM dll includes a monitor part where the application registers a function that is supposed to return a string value to the dll. I have done this before for javascript 'applications' where you would simply pass a function as a parameter to the dll. When the dll needs to query the javascript 'application' the following is run:
Type t = theScript.GetType();
object ret = t.InvokeMember("", BindingFlags.InvokeMethod, null, theScript, new object[] { });
'theScript' is stored as an Object in the C# dll. As far as I understand this is accomplished by IDispatch. My theory now is that I should be able to use the same approach for the other languages. So I made a COM visible method with this signature:
void RegisterQuery(object method);
However I can't figure out how to pass a parameter to this method from C#. I've tried using a delegate but calling the delegate simply returns the ToString() method from the delegate (the one that returns the calss name). Nothing else that I have tried even compiles.
So I have two questions:
How shall I pass a method parameter to this dll from C# so that the dll will be able to call the methods, in the manner specified above, when it needs to?
Is this really IDispatch or is there another reason why it works for javascript?
Thanks in advance
Turns out that this was really simple:
Type t = theScript.GetType();
object ret = t.InvokeMember(theObject, BindingFlags.InvokeMethod, null, theScriptName, new object[] { });
Trying to create a mapper for an Microsoft Office object to POCO's and found this
// doesn't work
// returns an empty array where o is a RCW on an office object
foreach(var pi in o.GetType().GetProperties() )
tgt.SetValue(rc, pi.GetValue(o, null));
so have to resort to this
foreach(var field in tgt.GetFields() ){
var pv = o.InvokeMember(field.Name, System.Reflection.BindingFlags.GetProperty, null, o, null);
i.SetValue(rc, pv);
}
which works for now but wondering why the RCW.GetProperties() doesn't work here?
The other two answers as of this writing are correct, but they miss an important opportunity to explain how the late binding of a COM object looks in terms of the .NET type system. When you call GetType on the COM object, the return value is the __ComObject internal type, not the COM interface type that you normally work with when writing interop code. You can see this in the debugger, or with some code like Console.WriteLine(o.GetType().Name);.
The __ComObject type has no properties; that's why you get an empty array when you call o.GetType().GetProperties(). (At least some things in life make sense!)
If you decompile the InvokeMember method, you'll find that it has special handling for COM objects, delegating the call to an internal native method. For "regular" .NET objects, the method uses "regular" .NET reflection, retrieving the appropriate MemberInfo for the requested member, and invoking it.
You can use .NET reflection on the interface type. For example, if you know that the object is an Excel Worksheet, you can use typeof(Worksheet).GetProperties(), and use the resulting PropertyInfo instances with your object. If you don't know the type of the object at compile time, however, you need to call GetType(), as in your example code. In that case, you're stuck with using InvokeMember.
It is because the COM object is lately bound. The run time does not know what methods/properties will be available on a COM object until they are accessed/invoked.
Here are some good articles on the subject:
http://support.microsoft.com/default.aspx?scid=kb;en-us;Q302902
http://www.codeproject.com/Articles/10838/How-To-Get-Properties-and-Methods-in-Late-Binding
You need to specify them by name using Type.InvokeMember(propertyName, BindingFlags.GetProperty, binder, target, args) because there's no way of knowing what properties a lately-bound object will have at compile-time. Instead, you need to perform that lookup at runtime, usually via string comparison.
RCW.GetProperties() would only work if you could determine the properties and their locations at compile-time.
Is it possible (with the new dynamic keyword in C# 4.0) to use interfaces (like IShellItem or other WinAPI interfaces) without defining them in my C# source code? Or at least not define the interface members?
I'm trying something like:
const string IShellItemGuid = "43826D1E-E718-42EE-BC55-A1E261C37BFE";
Guid guid = new Guid(IShellItemGuid);
dynamic nativeShellItem = null;
// Create and initializes a Shell item object (IShellItem) from a parsing name
SHCreateItemFromParsingName(#"M:\TEST.TXT", IntPtr.Zero, guid, out nativeShellItem);
if (nativeShellItem != null)
{
MessageBox.Show(nativeShellItem.GetDisplayName(0));
}
where
[DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
static extern void SHCreateItemFromParsingName(
[In][MarshalAs(UnmanagedType.LPWStr)] string pszPath,
[In] IntPtr pbc,
[In][MarshalAs(UnmanagedType.LPStruct)] Guid iIdIShellItem,
[Out][MarshalAs(UnmanagedType.Interface, IidParameterIndex = 2)] out dynamic iShellItem);
The SHCreateItemFromParsingName works fine and I'm getting an object if the file exists (and a correct error message if the file does not exist), however, trying to call nativeShellItem.GetDisplayName gives me a runtime exception:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled: 'System.__ComObject' does not contain a definition for 'GetDisplayName'
although IShellItem has a method called GetDisplayName, and my nativeShellItem dynamic object returned by SHCreateItemFromParsingName should implement this interface.
Yes, this cannot work, the DLR has no type info available about the COM object. This is a notorious problem in shell programming, it uses interfaces that are derived from IUnknown, not IDispatch so late binding isn't supported. And there is no type library available for them that lets .NET easily generate an interop library for the interface types.
IShellItem is declared in ShObjIdl.idl, it is filled with cpp_quote(). Which defeats any attempt to use midl.exe to generate a type library, only a .h file can be created. Already provided btw, ShObjIdl.h. Only a C++ compiler can use it.
Re-declaring the interface in C# is technically possible with the [ComImport], [Guid] and [PreserveSig] attributes. You have to do so very carefully, luckily IShellItem derives directly from IUnknown so you can dodge the multiple inheritance bullet. What doesn't help is that the interface method uses native C types that don't marshal automatically. Notably GetDisplayName() takes an LPWSTR, a raw pointer to a Unicode string. Not a BSTR, the automation compatible string type. That requires you to mess with unsafe pointers in your C# code. Best thing to do is to declare it IntPtr, allocate a chunk of memory with Marshal.AllocCoTaskMem() and marshal the string yourself after the call with Marshal.PtrToStringUni().
Yuck, shell programming is a complete pita. Google the heck out of this so you can copy/paste something that's known to work. If that comes up empty, using C++/CLI so you can use ShObjIdl.h is definitely a better way. Good luck with it.