Project Template DTE Visual Studio 2017 nuget - c#

I try to create a project automated with DTE this work perfect but i cannot add a nuget package...
Option1 (InstallNuGetPackage code below)
var componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel));
//componentModel is always null
I have installed this nuget package
NuGet.VisualStudio 4.0.0
And add following framework references
Microsoft.VisualStudio.ComponentModelHost 15.0.0.0
Microsoft.VisualStudio.Shell.15.0 15.0.0.0
I have found this example but is not work
http://tylerhughes.info/archive/2015/05/06/installing-a-nuget-package-programmatically/
Option2 (Add a own package.config)
I have also try with creating the packages.config xml but then i have no references to this package and must edit the csproj...
public string GetPackagesConfig()
{
var sb = new StringBuilder();
sb.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
sb.AppendLine("<packages>");
sb.AppendLine("<package id=\"log4net\" version=\"2.0.8\" targetFramework=\"net461\" />");
sb.AppendLine("</packages>");
return sb.ToString();
//Add file to project
}
Visual Studio control
var type = Type.GetTypeFromProgID("VisualStudio.DTE.15.0");
var obj = Activator.CreateInstance(type, true);
this._applicationObject = (DTE2)obj;
InstallNuGetPackage
public bool InstallNuGetPackage(EnvDTE.Project project, string package)
{
bool installedPkg = true;
try
{
var componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel));
IVsPackageInstallerServices installerServices = componentModel.GetService<IVsPackageInstallerServices>();
if (!installerServices.IsPackageInstalled(project, package))
{
var installer = componentModel.GetService<IVsPackageInstaller>();
installer.InstallPackage(null, project, package, (System.Version)null, false);
}
}
catch (Exception ex)
{
installedPkg = false;
}
return installedPkg;
}
Create Project
private void CreateProject(string projectSubFolder, string projectName)
{
Solution2 solution2;
string solutionFileFullName;
string solutionFolderFullName;
string projectFolderFullName;
try
{
solution2 = (Solution2)_applicationObject.Solution;
// Get the full name of the solution file
solutionFileFullName = solution2.FileName;
// Get the full name of the solution folder
solutionFolderFullName = Path.GetDirectoryName(solutionFileFullName);
// Compose the full name of the project folder
projectFolderFullName = Path.Combine(solutionFolderFullName, projectSubFolder);
if (!(projectFolderFullName.EndsWith("\\")))
{
projectFolderFullName += "\\";
}
var programfiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
var template = #"Microsoft Visual Studio\2017\Community\Common7\IDE\ProjectTemplates\CSharp\Windows\1033\ClassLibrary\csClassLibrary.vstemplate";
var projectTemplateFileName = Path.Combine(programfiles, template);
// Add the project
solution2.AddFromTemplate(projectTemplateFileName, projectFolderFullName, projectName, false);
//Save
_applicationObject.Solution.SaveAs(_solutionFullFileName);
}
catch (Exception exception)
{
Log.Error(nameof(CreateProject), exception);
}
}

With this example you can open the package manager console window and send a install-package command.
var packageManagerConsoleGuid = "{0AD07096-BBA9-4900-A651-0598D26F6D24}";
var window = this._visualStudioInstance.Windows.Item(packageManagerConsoleGuid);
window.Activate();
var commandName = "View.PackageManagerConsole";
var nugetCommand = "install-package log4net -ProjectName DemoProject";
this._visualStudioInstance.ExecuteCommand(commandName, nugetCommand);
I develop a project to automate create solution with projects you can found it here
Nager.TemplateBuilder
This example create a Windows Desktop Application with two nuget packages
//Configure Project
var demoProject = new ProjectInfo($"DemoProject", ProjectTemplate.WindowsClassicDesktopWindowsFormsApp);
demoProject.NugetPackages = new List<string> { "System.ServiceModel.NetTcp", "System.Runtime.Serialization.Xml" };
//Configure Solution
var folder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var solutionInfo = new SolutionInfo("Test", folder);
solutionInfo.ProjectInfos.Add(demoProject);
//Start building machine
var buildingMachine = new SolutionBuildingMachine();
buildingMachine.Build(solutionInfo);
buildingMachine.Dispose();

