I'm currently developing a client-server-based game using C# remoting. Everything seemed to work fine until I tried to set up the TcpServerChannel. I'm using the complex constructor (with IDictionary and stuff), but the problem seems to appear even with the normal TcpServerChannel(int port) constructor.
A SocketException with the message "No such host is known" is thrown when I try to create the channel instance. Stack trace:
at System.Net.Dns.GetAddrInfo(String name)
at System.Net.Dns.InternalGetHostByName(String hostName, Boolean includeIPv6)
at System.Net.Dns.GetHostEntry(String hostNameOrAddress)
at System.Runtime.Remoting.Channels.CoreChannel.GetMachineIp()
at System.Runtime.Remoting.Channels.Tcp.TcpServerChannel.SetupMachineName()
at System.Runtime.Remoting.Channels.Tcp.TcpServerChannel..ctor(IDictionary properties, IServerChannelSinkProvider sinkProvider, IAuthorizeRemotingConnection authorizeCallback)
at System.Runtime.Remoting.Channels.Tcp.TcpServerChannel..ctor(IDictionary properties, IServerChannelSinkProvider sinkProvider)
at Some.Namespace.Server.Start()
However, after some research I found out that the problem is the CoreChannel.GetMachineIp() method. I found the code of the CoreChannel class here and noticed, that the Dns.GetHostEntry(string hostNameOrAddress) method is called twice. In the first call the return value of the Dns.GetHostName() method (which correctly returns my computer name, lets say we got "hostname" here) is passed as argument while in the second call the returned IPHostEntry.HostName is passed. Here is where the problem occurs: Since my computer is inside a domain (lets call it "domain"), the returned IPHostEntry.HostName contains "hostname.domain" instead of just "hostname". The second Dns.GetHostEntry("hostname.domain") call then throws an exception because "hostname.domain" is not known.
There is a similar question, but it didn't help me in any way.
I don't know why this thing is working that way, but I can't change my computer name or the domain, so I am stuck at this point and really need some help.
I am using Windows 7 64bit (if it matters), VS Professional 2012 Update 4 and .NET Framework 4.5.
Thanks for the help
Related
I am maintaining a legacy module where the system gets some information from the user (asp.net) and then calls a remote server via remoting to print a receipt on a printer connected to the remote server. Until recently, the remote server was able to connect to the database, so we passed only a int value (the paymentId).
But now, our main application (asp.net) is being moved offsite along with the database, but the receipt printing still has to work, so now, we are trying to send all the receipt's information so the server can generate the receipt from that instead of using the id and generating the receipt from the database. So, the concept is pretty basic, except... remoting is a bit of a pain sometimes. ;)
My object is serializable and inherits from MarshalByRefObject. It contains some int, decimal and string properties. The object goes through and seems to be serialized and deserialized correctly, but when any property is called, I receive the exception. I have read on other posts/forums that I must open a client channel on my client application (asp.net), but I'm confused.
My client application connects to many such remoting services depending on the printer it must print on. Must I create a client channel for each one? can I configure a client channel "on demand" when I connect to the server or must I create it at the app start? Can I specify when I connect (Activator.GetObject(...)) to use a bidirectional channel? Is there a way not to need the client channel (as in transform all properties to fields or something)?
Here is my stack trace (so we see the problem is the PaymentID property, which is the first one that is accessed) :
at System.Runtime.Remoting.Proxies.RemotingProxy.InternalInvoke(IMethodCallMessage reqMcmMsg, Boolean useDispatchMessage, Int32 callType)
at System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(IMessage reqMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at GDCMLib.Reports.DataSources.PaymentReceiptItem.get_PaymentID()
at GDCMLib.Services.PaymentReceiptPrintRemoteObj.PaymentReceiptPrint.PrintPaymentReceipt(PaymentReceiptItem paymentReceipt) in u:\SVNClient\PortailDCM\trunk\Libraries\GDCMLib.Services.PaymentReceiptPrintRemoteObj\PaymentReceiptPrint.cs:ligne 37
Finally, as my data structure only used basic data types, I made sure it was transferred as a value type by changing it from a class to a struct. It now works perfectly.
I am using the API System.DirectoryServices.AccountManagement to bind to an AD-LDS instance. I am using simple bind with a user that exists locally in the AD-LDS instance. It works when I run the client on the server that hosts AD-LDS but it doesn't work when I run the client on a remote computer.
This is the code I use to bind and search for a user:
var c = new PrincipalContext(ContextType.ApplicationDirectory, "fullhostname:50001", "CN=Users,DC=app,DC=local", ContextOptions.SimpleBind, "CN=joe,CN=Users,DC=app,DC=local", "abc");
var u = UserPrincipal.FindByIdentity(c, IdentityType.Name, "john");
This is the exception that is thrown when I run it on a remote computer:
System.DirectoryServices.AccountManagement.PrincipalServerDownException: The server is not operational.
---> System.Runtime.InteropServices.COMException: The server is not operational.
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.DirectoryEntry.get_Options()
at System.DirectoryServices.AccountManagement.PrincipalContext.DoLDAPDirectoryInit()
--- End of inner exception stack trace ---
at System.DirectoryServices.AccountManagement.PrincipalContext.DoLDAPDirectoryInit()
at System.DirectoryServices.AccountManagement.PrincipalContext.DoApplicationDirectoryInit()
at System.DirectoryServices.AccountManagement.PrincipalContext.Initialize()
at System.DirectoryServices.AccountManagement.PrincipalContext.get_ConnectedServer()
at MyApplication.DiagnosticsController.TryAdLdsSettings(AdLdsData data) in C:\code\MyApplication\DiagnosticsController.cs:line 166
If I instead use the System.DirectoryServices API it works also from remote computer:
var obj = new DirectoryEntry("LDAP://fullhostname:50001/CN=Users,DC=app,DC=local", "CN=joe,CN=Users,DC=app,DC=local",
"abc", AuthenticationTypes.None);
obj.RefreshCache();
This works, but I need to use System.DirectoryServices.AccountManagement API instead.
Anyone knows what is wrong?
I was able to get this to work on a domain, with a few minor changes, I hope these tips help.
Your 2nd-to-last parameter when creating PrincipalContext, "CN=joe,CN=Users,DC=app,DC=local", should be the fully qualified username, not the LDAP path; this will normally look like COMPUTER-NAME\\joe (whatever your computer is named) or if you are on a domain as I am, DOMAIN-NAME\\joe. (If fullhostname is not your local workstation, then you may be on a domain, or you may need to specify fullhostname\joe to request authentication against the host server rather than your local, since your local's credentials probably won't work on the host server).
For testing this on a Domain, I had to change the first parameter from ContextType.ApplicationDirectory to ContextType.Domain; it sounds like you are not on a domain, so you will probably need ContextType.ApplicationDirectory, but the error message makes me think that Active Directory Services aren't running.
Since :50001 is high enough to be blocked, make sure you don't have firewall software that is blocking the request, either outgoing from your machine, or incoming to the "fullhostname" machine; and, of course, make sure your active directory services are actually available on 50001, and not some other authentication protocol.
I ended up rewriting my usages of PrincipalContext and UserIdentity to use DirectoryEntry directly instead. It took me a few hours to find appropriate reimplementations for the convenient UserIdentity functions I needed, but after doing so everything worked fine.
It's quite a mystery to me why the problem occurred in the first place. My only guess is that with my particular version of AD LDS in my particular configuration, there's a bug somewhere in the underlying libraries. Rewriting everything directly in DirectoryEntry, getting everything as exactly similar as I know how to do, fixed the problem.
I have developed a Quartz.Net windows service to run scheduled jobs and I have set up an SQLite jobstore for it to work. Here are the settings of my AdoJobStore:
# SQLite settings
quartz.jobStore.type = Quartz.Impl.AdoJobStore.JobStoreTX, Quartz
quartz.jobStore.misfireThreshold = 60000
quartz.jobStore.lockHandler.type = Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz
quartz.jobStore.useProperties = true
quartz.jobStore.dataSource = default
quartz.jobStore.tablePrefix = qrtz_
quartz.jobStore.driverDelegateType = Quartz.Impl.AdoJobStore.SQLiteDelegate, Quartz
quartz.dataSource.default.provider = SQLite-10
quartz.dataSource.default.connectionString = Data Source=.\jobs.db;Version=3
Before giving you a headache, I should say that this is a working solution! That means the job store works just fine! Each job consists of running an .exe file that will get its parameters from an SQL Server database using a connection string that is stored in a text file.
So if the service is started automatically, everything works fine.
After installing the Windows service, we give it a username that has access to the destination database in service's Log On tab. We set the service to start automatically (and have tried the Automatic Delayed mode as well). In the Recovery tab of the service we also command it to Restart in case it crashes.
However, the service does not seems to be starting by itself on some machines and that has kept me wondering for quite a while now. On machines with failure, this is what my service logs when it fails to start:
2015-02-13 15:09:15,674 [1] ERROR Quartz.Server.QuartzServer [(null)] - Server initialization failed:Unable to bind scheduler to remoting.
Quartz.SchedulerException: Unable to bind scheduler to remoting. ---> System.Net.Sockets.SocketException: No such host is known
at System.Net.Dns.InternalGetHostByName(String hostName, Boolean includeIPv6)
at System.Net.Dns.GetHostEntry(String hostNameOrAddress)
at System.Runtime.Remoting.Channels.CoreChannel.GetMachineIp()
at System.Runtime.Remoting.Channels.Tcp.TcpServerChannel.SetupMachineName()
at System.Runtime.Remoting.Channels.Tcp.TcpServerChannel..ctor(IDictionary properties, IServerChannelSinkProvider sinkProvider, IAuthorizeRemotingConnection authorizeCallback)
at System.Runtime.Remoting.Channels.Tcp.TcpChannel..ctor(IDictionary properties, IClientChannelSinkProvider clientSinkProvider, IServerChannelSinkProvider serverSinkProvider)
at Quartz.Simpl.RemotingSchedulerExporter.RegisterRemotingChannelIfNeeded()
at Quartz.Simpl.RemotingSchedulerExporter.Bind(IRemotableQuartzScheduler scheduler)
at Quartz.Core.QuartzScheduler.Bind()
at Quartz.Core.QuartzScheduler.Initialize()
--- End of inner exception stack trace ---
at Quartz.Core.QuartzScheduler.Initialize()
at Quartz.Impl.StdSchedulerFactory.Instantiate()
at Quartz.Impl.StdSchedulerFactory.GetScheduler()
at Quartz.Server.QuartzServer.Initialize() [See nested exception: System.Net.Sockets.SocketException (0x80004005): No such host is known
at System.Net.Dns.InternalGetHostByName(String hostName, Boolean includeIPv6)
at System.Net.Dns.GetHostEntry(String hostNameOrAddress)
at System.Runtime.Remoting.Channels.CoreChannel.GetMachineIp()
at System.Runtime.Remoting.Channels.Tcp.TcpServerChannel.SetupMachineName()
at System.Runtime.Remoting.Channels.Tcp.TcpServerChannel..ctor(IDictionary properties, IServerChannelSinkProvider sinkProvider, IAuthorizeRemotingConnection authorizeCallback)
at System.Runtime.Remoting.Channels.Tcp.TcpChannel..ctor(IDictionary properties, IClientChannelSinkProvider clientSinkProvider, IServerChannelSinkProvider serverSinkProvider)
at Quartz.Simpl.RemotingSchedulerExporter.RegisterRemotingChannelIfNeeded()
at Quartz.Simpl.RemotingSchedulerExporter.Bind(IRemotableQuartzScheduler scheduler)
at Quartz.Core.QuartzScheduler.Bind()
at Quartz.Core.QuartzScheduler.Initialize()]
The service is installed on host 127.0.0.1 port 555 and does not seem to need a dependency because it works under some other machines.
I have a feeling that the service is trying to start itself before there is an access to the database or before the TCP host is established. But if that's the case, why does it work on other devices?
I have asked the same question before but have failed to find a working answer. Any help from Quartz.Net dev team or Windows Services experts is greatly appreciated.
Disclaimer. I'm not part of the Quartz.net dev team, nor am I an expert on Windows services
The service is installed on host 127.0.0.1 port 555 and does not seem to need a dependency because it works under some other machines.
Your reasoning is false. It could be that the required services just happen to have always started on the other machines, but without setting a dependency this is not guaranteed.
So, since you are getting a DNS error, I would suggest you start by setting your service do that it depends on Dnscache and Tcpip .
I have a query about .Net Remoting, versioning and creating client activated objects.
Here is the scenario:
There are 2 interfaces, residing in their own assembly “SharedTypes”: IServer and IAccount. IServer contains methods “GetStatus” which returns a string, and “CreateAccount” which returns an IAccount type. This is registered into the GAC as v1.0.0.0.
Server application references SharedTypes and implements IServer and IAccount with concrete classes, Server and Account. These are MarshalByRefObject objects. The Server application marshals the Server class as a singleton object.
Client application references SharedTypes and connects to the remoteable Server object through the IServer interface successfully. Here I can call GetStatus and CreateAccount (which returns a client activated object) successfully. All OK so far.
Now I increment the version of SharedTypes to v2.0.0.0 and register into the GAC, removing the old v1.0.0.0 version.
The Server application is built against this version, but the client is not.
Now when I run the client application, it will as expected complain with a System.IO.FileNotFoundException, i.e. it could not find v1.0.0.0 of SharedTypes in the GAC.
If I copy v1.0.0.0 of SharedTypes in the exe directory of the client, the client application eventually binds to this (after GAC lookup was unsuccessful). The client application starts and I can call the GetStatus on the IServer object successfully (through the singleton object). However, if I call CreateAccount – which should return a client activated object, I get the following exception:
System.InvalidCastException: Return argument has an invalid type.
at System.Runtime.Remoting.Proxies.RealProxy.ValidateReturnArg(Object arg, Type paramType)
at System.Runtime.Remoting.Proxies.RealProxy.PropagateOutParameters(IMessage msg, Object[] outArgs, Object returnValue)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at SharedTypes.IServer.GetAccount()
My question is why does calling GetStatus on the server activated singleton object from the client (which is using v1.0.0.0) not throw this exception, whereas creating the client activated object through CreateAccount does? Since both types are created on the server, I would have thought that GetStatus call would have resulted in the same exception?
The CLR normally ensures that only one specific version of an assembly can be loaded into a process. That doesn't work in this scenario because there are two copies of the CLR at work, one on the server and one on the client. So now it is up to the remoting infrastructure to ensure that remoted type objects are compatible. Which is does with aplomb in your case. Not sure I got the question posed in your last sentence but the server is not otherwise aware of the version of the assembly loaded in the client.
Recompiling the client is required.
This may not answer your specific question, but yours is the only question I can find on StackOverflow regarding an inexplicable InvalidCastException with the message "Return argument has an invalid type" involving a server singleton.
In my case, my client started by requesting the server's singleton (i.e. the object that was passed to RemotingServices.Marshal() on the server), then in the course of its processing, it happened to get a reference to that singleton via other methods, and got this exception.
I worked around it by creating two remote-proxies for the top-level object, one to serve as the singleton, and one that could be used internally in other contexts. Perhaps it's because, in the course of client processing, it tried to get a reference to that singleton object through a different type than the one that was passed to RemotingServices.Marshal(). I'm still not sure. But having two remote-proxies for the same local object, one to serve as a remoting singleton, and one for all other internal purposes, worked around the problem. Luckily I have a stateless remote-proxy architecture, so having two for the same local-object didn't cause any trouble.
Edit: Later, I found a simpler solution, one that doesn't require two proxies -- call RemotingServices.Marshal() with the actual type of the proxy in parameter 3, instead of the interface type.
For me, client application was on .net 4.0 while server app was running on 4.5.
Installing .net framework 4.5 on client machine fixed this issue.
i'm trying to call a .net assembly that wraps a few COM calls (to a third party dll) from Sql Server. The assembly registers fine (i tried registering with unsafe and external access), but when i run the procedure i get this error:
A .NET Framework error occurred during execution of user-defined routine or aggregate "ManagedCodeCallTest":
System.UriFormatException: Invalid URI: The URI is empty.
System.UriFormatException:
at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind)
at System.Uri..ctor(String uriString)
at System.ComponentModel.Design.RuntimeLicenseContext.GetLocalPath(String fileName)
at System.ComponentModel.Design.RuntimeLicenseContext.GetSavedLicenseKey(Type type, Assembly resourceAssembly)
at System.ComponentModel.LicenseManager.LicenseInteropHelper.GetCurrentContextInfo(Int32& fDesignTime, IntPtr& bstrKey, RuntimeTypeHandle rth)
at ManagedCode.MyClass.ArielComponentCall()
Any ideas? Is what i'm trying to do even possible? I read something about licensed dlls but the information was very vague.
EDIT: CLR code in case it helps:
[SqlProcedure]
public static void ArielComponentCall()
{
Ariel.ApplicationClass application = new Ariel.ApplicationClass();
object arielDoc = application.OpenDocument(#"P:\Projects\COAT\Ariel1.run");
}
The project that contains this class has the reference to the com object.
The SqlClr implementation on SQL Server contains a list of "blessed" .net Assembly methods which will work within SQL Server. This is being managed through Host Protection Attributes. More precisely
SQL Server disallows the use of a type or member that has a
HostProtectionAttribute that specifies a HostProtectionResource value
of SharedState, Synchronization, MayLeakOnAbort, or
ExternalProcessMgmt. This prevents the assemblies from calling members
that enable sharing state, perform synchronization, might cause a
resource leak on termination, or affect the integrity of the SQL
Server process.
Depending on the "Access" settings of your assembly SQL Server will throw an error (when SAFE), or do nothing with the blocked method (UNSAFE and EXTERNAL ACCESS).
Unfortunately for you, the System.ComponentModel.LicenseContext class has the SharedState Host Protection Attribute and is part of the code which is not allowed. As a consequence, somewhere in your code there is a call to a method in the LicenseManager which will silently do nothing.
Either way, running com components within the SQL Server process is not a good idea as a crash in the com component will crash your entire SQL Server.