Remote OPC Connected but can't read - c#

I am writing a custom OPC Client application in C#, enabling reading of data from a RSLinx Server.
First of, I wasn't able to connect to the RSLINX Opc Server remotely. Exception Access Denied kept occurring.
I then alteredthe DCOM Settings of MyComputer -> Com Security -> Access Permissions and Launch and Activation Permissions, enabled everything for the User 'Everyone'
This then allowed me to connect, but when It comes to read the server I get the following exception -
[System.Runtime.InteropServices.COMException] {"Exception from HRESULT: 0x80040202"} System.Runtime.InteropServices.COMException
I have scoured the web as much as I possibly can, and they all boil down to Dcom related issues. I have simply tried everything with the Dcom Settings. I have made sure the settings are enabled for both MyComputer and RSLinx server.
I am using two .dll files from OPCFoundation - opcNetApi.dll , opcNetApi.Com.dll
Here is my code, (It may be handy)
private void readplc()
{
Opc.URL url = new Opc.URL("opcda://48.5.0.05/RSLinx OPC Server");
Opc.Da.Server server = null;
OpcCom.Factory fact = new OpcCom.Factory();
server = new Opc.Da.Server(fact, null);
try
{
server.Connect(url, new Opc.ConnectData(new System.Net.NetworkCredential()));
}
catch (Exception exy)
{
MessageBox.Show(exy.Message);
}
// Create a group
Opc.Da.Subscription group;
Opc.Da.SubscriptionState groupState = new Opc.Da.SubscriptionState();
groupState.Name = "Group";
groupState.Active = true;
group = (Opc.Da.Subscription)server.CreateSubscription(groupState);
// add items to the group.
Opc.Da.Item[] items = new Opc.Da.Item[6];
items[0] = new Opc.Da.Item();
items[0].ItemName = "[ALARM]F20:9";
items[1] = new Opc.Da.Item();
items[1].ItemName = "[ALARM]F22:30";
items[2] = new Opc.Da.Item();
items[2].ItemName = "[ALARM]F22:6";
items[3] = new Opc.Da.Item();
items[3].ItemName = "[ALARM]F18:8";
items[4] = new Opc.Da.Item();
items[4].ItemName = "[ALARM]F22:32";
items[5] = new Opc.Da.Item();
items[5].ItemName = "[ALARM]F22:5";
items = group.AddItems(items);
try
{
group.DataChanged += new Opc.Da.DataChangedEventHandler(OnTransactionCompleted); // COM EXCEPTION THROWN HERE
Console.ReadLine();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
Console.ReadKey();
}
}
private void OnTransactionCompleted(object group, object hReq, Opc.Da.ItemValueResult[] items)
{
for (int i = 0; i < items.GetLength(0); i++)
{
}
}
I know for certain that the code works as I have tried building the application connecting as a localhost on the PC I am trying to remote connect to, it reads the data happily.
Hopefully, someone will have some idea what is going on, I have spent over 12 hours in the last 4 working days trying to work it out!

This is working for me:
_opcServer = new Server(_comFactory, null) { Url = new Opc.URL("opcda://localhost/FactoryTalk Gateway") };
_opcServer.Connect();

Related

ServerManager fails with 0x80040154 in c# Winforms app creating simple web application in IIS