Related

TFS Get all build definitions

Good afternoon, I was scrolling here, reading questions and trying different code on how to get builds and all the definitions from them, however, whenever I try to execute code and get builds definitions it returns nothing, even though I for sure know there are both successful and unsuccessful builds in there. However I get nothing.
private static void Main(string[] args)
{
NetworkCredential credential = new NetworkCredential("MyUsername", "MyPassword");
VssBasicCredential basicCred = new VssBasicCredential(credential);
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri("https://tomheza.visualstudio.com/DefaultCollection"), basicCred);
tpc.Authenticate();
CatalogNode catalogNode = tpc.CatalogNode;
ReadOnlyCollection<CatalogNode> collectionNodes = tpc.CatalogNode.QueryChildren(new[] { CatalogResourceTypes.TeamProject }, false, CatalogQueryOptions.None);
foreach (CatalogNode collectionNode in collectionNodes)
{
Console.WriteLine(collectionNode.Resource.DisplayName);
}
var buildServer = (IBuildServer)tpc.GetService(typeof(IBuildServer));
var vcs = tpc.GetService<VersionControlServer>();
var teamProjects = vcs.GetAllTeamProjects(true);
foreach (TeamProject proj in teamProjects)
{
var defs = buildServer.QueryBuildDefinitions(proj.Name);
Console.WriteLine(string.Format("Team Project: {0}", proj.Name));
foreach (IBuildDefinition def in defs)
{
IBuildDetailSpec spec = buildServer.CreateBuildDetailSpec(proj.Name, def.Name);
spec.MaxBuildsPerDefinition = 1;
spec.QueryOrder = BuildQueryOrder.FinishTimeDescending;
var builds = buildServer.QueryBuilds(spec);
if (builds.Builds.Length > 0)
{
var buildDetail = builds.Builds[0];
Console.WriteLine(string.Format(" {0} - {1} - {2}", def.Name, buildDetail.Status.ToString(), buildDetail.FinishTime));
}
}
Console.WriteLine();
}
}
I am using VS2017 community version
Like the comment said above, the VNext build definition information couldn't be reached using the old version API. Install this TFS ExtendedClient Nuget package for your project, using the method below to get all build definitions.
using Microsoft.VisualStudio.Services.WebApi;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.TeamFoundation.Build.WebApi;
using Microsoft.TeamFoundation.Core.WebApi;
using Microsoft.VisualStudio.Services.Operations;
public static void GetBuild()
{
var u = new Uri("http://servername:8080/tfs/MyCollection/");
VssCredentials c = new VssCredentials(new Microsoft.VisualStudio.Services.Common.WindowsCredential(new NetworkCredential("v-tinmo", "123456.w", "fareast")));
VssConnection connection = new VssConnection(u, c);
BuildHttpClient buildServer = connection.GetClient<BuildHttpClient>();
//get all build definitions in your team projects
List<BuildDefinitionReference> builddefs = buildServer.GetDefinitionsAsync(project:"team project name").Result;
foreach (BuildDefinitionReference builddef in builddefs)
{
Console.WriteLine(builddef.Name);
...
}
//get all builds information in your team projects
var builds = buildServer.GetBuildsAsync(project: "team project name").Result;
foreach (var build in builds)
{
Console.WriteLine(build.Definition.Name + "--" + build.BuildNumber + "--" +build.Result);
}
}
You could also use this kind of REST API to get build definitions:
Http method: GET
http://v-tinmo-12r2:8080/tfs/DefaultCollection/teamprojectname/_apis/build/definitions?api-version=2.0

Starting a specific Firefox Profile with Selenium 3

