I am trying to extend an application which supports COM/ActiveX objects. The COM dll needs to send some data to other system on local network for further processing and actions.
I have tested a basic WCF Host-Client setup and it works fine from console client to console host. But now I need to send data through a client in com-visible dll.
This is the code of the dll :
namespace Client
{
[Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]
[ComVisible(true)]
public interface ISend
{
[DispId(1)]
bool SendData(string msg);
}
[ClassInterface(ClassInterfaceType.None), Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"), ProgId("Client.Send")]
[ComVisible(true)]
public class Send : ISend
{
static BasicHttpBinding binding = new BasicHttpBinding();
static EndpointAddress endpoint = new EndpointAddress(new Uri("http://192.168.1.6:8000/WCFHost/Service/GetData"));
GetDataClient client = new GetDataClient(binding, endpoint);
[ComVisible(true)]
public bool SendData(string msg)
{
try
{
if (client.getData(msg))
{
client.Close();
return true;
}
else
{
client.Close();
return false;
}
}
catch (Exception e)
{
client.Abort();
return false;
}
}
}
}
The dll works fine as a reference but cannot create object through target application(It has the functionality to access COM/ActiveX objects). When I try to access the dll by :
obj = CreateObject ("Client.Send");
obj.SendData("Hello")
It says :
COM/object handle is null
on second line nothing more!
I created a com-visible dll in similar way using Remoting to achieve this and it worked like a charm. But now its not working as a WCF Client.
It would be really appreciated if someone could point out what I am doing wrong.
I had moved to Remoting where this was not a problem, but I was suggested to stay away from it and achieve this through WCF.
P.S : I am new to C# so please excuse any stupid mistakes.
COM does not support static methods, see here for further details. You'll need to remove the static keyword from the class in order to let your client create an instance. This will also allow you to implement the interface, which is not possible for static classes.
As a side note, your code shouldn't even compile, since the static modifier on an interface is illegal. Remove it as well, then recompile and re-register your DLL.
Related
I have a WCF service written in C# what works fine. It's the usual self-hosted type in a Win32 console application. But now, for reasons of special updating (it's not a simple desktop computer where you simply update a program using the usual installers but a special embedded system) I'd like to move the actual type implementing the service to a DLL which is loaded by a very simple loader executable using reflection:
string DllFilename = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "myservice.dll");
var ServicesDll = Assembly.LoadFrom(DllFilename);
var ServicesType = ServicesDll.GetType("MyNamespace.MyServices");
var Instance = (ServiceBase)Activator.CreateInstance(ServicesType);
ServiceBase.Run(new ServiceBase[] { Instance });
I can deploy the service all three ways, either installing it normally via MSI, or using sc.exe, or even with a self-managing code like this:
ServicesType.GetMethod("InstallService").Invoke(null, null);
ServicesType.GetMethod("StartService").Invoke(null, null);
where
public class MyServices : ServiceBase {
//...
public static void InstallService() {
if (!IsInstalled())
using (AssemblyInstaller installer = GetInstaller()) {
var state = new Hashtable();
try {
installer.Install(state);
installer.Commit(state);
}
catch {
installer.Rollback(state);
throw;
}
}
}
public static void StartService() {
if (IsInstalled())
using (var controller = new ServiceController("MyService")) {
if (controller.Status != ServiceControllerStatus.Running) {
controller.Start(); // fails here
controller.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(10));
}
}
}
//...
}
As I mentioned in the beginning, everything works just fine in an EXE. But as soon as the MyService type is in the DLL, InstallService is still OK but StartService fails with System.ComponentModel.Win32Exception: %1 is not a valid Win32 application. It seems that Windows expects the service executable (which it stores itself not as the EXE I actually installed but as the DLL the service was implemented in) to be an executable to be called at its leisure.
How can I circumvent this limitation, maybe to signal somehow that it's my loader EXE that I want to be called, not the dependent DLL?
Split your MyServices into two classes - one a thin wrapper inherited from ServiceBase (to live in .EXE) and another doing real work including self-hosting to live in updatable .DLL. The first one can take an instance of the second as a .ctor argument and delegate start/stop calls to it.
You may also want to look at Topshelf.
I finally went with Rene's suggestion, the bulk of the service stays in an EXE and loaded as an assembly into the loader. Fortunately, loading an assembly can be done both from EXE and DLL.
I need to test old legacy remoting service.
The code comes with ready to use client side which means I have an object that takes care of all the channeling implementation.
When connecting to this client from executable - everything is good and works great, but when trying to do the same operation using dll (nunit is the test framework) SecurityException is thrown whenever I'm trying to query the service.
Why there is difference using dll code over executable?
This is the error message:
Type System.Runtime.Remoting.ObjRef and the types derived from it
(such as System.Runtime.Remoting.ObjRef) are not permitted to be
deserialized at this security level.
needless to say I have no access to the server side code, but it is .Net 4.5 (although remoting) and the test code is also .Net 4.5, and I must mention I run the code on the same machine
This is the code generates the client side channel
public class RemoteClient
{
public static RemoteClient GenerateClient()
{
SetupHttpChannelAndBuildUrl();
cm_remote obj = null;
try
{
obj = (cm_remote) Activator.GetObject(typeof (cm_remote_base), url);
obj.DoSomething();
return obj;
}
catch (Exception e)
{
...
}
}
private static SetupHttpChannelAndBuildUrl()
{
...
}
public void DoSomethig()
{
...
}
}
and this is the both dll and exe code triggering the functionality:
public RemoteClient GetRemoteClient()
{
return RemoteClient.GenerateClient();
}
from exe it returns instance of RemoteClient, from dll returns null
We have the following code...
DiscoveryService.cs
[ServiceContract]
public interface IDiscoveryService
{
[OperationContract]
void PrintHello();
}
public class DiscoveryService : IDiscoveryService
{
public void PrintHello()
{
MessageBox.Show("Hello");
}
}
Server.cs
private void InitializeDiscovery()
{
Uri baseAddress = DiscoveryHelper.AvailableTcpBaseAddress;
ServiceHost host = new ServiceHost(typeof (DiscoveryService), baseAddress);
host.EnableDiscovery();
host.Open();
}
Client.cs
private void DiscoverAddressClick(object sender, EventArgs e)
{
EndpointAddress address = DiscoveryHelper.DiscoverAddress<IDiscoveryService>();
Binding binding = new NetTcpBinding();
IDiscoveryService proxy = ChannelFactory<IDiscoveryService>.CreateChannel(binding, address);
proxy.PrintHello();
(proxy as ICommunicationObject).Close();
}
Now, this code seems to work up until the point where we modify anything having to do with the Service Contract, at which point we get a TCP Exception, Error Code 10061 (Connection Actively Refused). However, we cant figure out who is refusing, or why. If we try to revert our code to a previous state, when it was working, we can't, it simply refuses to work after (it seems) we add / remove things from the project.
Every time I build I clean first. We're also following the instructions posted here.
Any thoughts?
I will begin by stating that I'm not all that familiar with WCF Discovery....though I may implement it in our new server stack. But one thing I do not see here is the presence of a proxy server in what you present here.
As I understand it from what I've read on MSDN your WCF service will basically register it's presence to a proxy server running on the network. Your client will then 'discover' your service server via the DiscoveryProxy server. That may be the piece of the plumbing you are missing.
You current implementation of the server will close down the server as soon as the InitializeDiscovery Method is complete because your host only has function scope and will be cleaned up by the garbage collector unless you keep a reference somewhere else.
This is example code in java.
Shared interfaces:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Compute extends Remote {
public Object executeTask(Task t) throws RemoteException;
}
Task (this will be passed as parameter):
import java.io.Serializable;
public interface Task extends Serializable {
public Object execute();
}
Server:
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class ComputeEngine extends UnicastRemoteObject implements Compute {
public ComputeEngine() throws RemoteException {
super();
}
public Object executeTask(Task t) {
return t.execute();
}
public static void main(String[] args) {
setRmiCodebase();
System.setSecurityManager(new RMISecurityManager());
try {
Compute engine = new ComputeEngine();
Naming.rebind("//localhost:1099/Compute", engine);
System.out.println("ComputeEngine started.");
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static void setRmiCodebase() {
String codebase = System.getProperty("java.rmi.server.codebase");
if (codebase != null)
return;
// set codebase based on location of this clsas (is it in jar or filesistem?)
}
}
Client:
import java.math.BigDecimal;
/**
* Calculates Pi to arbitrary number of digits:
*/
public class Pi implements Task {
public Pi(int digits) {
this.digits = digits;
}
public Object execute() {
return computePi(digits);
}
public static BigDecimal computePi(int digits) {
// compute Pi
}
}
Client main:
import java.math.BigDecimal;
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
public class ComputePi {
public static void main(String[] args) {
setRmiCodebase();
System.setSecurityManager(new RMISecurityManager());
try {
Compute comp = (Compute)Naming.lookup("//localhost:1099/Compute");
Pi task = new Pi(100);
BigDecimal pi = (BigDecimal)comp.executeTask(task);
System.out.println(pi);
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static void setRmiCodebase() {
String codebase = System.getProperty("java.rmi.server.codebase");
if (codebase != null)
return;
// set codebase based on location of this clsas (is it in jar or filesistem?)
}
}
As you can see, code (not just data) from client is transfered to server and executed there and result of computation is returned. Server does not know that class Pi exists, it only knows about Task interface.
I need something like this to work in .net environment (C# if it is important). WCF would be nice, but I am looking for the most straightforward solution, so WCF is not compulsory. I am not sure even what keyword to use to google documetation or solution for this.
Any help will be appreciated.
Afaik .NET doesn't support this out of the box - you can do remoting but that won't (as is) let you run code from the client on the server. I think you would have to implement something that transfers the dll containing the code you want to execute to the server, and then probably load that dll in a separate AppDomain (because you can't unload dll's unless you load them into a separate AppDomain), and then have a way to specify the class to run.
.NET does not natively support "sending code" to be executed on another computer. Typically the necessary code would be compiled to assemblies and pre-installed on the server before it is called by the client. This is true of both remoting and WCF. You could have a two-way remoting situation where the server calls back to a method on the client via WCF, but I suspect this is not what you want. The only way I'm aware of to really run dynamic code on the server is to generate dynamic code, send it to the server as a string, and then have the server compile it to an in-memory assembly on the fly and then execute it. If you are interested in doing so, take a look at my answer to a similar question:
Autovivified properties?
However, it's not exactly something I would suggest in most cases. I would suggest you rethink your design, first, to see if there is any way to do what you need in a typical ".NET way".
What You want is .NET Remoting.
Here's link to article showing how to migrate from RMI to .NET Remoting.
But according to this MSDN article this is a legacy technology and You should use WCF.
Edit:
You can't "just like that" get .NET Remoting functionality with WCF.
Here you can read discussion about porting from .NET Remoting to WCF.
But if you don't know WCF at all You shoud start here. And You probably won't get your results fast :).
This MSDN page has more or less this exact use case you described. You just need to modify the ServiceContract
http://msdn.microsoft.com/en-us/library/system.servicemodel.netnamedpipebinding.aspx
You would probably only need to modify this part:
[ServiceContract(Namespace = "http://UE.Samples")]
public interface ICalculator
{
[OperationContract]
double Add(double n1, double n2);
}
// Service class which implements the service contract.
public class CalculatorService : ICalculator
{
public double Add(double n1, double n2)
{
return n1 + n2;
}
Instead of scalar values put your executeTask method with parameter of your own class there.
I don't believe .NET has a built-in solution for transferring executable code from client to server. Assuming the security constraints allow it, you might consider sending interpretable code such as Python or JavaScript which could be executed server-side via IronPython or IronJS respectively. If C# is a requirement (and you still have access to the source code), sending the source and compiling server-side (via Roslyn or the Mono's evaluator).
I'm writing a windows service application, which will be accessed through .NET Remoting.
The problem is I can't figure out how to access service objects from remotable class.
For example, I've a handler class:
class Service_console_handler
{
public int something_more = 20;
//some code...
TcpChannel serverChannel = new TcpChannel(9090);
ChannelServices.RegisterChannel(serverChannel);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(RemoteObject), "RemoteObject.rem",
WellKnownObjectMode.Singleton);
//from here on RemoteObject is accessible by clients.
//some more code doing something and preparing the data...
}
And I've a remotable class:
public class RemoteObject : MarshalByRefObject
{
private int something = 10;
public int Get_something()
{
return something;
}
}
Clients can access data in RemoteObect with no problem. But how can I access Service_console_handler object (i.e. to retrieve useful info from something_more)?
Sorry for dumb questions and thanks in advance.
What you want is somehow to access the instance of ServiceConsoleHandler via a RemoteObject instance, which is visible for the client.
For this you need to consider two things: (1) Get control over the object construction of the RemoteObject instance and make it accessible and (2) modify ServiceConsoleHandler so it can be accessed remotely.
(1)
How would you construct a RemoteObject instance in ServiceConsoleHandler, if you don’t need to consider remoting?
I guess you would do something like this:
class ServiceConsoleHandler
{
…
RemoteObject remoteObject = new RemoteObject();
// now assume that you also already have
// modified the RemoteObject class so it can hold
// a reference to your server:
remoteObject.Server = this;
…
}
It would be nice if you could make this object accessible for the client. You can do this by using RemotingServices.Marshal instead of RemotingConfiguration.RegisterWellKnownServiceType:
class ServiceConsoleHandler
{
…
TcpServerChannel channel = new TcpServerChannel(9090);
ChannelServices.RegisterChannel(channel, true);
RemoteObject remoteObject = new RemoteObject();
remoteObject.Server = this;
RemotingServices.Marshal(remoteObject, "RemoteObject.rem");
…
}
(2)
If you execute the code right now and access the remoteObject.Server in the client code you would get some remoting exception, because the class ServiceConsoleHandler cannot be accessed remotely. Therefore you need the add the [Serializable] attribute:
[Serializable]
class ServiceConsoleHandler
{ … }
Reason: Types which should be accessed remotely, need to be marshaled to some special transferrable representation. This way they can be squeezed through the TCP port and transferred via the TCP protocol. Basic data types can by marshaled by the framework, so you don't need to think about them. For custom types you will need to state, how to do this. One way to do this is by subclassing from MarshalByRefObject. That’s exactly what you have already done with RemoteObject. Another way is to mark your custom classes as [Serializable] as shown above.
That’s it. Now you should be able to access the server’s field in the client code. Note that you don’t need your existing code for object activation:
TcpClientChannel channel = new TcpClientChannel();
ChannelServices.RegisterChannel(channel, true);
RemoteObject remoteObject = (RemoteObject)Activator.GetObject(
typeof(RemoteObject), "tcp://localhost:9090/RemoteObject.rem");
Console.WriteLine(remoteObject.Server.SomethingMore);
For me .NET remoting is full of funny surprises and sleepless nights. To counter this, make yourself familiar with the remoting concepts (which are from my point of view poorly documented). Dig into the serialization concept (MarshalByRefObject vs. [Serializable]). If you want to make a production code out of it, think a very good ways to handle remoting exceptions. Also consider multithreading. There could be more than one client using this remote object at once.
Have fun!
Thank you! I very much appreciate thoroughness and clarity of you answer.
Most bizzare thing is that I didn't even know that you can publish object instance. About a dozen simple tutorials I studied proposed RemotingConfiguration.RegisterWellKnownServiceType as only method to do remoting. Stupid me.
Now remoting looks much more useful to me. I just wrote a quick test application and it worked. Thanks again.