.Net method that mimics windows default file rename behavior - c#

In Windows Explorer, if you copy a file and the file name already exists and you choose not to override the file, Windows Explorer uses a specific file rename algorithm, i.e. it tries to append something like "copy", if this file exists, it appends a number in brackets, which is then incremented, in case these file names also are already taken. Please note, that this is a simplified version of the algorithm. In reality it is more complex.
Since I do not want to reverse engineer this behavior, is there a c# .Net-Api available that gives me direct access to this behavior when copying or creating files?

No.
Point mostly being that this is absolutely not a windows standard behavior, but only done in the Explorer (i.e. it is this particular program that does that).

As #TomTom said, no there isn't, but the Windows Explorer behaviour is easy to reproduce:
Given
var source = #"C:\Temp\Source.txt";
var targetFolder = #"C:\Temp\";
Then,
var targetName = Path.GetFileName(source);
var target = Path.Combine(targetFolder, targetName);
while (File.Exists(target)) {
targetName = "Copy of "+ targetName;
var target = Path.Combine(targetFolder, targetName);
}
File.Copy(source, target);
Or you can do a Mac like:
var targetName = Path.GetFileName(source);
for (int i = 0; i < MAX_TRIES; i++) {
var idx = (i==0)?"":(" ("+i.ToString()+")");
var target = Path.Combine(targetFolder, targetName+idx);
if (!File.Exists(target)) break;
}
File.Copy(source, target);

Related

IconPath not exsist

When I'm searching for a IsSystemSoundsSession I can't find the IconPath provided. On my system (Windows 10 1909). I'm getting IconPath #%SystemRoot%\\System32\\AudioSrv.Dll,-203 This file doesn't exist. (yes I expanded the variable for this, but that resolves to #C:\Windows\System32\AudioSrv.dll (notice the '#').
Found a solution. The file that I'm searching for is located in C:\Windows\System32\AudioSrv.dll. Notice how there is an # part as first character? It looks like that is the problem. Not sure if this is a problem with NAudio or Windows API.
Code to retrieve the IconPath
MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
var devices = enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active);
foreach (var device in devices)
{
var sessions = device.AudioSessionManager.Sessions;
for (int i = 0; i < sessions.Count; i++)
{
var session = sessions[i];
var iconPath = session.IconPath;
// iconPath == '#%SystemRoot%\\System32\\AudioSrv.Dll,-203'
}
}
}
You could hardcode c:\windows\system32 instead, but actually you don't really know that location. That is where the environment variable %systemroot% comes in.
Your # string is a path name containing this environment variable. You cannot open it as a file, first the variable need be resolved, consider:
string trueIconPath = Environment.ExpandEnvironmentVariables( iconPath.Substring(1))
The outcome will probably be: c:\windows\system32\AudioSrv.dll as intended for your machine.. on my PC it would become f:\cfg1\Windows\System32\AudioSrv.dll
Refer for more info: https://learn.microsoft.com/en-us/dotnet/api/system.environment.expandenvironmentvariables?view=netcore-3.1

TFS - Get latest code in a folder

