Wednesday, March 25, 2009

Solving compile errors: CS0006, CS0009 in Visual Studio 2008

Recently, when I was trying to run any web application project, I was getting the following error message:

CS0006: Metadata file 'C:\WINDOWS\assembly\GAC_32\System.EnterpriseServices\2.0.0.0__b03f5f7f11d50a3a\System.EnterpriseServices.dll' could not be found

So, I searched the net for solution, and this is how I was able to solve it:

First, check whether "System.EnterpriseServices.dll" file actually exists at that folder or not. You cannot navigate to the GAC_32 folder using windows explorer. You have to use command line to go there. From start menu, select 'Run' > type 'cmd' press enter. then directly go there:

cd C:\WINDOWS\assembly\GAC_32\System.EnterpriseServices\2.0.0.0__b03f5f7f11d50a3a

press enter. then use dir /p command to see all files in it and search for the "System.EnterpriseServices.dll" file if you cannot find it there, then again go to this location:

cd C:\Windows\Microsoft.NET\Framework\v2.0.50727

press enter. From here you have to copy the file to the desired location. Use this command:

copy System.EnterpriseServices.dll C:\WINDOWS\assembly\GAC_32\System.EnterpriseServices\2.0.0.0__b03f5f7f11d50a3a

Now try to run that web project from visual studio, and it should work. If it doesnt work, and gives you another error: CS0009 compile error, then you have to copy all the files to that folder. First navigate to that source folder:

cd C:\Windows\Microsoft.NET\Framework\v2.0.50727

press enter. Then use this command to copy all DLLs:

copy *.dll C:\WINDOWS\assembly\GAC_32\System.EnterpriseServices\2.0.0.0__b03f5f7f11d50a3a

You dont need to overwrite any file that already exists at the destination folder. This should solve CS0009 error.

Sunday, March 15, 2009

How to email all AD (active directory) users from sharepoint event handlers

Sharepoint is normally (or should be) installed into server PCs. And the event handler features are also installed into servers. But AD users use sharepoint from their client PCs. In this case, program fails to log into the domain if you try to retrieve information directly. Normally this code is good enough to retrive all AD users:

ArrayList employeeEmailAddresses = new ArrayList();
string filter = "(&(objectCategory=person)(objectClass=user)(sn=*))";
DirectorySearcher search = new DirectorySearcher(filter);
foreach (SearchResult result in search.FindAll())
{
DirectoryEntry entry = result.GetDirectoryEntry();
if (entry.Properties["mail"].Value != null)
{
string str = entry.Properties["mail"].Value.ToString();
employeeEmailAddresses.Add(str);
}

}

But if you are trying to get information from sharepoint event handlers using the above code block, program will throw exception at search.FindAll() :

COMEXCEPTION was caught: {"An operations error occurred.\r\n"}

In order to solve this, you need to manually log in the program into the AD using the credentials of an existing AD user. Then, the program can read all the informations from AD.

you can use this method to get all the users from AD:

private ArrayList getEmployeeMailAddresses()
{
ArrayList employeeEmailAddresses = new ArrayList();
try
{
string filter = "(&(objectCategory=person)(objectClass=user)(sn=*))";

using (DirectoryEntry root = new DirectoryEntry("LDAP://domainName","userName","password", AuthenticationTypes.Secure))
{
using (DirectorySearcher searcher = new DirectorySearcher(root))
{
searcher.ReferralChasing = ReferralChasingOption.All;
searcher.SearchScope = SearchScope.Subtree;
searcher.Filter = filter;

foreach (SearchResult result in searcher.FindAll())
{
DirectoryEntry entry = result.GetDirectoryEntry();
if (entry.Properties["mail"].Value != null)
{
string str = entry.Properties["mail"].Value.ToString();
employeeEmailAddresses.Add(str);
}

}
}

}
}
catch (Exception ex)
{
}

return employeeEmailAddresses;
}

This line: DirectoryEntry("LDAP://domainName","userName","password", AuthenticationTypes.Secure)) will login the program into the appropriate domain, then you can get informations for all AD users. After getting the arraylist that contains all the users, you can mail all of them using this method:

private void sendEmail(string htmlBody, ArrayList employeeEmailAddresses, string itemCreator, string subject)
{
AlternateView avHtml = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html);
MailMessage m1 = new MailMessage();
m1.AlternateViews.Add(avHtml);

for (int i = 0; i < employeeEmailAddresses.Count; i++)
{
m1.To.Add(new MailAddress(employeeEmailAddresses[i].ToString()));
}
m1.Subject = subject;


SmtpClient client = new SmtpClient("smtpclientaddress");
client.Send(m1);

}

Sunday, March 8, 2009

How to find a user (SPUser or domain user) belongs to a AD (active Directory) group / domain group from sharepoint

First, Lets think a set of domain users belong to a Active Directory/ domain group named $DotNet-Developers. Of course, you have added users from active directory, not from sharepoint. Now, you want to find out from sharepoint whether a user belongs to that group. To do this, what you need to do is:

1. create a sharepoint group (SPGroup) in the site, for example named DotNetSPGroup.
2. Add the Domain group ($DotNet-Developers) inside that sharepoint Group.
3. Give that SPGroup at least "read permission" in the site.
4. Search for user under that SPGroup using group.ContainsCurrentUser method.

Now, while running any specific program, you want to find out whether a specific user belongs to that active directory group or not. Lets think, you are getting the user from a "Person or group field" from sharepoint. Now retrieve the SPUser from that field. after that just use this method:


private bool IsUserInGroup(SPUser targetUser, SPItemEventProperties properties, string groupName)
{
bool containsUser=false;


using(SPSite targerSite= new SPSite(properties.SiteId,targetUser.UserToken))
{
SPWeb targetWeb=targerSite.OpenWeb(properties.RelativeWebUrl);

SPGroupCollection LMSGroups = properties.OpenWeb().Groups;
try
{
SPGroup groupForDotNetDev = LMSGroups[groupName];
if (groupForDotNetDev.ContainsCurrentUser)
{
containsUser=true;
}
}
catch (Exception e)
{
}
}
return containsUser;
}

This should do the trick.

using(SPSite targerSite= new SPSite(properties.SiteId,targetUser.UserToken)) changes the current user to that user. Then, you can use groupName.ContainsCurrentUser user method to check whether the user is inside that group or not. This method even searches inside AD groups, if that AD group is added inside an SPGroup. So, it works just fine.

I must thank El Blanco for his post.