My team is using a program written in C# to read all users from a specific OU. The program behaves very strange. Sometimes it is working for a couple of weeks and then without any big changes on our AD or any other related component, it throws an exception. Then it is not working for a couple of weeks and after some time it start to run normally again.
Code
DirectoryEntry searchRoot = new DirectoryEntry("<LDAP string>")
searchRoot.AuthenticationType = AuthenticationTypes.None;
DirectorySearcher search = new DirectorySearcher(searchRoot);
search.Filter = <our filter>;
search.PropertiesToLoad.Add("<some property>");
search.PageSize = 1;
SearchResult result;
SearchResultCollection resultCol = null;
try
{
resultCol = search.FindAll();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
if (resultCol != null)
{
Console.WriteLine("Result Count: " + resultCol.Count); //.Count throws the Exception
}
Exception
Unhandled Exception: System.DirectoryServices.DirectoryServicesCOMException: An operations error occurred.
at System.DirectoryServices.SearchResultCollection.ResultsEnumerator.MoveNext()
at System.DirectoryServices.SearchResultCollection.get_InnerList()
at System.DirectoryServices.SearchResultCollection.get_Count()
Data: System.Collections.ListDictionaryInternal
Error Code: -2147016672
Extended Error: 8431
Extended Error Message: 000020EF: SvcErr: DSID-020A07A7, problem 5012 (DIR_ERROR), data -1018
HResult: -2147016672
Message: An operations error occured.
Source: System.DirectoryServices
Stack Trace: at System.DirectoryServices.SearchResultCollection.ResultsEnumerator.MoveNext()
Target Site: Boolean MoveNext()
Additional Information
Target Framework: .Net Framework 4.6.1 (no additional libraries)
The program is executed on a domain controller
What I have tried
I have created a loop to use the MoveNext() function of the
enumerator and found out that it loads results up to a specific
element and then crashes
It is always the same element
After the first exception all retries fail as well
The user that starts it is a domain admin (but I
have also tried it with an enterprise admin account, so it is
probably not a permission issue)
I have deleted the user that should be read when the exception happens but dring the next run the exception was thrown for a previous user
I have come to a point, where I have no more ideas on solving this problem. I would appreciate all your support.
This answer just summarizes our conversation in comments.
This thread partially matches the error you are getting:
problem 5012 (DIR_ERROR) data -1018
And the answer from a Microsoft MVP is:
That is a checksum error in the database, you have corruption in your
database which is usually due to a failing disk or disk subsystem or
possibly a system crash and data not being written from a write cache.
So it sounds like you might have the same thing going on.
But it may only be one DC that has the problem. So to help you narrow down which one, you can specify the DC in the LDAP path like so:
LDAP://dc1.example.com/OU=Target,OU=My User Group,OU=My Users,DC=example,DC=com
This can help you in two ways:
It can identify the bad DC so you know which one you need to fix (and possibly take it offline until it is fixed), and
You can specifically target a good DC so your script will keep working.
Related
I have some pretty basic code that seems to work for most people but there's at least one workstation that throws this HRESULT code when it runs these couple lines of code:
Outlook.Application _OutlookInstance = new Outlook.Application();
Outlook.Stores stores = _OutlookInstance.Session.Stores;
Any idea what HRESULT code 0xCA140115 is or what it means? I can't find it on MSDN anywhere...
The workstation that experiences the problem is at a remote call center location, so I can't do any immediate testing/debugging, or easily see what is specifically different about this workstation versus the others. I would imagine there might be more workstations at that same call center that could have the error, but this code is still in the testing phase.
Sorry for the delay, but I was able to get through several more iterations of testing and find out what the issue was. First off, my original post was incorrect. The code flow made it seem like the error was happening during those 2 initial lines, but it was actually happening a little later, when I was looping through the stores, like this:
Outlook.Stores stores = _OutlookInstance.Session.Stores;
foreach(Outlook.Store store in stores) // <----- THIS LINE
{
...
}
Each time the user ran this, he would get a different HRESULT error code:
0xCA140115
0xAF64011D
0xC1F4011D
0xC834011D
The only consistent factor was the "4011" in the middle.
When I upped the logging, I could see that the user had 18 mailboxes and the foreach() loop was getting through the first 3 but failing on the 4th. The 4th mailbox was a "Public Folders" store associated to another mailbox that was added in a different way than the rest of the mailboxes (it had something to do with it being an Outlook 365 mailbox that required different authentication).
So essentially it ended up being that any attempt to even touch that particular mailbox/store (including the "store" variable being set) would result in that COM exception.
I was able to work around it by looping through stores via numeric index so that the setting of "store" was inside my try/catch block, like this:
for(int i = 0; i < stores.Count; i++)
{
try
{
Outlook.Store store = stores[i];
...
}
catch(Exception)
{
...
}
}
Now when the loop hit that particular store, I could tell that it was Outlook saying that the server wasn't available, and the store was an online-only store, so the store couldn't be accessed.
I'm still not certain about why the error codes changed each time, but there you have it.
I am new to C# and Windows development in general. I need to use it to build an integration between our data in MySQL to Microsoft Dynamics GP (using eConnect). That part is not really relevant, but adds a little context to the examples below.
Ok, so when I connect to the service:
eConnectClient client = new eConnectClient();
string newCustomerDocument = "SOME_XML_HERE";
string connectionString = "Data Source=localhost;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=GPVPM;";
try
{
client.Open();
bool result = client.CreateEntity(connectionString, newCustomerDocument);
}
catch (FaultException<eConnectFault> e)
{
Console.Write("ECONNECT FAULT: " + e.ToString() + "\n");
}
Now, if I have an error in my XML, it will cause a FaultException to be thrown, but the resulting exception message is useless:
ECONNECT FAULT: System.ServiceModel.FaultException`1[GPConnect.eConnect.eConnectFault]: The creator of this fault did not specify a Reason. (Fault Detail is equal to GPConnect.eConnect.eConnectFault).
I found that if I look in the Event Viewer for Windows, it paints an entirely different picture of what happened:
Specifically:
Error Number = 250 Stored Procedure= taUpdateCreateCustomerRcd Error Description = The Tax Schedule does not exist in the Tax Schedule Master Table
So that is something actionable that can help me identify the problem and fix it.
The question:
With C#, how can I get the same level of details from an Exception as is recorded by the Event Manager?
The server may and may not return the detailed exception to the client. You may want to check
e.Detail /* of type GPConnect.eConnect.eConnectFault */
and
e.InnerException
inside the catch block for potential details.
Recently two users in our system started getting this error when trying to add them to a role.
System.Runtime.InteropServices.COMException: Cannot create a file when that file already exists. (Exception from HRESULT: 0x800700B7)
What is interesting is that the same error occurs regardless of the configuration, running locally we use an XML store and in the test environment it uses SQL Server.
Here is code where is blows up - AddMemberName() - as you can see this is pretty straightforward stuff and it's worked well for a while, it's just these two users all of the sudden
public void AddUserToRole(string roleName, string userName, bool upn)
{
string uName = userName;
if (upn)
uName = getAltUserNames(userName).First();
AzAuthorizationStore store = new AzAuthorizationStoreClass();
store.Initialize(2, _provider.StoreLocation, null);
IAzApplication app = store.OpenApplication(_provider.ApplicationName, null);
IAzRole role = app.OpenRole(roleName, null);
role.AddMemberName(uName, null);
role.Submit(0, null);
Marshal.FinalReleaseComObject(role);
}
I tried googling various different terms but can't find much of anything regarding this. I did read this post but it doesn't seem to be the issue.
Thanks
Check you Active Directory usernames and the underlying OU name especially. Check for duplicates and mismatches.
I had an issue once where a user got married and her name changed.
logging exception
the code below allows to save the content of an exception in a text file. Here I'm getting only the decription of the error.
but it is not telling me where the exception occured, at which line.
Can anyone tell me how can I achive that so I can get even the line number where the exception occured?
#region WriteLogError
/// <summary>
/// Write an error Log in File
/// </summary>
/// <param name="errorMessage"></param>
public void WriteLogError(string errorMessage)
{
try
{
string path = "~/Error/" + DateTime.Today.ToString("dd-mm-yy") + ".txt";
if (!File.Exists(System.Web.HttpContext.Current.Server.MapPath(path)))
{
File.Create(System.Web.HttpContext.Current.Server.MapPath(path))
.Close();
}
using (StreamWriter w = File.AppendText(System.Web.HttpContext.Current.Server.MapPath(path)))
{
w.WriteLine("\r\nLog Entry : ");
w.WriteLine("{0}", DateTime.Now.ToString(CultureInfo.InvariantCulture));
string err = "Error in: " + System.Web.HttpContext.Current.Request.Url.ToString()
+ ". Error Message:" + errorMessage;
w.WriteLine(err);
w.WriteLine("__________________________");
w.Flush();
w.Close();
}
}
catch (Exception ex)
{
WriteLogError(ex.Message);
}
}
#endregion
I find that the easiest way to log exceptions in C# is to call the ToString() method:
try
{
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
This usually gives you all the information you need such as the error message and the stack trace, plus any extra exception specific context information. (however note that the stack trace will only show you source files and line numbers if you have your application compiled with debug information)
It is worth noting however that seeing a full stack trace can be fairly offputting for the user and so wherever possible you should try to handle exceptions and print out a more friendly error message.
On another note - you should replace your method WriteLogError with a fully featured logging framework (like Serilog) instead of trying to write your own.
Your logging method is not thread safe (your log file will probably end up with log messages being intermingled with each other) and also should definitely not call itself if you catch an exception - this will mean that any exceptions that occur whilst logging errors will probably cause a difficult to diagnose StackOverflow exception.
I could suggest how to fix those things, however you would be much better served just using a proper logging framework.
Just log ToString(). Not only will it give you the stack trace, but it'll also include the inner exceptions.
Also, when you deploy a release build of your code to a production environment for instance, don't forget to include the .pdb files in the release package. You need that file to get the line number of the code that excepted (see How much information do pdb files contain? (C# / .NET))
Your solution is pretty good. I went through the same phase
and eventually needed to log more and more (it will come...):
logging source location
callstack before exception (could be in really different place)
all internal exceptions in the same way
process id / thread id
time (or request ticks)
for web - url, http headers, client ip, cookies, web session content
some other critical variable values
loaded assemblies in memory
...
Preferably in the way that I clicked on the file link where the error occurred,
or clicked on a link in the callstack, and Visual Studio opened up at the appropriate location.
(Of course, all you need to do is *.PDB files, where the paths from the IL code
to your released source in C # are stored.)
So I finally started using this solution:
It exists as a Nuget package - Desharp.
It's for both application types - web and desktop.
See it's Desharp Github documentation. It has many configuration options.
try {
var myStrangeObj = new { /*... something really mysterious ...*/ };
throw new Exception("Something really baaaaad with my strange object :-)");
} catch (Exception ex) {
// store any rendered object in debug.html or debug.log file
Desharp.Debug.Log(myStrangeObj, Desharp.Level.DEBUG);
// store exception with all inner exceptions and everything else
// you need to know later in exceptions.html or exceptions.log file
Desharp.Debug.Log(ex);
}
It has HTML log formats, every exception in one line,
and from html page you can open in browser, you can click
on file link and go to Visual Studio - it's really addictive!
It's only necessary to install this Desharp editor opener.
See some demos here:
Web Basic App
Web MVC App
Console App
Try to check out any of those repos and log something by the way above.
then you can see logged results into ~/Logs directory. Mostly anything is configurable.
I am only answering for the ask, other people have already mentioned about the code already. If you want the line number to be included in your log you need to include the generated debug files (pdb) in your deployment to the server. If its just your Dev/Test region that is fine but I don't recommend using in production.
Please note that the exception class is serializable. This means that you could easily write the exception class to disk using the builtin XmlSerializer - or use a custom serializer to write to a txt file for example.
Logging to output can ofcourse be done by using ToString() instead of only reading the error message as mentioned in other answers.
Exception class
https://learn.microsoft.com/en-us/dotnet/api/system.exception?redirectedfrom=MSDN&view=netframework-4.7.2
Info about serialization, the act of converting an object to a file on disk and vice versa.
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/serialization/
I have some code that is looking up group memberships from local groups on a machine. For each member, it tries to load some information about the user (eg. find a group and get the names of each of its members).
The code:
using (DirectoryEntry machine = new DirectoryEntry("WinNT://" + Environment.MachineName + ", Computer"))
{
using (DirectoryEntry group = machine.Children.Find(groupName, "group"))
{
object members = group.Invoke("members", null);
foreach (object groupMember in (IEnumerable) members)
{
using (DirectoryEntry member = new DirectoryEntry(groupMember))
{
member.RefreshCache();
string name = member.Name;
// <code snipped>
}
}
}
}
The code works fine most of the time, but for some group members, it throws a FileNotFoundException when the RefreshCache() method is thrown:
System.IO.FileNotFoundException:
The filename, directory name, or volume label syntax is incorrect.
(Exception from HRESULT: 0x8007007B)
at System.DirectoryServices.Interop.UnsafeNativeMethods.IAds.GetInfo()
at System.DirectoryServices.DirectoryEntry.RefreshCache()
at GroupLookup.GetLocalGroupMembership(String groupName)
What is causing the FileNotFoundException (and what file is it looking for)?
The file-not-found error is commonly used in the Win32 API as a "resource not found" error. So, it's returned for things like missing Registry keys, or - in this case - an ADSI node.
I am definitely not an ADSI expert, but your first call to the DirectoryEntry constructor seems to be using an invalid path style according to MSDN. I believe you'd need the domain name before the machine name.
Update:
Noticed this on another MSDN page: "GetInfo cannot be used for groups that contain members that are wellKnown security principals in the WinNT scope."
Given the stack trace, it seems like this may be causing the problem.
I didn't get to the bottom of what was causing the FileNotFoundException, although I suspect it was to do with the group set-up - the groups had both local and domain users in them.
Because I only needed the users' names and SIDs, and these were already present on the DirectoryEntry, I solved this issue by not calling the RefreshCache method. This lets the code execute without an exception.