Expose dll for COM Interop - c#

I thought I knew how to do this, but obviously not so I'd appreciate some help!
I can't get my dll to register so I can instantiate it in a VBS, or elsewhere.
I wrote the following sample class, checked "Make assembly COM Visible", checked "Register for COM Interop", then built it.
When I try to instantiate it from VBS I get the "Activex component can't create object" error.
This is the class code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Smurf
{
public class Pants
{
public string Explode(bool Loud)
{
string result;
if (Loud)
result = "BANG";
else
result = "pop";
return result;
}
}
}
...and this is the VBS:
Dim a
Set a = CreateObject("Smurf.Pants")
msgbox("ok")
What else do I need to do?
Thanks :)
[edit]
Forgot to mention, after the first failure I tried REGSVR32 and REGASM - no help!
[/edit]
Note that when I try REGSVR32, I get this message:
The Module "C:...\Smurf.dll" was loaded but the entry-point DllRegisterServer was not found.
Make sure that "C:...\Smurf.dll" is a valid DLL or OCX file and then try again.
How helpful is that??
This is the latest version of the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace Smurf
{
[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
public interface IPants
{
[DispId(1)]
string Explode(bool Loud);
}
[Guid("7BD20046-DF8C-44A6-8F6B-687FAA26FA71"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IPantsEvents
{
string Explode(bool Loud);
}
[ComVisible(true)]
[Guid("0D53A3E8-E51A-49C7-944E-E72A2064F938"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(IPantsEvents))]
public class Pants : IPants
{
public Pants() { }
[ComVisible(true)]
[ComRegisterFunction()]
public static void DllRegisterServer(string key) { }
[ComVisible(true)]
[ComUnregisterFunction()]
public static void DllUnregisterServer(string key) { }
[ComVisible(true)]
public string Explode(bool Loud)
{
string result;
if (Loud)
result = "BANG";
else
result = "pop";
return result;
}
}
}

There could be a few different things at play here. First, you'll want to use the regasm tool with the /codebase /tlb switch from an elevated command prompt (assuming Windows Vista, 7 or Windows Server 2008). Something like:
regasm "Path to Smurf.dll" /codebase /tlb
Once you have registered the dll using regasm you should be able to invoke it using VBS, VBA or VB6.
I was able to use early binding and late binding from VBA to call the Explode method. However, when I tried from VBScript I received the "ActiveX can't create object error as you did."
I'm running on Windows 7 64 bit, and I recalled that this can cause problems when compiling to 32 bit dlls and running them on 64 bit operating systems. On a whim, I fired up a command prompt and entered:
C:\Windows\SysWow64\CScript.exe "Path to VBScript"
The result was that the script ran correctly and displayed "Pop" on screen.
Here's the somewhat simplified C# code I used as well as the contents of the VBScript file.
namespace Smurf
{
[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
public interface IPants
{
string Explode(bool Loud);
}
[Guid("7BD20046-DF8C-44A6-8F6B-687FAA26FA71"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IPantsEvents
{
string Explode(bool Loud);
}
[ComVisible(true)]
[Guid("0D53A3E8-E51A-49C7-944E-E72A2064F938")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IPantsEvents))]
public class Pants : IPants
{
[ComVisible(true)]
public string Explode(bool Loud)
{
string result;
if (Loud)
result = "BANG";
else
result = "pop";
return result;
}
}
}
VBScript:
Dim x
Set x = CreateObject("Smurf.Pants")
MsgBox (x.Explode(False))
Set x = Nothing

Related

C# IConnectionPointContainer.FindConnectionPoint throws exception

I am trying to implement an ATL COM module with a connection point. The source code for this is all pretty much boilerplate that I copied by example from chapter 12 of Developer's Workshop to COM and ATL 3.0 by Andrew W. Troelsen.
That's a lot of code, with three source files (almost all Visual Studio rendered boilerplate).
I think have got everything that I need, though. There is a subclass called _IFooEvents_CP.h to handle events and the class implemments IConnectionPointImpl:
using namespace ATL;
template <class T>
class CProxy_IFooEvents : public IConnectionPointImpl<T, &__uuidof( _IFooEvents ), CComDynamicUnkArray>
{
// WARNING: This class may be regenerated by the wizard
public:
HRESULT Fire()
{
//...
// event proxy code
//...
}
}
Then, in the header to my coclass (Foo, of course):
// Foo.h : Declaration of the CFoo
#pragma once
#include "resource.h" // main symbols
#include "ConnectionPointTest_i.h"
#include "_IFooEvents_CP.h"
#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
#endif
using namespace ATL;
// CFoo
class ATL_NO_VTABLE CFoo :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CFoo, &CLSID_Foo>,
public IConnectionPointContainerImpl<CFoo>,
public IDispatchImpl<IFoo, &IID_IFoo, &LIBID_ConnectionPointTestLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
public CProxy_IFooEvents<CFoo>
{
public:
CFoo()
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_FOO)
BEGIN_COM_MAP(CFoo)
COM_INTERFACE_ENTRY(IFoo)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IConnectionPointContainer)
END_COM_MAP()
BEGIN_CONNECTION_POINT_MAP(CFoo)
CONNECTION_POINT_ENTRY(__uuidof(_IFooEvents))
END_CONNECTION_POINT_MAP()
Furthermore, the idl file has the event source forward declared and all set thanks to the ATL connection point wizard that I used to make this thing:
library ConnectionPointTestLib
{
importlib("stdole2.tlb");
[
uuid(25EAB56B-884A-4AA9-B470-BAA975E08343)
]
dispinterface _IFooEvents
{
properties:
methods:
[id(1), helpstring("test")] HRESULT bar();
};
[
uuid(54B6050F-1090-4660-9DF0-D8A0853F96CF)
]
coclass Foo
{
[default] interface IFoo;
[default, source] dispinterface _IFooEvents;
};
};
So, I have an implementation of the connection point container interface, I have a connection point in the connection point map, and I have forward declared an event function to implement on the client side.
It compiles without warning or error. Then on the C# side I have the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices;
using ConnectionPointTestLib;
namespace TestConnectionPoint
{
class EventSink
{
public EventSink() { }
public void bar()
{
Console.WriteLine("hello");
Console.ReadLine();
}
}
class Program
{
static void Main(string[] args)
{
try
{
ConnectionPointTestLib.IFoo cpTest = new ConnectionPointTestLib.Foo();
IConnectionPointContainer icpc;
icpc = (IConnectionPointContainer)cpTest;
IConnectionPoint icp;
Guid id = typeof(Foo).GUID;
icpc.FindConnectionPoint(ref id, out icp);
EventSink es = new EventSink();
int cookie;
icp.Advise(es, out cookie);
cpTest.testCP();
Console.ReadLine();
}
catch (COMException e)
{
Console.WriteLine(e);
Console.ReadLine();
}
}
}
}
The exception is thrown on this line:
icpc.FindConnectionPoint(ref id, out icp);
and the error code is exotic: 0x80040200. I couldn't locate this code anywhere, but I think it is not an official Windows code. At least, I couldn't find it on MSDN's list.
I copied the client code from an example I found online, so it could be that I am missing a step.
Later:
As #Hans Passant pointed out, the error code is CONNECT_E_NOCONNECTION. My understanding is that this means that there are no connections exposed by the interface. But, as far as I can tell (and I am new to this game) I have done everything I need to do in order to have an outgoinginterface here.
I feel like I'm missing some tiny magical step.

Force application to run with optional DLL

I have made desktop application. I have make class library and then make its DLL from University assembly. Now i want to make library DLL optional. In short i want to run the application weather or not library DLL is refereed.
Right now if i remove reference of library DLL then it gives error on library methods that they are not defined. I want this application to run with oujt giving error of library method.
I have search on google but i am unable to find out any reliable answer.
Check if assembly exists on disk, and if it's true use dynamic assembly loading:
http://msdn.microsoft.com/en-us/library/25y1ya39.aspx
Called classes/methods in your library can be replaced by stubs(new level of abstraction), in which you can check if assembly is successfully loaded, and invoke from it if yes.
Ok.. Very simple example:
"Real Assembly" code(First project, compiled as class library "RealAssembly.dll"):
namespace RealAssembly
{
using System;
public class RealClass
{
Random rand = new Random();
public int SomeProperty { get { return rand.Next(); } }
public string SomeMethod()
{
return "We used real library! Meow!";
}
}
}
"Our project" code with Fake(stub) class(Second project, compiled as Console applicaiton - "ClientApp.exe"):
using System;
using System.IO;
using System.Reflection;
namespace ClientApp
{
class FakeClass
{
public int SomeProperty { get { return 0; } }
public string SomeMethod()
{
return "Library not exists, so we used stub! :)";
}
}
class Program
{
// dynamic instance of Real or Fake class
private static dynamic RealOfFakeObject;
static void Main(string[] args)
{
TryLoadAssembly();
Console.WriteLine(RealOfFakeObject.SomeMethod());
Console.WriteLine(RealOfFakeObject.SomeProperty);
Console.WriteLine("Press any key...");
Console.ReadKey();
}
private static void TryLoadAssembly()
{
string assemblyFullName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RealAssembly.dll");
if (File.Exists(assemblyFullName))
{
var RealAssembly = Assembly.LoadFrom(assemblyFullName);
var RealClassType = RealAssembly.GetType("RealAssembly.RealClass");
RealOfFakeObject = Activator.CreateInstance(RealClassType);
}
else
{
RealOfFakeObject = new FakeClass();
}
}
}
}
This two projects are not referenced directly. "System" is the only reference used in this two projects.
So now, if compiled "RealAssembly.dll" exists in same directory we will have "We used real library! Meow!" string and random integer at console output. Otherwise if "RealAssembly.dll" not exists in same directory - "Library not exists, so we used stub! :)" and 0 will be shown.

Getting C# ActiveX/COM Library to Work through JScript

I have checked on stackoverflow (and what seems like everywhere else). I would like to get a COM solution working so that a jscript file can be written as
var T = new ActiveXObject("MySimulator.World");
T.MyMethod();
It would be executed at the command prompt by
cscript mytest.js
In this case, I get the error "Automation server can't create object".
In C#, I have followed various suggestions, with the latest interface being:
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83B")]
public interface IComMyReaderInterface
{
void MyFunction();
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None), Guid("0D53A3E8-E51A-49C7-944E-E72A2064F9DD"), ProgId("MySimulator.World")]
[ComDefaultInterface(typeof(IComMyReaderInterface))]
public class MyReader : IComMyReaderInterface
{
public MyReader()
{
...
}
public void MyFunction()
{
...
}
...
}
Thanks and just let me know if more information is needed.
I'd assume the following. Your development environment is probably a 64-bit OS and your C# DLL project is probably configured to compile with Any CPU as Platform Target. Read on if that's the case.
Choose either x86 or x64 and compile the project. If you go with x86, then register your assembly with the 32-bit version of RegAsm.exe:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /codebase assembly.dll
Then run your JavaScript test with the 32-bit version of cscript.exe:
C:\Windows\SysWOW64\cscript.exe mytest.js
If you go with x64, that would be:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe /codebase assembly.dll
C:\Windows\System32\cscript.exe mytest.js
[EDITED] The following code has been verified to work using the above instructions.
C#:
using System;
using System.Runtime.InteropServices;
namespace ComLibrary
{
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual),
Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83B")]
public interface IComMyReaderInterface
{
void MyFunction();
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None),
Guid("0D53A3E8-E51A-49C7-944E-E72A2064F9DD"),
ProgId("MySimulator.World")]
[ComDefaultInterface(typeof(IComMyReaderInterface))]
public class MyReader : IComMyReaderInterface
{
public MyReader()
{
}
public void MyFunction()
{
Console.WriteLine("MyFunction called");
}
}
}
JavaScript (mytest.js):
var T = new ActiveXObject("MySimulator.World");
T.MyFunction();
Output:
MyFunction called

