how to call a C# dll from unmanaged c++ using IDispatch? - c#

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

Related

C# COM library cannot be referenced

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.

Why does instantiating COM object in C# throw exception

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.

Call a C# .net .dll script using PHP

I have a C# .net dll script that calls a SQL stored procedure that SELECTs data in order to do run relevant methods.
I need to run the dll using PHP as my entire application is built in PHP.
What is the best way of doing this?
I'm not experienced at all with C#.
EDIT
I successfully registered the .net dll using:
RegAsm.exe DllName.dll /tlb:DllName.tlb
I should now be able to use PHP's COM() method as described below to call the dll's functions/methods.
But will these functions still be accessible through the COM() method as the .net dll was registered as an assembly? Does it make a difference?
EDIT
After registering the .net dll assembly I get an error message when I try to call the method using COM():
"PHP Fatal error: Uncaught exception 'com_exception' with message 'Failed
to create COM object `DllName.ClassName': Invalid syntax"
EDIT
Tried using:
new DOTNET('DllName, Version=4.0.30319.33440, Culture=neutral,
PublicTokenKey=14843e0419858c21', 'ClassName');
got an internal server 500 error
Is this because PHP doesn't communicate with .net 4 assemblies?
Option 1: Use the DLL
You can call the function using PHP's COM class.
You'll need to be running PHP on Windows, which I assume you are if you're using a C# DLL.
Steps:
Register the DLL using the command regasm yourdllname.dll in Command Prompt or the Run dialog.
You have one RegAsm.exe for each version of .NET installed on your computer, so make sure to execute the one for the version of .NET that the DLL targets by running it from %windir%\Microsoft.NET\AppropriateVersion.
Create a COM object in PHP that references the class name of the DLL.
Call the function, which will be available as a method of the COM object you've created.
Example:
$object = new COM('MyDllProjectName.MyDllClassName');
$object->myMethod();
Option 2: Rewrite in PHP
As has been mentioned, the cleaner, cross-platform option is to re-implement the SQL query in PHP directly, especially if your only reason for using the DLL is to run a query.
Using COM directly has many caveats and issues.
There is a library called NetPhp that abstracts on top of COM using reflection, so that you can call ANY .dll from within PHP without hassle:
http://www.drupalonwindows.com/en/blog/calling-net-framework-and-net-assemblies-php

CoCreateInstance fail with error "Class not registered" while using COM visible .NET class

Hello
I created COM visible class MyClass on C#
registered by regasm command - registration passed OK
And I can see in registry progid corresponding to MyClass
In the client C++ code :
1) call of CLSIDFromProgID passed OK
2) call of CoCreateInstance fail with error "Class not registered" (80040154)
What can be possible reasons of such behavior - your help will be very valuable
Thanks in advance
I just want to add that the "class not registered" problem may happen because one uses the incorrect bit-depth version of regasm. The .NET framework comes with 2 versions of regasm.exe. One version in the “Framework” path, and the other in “Framework64″ path.
On my 64 bit machine i got the error when using the 32-bit regasm to register the dll and tried to get an instance from a 64-bit C++ exe.
I had the same issue, solved executing these commands from the console:
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\regasm dllname.dll /tlb:dllname.tlb
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\regasm dllname.dll /regfile
then doing a double click on the reg file generated.
to test if everything is ok you can have a very small vb script file which created the object, just put a command like this in a .vbs file:
SET testObj = CreateObject("Namespace.ClassName")
your COM visible assembly should be strongly named.

Call unmanaged C++ VS 6.0 MFC dll from C#

I have an unmanaged C++ MFC dll that was developed in VS 6.0. I would like to use it in my C# app. I'm trying to use PInvoke.
Here is the C++ code:
// testDll.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
extern "C" {
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
__declspec(dllexport) int test(int a)
{
return a * a;
}
}
And here is how I'm trying to invoke the method "test" from C#:
// PInvokeTest.cs
using System;
using System.Runtime.InteropServices;
class PlatformInvokeTest
{
[DllImport("TestDll.dll")]
internal static extern int test(int number);
public static void Main()
{
Console.WriteLine(test(5));
}
}
This approach works just fine when I set C++ dll to be just a regular Win32 dll.
But once I change the project type to MFC ("Use MFC in a Shared DLL") I'm getting this error:
Unhandled Exception: System.DllNotFoundException: Unable to load DLL 'TestDll.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
at PlatformInvokeTest.test(Int32 number)
Thanks!
TestDll.dll probably can't load one of it's dependent DLL's.
Try loading your TestDll.dll file in the Depends (Dependency Walker) utility. Depends should be installed with VC 6, under Microsoft Visual Studio 6.0 Tools. That will show you what dependencies the DLL has and will flag if one of the dependencies failed.
Make sure you load the TestDll.dll from the same folder that the C# code does.
Note that Depends only works with unmanaged DLL's.
The DLL needs to be in a path where the system can pick it up. have you put it in the directory where your application starts ? (probably in a DEBUG folder of you VS solution).
The second option is to give it the absolute path of the DLL.
The third option is to place it in "c:\windows\System" but this is a '-1' approach :D
In general, when you hit errors like this you can use Assembly Binding Log Viewer to determine if the dll is failing to load because of a missing dependency.
In your particular case, the most probabl cause is that you are dynamically linking your dll to MFC and when the C# app attempts to load your dll, it is failing to load the MFC dlls.
You can either copy the required MFC dlls side-by-side or you can switch to statically linking MFC to your lib.

Categories