Having spent the last few days reading everything I can find about C# reflection on COM objects, trying many experiments in code, and analyzing sample code to try and improve my understanding, I am now forced to admit that I just don't know enough, so I am requesting help from the Community.
I need to be able to access and update the properties of a late bound COM object that is wrapped as System._COM Object.
I tried all the standard refection stuff without success and I looked through using IDispatch, but I'm not comfortable with using the pointers involved, so I'm hoping I have missed something pretty simple in the normal interface. I found papers on MSDN that DO show how to do what I need, but all the examples are in C++ and it is over my head.
It would be really helpful if someone could explain why the following simple C# code just doesn't work as expected:
try
{
// late binding:
// localCB is a COM object (System._COMObject) created by Activator.CreateInstance() from
// the ProgID of a registered COM .DLL.
//
// The original .DLL has a string PROPERTY called
// "TESTEXTERNAL1". localCB has an IDispatch Interface. The original COM .DLL has a separate Typelib,
// and, although I did not register the typelib separately, I can see from OLEView on the COM object
// that the GUID for the typelib is included in it.
// Here's the code that is puzzling me...
var vv = localCB.GetType().InvokeMember("TESTEXTERNAL1", BindingFlags.GetProperty,
null, localCB, null);
string rt = vv.ToString();
// works exactly as expected and returns the value of TESTEXTERNAL1 - OK.
// now try and update the SAME PROPERTY, on the SAME COM object...
Parameters = new Object[1];
Parameters[0] = "Hello, World!";
localCB.GetType().InvokeMember("TESTEXTERNAL1", BindingFlags.SetProperty,
null, localCB, Parameters);
// throws an (inner) exception: HRESULT 0x8002003 DISP_E_MEMBERNOTFOUND !!!
}
catch (Exception xa)
{
string xam = xa.Message;
}
Is it unreasonable to expect an object that has already found and provided a property, to be able to update the same property? Is there some "alternative update" strategy that I am not aware of?
Many thanks for any help,
Pete.
UPDATE:
in response to Jon's request, here are snippets of the OleView:
(I had to use images because Oleview would not let me cut & paste, sorry...)
OleView of the COM .DLL
OLEView typelib view
Jon, I think you have correctly identified that the problem is with a setter method. The DLL is written in Fujitsu COBOL and provides an "under the covers" GET and SET for fields identified as PROPERTY. Accessing the COM component from C# or COBOL, it works fine, but, as you can see, it doesn't work when I try and access it for SET with reflection. Because I am unfamiliar with using reflection I was doubtful whether I had the syntax right, so I tried to make the SET as close as possible to the GET. I think I will need to generate my own SET methods (for each PROPERTY) into the COBOL and then change my "BindingFlags.SetProperty" to be "BindingFlags.InvokeMember". (I did the homework on BindingFlags and found that if you specify "SetProperty" it automatically implies the other 2 flags you mentioned.)
I think the key to it all is in recognizing that the problem is with the Fujitsu *COM Class SET, and it took your experienced eye to see that. Many thanks. If you have any other comments after seeing the OLEView, or can suggest any alternative approach in order to get the properties set, I'd be very interested. (I'm not looking forward to having to generate SETter methods for every property; it smacks of brute force... :-))
Thanks again,
Pete.
Hans was correct. The problem was with the setter method. I have written code to generate a setter for each of the properties, back in the original COBOL COM component. It wasn't as tedious or ugly as I thought it would be (about 7 lines of COBOL for each PROPERTY) and it is all working very well now. Many thanks to the community and particularly Hans Passant for support.
Related
I've deleted my first attempt at asking this question, as I wasn't quite up to speed on the semantics. Now I'm not much better. Basically here's the issue. I have a small block of online demo code that I'm analyzing that is showing how to access assembly-related functions in VB from C#. Basically loading and executing an assembly in C# using CallbyName to VB functions. Here is the relevant code, with all variables defined:
// the only non-default usings for this code are:
using Microsoft.VisualBasic;
using System.Reflection;
private void Form1_Load(object sender, EventArgs e)
{
string exefile = Properties.Resources.binaryfile;
// Convert base64 to bytes
byte[] exe_bytes = Convert.FromBase64String(exefile);
// okay, the load call takes as an argument the byte array exe_bytes,
// this makes sense
object loaded = Interaction.CallByName(AppDomain.CurrentDomain, "load",CallType.Method, exe_bytes);
// next, the entrypoint is taken from the loaded assembly, and
// passed to the object entry
object entry = Interaction.CallByName(loaded, "entrypoint", CallType.Get);
// finally, invoke is called to execute it, being passed the
// entrypoint. But what are the null, null for? This seems
// to be what is throwing the runtime error.
object invocation = Interaction.CallByName(entry, "invoke", CallType.Method, null, null);
Okay, let me stop here to note a few things. This builds successfully in C# (.NET 4.5), but throws a runtime error for parameter mismatch at the invocation line. It seems to me from looking at the code that someone just put that into three separate lines (load,entrypoint,invoke) to demonstrate what they were doing, as we could substitute object definitions to create one line. I add and subtract "nulls" from the 2 listed, and the error message remains, either saying it doesn't take 3 arguments, or that I erroneously have no arguments.
In my previous ask of the question a few hours ago, a helpful post suggested that I (quoting) "load the assembly (Reflection.Assembly.Load) and then inspect the parameters on the Assembly.EntryPoint property (MethodInfo) by calling its GetParameters method. Once you know what you are dealing with, then you can play with CallByName to start it." (credit to user TnTinMn)
So after about two hours of reading online docs with sparse examples, I've come up with only this line:
object loaded = System.Reflection.Assembly.Load(exe_bytes);
to replace the loaded definition in the code. After that, I have absolutely no idea what I am reading or doing! Assembly.Entrypoint, GetParameters, MethodInfo, oh my! Basically, I just want "how many parameters do you need? please print to console!" translated into C# .NET OO speak. Because I am utterly lost. Thank you!
The above seemed a good start, but alternative courses to a solution are certainly welcome. If you can, please please provide code in your answer, as my ability translating tasks from concept to OO programming is noticeably deficient.
EDIT: This may be relevant for examining the number of parameters?
MethodInfo[] methods = BUT_WHAT_OBJECT_GOES_HERE.GetMethods();
foreach (MethodInfo info in methods)
{
Console.WriteLine(info.Name);
}
In the example I took the code from, the object for the method GetMethods was a Program class. I tried using the assembly object exefile and it won't take it.
I have .Net DLL for Max with ui, and I want to react to parameter changes of some nodes in the viewport. The easiest solution that came up to me, was to create ReferenceMaker plugin and set reference for node I want to watch. According to the documentation it should be
public class ReferenceListener : Autodesk.Max.Plugins.ReferenceMaker{ ... }
But when I create new instance of this class and try to set reference, it crashes on "Object reference not set to an instance of an object."
When I try to debug it, I see that all baseclass attributes are null, so it seams ReferenceMaker plugin instance was not created in Max.
Finally I found MaxSharp source code here but using resulting dll let me to the same result and frankly the implementation is quiet the same as I had before. Trying to attach ReferenceListener to ReferenceTarget allways crashes beacause of nulls in base class.
So I really don't know how to solve this, but maybe someone tried to create something like this and succedded? For now I'm thinking about writing those parameter changes callbacks to maxscript, and call .net form it, but it feels hacky..
I'm using Max 2014 (and MaxSharp is for 2013) but I did not found any differences mentioned in documentation and any help would be appreciated.
Thank you
UPDATE
So, I narrowed it down to really strange problem. I've created C++/CLI plugin, made ReferenceMaker class in C++ SDK and did .net wrapper to call the plugin form C#, but it still wasn't working with same symptoms.
It seems that wrong pointer address is stored inside Autodesk.Max wrapper objects, so this is the reason why it is failing. I did a comparison of pointer returned from .Net DLL and from C++ SDK, and they are always different by 64. And it is always like that.
C++/CLI code
IINode^ al = Autodesk::Max::GlobalInterface::Instance->COREInterface->GetSelNode(0);
IReferenceTarget^ ak = (IReferenceTarget^)al;
ReferenceTarget* nativeTarget = (ReferenceTarget*)ak->Handle.ToPointer();
m_notifyListener->Test(nativeTarget);
C++ Max SDK code
void NotifyListener::Test(RefTargetHandle managedPointer)
{
Interface* ip = GetCOREInterface();
RefTargetHandle nativePointer = ip->GetSelNode(0);
intptr_t P1 = (intptr_t)managedPointer;
intptr_t P2 = (intptr_t)nativePointer ;
}
and than resulting variables are eg.
P1 = 1490452112
P2 = 1490452048
P2 is always smaller than P1 by 64.
I would understand if those pointers were totally different, but this slight shift is really strange to me.
Does anyone have any idea what is happening there? This is something I really don't get.
I need to test if the same behaviour is in Max 2013 or 2015 as I'm using 2014. I saw on some other forums that other people are complaining sample .net plugins are not working in 2014, so maybe this is the reason?
Thank you for any advice.
Let's say I have:
#{
var str= "DateTime.Now";
}
I want to process this string as a c# code
#Html.Raw(App.ProcessAsCode(str));
The output should be the current date time.
Final Edit:
Based on further information - if the goal here is to simply have a formatting engine there are lots of options out there. One such option is based around the .liquid syntax from shopify (see here). You can find a .NET port of this on gitHub here: https://github.com/formosatek/dotliquid/. The main purpose of this is to turn something like:
<h2>{{product.name}}</h2>
Into something like:
<h2>Beef Jerky</h2>
I would strongly recommend reading more about the liquid engine and syntax and I believe this will lead you in the right direction. Best of luck!
Initial Answer
This is definitely possible - although as others have said you will want to be careful in what you do. Using C# the key to compiling and running code generically is the "CSharpCodeProvider" class. Here is a brief example of how that looks:
string[] references = { "System.dll" };
CompilerParams.ReferencedAssemblies.AddRange(references);
var provider = new CSharpCodeProvider();
CompilerResults compile = provider.CompileAssemblyFromSource(CompilerParams, formattedCode);
In this example, "formattedCode" is a string with the C# code. Any references must be manually added. For the full example see this stack question (How to get a Type from a C# type name string?).
NOTE -- If all you are looking to do here is a format string or something simple like that you might have the user pass in a .NET format string (eg "MM/dd/yyyy"), then use that in a call to the "ToString" method. That would provide the user some configurability, while still making sure your system stays secure. In general running code on a server that hasn't been properly checked/escaped is really dangerous!
Reference - For your reference, the current msdn page for CSharpCodeProvider also has some examples.
Another option would be using a dynamic language such as IronRuby or IronPython.
I used reflection a couple of time to get values from the screen element, call methods etc. But now I am working on an application called Avaya, as this is not a really famous application I cannot find any help. My goal is to get some fields from this application, using automation UI library is not possible as the fields are not visible with any UI inspector like Spy++ and etc..
The only way to solve my issue is to develop a small code that interrogate the application and the result will be what is available for me, each method, each value, etc..
Then I have to start from the interface, I went to regedit and I got a list of 20 interfaces, then I start my code looping at these interfaces to get all classes and after that loop to each methods and proprieties, is that correct? Then I start my code getting a type and creating and instance..:
try {
foreach (string ThisInterface in ArrayInterfaces)
{
ThisInterfacee = ThisInterface;
//Call this interface
Type AvayaWorkspaceType = Type.GetTypeFromProgID(ThisInterface, true);
//Create Instance of this
object workspace = Activator.CreateInstance(AvayaWorkspaceType);
Any ideas of how to continues??
Thanks a lot any suggestion is welcome :)
I use reflection and C#
Solution: Reason why I am not able is because Reflection will only work with .NET base application, for other applications you should use IUIautomation or user32.dll
I am trying to save and restore state by using a StateBlock in SlimDX via the following snippet:
StateBlockMask mask = new StateBlockMask(null) { RasterizerState = true };
var sb = new StateBlock(device.Device, mask);
Both StateBlockMask and StateBlock are classes. This gives me a compilation error:
'.ctor' is not supported by the language
Reading from some other posts here on SO, it seems that this is a problem that has to do with calling the managed code with the wrong arguments. In the source of SlimDX, I find:
StateBlock::StateBlock(SlimDX::Direct3D10::Device^ device, StateBlockMask mask)
I have no experience at all with C++/CLI, so I am wondering if there is something wrong here (like a missing or extra ^), or should I concentrate of faults on my side?
(NOTE: This question has been cross-posted to gamedev.net, future users with the same question may also want to check for answers given there)
Is StateBlockMask a struct? If not, use StateBlockMask^ mask in the C++ constructor.
This looks like a bug in SlimDX. You might want to use the issue tracker to make sure it gets dealt with properly.