I am writing a small app that installs IIS and configures a website before deploying the files to it.
On a freshly reset Windows 10, the first attempt always fails with the 0x80040154 COM+ component failure as documented in This question
I looked at the version I am using and it is the latest and correct one for .net standard (4.8) and not the one meant for .net core
When I press the button to rerun the function it always finishes correctly. I tried using a retry routine, and it fails on each retry, yet runs fine again when the button is pressed. The reason for this I assume is that the server manager object isn't disposed when it hits the catch block since its in a using statement.
I can work around that, but I really want to understand the issue and make a permanent fix.
My routine simply creates a website in IIS and creates an app pool to assign to it.
And it is running with elevated privileges
For reference:
Machine is Windows 10 latest from the downloadable media creator.
Microsoft.Web.Administrator version is 7.0.0.0
App is .net 4.8 standard windows forms
using (var serverManager = new ServerManager())
{
string iisrootdir = drive;
//Check for inetpub/wwwroot
if (!Directory.Exists(iisrootdir)) //Check for Drive D
{
iisrootdir = #"C:\";
}
string iiscmsdir = Path.Combine(iisrootdir, "webdir", "appdir");
if (!Directory.Exists(iiscmsdir))
Directory.CreateDirectory(iiscmsdir);
var settings = new ApplicationSettings();
settings.ReadFromFile();
settings.CMSPATH = iiscmsdir;
settings.SaveToFile();
try
{
string poolName = "DefaultAppPool";
if (serverManager.Sites.Count > 0)
{
Site myDefualtWebsite = serverManager.Sites[0];
if (myDefualtWebsite != null)
{
OnRaiseInstallEvent(new InstallEventArgs("CreateWebsite", ProcessState.Started,
"Remove Default Website"));
serverManager.Sites.Remove(myDefualtWebsite);
serverManager.CommitChanges();
}
}
if (!WebsiteExists("sitename"))
{
mySite.ServerAutoStart = true;
}
Site site = serverManager.Sites["sitename"];
if (!AppPoolExists(poolName))
{
serverManager.ApplicationPools.Add(poolName);
}
ApplicationPool apppool = serverManager.ApplicationPools[poolName];
apppool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
apppool.ManagedRuntimeVersion = "";
serverManager.Sites["sitename"].ApplicationDefaults.ApplicationPoolName = poolName;
foreach (var item in serverManager.Sites["sitename"].Applications)
{
item.ApplicationPoolName = poolName;
}
serverManager.CommitChanges();
apppool.Recycle();
serverManager.CommitChanges();
}
catch (Exception ex)
{
if (ex.Message.Contains("80040154") && errorCount < 4)
{
if (serverManager != null)
serverManager.Dispose();
errorCount++;
OnRaiseInstallEvent(new InstallEventArgs("CreateWebsite", ProcessState.Started,
"Error encountered with COM+ object, trying again: " + errorCount));
CreateWebsite(#"D:\");
}
else
{
if (serverManager != null)
serverManager.Dispose();
errorCount = 0;
OnRaiseErrorEvent(new InstallErrorEventArgs("CreateWebsite", ProcessState.Error, ex));
return false;
}
}
finally
{
serverManager?.Dispose();
}
Thanks for the help Guys. I found the problem.
DISM was running in its own thread. The Process object exited the moment it launched. My function was then attempting to configure IIS before it had finished installing.

C# LDAP authentication strange issue

I have a VMWare machine with Windows Server 2012 and Active Directory installed. The domain name is "cpx.local" and I have created a new user "testad".
I have a C# Winform application so I can test the connection to the LDAP server and then get all the users or groups in the Active Directory.
This is the code that works fine:
string server = "192.168.238.129";
string port = "389";
System.DirectoryServices.Protocols.LdapConnection ldapConnection =
new System.DirectoryServices.Protocols.LdapConnection(new LdapDirectoryIdentifier(server + ":" + port));
TimeSpan mytimeout = new TimeSpan(0, 0, 0, 1);
try
{
ldapConnection.AuthType = AuthType.Anonymous;
ldapConnection.AutoBind = false;
ldapConnection.Timeout = mytimeout;
ldapConnection.Bind();
Console.WriteLine(("Successfully authenticated to ldap server "));
ldapConnection.Dispose();
}
catch (LdapException ex)
{
Console.WriteLine(("Error with ldap server "));
Console.WriteLine((ex.GetType().ToString() + (":" + ex.Message)));
}
The problem is that if I want to authenticate with the new user "testad" it doesn't work.
I change the AuthType to be Basic and set the credentials.
ldapConnection.AuthType = AuthType.Basic;
ldapConnection.Credential = new NetworkCredential(#"cpx\testad", "test#D12345", "cpx.local");
ldapConnection.AutoBind = false;
ldapConnection.Timeout = mytimeout;
ldapConnection.Bind();
I get the following error:
I have tried to Login the Windows Server 2012 with this user and I can login perfect.
The interesting thing is that the following code is working fine:
var dirEntry = new DirectoryEntry(string.Format("LDAP://{0}/{1}", "192.168.238.129:389", "DC=cpx,DC=local"), "testad", "test#D12345");
var searcher = new DirectorySearcher(dirEntry)
{
Filter = "(&(&(objectClass=user)(objectClass=person)))"
};
var resultCollection = searcher.FindAll();
Am I doing something wrong with the NetworkCredentials?
maybe doubleccheck credentials.in NetworkCredential support username without 'cpx/' in front. as domain is provided
ldapConnection.Credential = new NetworkCredential(#"testad", "test#D12345", "cpx.local");
If you set the AuthType to Negotiate, does it work ?
AuthType details here
change:
ldapConnection.AuthType = AuthType.Basic;
to:
ldapConnection.AuthType = AuthType.Negotiate;
Regarding the domain name - cpx vs cpx.local - you can take a look at this article about some recommended practices
http://www.mdmarra.com/2012/11/why-you-shouldnt-use-local-in-your.html
The correct way to name an Active Directory domain is to create a subdomain that is the delegation of a parent domain that you have registered and have control over. As an example, if I ever started a consulting business and used the Internet-facing website mdmarra.com as my company's site, I should name my Active Directory domain ad.mdmarra.com or internal.mdmarra.com, or something similar. You want to avoid making up a TLD like .local and you also want to avoid the headache of using mdmarra.com for the Internet-facing zone and the internal zone.
Change: ldapConnection.AutoBind= false;
to: ldapConnection.AuthType = true;

Having trouble retrieving collection from MongoDB database

I am trying to learn MongoDB for my next project. I have installed MongoDB on my Windows 7 machine. I am able to create collection and retrieve data using mongo.exe. I am trying to use official C# driver to manipulate collection but my test console application goes in "abyss" while trying to connect to server (I guess).
I have written following code:
static void Main(string[] args)
{
try
{
// Create server settings to pass connection string, timeout, etc.
var settings = new MongoServerSettings();
settings.Server = new MongoServerAddress("localhost", 27017);
// Create server object to communicate with our server
var server = new MongoServer(settings);
server.Connect(TimeSpan.FromMilliseconds(1000));
// Get our database instance to reach collections and data
var message = string.Empty;
server.IsDatabaseNameValid("test", out message);
Console.WriteLine(message);
var database = server.GetDatabase("test");
var users = database.GetCollection("users").FindAll();
foreach (var user in users)
{
Console.WriteLine("User found");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
Console.WriteLine(ex.StackTrace);
}
Console.ReadLine();
}
Console application stops working on line server.Connect(). I have put this line just to find problem, I know it is not necessary to connect to server explicitly. If I remove that line then Console application stops working on line foreach (var user in users)
I also tried to get server using following code with no success:
var mongoClient = new MongoClient("mongodb://<host>:27017");
var server = mongoClient.GetServer();
Code is perfect! It was idiosyncrasy of MongoDB that when you try to connect to MongoDB through C# driver, you will have to accept connection for first time. Next time onwards MongoDB will remember your last acceptance and move on.
There has to be setting to remove this condition but I haven't found it yet. I will update as soon as possible.
Happy MangoDB Learning.
use your last example like this:
var mongoClient = new MongoClient("mongodb://localhost");
var server = mongoClient.GetServer();
Unless it's not running on the same machine or not on the standard port

virtual directory properties in c#

I did a quick search and got this link
From StackOverflow
I get this error message
"Unknown error (0x80005000)"
when it hits
bool canCreate = !(schema.Properties["Syntax"].Value.ToString().ToUpper() == "BOOLEAN");
My requirement is to create a virtual directory and set AccessRead, IsolationMode, Scripts and Executables, ApplicationProtection to medium.
Got it to work, here is the code:
Note: I still couldn't get it to work on windows 7, it throws the same error and the message includes reference to System.InterOp... At the moment I don't care about getting it to work on windows 7, I deployed it on windows 2003, it works.
public void CreateVirtualDirectory(string virtualdirectory, string physicalpath)
{
try
{
///check if path exists
if (!Directory.Exists(physicalpath))
{
Log(string.Format(#"CreateVirtualDirectory; Path not found - {0}", physicalpath));
return;
}
DirectoryEntry parent = new DirectoryEntry(string.Format("IIS://{0}/W3SVC/1/Root", Environment.MachineName));
foreach (System.DirectoryServices.DirectoryEntry v in parent.Children)
{
if (v.Name == virtualdirectory)
{
try
{
parent.Invoke("Delete", new string[] { v.SchemaClassName, virtualdirectory });
}
catch
{
}
}
}
DirectoryEntry newFolder = (DirectoryEntry)parent.Invoke("Create", "IIsWebVirtualDir", virtualdirectory);
newFolder.InvokeSet("Path", physicalpath);
newFolder.Invoke("AppCreate", false);
newFolder.InvokeSet("AppFriendlyName", virtualdirectory);
const int MEDIUM_POOL = 2;
newFolder.Properties["AccessRead"][0] = true;
newFolder.Properties["AccessExecute"][0] = true;
newFolder.Properties["AccessWrite"][0] = false;
newFolder.Properties["AccessScript"][0] = true;
newFolder.Properties["AuthNTLM"][0] = true;
newFolder.Properties["AppIsolated"].Clear();
newFolder.Properties["AppIsolated"].Add(MEDIUM_POOL);
newFolder.CommitChanges();
}
catch (Exception ex)
{
Log(string.Format(#"CreateVirtualDirectory '{0}' failed; {1}", virtualdirectory, ex.Message));
}
}
You're using DirectoryEntry to create websites which requires IIS Compat Mode to be installed/configured in IIS. That's compat mode for the IIS6 directory entry interfaces if you're on IIS7 or later.
If it's not installed, you'll get 80005000.
If you're on IIS7 or later (Windows Server 2008 or Vista and later), the newer (non-compat mode) approach is the new managed Microsoft.Web.Administration.
http://blogs.msdn.com/b/carlosag/archive/2006/04/17/microsoftwebadministration.aspx

When programmatically creating a new IIS web site, how can I add it to an existing application pool?

I have successfully automated the process of creating a new IIS website, however the code I've written doesn't care about application pools, it just gets added to DefaultAppPool. However I'd like to add this newly created site to an existing application pool.
Here is the code I'm using to create the new website.
var w3Svc = new DirectoryEntry(string.Format("IIS://{0}/w3svc", webserver));
var newsite = new object[] { serverComment, new object[] { serverBindings }, homeDirectory };
var websiteId = w3Svc.Invoke("CreateNewSite", newsite);
site.Invoke("Start", null);
site.CommitChanges();
<update>
Although this is not directly related to the question, here are some sample values being used above. This might help someone understand exactly what the code above is doing more easily.
webServer: "localhost"
serverComment: "testing.dev"
serverBindings: ":80:testing.dev"
homeDirectory: "c:\inetpub\wwwroot\testing\"
</update>
If I know the name of the application pool that I'd like this web site to be in, how can I find it and add this site to it?
You have to assign the AppPool on the virtual dir (not the webserver) and set the AppIsolated property to 2 which mean pooled-process ;)
http://msdn.microsoft.com/en-us/library/ms525598%28v=VS.90%29.aspx
Relevant code sample from link:
static void AssignVDirToAppPool(string metabasePath, string appPoolName)
{
// metabasePath is of the form "IIS://<servername>/W3SVC/<siteID>/Root[/<vDir>]"
// for example "IIS://localhost/W3SVC/1/Root/MyVDir"
// appPoolName is of the form "<name>", for example, "MyAppPool"
Console.WriteLine("\nAssigning application {0} to the application pool named {1}:", metabasePath, appPoolName);
try
{
DirectoryEntry vDir = new DirectoryEntry(metabasePath);
string className = vDir.SchemaClassName.ToString();
if (className.EndsWith("VirtualDir"))
{
object[] param = { 0, appPoolName, true };
vDir.Invoke("AppCreate3", param);
vDir.Properties["AppIsolated"][0] = "2";
Console.WriteLine(" Done.");
}
else
Console.WriteLine(" Failed in AssignVDirToAppPool; only virtual directories can be assigned to application pools");
}
catch (Exception ex)
{
Console.WriteLine("Failed in AssignVDirToAppPool with the following exception: \n{0}", ex.Message);
}
}
Note that if you are not explicitly adding a new virtual directory to the application, the metabasePath will simply be "IIS://<servername>/W3SVC/<siteID>/Root"
You need to get the AppPoolfrom IIS://{0}/W3SVC/AppPools, and attach it to the site's AppPoolId. Something like:
var appPool = new DirectoryEntry(
string.Format("IIS://{0}/W3SVC/AppPools/{1}", webServer, appPoolName)
);
site.Properties["AppPoolId"].Value = appPool;
site.Properties["AppPoolId"][0]= "poolname";

Categories