I am using the TFS API to get latest code files, directories, .csproj files, etc. under a TFS-bound folder.
For the same, I use something like the following:
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new
Uri(ConfigurationManager.AppSettings["TFSUrl"]));
tfs.EnsureAuthenticated();
var vsStore = tfs.GetService<VersionControlServer>();
string workingFolder = #"C:\TFS\SolutionFolder";
Workspace wsp = vsStore.TryGetWorkspace(workingFolder);
if (wsp != null)
{
ItemSet items = vsStore.GetItems(workingFolder, VersionSpec.Latest, RecursionType.Full);
string relativePath = workingFolder + #"/";
foreach (Item item in items.Items)
{
string relativePath1 = item.ServerItem.Replace("$/TFS/SolutionFolder", relativePath);
if (item.ItemType == ItemType.Folder)
{
Directory.CreateDirectory(relativePath1);
}
else
{
item.DownloadFile(relativePath1);
}
}
}
Now, I get the items to download and then download happens. However, I want it to be like how VS handles it - if (and only if) there is a change in a file/folder, then only download the same. With this code, I always get 'n' number of files/folders in that folder and then I overwrite the same. Wrong approach, I know. I can, however, modify this code to check for the folder's or file's last change time and then choose to either overwrite it or ignore it. That's an option, albeit a bad one at that.
Now, what I would ideally like is to get ONLY the list of files/folders that actually need to be changed i.e. the incremental change. After that, I can choose to overwrite/ignore each item in that list. So, in the present case, if a new file/folder is created (or one of the existing ones got changed inside $/TFS/SolutionFolder i.e. in the sever), then only I want to pull that item in the list of files/folders to change(and decide what I want to do with it inside C:\TFS\SolutionFolder).
Also, is using one of the overloads of VersionControlServer.QueryHistory() an option? I had something like this:
(latestVersionIdOf $/TFS/SolutionFolder) - (existingVersionIdOf C:\TFS\SolutionFolder) = (Versions that I'd go out and get back from the server, for that folder)
in mind.
Any pointers will be very helpful. Thanks!
Just use Workspace.Get() or overload method (wsp.Get()), it just update updated files.
I don't think we can achieve that. If the files are downloaded to a folder without in source control, there are no versions compared within the folder, even if the folder is in source control, the behavior is just download also no version compare actions. So, it will download all the files ever time and then overwrite the same ones.
In VS, the files are all in TFS source control system, so when we Get Latest Version the changed/added files will be retrieved from TFS. If you want to get the same behavior as VS handles, you can use the tf get command. See Get Command
You can reference this article to use the tf get command :
get-latest-version-of-specific-files-with-tfs-power-tools
Update :-
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(ConfigurationManager.AppSettings["TFSUrl"]));
tfs.EnsureAuthenticated();
var vsStore = tfs.GetService<VersionControlServer>();
string workingFolder = ConfigurationManager.AppSettings["LocalPathToFolder"]; // C:\TFS\SolutionFolder
string tfsPathToFolder = ConfigurationManager.AppSettings["TFSPathToFolder"]; // $/TFS/SolutionFolder
Workspace wsp = vsStore.GetWorkspace(workingFolder);
if (wsp != null)
{
ItemSpec[] specs = { new ItemSpec(tfsPathToFolder, RecursionType.Full) };
ExtendedItem[][] extendedItems = wsp.GetExtendedItems(specs, DeletedState.NonDeleted, ItemType.Any);
ExtendedItem[] extendedItem = extendedItems[0];
var itemsToDownload = extendedItem.Where(itemToDownload => itemToDownload.IsLatest == false);
foreach (var itemToDownload in itemsToDownload)
{
try
{
switch (itemToDownload.ItemType)
{
case ItemType.File:
if (itemToDownload.LocalItem != null)
{
vsStore.DownloadFile(itemToDownload.SourceServerItem, itemToDownload.LocalItem);
}
else
{
string localItemPath = itemToDownload.SourceServerItem.Replace(tfsPathToFolder,
workingFolder);
vsStore.DownloadFile(itemToDownload.SourceServerItem, localItemPath);
}
break;
case ItemType.Folder:
string folderName = itemToDownload.SourceServerItem.Replace(tfsPathToFolder, workingFolder);
if ((!string.IsNullOrEmpty(folderName)) && (!Directory.Exists(folderName)))
{
Directory.CreateDirectory(folderName);
}
break;
}
}
catch (Exception e)
{
File.AppendAllText(#"C:\TempLocation\GetLatestExceptions.txt", e.Message);
}
}
}
This code works well, except:
a. Whenever it downloads the latest copy of, let's say a file, it 'checks it out' in TFS :(
b. For some items, it throws errors like 'Item $/TFS/SolutionFolder/FolderX/abc.cs was not found in source control at version T.' - I have to find out what the exact cause of this issue is, though.
Any ideas on how to get around these two issues or any other problems you see with this code? Thanks!

How to launch a process which will open a text file in any editor and automatically move cursor to a certain line number?

From c#, I want to launch a process which will open a text file in any editor and automatically move cursor to a certain line number.
I can open a file using
Process.Start(#"c:\myfile.txt");
but I don't know how to move cursor at specific location in that file.
Answer with source code:
yes, I used notepad++
private void openLog() {
try {
// see if notepad++ is installed on user's machine
var nppDir = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Notepad++", null, null);
if (nppDir != null) {
var nppExePath = Path.Combine(nppDir, "Notepad++.exe");
var nppReadmePath = Path.Combine(yourDirectory,fileName );
var line = 20;
var sb = new StringBuilder();
sb.AppendFormat("\"{0}\" -n{1}", nppReadmePath, lineNo);
Process.Start(nppExePath, sb.ToString());
} else {
string newPath = #"\\mySharedDrive\notpad++\bin\notepad++.exe";
Process.Start(newPath, #"\\" + filePath + " -n" + lineNo); // take exe from my shared drive
}
} catch (Exception e) {
Process.Start(#"\\" + FilePath); // open using notepad
}
}
Get Notepad++, then you can do this:
var nppDir = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Notepad++", null, null);
var nppExePath = Path.Combine(nppDir, "Notepad++.exe");
var nppReadmePath = Path.Combine(nppDir, "readme.txt");
var line = 20;
var sb = new StringBuilder();
sb.AppendFormat("\"{0}\" -n{1}", nppReadmePath, line);
Process.Start(nppExePath, sb.ToString());
In this example we get install path of n++ from the registry, build path to exe and readme.txt file, opens its own readme.txt file with cursor on line 20.
Using StringBuilder is more efficient than using multiple appends (explanation somewhere on SO).
The solution very heavily depends on which process/editor is opened on your system. That editor would have to have a developer API that you could use to access functionality such as setting ranges and altering the cursor position. For example, if the editor that is opened is Microsoft Word, you would use the Word Interop API to set a selection at a specific position. There is no universal way to do this in 'any editor' since each one has its own API (or no outward facing API at all).
Perhaps you are going this the wrong way. I'm not sure what you are trying to accomplish, but I think it would be alot easier to just open the text file in an editor that belongs to your application. Perhaps another form with a WYSIWYG editor control. That way you have full control on where the cursor will land in that editor. Otherwise, there are just way too many unknowns for anything feasibly workable.

Write file extended property "Revision Number" on all type of files

I would like to read/write the window information file (extended file properties) using c#
The one found by doing the following: In window explorer right click => properties => Summary tab. I want mainly to have access to the properties:
Title
Category
Revision Number
For office document I can use the following (using Office.Interop) or using DSOFile
private static string GetExcelWorkbookPropertyValue(_Workbook workbook, string propertyName)
{
DocumentProperties builtInProperties = (DocumentProperties)workbook.BuiltinDocumentProperties;
string value = builtInProperties.Cast<DocumentProperty>().First(x => x.Name.Equals(propertyName, StringComparison.InvariantCultureIgnoreCase)).Value;
return value ?? "";
}
But what i would like is a more general solution that will work with all files.
Could someone help?
Question additional information you can also read the properties Title and Category by using Shell32
Shell32.Shell shell = new Shell32.Shell();
//set the namespace to file path
Shell32.Folder folder = shell.NameSpace(Path.GetDirectoryName(file));
//get ahandle to the file
Shell32.FolderItem folderItem = folder.ParseName(Path.GetFileName(file));
//did we get a handle ?
if (folderItem != null)
{
for (int i = 0; i < 100; i++)
{
string s = folder.GetDetailsOf(folderItem, i);
System.Diagnostics.Debug.WriteLine(s);
}
}
However I still write the properity Revision Number, however it's look like Revision number is an office document property and cannot be written (I guess it will break the tracking process of office).
What does not make sense is that I can modify it using window explorer and the property is also visible for non-office documents... I'm struggling to understand that.
This information is stored in properties. Here are some of the standard properties. I'm not sure if the .NET Framework provides a wrapper around these interfaces, though.

How do I get build details in a custom workflow activity?

I need to add a custom activity to the default workflow template to increase assembly versions at the earliest point possible in the build process.
What I would like to achieve is to create and map the exact same workspace (that is be created further down in the workflow) inside my custom activity so that I can check out an xml file, increase the version number held within, write it back to the xml file and check the xml file back in.
I'm aware that this workspace will be created later on in the workflow but that will be too late in the build process for what I'm trying to achieve, so instead of moving any of the activities or duplicating them in a position above my custom activity (this should be ok as this workspace will be deleted and recreated again later)
I think the details I need are the BuildDirectory, WorkspaceName and SourcesDirectory. Can anyone tell me how to achieve the creation of the workspace or how obtain this data in code?
the build will be carried out on a build server, and I am using TFS 2010 and c#.
Thanks in advance
I followed the series of blog articles by Ewald Hofman as a primer and created a custom activity that does the check-out, update and check-in of a GlobalAssemblyInfo file that I parse the current version from. My task is inserted at the top of the "Update Drop Location" which is right after it does the "Get the build" portion of the workflow. I just use require the IBuildDetail and a File Mask as arguments from which you can pull out the VersionControlServer to be able to access TFS. My code is below:
protected override string Execute(CodeActivityContext context)
{
// Obtain the runtime value of the input arguments.
string assemblyInfoFileMask = context.GetValue(AssemblyInfoFileMask);
IBuildDetail buildDetail = context.GetValue(BuildDetail);
var workspace = buildDetail.BuildDefinition.Workspace;
var versionControl = buildDetail.BuildServer.TeamProjectCollection.GetService<VersionControlServer>();
Regex regex = new Regex(AttributeKey + VersionRegex);
// Iterate of the folder mappings in the workspace and find the AssemblyInfo files
// that match the mask.
foreach (var folder in workspace.Mappings)
{
string path = Path.Combine(folder.ServerItem, assemblyInfoFileMask);
context.TrackBuildMessage(string.Format("Checking for file: {0}", path));
ItemSet itemSet = versionControl.GetItems(path, RecursionType.Full);
foreach (Item item in itemSet.Items)
{
context.TrackBuildMessage(string.Format("Download {0}", item.ServerItem));
string localFile = Path.GetTempFileName();
try
{
// Download the file and try to extract the version.
item.DownloadFile(localFile);
string text = File.ReadAllText(localFile);
Match match = regex.Match(text);
if (match.Success)
{
string versionNumber = match.Value.Substring(AttributeKey.Length + 2, match.Value.Length - AttributeKey.Length - 4);
Version version = new Version(versionNumber);
Version newVersion = new Version(version.Major, version.Minor, version.Build + 1, version.Revision);
context.TrackBuildMessage(string.Format("Version found {0}", newVersion));
return newVersion.ToString();
}
}
finally
{
File.Delete(localFile);
}
}
}
return null;
}

Categories

Resources