C# com interopt failure on american machines

I've been working on a simple dll library that is com-accessible so that other softwares can use our library (from any managed or unmanaged language).
Creating a com-accessible dll is fairy easy:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace MyNamespace
{
//This interface defines purely the events. DotNetEventSender should implement this interface with the ComSourceInterfaces() attribute
//to become an Event Source.
[ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface COMEventsInterface
{
//[DispId(1)]
// we don't have any events, but if needed, include them here
}
[ComVisible(true)]
public interface ICOM
{
//Methods
int Sum(int[] intsToSum)
}
//Identifies this interfaces that are exposed as COM event sources for the attributed class.
[ComSourceInterfaces(typeof(COMEventsInterface))]
//Tells the compiler not to generate an interface automatically and that we are implementing our own interface (IDotNetEventSender)
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class COM : ICOM
{
// Methods
public int Sum(int[] intsToSum)
{
int sum = 0;
foreach ( int i in intsToSum )
{
sum += i;
}
return sum;
}
}
}
In debug mode in would now mark this project to register for com-interop via Project>Properties>Build>Register for com interop.
In release mode I have an installer which marks the primary output from my project as "vsdrpCOM".
And this works great, in most cases. But somehow on some machines (all American) this won't work. The com class gets registered but I constantly get the error: HRESULT 0x80131534, which is actually already descibed here on SO: Error when instantiating .NET/COM interop class via classic ASP
But realy, I don't see any solution here. I've checked for user rights, domain rights, ...
EDIT:
The constructor of my real class does this one thing:
(I've added the try catch because I found on SO that this is an error in the constructor...)
// Constructor
public COM()
{
try
{
// register itself with the application
MyApplication.COMObject = this;
}
catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
It just registers itself to a static class' property COMObject:
private static COM _comObject;
public static COM COMObject
{
get
{
return _comObject;
}
set
{
_comObject = value;
}
}
Although, the COM class doesn't really need to register itself, i've done this for future use if I would like to trigger Events
Well, I happens to be that i have faulty declared a DateTime in one of my declarations of a static class... private DateTime myDateTime = Convert.ToDateTime("15/09/2013 12:00:00");
And ofcourse, on a EU system, this will work, but on an American (or even others) this gives an error because there is no 15th month...
This gets triggered even before the constructor of my com-accessible class and that's why the error couldn't be handled.
Dumb mistake, but proves that sometimes errors look very complex while they are very simple.

Argument Confusion Running a C# Method In IronPython

I am trying to run a c# method in the IronPython (2.7.3) console:
The c# (compiled to a dll) is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PythonTest
{
public class PythonTest
{
public PythonTest(){}
public int GetOne()
{
return 1;
}
public double Sum(double d1, double d2)
{
return d1+d2;
}
public string HiPlanet()
{
return "Hi Planeta";
}
}
}
the python is
import sys
sys.path.append("Y:\\")
import clr
clr.AddReferenceToFile('./PythonTest')
import PythonTest
a = PythonTest.PythonTest.GetOne()
I get a TypeError in ironpython saying that the function takes one arguement (which it doesn't according to my c#!). I am confused and woudl appeciate help here, I'm just trying to call some c# functions provide the arguements and get the results, thanks in advance!
Since it's an instance method, you need to instantiate the object before calling GetOne method:
obj = PythonTest.PythonTest()
a = obj.GetOne()
or, in one-liner:
a = PythonTest.PythonTest().GetOne()

Categories

Resources