I am trying to upgrade from Selenium 2 to Selenium 3 but the old handling, which was pretty easy and fast doesn't work anymore (and the documentation is nonexisting as it seems)
This is the program at the moment and what I want is to open a Firefox driver with the profile: SELENIUM
Sadly it doesn't work and always shuts down with the Error:
An unhandled exception of type 'System.InvalidOperationException' > occurred in WebDriver.dll
Additional information: corrupt deflate stream
This is my program at the moment:
public Program()
{
FirefoxOptions _options = new FirefoxOptions();
FirefoxProfileManager _profileIni = new FirefoxProfileManager();
FirefoxDriverService _service = FirefoxDriverService.CreateDefaultService(#"C:\Programme\IMaT\Output\Release\Bin");
_service.FirefoxBinaryPath = #"C:\Program Files (x86)\Mozilla Firefox\firefox.exe";
try
{
if ((_options.Profile = _profileIni.GetProfile("SELENIUM")) == null)
{
Console.WriteLine("SELENIUM PROFILE NOT FOUND");
_profile.SetPreference("network.proxy.type", 0); // disable proxy
_profile = new FirefoxProfile();
}
}
catch
{
throw new Exception("Firefox needs a Profile with \"SELENIUM\"");
}
IWebDriver driver = new FirefoxDriver(_service,_options,new System.TimeSpan(0,0,30));
driver.Navigate().GoToUrl("ld-hybrid.fronius.com");
Console.Write("rtest");
}
static void Main(string[] args)
{
new Program();
}
Without Loading the Profile it works with just new FirefoxDriver(_service) but the profile is mandatory.
In Selenium 2 I handled it with this code:
FirefoxProfileManager _profileIni = new FirefoxProfileManager();
// use custom temporary profile
try {
if ((_profile = _profileIni.GetProfile("SELENIUM")) == null)
{
Console.WriteLine("SELENIUM PROFILE NOT FOUND");
_profile.SetPreference("network.proxy.type", 0); // disable proxy
_profile = new FirefoxProfile();
}
}
catch
{
throw new Exception("Firefox needs a Profile with \"SELENIUM\"");
}
_profile.SetPreference("intl.accept_languages", _languageConfig);
_driver = new FirefoxDriver(_profile);
Fast and simple, but as the Driver doesn't support a Constructor with service and profile I don't really know how to get this to work, any help would be appreciated
This exception is due to a bug in the .Net library. The code generating the Zip of the profile is failing to provide a proper Zip.
One way to overcome this issue would be to overload FirefoxOptions and use the archiver from .Net framework (System.IO.Compression.ZipArchive) instead of the faulty ZipStorer:
var options = new FirefoxOptionsEx();
options.Profile = #"C:\Users\...\AppData\Roaming\Mozilla\Firefox\Profiles\ez3krw80.Selenium";
options.SetPreference("network.proxy.type", 0);
var service = FirefoxDriverService.CreateDefaultService(#"C:\downloads", "geckodriver.exe");
var driver = new FirefoxDriver(service, options, TimeSpan.FromMinutes(1));
class FirefoxOptionsEx : FirefoxOptions {
public new string Profile { get; set; }
public override ICapabilities ToCapabilities() {
var capabilities = (DesiredCapabilities)base.ToCapabilities();
var options = (IDictionary)capabilities.GetCapability("moz:firefoxOptions");
var mstream = new MemoryStream();
using (var archive = new ZipArchive(mstream, ZipArchiveMode.Create, true)) {
foreach (string file in Directory.EnumerateFiles(Profile, "*", SearchOption.AllDirectories)) {
string name = file.Substring(Profile.Length + 1).Replace('\\', '/');
if (name != "parent.lock") {
using (Stream src = File.OpenRead(file), dest = archive.CreateEntry(name).Open())
src.CopyTo(dest);
}
}
}
options["profile"] = Convert.ToBase64String(mstream.GetBuffer(), 0, (int)mstream.Length);
return capabilities;
}
}
And to get the directory for a profile by name:
var manager = new FirefoxProfileManager();
var profiles = (Dictionary<string, string>)manager.GetType()
.GetField("profiles", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(manager);
string directory;
if (profiles.TryGetValue("Selenium", out directory))
options.Profile = directory;

How to get project inside of Solution Folder in VSIX project

Hi I am having a problem, with a custom build task inside of a Visual Studio Extension. I need to identify projects of my custom project type. I can do this fine if they are on the root of the solution, but the problem occurs when it is inside of a solution folder. I can get the solution folder as a EnvDTE.Project, but am not sure how to get projects from within that folder.
I thought I would be able to get it from the projects Collection property but that is null.
Any assistance would be greatly appreciated.
if (Scope == EnvDTE.vsBuildScope.vsBuildScopeSolution)
{
DTE2 dte2 = Package.GetGlobalService(typeof(EnvDTE.DTE)) as DTE2;
var sol = dte2.Solution;
EnvDTE.DTE t = dte2.DTE;
var x = t.Solution.Projects;
foreach(var proj in x)
{
try
{
var project = proj as EnvDTE.Project;
var guid = GetProjectTypeGuids(project);
if (guid.Contains("FOLDERGUID"))
{
//here is where I would get the project from the folder
}
I managed to resolve this with a bit more research and some trial and error. In case anybody else comes up with this problem, I changed the main code to
if (Scope == EnvDTE.vsBuildScope.vsBuildScopeSolution)
{
errorListProvider.Tasks.Clear();
DTE2 dte2 = Package.GetGlobalService(typeof(DTE)) as DTE2;
var sol = dte2.Solution;
var projs = sol.Projects;
foreach(var proj in sol)
{
var project = proj as Project;
if (project.Kind == ProjectKinds.vsProjectKindSolutionFolder)
{
var innerProjects = GetSolutionFolderProjects(project);
foreach(var innerProject in innerProjects)
{
//carry out actions here.
}
}
}
}
The code for the GetSolutionFolderForProjects was
private IEnumerable<Project> GetSolutionFolderProjects(Project project)
{
List<Project> projects = new List<Project>();
var y = (project.ProjectItems as ProjectItems).Count;
for(var i = 1; i <= y; i++)
{
var x = project.ProjectItems.Item(i).SubProject;
var subProject = x as Project;
if (subProject != null)
{
//Carried out work and added projects as appropriate
}
}
return projects;
}
Hope this helps somebody else.
I had a similar question within a T4 template, where I had to find a project by name within the solution, at any level: root, folder, nested folder.
For reference purposes, I'm pasting it here. It's totally based on the solution from #DaveGreen, so credits to him:
<## import namespace="System.Linq" #>
<#
var dte = (DTE)hostServiceProvider.GetService(typeof(DTE));
var project = GetProject(dte.Solution, "ProjectName");
#>
<#+
public static Project GetProject(Solution solution, string name)
{
var project = GetProject(solution.Projects.OfType<Project>(), name);
if (project == null)
{
throw new Exception($"Project {name} not found in solution");
}
return project;
}
public static Project GetProject(IEnumerable<Project> projects, string name)
{
foreach (Project project in projects)
{
var projectName = project.Name;
if (projectName == name)
{
return project;
}
else if (project.Kind == EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder)
{
var subProjects = project
.ProjectItems
.OfType<ProjectItem>()
.Where(item => item.SubProject != null)
.Select(item => item.SubProject);
var projectInFolder = GetProject(subProjects, name);
if (projectInFolder != null)
{
return projectInFolder;
}
}
}
return null;
}
#>

How to compile a C# file with Roslyn programmatically?

I read that you can't compile C# 6.0 with CSharpCodeProvider and therefor trying to do with with Roslyn. But I can't find a good example how to load a file and then compile it to a dll.
How should I write something similar to this code with Roslyn? Or is there some other way to do it? Now when I try to compile files that contain reference to projects with C# 6.0 code it just say "The type or namespace name 'x' does not exist in the namespace 'y' (are you missing an assembly reference?)"
public string CompileCode()
{
var provider = new CSharpCodeProvider();
var outputPath = Path.Combine(Path.GetDirectoryName(_path), $"Code.dll");
var compilerparams = new CompilerParameters(_referencedAssemblies, outputPath);
CompilerResults results = provider.CompileAssemblyFromFile(compilerparams, _path);
var dllPath = results.PathToAssembly;
if (!results.Errors.HasErrors)
return dllPath;
PrintError(results.Errors);
return "";
}
In summary I want to:
Load a C# file
Compile it to a dll so I can load it later.
I have created a sample for you to work with. You need to tweak it to use the run time for .Net 4.6 so that CSharp6 version is availble to you. I have added little details so that you can choose the options of compilations.
Changes required -
Change the path of runtime to target .Net 4.6
Change the LanguageVersion.Csharp5 to LanguageVersion.Csharp6 in below sample.
class Program
{
private static readonly IEnumerable<string> DefaultNamespaces =
new[]
{
"System",
"System.IO",
"System.Net",
"System.Linq",
"System.Text",
"System.Text.RegularExpressions",
"System.Collections.Generic"
};
private static string runtimePath = #"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1\{0}.dll";
private static readonly IEnumerable<MetadataReference> DefaultReferences =
new[]
{
MetadataReference.CreateFromFile(string.Format(runtimePath, "mscorlib")),
MetadataReference.CreateFromFile(string.Format(runtimePath, "System")),
MetadataReference.CreateFromFile(string.Format(runtimePath, "System.Core"))
};
private static readonly CSharpCompilationOptions DefaultCompilationOptions =
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
.WithOverflowChecks(true).WithOptimizationLevel(OptimizationLevel.Release)
.WithUsings(DefaultNamespaces);
public static SyntaxTree Parse(string text, string filename = "", CSharpParseOptions options = null)
{
var stringText = SourceText.From(text, Encoding.UTF8);
return SyntaxFactory.ParseSyntaxTree(stringText, options, filename);
}
static void Main(string[] args)
{
var fileToCompile = #"C:\Users\DesktopHome\Documents\Visual Studio 2013\Projects\ConsoleForEverything\SignalR_Everything\Program.cs";
var source = File.ReadAllText(fileToCompile);
var parsedSyntaxTree = Parse(source, "", CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5));
var compilation
= CSharpCompilation.Create("Test.dll", new SyntaxTree[] { parsedSyntaxTree }, DefaultReferences, DefaultCompilationOptions);
try
{
var result = compilation.Emit(#"c:\temp\Test.dll");
Console.WriteLine(result.Success ? "Sucess!!" : "Failed");
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.Read();
}
This would need little tweaks but it should give you desired results. Change it as you may wish.
You have to use the NuGet package Microsoft.CodeAnalysis.CSharp.
var syntaxTree = CSharpSyntaxTree.ParseText(source);
CSharpCompilation compilation = CSharpCompilation.Create(
"assemblyName",
new[] { syntaxTree },
new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) },
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var dllStream = new MemoryStream())
using (var pdbStream = new MemoryStream())
{
var emitResult = compilation.Emit(dllStream, pdbStream);
if (!emitResult.Success)
{
// emitResult.Diagnostics
}
}

How can I programmatically check-out an item for edit in TFS?

I'm working on a utility processing files being under source control using TFS 2010.
If an item is not yet checked-out for edit, I'm getting an exception, what is definitely predictable because file is in read-only mode.
What ways exist to check-out a file?
P.S. I want something for programmatic rather then Process.Start("tf.exe", "..."); if that's applicable.
Some of the other approaches mentioned here only work for certain versions of TFS or make use of obsolete methods. If you are receiving a 404, the approach you are using is probably not compatible with your server version.
This approach works on 2005, 2008, and 2010. I don't use TFS any longer, so I haven't tested 2013.
var workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(fileName);
using (var server = new TfsTeamProjectCollection(workspaceInfo.ServerUri))
{
var workspace = workspaceInfo.GetWorkspace(server);
workspace.PendEdit(fileName);
}
private const string tfsServer = #"http://tfsserver.org:8080/tfs";
public void CheckOutFromTFS(string fileName)
{
using (TfsTeamProjectCollection pc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsServer)))
{
if (pc != null)
{
WorkspaceInfo workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(fileName);
if (null != workspaceInfo)
{
Workspace workspace = workspaceInfo.GetWorkspace(pc);
workspace.PendEdit(fileName);
}
}
}
FileInfo fi = new FileInfo(fileName);
}
Note that Microsoft.TeamFoundation.Client.TeamFoundationServerFactory is obsolete: The TeamFoundationServer class is obsolete. Use the TeamFoundationProjectCollection or TfsConfigurationServer classes to talk to a 2010 Team Foundation Server.
In order to talk to a 2005 or 2008 Team Foundation Server use the TeamFoundationProjectCollection class. The corresponding factory class for that is the TfsTeamProjectCollectionFactory.
You can use Team Foundation Version Control client API.
The method is PendEdit()
workspace.PendEdit(fileName);
Checkout detailed example on MSDN
http://blogs.msdn.com/b/buckh/archive/2006/03/15/552288.aspx
First get the workspace
var tfs = new TeamFoundationServer("http://server:8080/tfs/collection");
var version = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
var workspace = version.GetWorkspace("WORKSPACE-NAME", version.AuthorizedUser);
With the workspace you can checkout the file
workspace.PendEdit(fileName);
var registerdCollection = RegisteredTfsConnections.GetProjectCollections().First();
var projectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(registerdCollection);
var versionControl = projectCollection.GetService<VersionControlServer>();
var workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(_fileName);
var server = new TeamFoundationServer(workspaceInfo.ServerUri.ToString());
var workspace = workspaceInfo.GetWorkspace(server);
workspace.PendEdit(fileName);
I have two approaches how to do that: simple and advanced.
1). Simple:
#region Check Out
public bool CheckOut(string path)
{
using (TfsTeamProjectCollection pc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(ConstTfsServerUri)))
{
if (pc == null) return false;
WorkspaceInfo workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(path);
Workspace workspace = workspaceInfo?.GetWorkspace(pc);
return workspace?.PendEdit(path, RecursionType.Full) == 1;
}
}
public async Task<bool> CheckoutAsync(string path)
{
return await Task.Run(() => CheckOut(path));
}
#endregion
2). Advanced (with receiving status):
private static string GetOwnerDisplayName(PendingSet[] pending)
{
var result = pending.FirstOrDefault(pendingSet => pendingSet.Computer != Environment.MachineName) ?? pending[0];
return result.OwnerDisplayName;
}
private string CheckoutFileInternal(string[] wsFiles, string folder = null)
{
try
{
var workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(folder);
var server = new TfsTeamProjectCollection(workspaceInfo.ServerUri);
var workspace = workspaceInfo.GetWorkspace(server);
var request = new GetRequest(folder, RecursionType.Full, VersionSpec.Latest);
GetStatus status = workspace.Get(request, GetOptions.None);
int result = workspace.PendEdit(wsFiles, RecursionType.Full, null, LockLevel.None);
if (result == wsFiles.Length)
{
//TODO: write info (succeed) to log here - messageText
return null;
}
var pending = server.GetService<VersionControlServer>().QueryPendingSets(wsFiles, RecursionType.None, null, null);
var messageText = "Failed to checkout !.";
if (pending.Any())
{
messageText = string.Format("{0}\nFile is locked by {1}", messageText, GetOwnerDisplayName(pending));
}
//TODO: write error to log here - messageText
return messageText;
}
catch (Exception ex)
{
UIHelper.Instance.RunOnUiThread(() =>
{
MessageBox.Show(Application.Current.MainWindow, string.Format("Failed checking out TFS files : {0}", ex.Message), "Check-out from TFS",
MessageBoxButton.OK, MessageBoxImage.Error);
});
return null;
}
}
public async Task<string> CheckoutFileInternalAsync(string[] wsFiles, string folder)
{
return await Task.Run(() => CheckoutFileInternal(wsFiles, folder));
}

Categories