Environment
Windows 8.1
Visual Studio 2017 Community
C#
WPF application
Issue
YoutubeExtractor throws System.Net.WebException when downloading a video.
I got YoutubeExtractor by Nuget and it doesn't work. The VideoInfo object isn't null. I tried several videos on Youtube and the same exceptions popped up. I googled the issue and it didn't give me much help.
Here's the code.
var videos = DownloadUrlResolver.GetDownloadUrls(#"https://www.youtube.com/watch?v=M1wLtAXDgqg");
VideoInfo video = videos
.First(info => info.VideoType == VideoType.Mp4 && info.Resolution == 360);
if (video.RequiresDecryption)
DownloadUrlResolver.DecryptDownloadUrl(video);
var videoDownloader = new VideoDownloader(video, System.IO.Path.Combine("D:", video.Title + video.VideoExtension));
videoDownloader.DownloadProgressChanged += (sender_, args) => Console.WriteLine(args.ProgressPercentage);
videoDownloader.Execute(); // I got the exception here.
How can I solve this issue? Thanks.
EDIT : 2017/10/26 13:42 GMT
Alexey 'Tyrrrz' Golub's answer helped so much! I corrected his original code and here it is.
using YoutubeExplode;
using YoutubeExplode.Models.MediaStreams;
var client = new YoutubeClient();
var video = await client.GetVideoAsync("bHzHlSLhtmM");
// double equal signs after s.VideoQuality instead of one
var streamInfo = video.MuxedStreamInfos.First(s => s.Container == Container.Mp4 && s.VideoQuality == VideoQuality.Medium360);
// "D:\\" instead of "D:"
var pathWithoutExtension = System.IO.Path.Combine("D:\\", video.Title);
// streamInfo.Container.GetFileExtension() instead of video.VideoExtension (video doesn't have the property)
var path = System.IO.Path.ChangeExtension(pathWithoutExtension, streamInfo.Container.GetFileExtension());
// new Progress<double>() instead of new Progress()
await client.DownloadMediaStreamAsync(streamInfo, path, new Progress<double>(d => Console.WriteLine(d.ToString("p2"))));
I am using VMWare Workstation (v11) to run a Virtual Machine for testing.
Now I'd like to pragmatically add and remove USB-devices attached to the system.
using VMware.Vim; I can connect to the server and query it. I can also remove devices like CD-Drives and other things. But I can't disconnect any USB-Passthrough-Device. I either get : "Invalid Config" or "Unknown Error" (most likely due to the lib being interopt and not transporting all information)
My current code:
using VMware.Vim;
var dongle = devs.SingleOrDefault(i => i.DeviceInfo.Summary.Contains("Silicon"));
if (dongle != null) {
var usbDongle = (dongle as VirtualUSB);
usbDongle.Connected = false;
var spec = new VirtualMachineConfigSpec() {
DeviceChange = new[] {
new VirtualDeviceConfigSpec() {
Device = dongle,
Operation = VirtualDeviceConfigSpecOperation.remove,
}
}
};
vm.ReconfigVM(spec);
Neither Operation = "remove" or "edit" has any effect. I'm always getting some kind of error...
I appreciate any ideas!
Regards,
Corelgott
I'm trying to get the current iteration path for the teams TFS project. The way I'm trying to do this is by using the blog from http://blog.johnsworkshop.net/tfs11-api-reading-the-team-configuration-iterations-and-areas/ . I start by getting the team configurations from the following code:
TfsTeamProjectCollection tpc = TFSConncetion(#"http://tfs/url");
var configSvc = tpc.GetService<TeamSettingsConfigurationService>();
var configs = configSvc.GetTeamConfigurationsForUser(projectUri);
The problem with this is that my configs is always null, even though I'm a member of the team. I'm positive my projects URI is correct as well. After this I would get the team settings and use that to display the current iteration path.
TeamSettings ts = config.TeamSettings;
Console.WriteLine(ts.CurrentIterationPath);
Even if this didn't work I can still query the iteration dates from the team setting to get the one iteration that has a start date before today and finish date after today. The main problem is that I can't get my TeamSettingsConfigurationService to return anything but null when I try to get the team configurations with my projects URI.
There must be something wrong with your server connection or the project uri you're passing as the other code looks okay.
Maybe try something like this:
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri("http://server:8080/tfs/collection"),
new System.Net.NetworkCredential(tfsUserName, tfsPassword));
tpc.EnsureAuthenticated();
Connect to Team Foundation Server from a Console Application
There is a good sample here which you can download (WPF client) and it will allow you to select a server connection, Team Project and Team:
TFS API Part 46 (VS11) – Team Settings
You can step through it and check the values you're passing into your code.
The sample gets the team configuration information is the same way you have in your code.
TeamSettingsConfigurationService teamConfig = tfs.GetService<TeamSettingsConfigurationService>();
var configs = teamConfig.GetTeamConfigurationsForUser(new[] { projectInfo.Uri });
Once you have the collection of TeamConfiguration items then you need TeamSettings.CurrentIterationPath
I actually got the answer myself without using TeamSettingsConfigurationService at all. Here's how I did it:
private static XmlNode currentIterationNode;
TfsTeamProjectCollection tpc = TFSConncetion(#"http://tfs/url");
ICommonStructureService4 css = tpc.GetService<ICommonStructureService4>();;
WorkItemStore workItemStore = new WorkItemStore(tpc);
foreach (Project teamProject in workItemStore.Projects)
{
if (teamProject.Name.Equals("TeamProjectNameGoesHere"))
{
NodeInfo[] structures = css.ListStructures(teamProject.Uri.ToString());
NodeInfo iterations = structures.FirstOrDefault(n => n.StructureType.Equals("ProjectLifecycle"));
if (iterations != null)
{
XmlElement iterationsTree = css.GetNodesXml(new[] { iterations.Uri }, true);
XmlNodeList nodeList = iterationsTree.ChildNodes;
currentIterationNode = FindCurrentIteration(nodeList);
String currentIterationPath = currentIterationNode.Attributes["Path"].Value;
}
}
}
Where currentIterationPath is the current iteration path from TFS. The key to doing this was to get the NodeInfo[] array of structures and the NodeInfo iterations from these two lines of code I got from chamindacNavantis https://social.msdn.microsoft.com/Forums/vstudio/en-US/4b785ae7-66c0-47ee-a6d2-c0ad8a3bd420/tfs-get-iteration-dates-metadata?forum=tfsgeneral:
NodeInfo[] structures = css.ListStructures(teamProject.Uri.ToString());
NodeInfo iterations = structures.FirstOrDefault(n => n.StructureType.Equals("ProjectLifecycle"));
After that I created an xml with nodes of every iteration inside the team project. These nodes also have the start date and end dates of each iteration. So I checked each node for a start date before DateTime.Now and finish date after DateTime.Now, which is all FindCurrentIteration(nodeList) does.
And that will give you the current iteration node.
The simplest way I've found to do it was by using ICommonStructureService4 and TeamSettingsConfigurationService methods:
static TfsTeamProjectCollection _tfs = TfsTeamProjectCollectionFactory
.GetTeamProjectCollection("<tfsUri>")
(...)
static string GetCurrentIterationPath()
{
var css = _tfs.GetService<ICommonStructureService4>();
var teamProjectName = "<teamProjectName>";
var project = css.GetProjectFromName(teamProjectName);
var teamName = "<teamName>";
var teamSettingsStore = _tfs.GetService<TeamSettingsConfigurationService>();
var settings = teamSettingsStore
.GetTeamConfigurationsForUser(new[] { project.Uri })
.Where(c => c.TeamName == teamName)
.FirstOrDefault();
if (settings == null)
{
var currentUser = System.Threading.Thread.CurrentPrincipal.Identity.Name;
throw new InvalidOperationException(
$"User '{currentUser}' doesn't have access to '{teamName}' team project.");
}
return settings.TeamSettings.CurrentIterationPath;
}
Using C#, I'm trying to set dhcp option 12 using ManageDHCP.dll. In a nutshell, this is what I'm doing:
var dhcpManager = new DHCPManager(dhcpServerName)
var hostRecord = new HostInfo
{
Name = deviceDhcpName,
IPv4 = deviceIpAddress,
IsReserved = true,
MAC = deviceMacAddress
};
dhcpManager.SetRecord(dhcpServerScope, hostRecord);
String hostName = deviceDhcpName + "." + dnsZoneName;
var hostnameOpt = new Option
{
ID = 12,
Values = new List<string> { hostName }
};
dhcpManager.SetOption(hostnameOpt, dhcpServerScope, deviceIpAddress);
The thing is, is that it just dies at this point and the logs don't record an exception. So I was thinking maybe the format I'm sending it is not correct. So I went to try and get an option. So instead, after creating the reservation, with SetRecord function above - I called this instead, just to try an see if I could get an option to see the correct format:
Option test = dhcpManager.GetOption(3, dhcpServerScope, deviceIpAddress);
Which throws this exception:
ManageDHCP.DhcpException: DhcpGetOptionInfo
at ManageDHCP.DHCPManager._GetOption(OptionTarget target, UInt32 optionID, String subnet, String reservedIP)
I'm new to C# and I'm not really sure what's causing this exception. Here is a link to the source code for ManageDHCP. What am I doing wrong? Is my format incorrect?
It seems like the 32-bit dll's setOption function does not work (DhcpGetOptionInfo throws an exception), so I switched to 64 bit dll.
I have an issue using c# on .Net 4 in a MVC web application, where when I query Active Directory, I frequently get an error: Attempted to access an unloaded appdomain. (Exception from HRESULT: 0x80131014).
The strange thing is, that it will work flawlessly for a time, and then it will just start happening, and then just disappear again.
I have made a few modifications to the function to get it to work , but they all seem to fail. I am wondering if I am doing something wrong, or if there is a better way to do it.
Here is my current function, that will accept a loginId, and a PrincipalContext. The loginId can either be the user DisplayName i.e "John Smith", or DOMAINNAME\josmi. The default is to use the first 2 letters of their firstname, and then the first 3 letters of their surname. There is a check in there if this is not the case. This part if fine.
public List<ADGroup> GetMemberGroups(string loginId, PrincipalContext principalContext, int tries = 0)
{
var result = new List<ADGroup>();
try
{
var samAccountName = "";
if (loginId.Contains(" "))
{
var fName = loginId.Split(Char.Parse(" "))[0].ToLower();
var sName = loginId.Split(Char.Parse(" "))[1].ToLower();
if (sName.Trim().Length == 2)
samAccountName = string.Format("{0}{1}", fName.StartsWith(".") ? fName.Substring(0, 4) : fName.Substring(0, 3), sName.Substring(0, 2));
else
samAccountName = string.Format("{0}{1}", fName.StartsWith(".") ? fName.Substring(0, 3) : fName.Substring(0, 2), sName.Substring(0, 3));
}
else
samAccountName = loginId.Substring(loginId.IndexOf(#"\") + 1);
var authPrincipal = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, samAccountName);
if (authPrincipal == null)
throw new Exception(string.Format("authPrincipal is null for loginId - {0}", loginId));
var firstLevelGroups = authPrincipal.GetGroups();
AddGroups(firstLevelGroups, ref result);
}
catch
{
if (tries > 5)
throw;
tries += 1;
System.Threading.Thread.Sleep(1000);
GetMemberGroups(loginId, principalContext, tries);
}
return result;
}
private void AddGroups(PrincipalSearchResult<Principal> principal, ref List<ADGroup> returnList)
{
foreach (var item in principal)
{
if (item.GetGroups().Count() > 0)
AddGroups(item.GetGroups(), ref returnList);
returnList.Add(new ADGroup(item.SamAccountName, item.Sid.Value));
}
}
This function is called like this:
MembershipGroups = ad.GetMemberGroups(user.SamAccountName, new PrincipalContext(ContextType.Domain));
The the error that I SOMETIMES get is:
System.AppDomainUnloadedException:
Attempted to access an unloaded
appdomain. (Exception from HRESULT:
0x80131014) at
System.StubHelpers.StubHelpers.InternalGetCOMHRExceptionObject(Int32
hr, IntPtr pCPCMD, Object pThis) at
System.StubHelpers.StubHelpers.GetCOMHRExceptionObject(Int32
hr, IntPtr pCPCMD, Object pThis) at
System.DirectoryServices.AccountManagement.UnsafeNativeMethods.IADsPathname.Retrieve(Int32
lnFormatType) at
System.DirectoryServices.AccountManagement.ADStoreCtx.LoadDomainInfo()
at
System.DirectoryServices.AccountManagement.ADStoreCtx.get_UserSuppliedServerName()
at
System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet.BuildPathFromDN(String
dn) at
System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet.MoveNextPrimaryGroupDN()
at
System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet.MoveNext()
at
System.DirectoryServices.AccountManagement.FindResultEnumerator1.MoveNext()
at
System.DirectoryServices.AccountManagement.FindResultEnumerator1.System.Collections.IEnumerator.MoveNext()
looking though reflector at System.DirectoryServices.AccountManagement the internal class "UnsafeNativeMethods" is implemented in native code, so UserSuppliedServerName one level up is all I can go on without looking at the CLR VM, (frankly im not sure even how to do that) Seems that a node is failing to return its primary group, so perhaps consider other implementations, after a bit of googling ive come across these that may help
Active Directory and nested groups this one may be promising heres the code sample..
public IList<string> FindUserGroupsLdap(string username)
{
// setup credentials and connection
var credentials = new NetworkCredential("username", "password", "domain");
var ldapidentifier = new LdapDirectoryIdentifier("server", 389, true, false);
var ldapConn = new LdapConnection(ldapidentifier, credentials);
// retrieving the rootDomainNamingContext, this will make sure we query the absolute root
var getRootRequest = new SearchRequest(string.Empty, "objectClass=*", SearchScope.Base, "rootDomainNamingContext");
var rootResponse = (SearchResponse)ldapConn.SendRequest(getRootRequest);
var rootContext = rootResponse.Entries[0].Attributes["rootDomainNamingContext"][0].ToString();
// retrieve the user
string ldapFilter = string.Format("(&(objectCategory=person)(sAMAccountName={0}))", username);
var getUserRequest = new SearchRequest(rootContext, ldapFilter, SearchScope.Subtree, null);
var userResponse = (SearchResponse)ldapConn.SendRequest(getUserRequest);
// send a new request to retrieve the tokenGroups attribute, we can not do this with our previous request since
// tokenGroups needs SearchScope.Base (dont know why...)
var tokenRequest = new SearchRequest(userResponse.Entries[0].DistinguishedName, "(&(objectCategory=person))", SearchScope.Base, "tokenGroups");
var tokenResponse = (SearchResponse)ldapConn.SendRequest(tokenRequest);
var tokengroups = tokenResponse.Entries[0].Attributes["tokenGroups"].GetValues(typeof(byte[]));
// build query string this query will then look like (|(objectSid=sid)(objectSid=sid2)(objectSid=sid3))
// we need to convert the given bytes to a hexadecimal representation because thats the way they
// sit in ActiveDirectory
var sb = new StringBuilder();
sb.Append("(|");
for (int i = 0; i < tokengroups.Length; i++)
{
var arr = (byte[])tokengroups[i];
sb.AppendFormat("(objectSid={0})", BuildHexString(arr));
}
sb.Append(")");
// send the request with our build query. This will retrieve all groups with the given objectSid
var groupsRequest = new SearchRequest(rootContext, sb.ToString(), SearchScope.Subtree, "sAMAccountName");
var groupsResponse = (SearchResponse)ldapConn.SendRequest(groupsRequest);
// loop trough and get the sAMAccountName (normal, readable name)
var userMemberOfGroups = new List<string>();
foreach (SearchResultEntry entry in groupsResponse.Entries)
userMemberOfGroups.Add(entry.Attributes["sAMAccountName"][0].ToString());
return userMemberOfGroups;
}
private string BuildHexString(byte[] bytes)
{
var sb = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
sb.AppendFormat("\\{0}", bytes[i].ToString("X2"));
return sb.ToString();
}
These are more for info purposes
How to use the PrimaryGroupID attribute to find the primary group for a user
Determining User Group Membership in Active Directory and ADAM
I don't know how PrincipalContext is being passed in, here, but one thing I noticed in my own code and research when I had this error, I had:
PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain);
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(oPrincipalContext , strUserName);
Where strUserName was some user, i.e. DOMAIN\johndoe
I was calling that code (which was in a separate function) and returning the UserPrincipal object as up and passing it to:
using (PrincipalSearchResult<Principal> result = up.GetGroups())
{
// do something with result, here
}
result wouldn't be null, but after I checked for that condition, I checked if result.Count() > 0, and that's when it would fail (sometimes - though I could re-create the conditions when it would happen by clicking on a particular tab in my app that called this code - even though the same code was called onload of my app and had no issues). The Message property in result was Attempted to access an unloaded appdomain. (Exception from HRESULT: 0x80131014).
I found in a similar post to this one that all I had to do was specify the domain in my PrincipalContext. Since I could not hard code mine in, as we move our code between Dev, Test, and Production environments where they have different domains for each of these, I was able to specify it as Environment.UserDomainName:
PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain, Environment.UserDomainName);
This got rid of the error, for me.
This issue is the same as Determine if user is in AD group for .NET 4.0 application
It appears to be a bug in ADSI that was resolved with a hotfix. Windows 7 SP1 and Windows Server 2008 R2 SP1 don't include the fix, so it will need to be manually deployed on your development machines and server environments.
http://support.microsoft.com/kb/2683913
You could put in some logging to narrow down the problem. That Thread.Sleep does not look like something one would want in a web application :)
If you are getting exceptions maybe you could handle them differently.
I reckon your AppDomain is being recycled while AD is doing its voodoo. Adding logging to the Application_End could also provide some clues.
try
public List<ADGroup> GetMemberGroups(string loginId, PrincipalContext principalContext, int tries = 0)
{
var result = new List<ADGroup>();
bool Done = false;
try
{
var samAccountName = "";
if (loginId.Contains(" "))
{
var fName = loginId.Split(Char.Parse(" "))[0].ToLower();
var sName = loginId.Split(Char.Parse(" "))[1].ToLower();
if (sName.Trim().Length == 2)
samAccountName = string.Format("{0}{1}", fName.StartsWith(".") ? fName.Substring(0, 4) : fName.Substring(0, 3), sName.Substring(0, 2));
else
samAccountName = string.Format("{0}{1}", fName.StartsWith(".") ? fName.Substring(0, 3) : fName.Substring(0, 2), sName.Substring(0, 3));
}
else
samAccountName = loginId.Substring(loginId.IndexOf(#"\") + 1);
var authPrincipal = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, samAccountName);
if (authPrincipal == null)
throw new Exception(string.Format("authPrincipal is null for loginId - {0}", loginId));
var firstLevelGroups = authPrincipal.GetGroups();
AddGroups(firstLevelGroups, ref result);
Done = true;
}
catch
{
if (tries > 5)
throw;
tries += 1;
}
if ( ( !Done) && (tries < 6) )
{
System.Threading.Thread.Sleep(1000);
result = GetMemberGroups(loginId, principalContext, tries);
}
return result;
}
private void AddGroups(PrincipalSearchResult<Principal> principal, ref List<ADGroup> returnList)
{
if ( principal == null )
return;
foreach (var item in principal)
{
if (item.GetGroups().Count() > 0)
AddGroups(item.GetGroups(), ref returnList);
returnList.Add(new ADGroup(item.SamAccountName, item.Sid.Value));
}
}
When an exception happens you called the function again from the catch-block (depending on the value of tries) but discarded its return value - so even if the second/third... call worked you returned an empty result to the original caller.
I changed that so the result won't be discarded anymore...
In the second function you never checked the principal param for null before starting the foreach... I changed that too...
And I removed the recursion from within the catch block catch (although I am really not sure whether this change has any real effect).