I have a C# library project which is designed to be used from unmanaged C++ code via COM.
Only 2 or 3 methods are going to be called in this way but I get warnings like this:
warning : Type library exporter warning processing ''.
Warning: Type library exporter encountered a generic type instance in
a signature. Generic code may not be exported to COM.
These properties and methods are not designed to be accessed from C++, an in fact they are not even public methods so they (surely) wouldn't be visible anyway.
Two questions really:
Q1: How can I control what is exported? Access modifiers on classes/methods or something else?
Q2: How can I see what is exported e.g. check what's in the type library to see if I missed something
It would be nice to double check I'm not bloating my type-library with a load of stuff that's not supposed to be there...
I can declare the whole assembly to be invisible to COM, like this (in fact when you use Visual Studio C# class library template it should put it itself in AssemblyInfo.cs):
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
Now, in each class I can decide it will be visible to COM or not like here:
using System;
using System.Runtime.InteropServices;
namespace ClassLibrary1
{
[ProgId("MyCoolClass")]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class MyCoolVisibleClass
{
public void SayHello()
{
Console.WriteLine("Hello COM world");
}
// explicit non COM visible because it's set to true at class level
[ComVisible(false)]
public void SayHello2()
{
Console.WriteLine("Hello world");
}
}
// implicit non COM visible
public class MyCoolInvisibleClass
{
public void SayHello()
{
Console.WriteLine("Hello world");
}
}
}
You can use the project properties to register ("Register for COM Interop" checkbox) , but I personally register myself with a command line like this (for 64-bit registry world):
%windir%\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe ClassLibrary1.dll /codebase /tlb
This outputs something like this:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe ClassLibrary1.dll /codebase /tlb
Microsoft .NET Framework Assembly Registration Utility version 4.8.3752.0
for Microsoft .NET Framework version 4.8.3752.0
Copyright (C) Microsoft Corporation. All rights reserved.
RegAsm : warning RA0000 : Registering an unsigned assembly with /codebase can cause your assembly to interfere with other applications that may be installed on the same computer. The /codebase switch is intended to be used only with signed assemblies. Please give your assembly a strong name and re-register it.
Types registered successfully
Assembly exported to 'D:\KilroyWasHere\ClassLibrary1.tlb', and the type library was registered successfully
And I can check what's really inside the .tlb using OleView from Windows SDK:
Related
I'm trying to create a COM library from a C# library I have. In order to do this I've defined my C# class as follows:
[Guid("830D0BFA-8045-455B-AAB7-2A7D4A16455C")]
[ComVisible(true)]
public interface IMyInterface
{
uint Start();
}
[Guid("22B91F20-1CC3-4276-BB51-0AE75D7DA5BB")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class MyInterface_Impl : IMyInterface, IDisposable
{
public uint Start()
{
return 0;
}
}
Additionally, I've enabled "Register for COM interop" in the project's build properties. The next step to allow a Win32 app (actually an MFC app) to use it is I think to generate a .tlb for the assembly. I'm doing this with the following post-build command:
"$(FrameworkSdkDir)\bin\NETFX 4.8 Tools\tlbexp.exe"
"$(TargetFileName)" "$(TargetName).tlb" /win32
I see the .tlb in the build folder and then proceed to add it to the MFC project, right clicking it and setting it to Item Type: MIDL tool. When I then build this project, I get the following warnings and errors:
1>------ Build started: Project: My_Dll_Test_MFC, Configuration: Debug Win32 ------
1>Processing ..\My_Dll\bin\x86\Debug\My_Dll.tlb 1>My_Dll.tlb
1>..\My_Dll\bin\x86\Debug\My_Dll.tlb(1): warning C4335: Mac file format detected: please convert the source file to either DOS or UNIX format
1>..\My_Dll\bin\x86\Debug\My_Dll.tlb(1): error MIDL2025: syntax error : expecting an interface name or DispatchInterfaceName or CoclassName or ModuleName or LibraryName or ContractName or a type specification near "MSFT"
1>..\My_Dll\bin\x86\Debug\My_Dll.tlb(1): error MIDL2026: cannot recover from earlier syntax errors; aborting compilation 1>Done building project "My_Dll_Test_MFC.vcxproj" -- FAILED.
What mistake have I made here?
You should not use MIDL with a tlb (the MIDL need the idl input, not the tlb). To consume the tlb in your C++ project, you just need #import directive
I am trying to develop a calculation engine and expose the implemented classes as COM objects following this Article. The exposed DLL (Com Object) is going to be consumed by a third-party application. Some older DLLs implemented by VB6 are now using and working properly.
While the final DLL is registered via RegAsm command in the Command Prompt, the COM object becomes visible in the target application but I receive the error messages of
"Failed to create object", "Object has no properties or methods".
What I have tried so far, in addition to the code below, is listed below :
ComVisible is set to true in AssemblyIfo.cs
Checked "Register for COM interop" in Build Options
Set [ComVisible(true)] attribute on classe/interface and/or method
Set [DispId(0)] attribute on classe/interface and/or method with different values !!
Set [ProgId] attribute on classe/interface and/or method
All methods and classes are defined as public members
Tried all the above actions to the class with/without interface
Tried all the above actions to the class with/without event interface
I have created a sample code as an example here, any further help would be appreciated :)
using System;
using System.Runtime.InteropServices;
namespace project_name
{
[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
[ComVisible(true)]
public interface ComClass1Interface
{
[DispId(0)]
[ComVisible(true)]
double calc();
}
[Guid("7BD20046-DF8C-44A6-8F6B-687FAA26FA71"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ComClass1Events
{
}
[Guid("0D53A3E8-E51A-49C7-944E-E72A2064F938"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(ComClass1Events))]
public class ComClass1 : ComClass1Interface
{
public double calc()
{
return 13;
}
}
}
The DLL is registered using RegAsm command to be listed on COM Objects.
The Com Object becomes visible to the target application.
When trying to select the implemented method I face these errors :-/:
I am using Visual Studio 2019, C#, .NET Framework 4.0 and the target application is running on Windows Server 2008 R2 and .NET Frameworks 3.5, 4.6 are installed.
The issue solved by These actions:
The assembly should be signed with a strong name (I used Visual Studio Signing tool)
The COM Object should be registered with regasm path/dll-name.dll /codebase /tlb /nologo
I have a WSDL generated class
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public partial class FVZServicesClient : System.ServiceModel.ClientBase<FVZScanningDocumentInfo.FVZServices.FVZServices>, FVZScanningDocumentInfo.FVZServices.FVZServices {
...
}
I need to provide a library that is used in VB6. I use a COM Interface which happens to use the above class.
When I register the assembly with
regasm MyAssembly.dll /tlb:MyAssembly.tlb
I get the error message
Microsoft .NET Framework Assembly Registration Utility version
4.7.3062.0 for Microsoft .NET Framework version 4.7.3062.0 Copyright (C) Microsoft Corporation. All rights reserved.
Types registered successfully Type library exporter warning processing
'MyAssembly.FVZServices.FVZServicesClient, MyAssembly'. Warning: Type
library exporter encountered a type that derives from a generic class
and is not marked as [ClassInterface(ClassInterfaceType.None)]. Class
interfaces cannot be exposed for such types. Consider marking the type
with [ClassInterface(ClassInterfaceType.None)] and exposing an
explicit interface as the default interface to COM using the
ComDefaultInterface attribute. Assembly exported to
'C:\Users\MyName\Documents\ScanLibrary\Packet\MyAssembly.tlb', and the
type library was registered successfully
When I add the Attribute [ClassInterface(ClassInterfaceType.None)] as suggested to FVZServicesClient, I still get the same error message the next time I try the regasm. I know that the generated code can be overwritten at any time, but I verified with DotPeek that the ClassInterface-attribute was still in the compiled code.
Another thing I tried, was to create a partial class of FVZServicesClient that had the ClassInterface-attribute. I also marked the default interface with COMDefaultInterface as the message suggests. The result was the same. Adding the Attribute [ComVisible(false)] for the FVZServicesClient did not help either.
When the library is referenced in VB6, the compile results in an error: "User-defined type not defined".
How can I exclude FVZServicesClient from the COM interface?
Try adding the attribute [ComVisible(false)] to class FVZServicesClient.
I'm not sure how your code generation process works, but as noted in the question you can at least try adding this manually and seeing if it has the desired effect.
The error message was deceiving. In FVZServicesClient there is a method that expects a parameter to be passed by reference.
public ScanBatchItemType[] CheckBatch(ref ScanBatchItemType[] scanBatchItemTypes)
I did not pass it by ref. Once I corrected that, the error message disappeared. At the moment there is no COMVisibility or ClassInterface Attribute on FVZServicesClient and COM is Ok with that.
I have a .NET library and I want to wrap it by COM to invoke its functions from C++. Fortunately, this library is open source and if I add COM-visible class right inside this project - it works:
[Guid("38F752CC-20F1-4729-B1E3-EE0AAD145052")]
public interface IQRCodeUI
{
string GetDecodedString(string encodedString);
}
[Guid("D4CFCDFA-6718-494D-A23F-EBC0F9550377")]
public class QRCodeUI : IQRCodeUI
{
public string GetDecodedString(string encodedString)
{
return decoder.decode(encodedString);
}
}
decoder is a class from this very library.
But what to do in case if I would have compiled assembly? I tried to create class library (COM) and add .NET library as embedded resource to it. Without results! During compile it said something like cannot register assembly "path\name". Cannot load file or assembly "nameOfAssembly" or its dependency. Cannot find the file. Apologise, I can't provide original text of the error, because I have MSVS which language differs from English. Is it possible to resolve this issue?
If your task is to call .NET library from C++ via COM, why you're adding .NET library as an embedded resource into some new COM class library?
Just make existing third party assembly COM-visible and call it from C++, VB6, etc.
There are a number of docs all over the internet of how to make .NET assembly COM-visible. None of them, as I recall, contain recommendations to embed .NET assembly into COM class assembly.
Start with:
Best Practice in Writing a COM-Visible Assembly (C#)
I have a .NET assembly which I am exposing to COM. The assembly has two public interfaces and one public class. When I build the assembly I get this warning:
(assemblyName.dll) does not contain any types that can be registered for COM Interop.
My assembly information includes the following line.
[assembly: ComVisible(true)]
Most people having this problem on the web, that I have found, fixed it with the above line in their assembly information. This has not helped for me.
I also tried adding [ComVisible(true)] to the class and interface definitions, and it also did not help.
ComVisible classes generally need to have a public default constructor. Its members should typically also reference only ComVisible types.
You don't need to specify ComVisible(true) on the class if you have specified it at the assembly level.
However, the usual way to generate an assembly with ComVisible classes is:
Specify ComVisible(false) at assembly-level. Thus only classes that are explicitly marked with ComVisible(true) are exposed to COM.
Define an explicit ComVisible interface :
e.g.
[
ComVisible(true),
GuidAttribute("..."),
Description("...")
]
public interface IMyComVisibleType
{
// members...
}
Your ComVisible class should specify ClassInterfaceType.None, and should implement the ComVisible interface:
e.g.
[
ComVisible(true),
GuidAttribute("..."),
ClassInterface(ClassInterfaceType.None)
]
public sealed class MyComVisibleType : IMyComVisibleType
{
// implementation ...
}
Note that the Guid and Description attributes are not required, but useful to give you more control of the COM generation.
If the above doesn't help, try posting some sample code and I'm sure someone will be able to help.
I ran into the default constructor problem. What fooled me was that the type library file will contain the class GUID reference even though that class is not being registered. A quick way to see what will be registered is to create a registry file ('assembly.reg') like this:
regasm assembly.dll /regfile:assembly.reg /codebase
There's a good discussion of exposing interfaces in COM Interop: Base class properties not exposed to COM. Some example code is here: Exposing .NET Components to COM.
Here are a few other things to true
Make sure the types you want to register are marked as public
Add the ComVisible(true) attribute to the type directly in addition to the assembly
Add the Guid attribute
Not sure if the last 2 are strictly necessary but I would try them out.
I think you need to have a strong-named assembly. Do you sign your assembly with a key file?
Further, also try specifying :
[Guid("{newly generated GUID}")]
[ClassInterface(ClassInterfaceType.AutoDual)]
I had the same problem on a project where I checked the "Register For COM interop" option for my C# project. There is an easy solution:
While in the Solution Explorer, go to the Properties folder. Open the AssemblyInfo.cs file. Scroll down to the line that says:
[assembly: ComVisible(false)]
Change this to:
[assembly: ComVisible(true)]
This will remove the warning messages, and allows the .tlb file to be created, which then enables the .NET code for COM visibility.
If you don't want the entire assembly to be COM-visible then follow one of the other tips above.