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
Related
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:
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 C# project which is a library I reference in one of my C# tests I have:
myproj
|-bin
+-External
| |-pHash.dll
|-main.cs
|-MyClass.cs
|-myproj.csproj
pHash.dll is a Visual C++ library (compiled using a vcproj project, so we are talking about Microsoft C++ flavor) called phash which exposes this method:
int ph_dct_imagehash(const char* file, ulong64& hash);
I want to use this method from MyClass.cs. So I do this:
public class MyClass {
[DllImport("pHash.dll")]
static extern int ph_dct_imagehash(
[MarshalAs(UnmanagedType.LPStr)] string file,
[MarshalAs(UnmanagedType.U8)] UInt64 hash);
public MyClass() {...}
public DoStuff() {
UInt64 outputValue = 0;
ph_dct_imagehash("a string", outputValue); // The second parameter should be a pointer in the C++ implementation, not really sure if I am doing it right here
}
}
In my project, I set External/pHash.dll to be Content and also set it to be Always copy in the output folder.
A possible mistake As I pointed out, the way I use ph_dct_imagehash might be wrong, but this question is about a different error I get, so please skip this and move on :)
MyClass is then referenced in one of my tests I execute using MSText.exe.
The error
When I try to run my test which is using that class I get:
System.DllNotFoundException: Unable to load DLL 'pHash.dll': The
specified module could not be found. (Exception from HRESULT:
0x8007007E)
What am I doing wrong?
Second attempt
I saw that, when building, actually my bin folder looks like this:
myproj
+-bin
|-External
|-pHash.dll
So I thought I had to change the path to the DLL:
public class MyClass {
[DllImport("External/pHash.dll")]
static extern int ph_dct_imagehash([MarshalAs(UnmanagedType.LPStr)] string file, [MarshalAs(UnmanagedType.U8)] UInt64 hash);
...
}
But then I get:
System.DllNotFoundException: Unable to load DLL 'External/pHash.dll': The
specified module could not be found. (Exception from HRESULT:
0x8007007E)
Another question
Is there a (maybe debugging?) tool to understand what path is the CLR trying to follow when encountering DllImport? So at least I know where it is trying to look for that dll...
More info
I have been asked to inspect my dll, so by using DependencyWalker on pHash.dll, I get this error:
Error: The Side-by-Side configuration information for
"c:\myuser\testresults\myuser-0131
2016-05-06 08_56_50\out\PHASH.DLL" contains errors. The application
has failed to start because its side-by-side configuration is
incorrect. Please see the application event log or use the
command-line sxstrace.exe tool for more detail (14001). Error: At
least one required implicit or forwarded dependency was not found.
Error: At least one module has an unresolved import due to a missing
export function in an implicitly dependent module. Error: Modules with
different CPU types were found. 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.
This error is also reported in the stack trace when I run my test. I thought it was a message coming from MSTest about something else, but now I realize it is a problem of this DLL!
Even if your DLL is found (sits in the test directory), it is still reported as missing when some of it's dependencies are not found. Make sure you copy the DLL itself and all DLLs it depends on.
You can set select your dll in Solution Explorer, right click on it and set BuildAction to Content and Copy To Output directory to CopyAlways. That way your dll will be copied to application directory and DllImport should find it.
Alternatively, use DllImport's SetDllDirectory with DLL path before first call of your imported dll
[DllImport("kernel32.dll")]
static extern bool SetDllDirectory(string yourPath);
Currently I'm working on a solution that contains a C++/CLI project (which is a wrapper of a native C++ dll) and a C# WPF project (which uses the functionality of the C++/CLI wrapper).
The problem I have is that when a pointer to a native class is instantiated (within the wrapper) a linker error occurs.
Class:
public ref class MCBiosUpdate
{
public:
MCBiosUpdate(void);
MCBiosUpdate(MCBiosUpdate^);
MCBiosUpdate(CBIOSUpdate&);
~MCBiosUpdate(void);
!MCBiosUpdate(void);
private:
CBIOSUpdate *pBiosUpdate; //Native C++ Class
};
Code that causes error:
MCBiosUpdate::MCBiosUpdate(void) : pBiosUpdate(new CBIOSUpdate) //error LNK2028
{
}
Output:
1>MCBiosUpdate.obj : error LNK2028: unresolved token (0A00000E)
"public: __thiscall CBIOSUpdate::~CBIOSUpdate(void)"
(??1CBIOSUpdate##$$FQAE#XZ)
How can I resolve the linker error so that the program runs as intended?
What causes this linker error?
If there is any improvements to the code or any suggestions to help me resolve the problem; feel free to comment.
What causes this linker error?
Reason could be pBiosUpdate interface is not exported from the DLL to be visible to the DLL'S clients, in your case the C# code.
How can I resolve the linker error so that the program runs as intended?
use dllexport as mentioned here
(or)
Use a module definition file (.def file) to export the DLL's function as mentioned here
In my visual studio solution I have a dll containing native C++ code, a C++/CLI dll where I am trying to implement a wrapper for my unmanaged types, and finally my C# application.
I am currently unable to reference my unmanaged types in my wrapper dll.
Code Snippet:
// In the unmanged file (in the unmanged dll project)
using std::vector;
namespace populationwin32
{
class PopulationWin32
{
public:
PopulationWin32();
// ...
};
}
// In the managed file. (in the manged dll project)
using namespace system;
using populationwin32::PopulationWin32;
namespace GSODFileParsing
{
public ref class PopulationWin32Wrapper
{
public:
PopulationWin32Wrapper();
private:
PopulationWin32 *_populationWin32; // unmanaged object
}; // End class
} // End namespace
When I try to compile the managed dll (containing the wrapper) I get the following errors:
Error 1 error C2653: 'populationwin32' : is not a class or namespace name
Error 2 error C2873: 'PopulationWin32' : symbol cannot be used in a using-declaration
Error 4 error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
Any ideas what these errors mean? I'm thinking they are from not correctly linking to the unmanged dll, but I'm not sure how to do this and everything I find online says something different. Any body know how to correctly and completely link an unmanged library (dll project) with a managed VC++/CLI wrapper library (dll project)?
Note: Both these libraries are projects in a single visual studio solution.
Edit: Thanks to Olaf Dietsche's comments I added
#include "PopulationWin32.h"
To the wrapper header file but now I get the following error:
Error 1 error C1083: Cannot open include file: 'PopulationWin32.h': No such file or directory
Is there a setting I'm missing or do I need to include the header file in both DLL projects? BTW, I added the unmanged dll output .lib file as a dependency to the managed dll via properties->configuration properties->linker->input->Additional Dependencies.
Your first error 'populationwin32' : is not a class or namespace name means, the compiler has no knowledge about this namespace.
Add an
#include "PopulationWin32.h"
to make this known to Visual Studio.
You also must add the path to your include file, so the compiler can find it. This is described here VC++ Directories, Projects and Solutions, Options Dialog Box and here The #include Directive, for example.