Following the guidelines from Turn a simple C# DLL into a COM interop component, I've created a tiny C# COM server:
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("410B8E64-27DB-40BD-8847-FC3A0E96147D")]
public interface IFactory
{
int PlusOne(int i);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("207FC3AB-0EFB-459A-B222-41E281F044F9")]
public class Factory : IFactory
{
public int PlusOne(int i) => i + 1;
}
I've created as well a client C# project which just references this COM server. And here is the problem: COM reference is not working, in Dependencies->COM section it is marked with a yellow warning triangle without any further explanations, and types from the COM component are not available in the client code.
These projects are published on github.
Note, that COM server atomaticly registers itself (regasm) upon rebuild of the project.
I'll appreciate any help. The answers like "you can reference your server as a .net assembly directly without need to use COM", are not acceptable, as this is just a test of a part of a bigger workflow which we need to establish.
The reason for inactive reference was found after examining compiler warnings and it was in explicit prohibition for .Net client to reference .Net COM server.
There is such a possibility, though, as described here:
https://learn.microsoft.com/en-us/samples/dotnet/samples/out-of-process-com-server/
I've checked, and this really worked fine, and it became possible for a .Net client to reference a Server.Contract.tlb from the example.
As a bonus, Microsoft added the possibility for .Net Core targeting COM server to be loaded into COM Surrogate or to be run as out-of-process (standalone exe).
This works indeed just for netcoreapp3.1 server, as when I've changed target framework to net48, no DllServer.comhost.dll (containing, most probably, proxy/stub code) was autogenerated as it did for netcoreapp3.1, the project property <EnableComHosting>true</EnableComHosting> obviously didn't have any effect.
Related
There is a third-party COM server DLL I need to include in my ASP.NET Core project. Adding via the Add > COM Reference worked fine and I end up with a COM > Interop.ThirdPartyServer under the Dependencies.
In my code I want to create an instance of one of the classes but it keeps failing with "Class not registered" even though it is.
I used
using ThirdPartyServer;
Type widgetType = Type.GetTypeFromProgID("ThirdPartyServer.Widget");
IWidget myWidget = (IWidget)Activator.CreateInstance(widgetType);
But I am seeing a "80040154 Class not registered" error.
I am sure it is registered, as I had to find the ProgID from the registry as it's not shown anywhere in the disassembled type library. In any case, I re-ran regsvr32 ThirdPartyServer.dll and that seemed to work with no errors, as it is a 32-bit DLL.
The 0x80040154 aka REGDB_E_CLASSNOTREG or "Class not registered" error usually means ... well, that the class is ... not registered. But the exact reason can be a bit complex.
COM clients and COM servers that run both in-process need to run with the same bitness, so both in x86 or both in x64. So:
If a COM client runs as x86, it talks to the x86 side of the
registry, so the COM server must registered in the x86 side of the
registry (regsvr32, regasm, custom reg code, etc. must run as x86).
If a COM client runs as x64, it talks to the x64 side of the
registry, so the COM server must registered in the x64 side of the
registry (regsvr32, regasm, custom reg code, etc. must run as x64).
Note 1: for out-of-process communications x86 clients or servers can talk to x64 servers or clients. That's one benefit of COM.
Note 2: the .NET "Any CPU" compilation feature allows to use the exact same binary for x86 and x64 but this binary still needs to be registered in the needed registry side, or in both registry sides if x86 clients and x64 clients support is needed.
I'm trying to instantiate a COM object, defined in a x86 dll written in Borland C++, in a testing program i write in C# (.net 4.7.2). The COM dll (server) is working, I have a windows service also written in C++ Borland that can use it and instantiate a COM object from the class (using CoCreateInstance). The dll is registered and the InprocServer32 entry has the correct path to the dll. There is no coclass existing in a typelib, only interfaces (those exist in the typelib). I have used the TlbImp to create dll:s which i reference in the c# project. In the project the target platform is set to x86. The way i try to instantiate an object is:
var comType = Type.GetTypeFromProgID("ins.MyComType");
object comObj = Activator.CreateInstance(comType);
however the second line gives me
"Exception thrown: 'System.IO.FileNotFoundException' in mscorlib.dll"
with the message 'Retrieving the COM class factory for component with
CLSID {C4363C5E-3831-46DF-8701-60C8D1B612BA} failed due to the
following error: 8007007e The specified module could not be found.
(Exception from HRESULT: 0x8007007E).".
It does not matter if i try to run the app as administrator. I have a vague memory of trying out a similar thing a couple of years ago and that it at that time worked. It was probably on a Win 7 machine (might even have been a 32-bit system). I have tried to open the project in DependencyWalker but i'm not sure what i'm looking at. I get a couple of errors:
*Error: At least one required implicit or forwarded dependency was not found.
*Error: Modules with different CPU types were found.
*Error: A circular dependency was detected.
*Warning: At least one delay-load dependency module was not found.
*Warning: At least one module has an unresolved import due to a missing export function in a delay-load dependent module.
Does any one have any idea on what it could be causing the exception? Or if i could get some hints as of how to dig deeper into dependencywalker? I get a gigantic tree of systemassembly stuff but i cannot see any obvious assembly standing out, though DW refers to many of them as being 64 bit. My guess is some dependent dll(s) somewhere should be x86 but which one(s). Is there a redist similar thingi i should have installed for this to work?
best regards
/Erik
You should write a simple VBScript that contains these lines:
set obj = CreateObject("ins.MyComType")
MsgBox TypeName(obj)
Name the file test.vbs
Execute the command:
c:\windows\syswow64\wscript.exe test.vbs
Using the version from syswow64 ensures that it uses the 32-bit version of wscript.exe which can instantiate 32-bit COM objects. The version in c:\windows\system32 can only instantiate 64-bit In-process COM objects in DLLs...you said your object is a 32-bit COM DLL server.
If the vbscript fails, it could be that the object is not automation compatible--implements IDispatch. Otherwise you will get an error message why it fails.
If it succeeds, you will know there is probably nothing on the C++ side causing problems. You THINK this is the case...but where is the runtime for Borland C++? Is everything statically linked, or is some of it dynamically linked? If it is dynamically linked, where is it in the path? It could be that the C++ service you have has the library in its path so that when it loads your COM object, the library is available. But, when you try to load from a 3rd party, like .NET or VBScript then the path to the library manifests itself. Who knows? I'm just making suggestions.
If you use ProcMon, it can see where the problems are. It will show you what registry entries are being read and which files are trying to be loaded.
I am creating an Azure Function in C# using a target framework of netstandard2.0 in a Windows 10 environment. The function calls a method that's in another class library and that method creates an instance of SqlConnection. When I run the function I get the following exception:
Microsoft.Azure.WebJobs.Host.FunctionInvocationException : Exception
while executing function: Functions.RefreshImages --->
System.Reflection.TargetInvocationException : Exception has been
thrown by the target of an invocation. --->
System.PlatformNotSupportedException : System.Data.SqlClient is not
supported on this platform. at
System.Data.SqlClient.SqlConnection..ctor(String connectionString)......
Obviously SqlConnection is supported on Windows so I assume there's something else going on here.
This happens when a .Net Standard lib that uses a SqlConnection is loaded dynamically via reflection. The .Net Standard lib will typically reference System.Data.SqlClient which seems to be a dummy lib without actual implementation. It apparently ensures that the lib will compile on all platforms, including those without Registry and other platform specific stuff that the real SqlClient implementation relies on.
The easiest solution I can find is to add a reference to the Microsoft.Data.SqlClient NuGet package in the host application (the .Net core application that dynamically loads the .Net standard lib).
You may see a small yellow warning exclamation icon in the Solution explorer because Visual Studio thinks you are not using the lib and if you use the "Remove Unused References" feature it will also suggest removing the package. There is a feature to suppress warnings in the PropertyGrid, but I cannot figure out wat number should be filled in since the warning does not appear in the error list when compiling...
It looks like this is related to loading a SQL connection via reflection in .NET core (you are running on netstandard2.0 but the principle should still be the same).
I am adapting an existing .NET class library to a Portable Class Library. I am using profile 78 (.NET 4.5, Windows Store 8, Windows Phone 8) in favor of profile 158 (which also targets Silverlight 5) because I want to be able to compile the unsafe code of the original library.
The .NET library contains quite a lot of classes marked [Serializable], so I have implemented a support PCL library containing a dummy SerializableAttribute implementation:
public class SerializableAttribute : Attribute { }
which is referenced from the main PCL library.
To sufficiently use the main PCL library in a .NET application while avoiding type name clashes I have also prepared a .NET support library (with the same strong name as the PCL support library), containing a type forwarding declaration:
[assembly: TypeForwardedTo(SerializableAttribute)]
and in my .NET application explicitly reference the .NET support library instead of the PCL one.
After having prepared all this and being able to successfully compile the PCL adapted library, I am re-using the unit tests from the original .NET library, now referencing the PCL main library and the .NET support library.
This generally works very well, but for unit tests that include a [Serializable] class with an [OnDeserialized] decorated method:
[Serializable]
public class Foo
{
[OnDeserialized]
private void DoSomething(StreamingContext context) { }
}
I get the following TypeLoadException:
Type 'Foo' in assembly 'MyPclAssembly' has method 'DoSomething' with an incorrect signature for the serialization attribute that it is decorated with.
(It can be noted that OnDeserializedAttribute is included in the portable subset, presumably because it is also recognized in [DataContract] serialization.)
I do not obtain the exception when running the unit tests on the original .NET library. I have carefully analyzed the method signature in the Foo class, and it is completely in line with the signature these (de-) serialization helper methods should have, see e.g. here. I have also tried changing the visibility of the [OnDeserialized] method to internal and public, to no avail.
What is the cause of this exception when using the PCL library, and what can I do to avoid it?
EDIT I have examined the IL code of the PCL library and the .NET library for the [OnDeserialized] method, and I can't see any relevant difference:
PCL
.method private hidebysig instance void DoSomething(valuetype [System.Runtime.Serialization.Primitives]System.Runtime.Serialization.StreamingContext context) cil managed
.NET
.method private hidebysig instance void DoSomething(valuetype [mscorlib]System.Runtime.Serialization.StreamingContext context) cil managed
The assembly references for StreamingContext are different, but I assume that the PCL System.Runtime.Serialization.Primitives assembly is simply a type forwarding assembly to mscorlib types?
For now, I have decided to exclude the [OnDeserialized] methods from my PCL project, since I do not plan to make use of serialization anyway. An answer to why I am experiencing the TypeLoadException is still welcome, though.
Yeah, this is a game you cannot win. Up front, the [Serializable] attribute is only ever relevant to the BinaryFormatter class, the one that implements binary serialization. That class is not available in the .NET Framework version that's resident on a Phone or a slate so there's no point in trying to make it work.
You are battling the notion of type identity in .NET. Which states that a type is not just identified by the namespace and type name but also the assembly from which it came. It is a very strong anti-DLL Hell countermeasure, the kind you are skirting with here by using types that won't be available on the target architecture.
And the cold hard fact is that in a 4.5 PCL library, the StreamingContext type lives in the System.Runtime.Serialization.dll assembly. An app that targets the desktop will use the one from mscorlib.dll. It is not a forwarded type, it is duplicated. The System.Runtime.Serialization.dll assembly is a small shim assembly with the express intent to isolate these dependencies and prevent DLL Hell.
Kaboom at runtime, it sees a method that has an argument with the wrong type identity.
I have a C# dll that I need to call from unmanaged C++. The main problem that I have is that my c++ code corresponds to an excel add-in, that can be installed for excel 2003 and excel 2007, when I install my add-in in excel 2007, and I try to call my C# dll, it works just fine, but for some reason that I still haven't been able to find, in excel 2003 it crashes, excel show me a Runtime Error message, and when debugging my c++ code I can see that the code fails when trying to create an instance of my C# dll, it says that the class is not registered even if I registered with regasm.
this is my C# code:
namespace ManagedDLL
{
[
Guid("3C80EE60-D9B8-4daf-89BE-6C7B748F613C"),
InterfaceType( ComInterfaceType.InterfaceIsDual),
ComVisible(true)
]
public interface ICalculator
{
[DispId(1)]
int main(string args, IntPtr _handle);
};
[
Guid("5134F342-5B7F-4db2-94F0-F450610419CF"),
ProgId("myapp.CCOMEntryPoint"),
ClassInterface(ClassInterfaceType.None),
ComDefaultInterface(typeof(ICalculator)),
ComVisible(true)
]
public class COMEntryPoint : ICalculator
{
public int main(string args, IntPtr _handle)
{
string[] _args = args.Split(new char[] { ':' });
Program.handle = _handle;
return Program.Main(_args);
}
}
}
and in C++ what I do is to import the .tlb file that is generated when I use regasm to register my C# dll, like this:
\#import "..\bin\release\ManagedDLL.tlb" raw_interfaces_only
using namespace ManagedDLL;
.
.
.
int callMyDll()
{
long handle = 0, result = 0;
BSTR args;
HRESULT hr = CoInitialize(NULL);
ICalculatorPtr pICalc(__uuidof(COMEntryPoint));
pICalc->main(bstrStr, handle, &result);
return result;
}
But as I mentioned before, this code doesn't work for excel 2003, so my questions are:
I'm I doing something wrong in the way in which I declare my C# dll that is causing me problems in excel 2003?
Just as it is now, can my C# dll be considered an ActiveX object?
How can I call my C# dll in another way from c++? like using IDIspatch for example
Thanks
I've had a similar problem before. I wasn't calling C# from C++, but the concept is the same.
I had to load a .NET dll into a host application via COM, which looks like what you are trying to do. The problem was the host application (in your case excel) was loading the .NET runtime 1.1. Our dll was compiled for .NET 2.0.
It could be that Excel 2003 is loading the 1.1 runtime and 2007 loads a more recent version. Check out this forum this:
Excel selects wrong .NET runtime.
You could also test this by using MSBee to target the 1.1 runtime and then try load your dll in Excel 2003.
I'm not a C++ coder, so I can't comment on that part, but to answer it from the C# side:
"I'm I doing something wrong in the
way in which I declare my C# dll that
is causing me problems in excel 2003?"
No, your attribute usage looks exactly correct. Well done.
"Just as it is now, can my C# dll be
considered an ActiveX object?"
By compiling with the attributes you show and then registering via RegAsm, you have created and properly exposed your assembly to COM, which is what you want. (The term "ActiveX" is usually used in reference to COM controls, and your class is not a control.)
"How can I call my C# dll in another
way from c++? like using IDIspatch for
example."
You are using the [InterfaceType(ComInterfaceType.InterfaceIsDual)] attribute, which means that the interface is exposed to both early binding and late binding via IDispatch.
In short, I don't know what's wrong here, so I would try dequadin's idea to check that the .NET Framework version being loaded is at or above the framework on which you are building.
If that isn't it, the only other thing I can think of is the fact that you are getting a straight crash, without a recoverable error, suggests to me that there could be some sort of mis-alignment between the registered interface vs. the interface on which the caller was compiled. This can happen because the GUID does not change if you change the interface -- you have explicitly set the GUID via an attribute -- so if the interface changes at all without everything being re-built and re-registered from bottom-to-top, all hell breaks loose. Therefore, if you changed your interface in any way, then you need to re-build the C# assembly, re-register with RegAsm, and then re-compile your C++ add-in that is referencing it.
This is just my best guess though. And does not explain the Excel 2003 vs. 2007 issue if you are using the same exact assembly for each. In short, it's hard to know what's wrong because your C# code looks 100% clean.
-- Mike