IUnknown on Xamarin/Mono, GetObjectForIUnknown throws PlatformNotSupportedException - c#

I have unmanaged C++ classes, that implement IUnknown properly. I compile the code to a shared library and deploy the library to be used on Android phone. I implemented a simple Xamarin.Forms (C#) application, which uses this library via the standard P/Invoke. Using a factory method, I create a set of these classes, each of its own specific type (and implementing its own interface as well, thus returning S_OK to QueryInterface call with supported iid).
The object gets created using P/Invoked native factory method, so I obtain a valid IntPtr-typed pointer.
However, when I try to call
IntPtr myObj = NativeBridge.CreateMyObject();
var obj = Marshal.GetObjectForIUnknown(myObj);
the code throws PlatformNotSupportedException.
The site https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.getobjectforiunknown?view=netstandard-2.0 states, that this method is supported on .NET Standard 2.0, which I use.
My Xamarin.Forms project is compiled as Xamarin.Android + Shared .NET Standard 2.0 Library, in MS Visual Studio 2017.
Are there some implications I missed? Is GetObjectForIUnknown truly not supported on Xamarin in this configuration? Are there any alternatives to accomplish the same/similar results? Or am I doing something completely wrong on the C# side?
EDIT1: the Marshal.QueryInterface call on the same IntPtr throws NotImplementedException
EDIT2: seems not supported in Mono in master: https://github.com/mono/mono/blob/master/mcs/class/corlib/System.Runtime.InteropServices/Marshal.cs - the QueryInterface and GetObjectForIUnknown method implementations are wrapped in conditionals for MOBILE.
So my question is: is there any way I can use COM model on Mono/Android within Xamarin.Forms, without rolling my own native library that bridges the implementations?

Related

Sharing generic code between UWP projects

I'm developing an UWP app via C#, that uses IBasicVideoEffect with IDirect3DSurface. As mentioned in documentation, I have created Windows Runtime Component (Universal Windows) project.
But settings of effects are stored in some implementation of IPropertySet (smth like Dictionary<object, object>).
To use high - level operations on this storage, I've introduced IPropertySetExtensions into runtime component, that has for example Get<T>, GetOrDefault<T> methods, which return instance of T (not object) from storage.
Than I realized, that same operations will be needed in main project, but when I changed
internal static class IPropertySetExtensions into public one, I got an error like "winmd components cant contain generic methods".
That's why I duplicated extension class into main project.
How can I avoid this duplicating? Maybe move shared code into NuGet .dll or smth?
Unfortunately, when writing a WinRT Component there are quite a few limitations due to the fact that WinRT Components are usable in C++, C#, VB and JS (yes also JS). The following document covers part of what to watch out for: https://learn.microsoft.com/en-us/windows/uwp/winrt-components/creating-windows-runtime-components-in-csharp-and-visual-basic
In your case, I think creating a "Class Library (Universal Windows)" will solve that problem since you can write C# code as you like without having to watch out for WinRT Component limitations.

NET Native Tool Chain for UWP throws NotImplementedException in Portable Libraries

We have quite a large shared project, which has 3 heads so to speak:
Windows.UWP
Windows.Desktop81
Windows.Phone81
These three each reference Portable Libraries which target both Windows 8.1 and Windows Phone 8.1.
When compiling the UWP project using the native tool chain, the portable libraries cannot access any type information so they can’t perform any reflection.
The method which is failing is a generic one, and inspects typeof(T) to do various operations depending on what type it is.
First line that throws an System.NotImplementedException is:
If (typeof(T).IsArray)
In this case, T is System.String, and if I break the debugger on the failing method and type into the immediate window of visual studio 2015, I get:
>> typeof(string).IsArray
An internal error has occurred while evaluating method System.Type.get_IsArray().
However, if I do the same in the App.OnLaunched method, that works fine. So the portable libraries cannot access any type information, even for system types like System.String.
I have tried adding platform directives for the Portable Libraries, but so far no luck.
Do you have any information regarding how to enable Portable Libraries to access type information.
I got a response back from Michal at Microsoft via email, explaining the root cause and how to get around it.
You seem to be hitting the same issue described here:
https://github.com/dotnet/corert/issues/3565, except the method in
question is Type.IsArray instead of ConstructorInfo.Invoke.
The problem is that the method Type.IsArray is declared as non-virtual
in the Portable Library contracts that your source code compiles
against, but it is virtual in the implementation assemblies used in
.NET Native. This is normally not a big problem because the C#
compiler pretty much always uses “callvirt” instruction to call the
method (even if it’s not virtual). The C# compiler shipped in Visual
Studio 2017 started doing an optimization that if a method is not
virtual and the 'this' passed to the method is known not to be null,
it uses “call” instead of “callvirt”. The result is that the code ends
up calling a method that should never have been called. The result of
the typeof() expression is known to be never null.
The good news is that we made IsArray non-virtual again as part of the
NetStandard 2.0 effort. The bad news is that .NET Native with support
for NetStandard 2.0 hasn’t shipped yet.
You’ll need a workaround. The easiest I can think of is to add an
extension method and use that instead:
static class NetNativeWorkarounds
{
public static bool IsArray(this Type type) => type.IsArray;
}
Using the extension method avoids the C# compiler optimization because
this condition is not met (the compiler doesn’t know what type you’ll
pass to the extension method and has to do a callvirt to the
Type.IsArray method).

How to interact with another programs with OLE Automation in C#?

I create an application with C# that can read item data from SQL Server and push it to the scale system named "SLP-V Ishida Retail Scales". They have an interface "SLP-V Automation Interface" that allows user programs to interact with their systems. This is the note from help page in SLP-V :
The automation interface (also known as the "COM (common object model) interface") provides a method for user programs to access SLP-V functions. The most common application for this is the use of VB Script to automate SLP-V operations such as importing host files. However, the automation interface can be used from any programming environment that supports automation (COM), and this is the preferred method for incorporating SLP-V functions into end-user applications.
This topic provides a reference for the methods and properties of the SLP-V automation object and includes some sample programs.
SLP-V Automation Object
The SLP-V automation object name is "Ishida.Slp.Scripting.CommonApi" and the type library file is "SlpScripting.tlb".
My question is, does the C# language allow us to interact other programs using OLE Automation? And if the answer is yes, how do I interact with my program?
I mean like calling their method. Because I can't add SlpScripting.tlb as a reference. It says
A reference to 'SLP Scripting Interface ' could not be added
The ActiveX type library 'SlpScripting.tlb' was exported from a .NET assembly and cannot be added as reference. Add a reference to the .NET assembly instead
And I have searched Google about this, but I didn't find the answer.
Finally found a solution
I don't have to add reference in c#, instead of just using :
System.Type objType = System.Type.GetTypeFromProgID("The name of progID that you want to call");
dynamic comObject = System.Activator.CreateInstance(objType);
Example ProgID = "Ishida.Slp.Scripting.CommonApi".
And then just call the function / method that exist in that object, for example :
comObject.LoginToHost("localhost", 8085, username, pass);
OLE automation is old wording for what we now call COM. And yes, .NET can access COM very easily (starting with .NET 1.0).
You have these options:
Method 1
First "register" the COM library on your development system. Look in the documentation of the SLP system, probably this was done already during setup. If not, normally a COM DLL can be registered manually with regsvr32 XXX.DLL. Be aware of 32/64 Bit issues (if you want to register a 32 bit COM DLL in 64 bit Windows, use C:\Windows\SysWOW64\regsvr32.exe).
Then your COM DLL should be listed in Visual Studio if you go to
Add Reference ==> COM
as "SlpScripting Type Library 1.0" or similar.
Then, add a "using SLPxxxx" or similar (Intellisense should show the real name).
After this, you should be able to create a new instance of your COM object.
For additional help, search for "C# COM interop", you will find lots of informations.
Method 2
Open a Visual Studio command prompt, and enter:
tlbimp SlpScripting.tlb
A DLL will be created, which you can add as a reference.

Adding an in-proc COM Server object to a C# application

I am new to COM and need to add a Server COM object to my c# application so I can call its methods and implement events. The documentation I have says this requires a COM-aware language such as Visual C++. My app is written in C# so I'm not sure how this is going to work. Any direction would be appreciated.
I am writing an app that communicates with a serial hypercom terminal. The device came with a DLL (the com server interface) that I will need to figure out how to use in my c# application.
I added a reference to the DLL to my project, which shows up as a type library. In object explorer I get interfaces, and some classes etc.
Thanks,
Matt
You can add the COM object as a reference. .NET will create an interop assembly to work with the COM object, just like it was a .NET type.
CComObjectRoot can be used as a base class for all COM objects implemented with ATL. As such you don't have to worry to implement something similar in C#, the required methods (AddRef, Release, QueryInterface) will be generated by tlbexp for classes that are tagged with ClassInterface.
STDMETHODIMP is a define which serves to declare the most common methods that can be called from COM (#define STDMETHODIMP HRESULT STDMETHODCALLTYPE). Again if your class is tagged with ClassInterface you will not have to worry about.
Such construction is required in C++ when your class implements several interfaces. I think this is not required if you tell C# compiler that your C# object implement IDispatch, IFPESOlementationEvents. The appropriate code will be written automatically by the compiler.
Probably everything will not make much sense if you are new to COM and C#, I'll suggest to take a look at the various introduction that you may find on the web, like this.

Replacing a C++ ActiveX component with a .NET implementation?

I have existing managed and unmanaged software using an ActiveX component supplied by a third party to perform some communications, but it is now required that this communication be routed through my application.
Ideally I'd be able to install a .NET component which will expose exactly the same interface, and will be usable as a drop-in replacement.
However, I am running into the limits of my understanding of COM, which admittedly is quite minimal.
How best to ensure that my implementation of the interface is 100% binary compatible with the existing object?
How do I ensure that applications use my implementation of the interface instead of the legacy implementation? Is it simply a matter of registering my implementation, and unregistering the legacy one?
How do I ensure it's a "drop-in" replacement, and requires no changes to existing software?
How do I ensure unmanaged code can use it without issue?
Note: I am able to require that .NET 4.0 be used, if that will make things simpler.
Edit: Bounty will be moved here How to debug why a VB6 application using my .NET ActiveX control does not register for events? after 2 days.
Use the type library of the ActiveX component. Import it with Tlbimp.exe to get the interop library, you probably already have it if you use this component yourself. Implement your own code by inheriting the interfaces in that type library.
Your implementation must use the exact same GUIDs and ProgIDs as the ActiveX component. Use OleView.exe, File + View Typelib and select the ActiveX DLL to see the GUIDs. The ProgIDs are more difficult, best thing to do is to watch how the registry is modified with the SysInternals' ProcMon utility when you register the ActiveX DLL with Regsvr32.exe. Ultimately, the exact same changes need to be made by Regasm.exe when you register your replacement.
As point 2.
Same, the registration gets unmanaged code to use yours instead.
To make this work out well, you really have to know what the interfaces do. You cannot make this work if the ActiveX component is actually an out-of-process server (an EXE).
Well, I've gotten a lot further along with this, but I seem to have encountered an intractable problem.
The object I am replacing uses COM events. When one of the client applications (VB6 I believe, as depends.exe tells me it uses msvbvm60.dll) instantiates and uses my replacement, it does not register for any of the events, and unfortunately, the way it works is that after a particular method call has completed, the client application does nothing until an event fires.
Note: My replacement ActiveX control inherits from System.Windows.Forms.Control, and sets MiscOptions of 131457 on the coclass registry entries as suggested by http://ondotnet.com/pub/a/dotnet/2003/01/20/winformshosting.html, the reason being that the thing I am replacing was an honest to goodness ActiveX control, and I could not get these existing clients to instantiate my object successfully without any code changes at all until I inherited from WinForms control.
I have tried the approach where my coclass declares public events with the same name as the interface specified by ComSourceInterfaces, this works 100% from a C# app that uses AxHost, events are triggered.
I have also tried instead to implement IConnectionPointContainer and all supporting interfaces on my replacement control, and this works 100% from a C# app, but in the VB app, it never actually attempts to Advise() the connection point of the client sink interface to call, it only calls Unadvise() with an invalid cookie value of 0.
One issue with the typelib that I have noticed is that I cannot get tlbexp.exe to export one of the properties on the coclass interface as OLE_HANDLE, it just ends up being a long in the TLB generated from the assembly (this TLB is referenced by the TypeLib entry in the registry). Could this cause issues with eventing?
Any ideas how to debug this?

Categories