I'm working with the Visual studio 2010 SDK, developing an extension. I'm trying to get a list of all the files in a project, however currently I cannot get the list of files within a filter. All that I can see returned is the filter name, and the number of files inside that filter.
I'm currently using the ProjectItem interface, specifically the FileNamesProperty. However, in their documentation for this they say:
When the project item's ProjectItems property has a value, and the ProjectItem object represents a filter folder on the disk, then the FileNames property returns only the name of the filter folder.
Is there another method that I can use to list the files inside a filter, or am I tackling this the wrong way?
So, I've made enough progress on this to continue. I only need to use this extension in a Visual C++ project, so I was able to use the VCProject Interface to get a list of projects, and the VCProject Files property to get the list of files from the projects.
Something like this
VCProject prj;
int count = _applicationObject.Solution.Projects.Count;
for(int i = 1; i <= count; i++ )
{
//Start counting at one for some reason
prj = (Microsoft.VisualStudio.VCProjectEngine.VCProject)_applicationObject.Solution.Projects.Item(i).Object;
foreach (VCFile item in prj.Files)
{
//Item is the file, do whatever you want with it here
}
}
I would still like to know if there is a way to do this for all projects(not just VisualC++).
Related
I am searching for the possibility to open default property dialogues for particular Windows objects, like:
the property dialogue for a particular service in services.msc
the property dialogue for a particular scheduled taks in taskschd.msc
etc.
I do not want to interact with that dialogues or change any of the properties. I just want to open them to give the user direct access to a single items properties (instead of opening the listings (by calling the *.msc executables) in which the user has to search the object again).
I have already partially copied the dialogues functions into own forms and code for other purposes, by the way, but I want to give the user the option to open the default ones and make any changes directly.
Now, I have found some hints but I am stuck as there is always some crucial information missing:
1. Using so-called SnapIns of MMC (Microsoft Management Console)
There is this relatively new answer which uses VB code but I have no clue how I could use the MMC Automation Object Model in C# .NET Framework.
Furthermore, there is no clean and easy example/explanation of how to simply call an existing .msc process/list/window by usage of the Microsoft.ManagementConsole. Instead, there are several horrifying complex tutorials how to implement SnapIns into C#.
To be clear here: What I want to do is to reference a dll, go through some list (if necessary) and just call the properties dialogue.
2. COM invoke of old API
There is this old answer where someone recommends using invoke on an outdated ITaskScheduler class which does not solve the general dialogue call but at least the one for scheduled tasks. Perhaps it is also possible to use something similar for services, etc. - but, again, there is no clear example or explanation of how to implement this approach.
It's relatively simple.
Add a COM Reference to Microsoft Management Console 2.0.
Add the using MMC20 directive.
Create a new MMC20.Application object
Use the Application.Load() method to load a Snap-In (services.msc here)
The ActiveView of the Application Document contains the list of items: ListItems Property
Select a Node by name or Index and call the DisplaySelectionPropertySheet() method to show its Property pane
For example:
Note: setting mmcApp.UserControl = 1; leaves the Console open, otherwise it would close.
using MMC20;
// [...]
MMC20.Application mmcApp = new MMC20.Application();
mmcApp.UserControl = 1;
mmcApp.Load("services.msc");
var doc = mmcApp.Document;
var view = doc.ActiveView;
var node = view.ListItems.OfType<Node>().FirstOrDefault(n => n.Name == "Base Filtering Engine");
if (node != null) {
view.Select(node);
view.DisplaySelectionPropertySheet();
}
To enumerate the ListItems, use a standard loop or an extension method as shown above:
var nodes = view.ListItems;
foreach (MMC20.Node node in nodes) {
Console.WriteLine(node.Name);
}
Here's documentation on how to use the MMC SDK. It's a Win32 API, so you'll have to use COM, PInvoke, or other interop to use it.
https://learn.microsoft.com/en-us/previous-versions/windows/desktop/mmc/microsoft-management-console-start-page#developer-audience
The C++ examples are probably more informative than the VB ones. The .h files are part of the windows sdk so you should be able to find the clsid and other constants that you need in there: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/mmc/using-c-with-the-mmc-2-0-automation-object-model
I'm creating an MSI using Visual Studio 2015. I want to check in certain file exists already in TARGETDIR and pass the result of the search to a custom action in c#. So far, I create the search in Launch Conditions view and set properties accordingly.
Then, I pass the property name in Custom Actions view as Custom Data Parameter:
/CONFIG_EXISTS="[CONFIG_EXISTS]"
But when I read the value in my custom action code, its value is empty, but I expect to find a bolean, at least as string.
protected override void OnAfterInstall(IDictionary savedState){
var existingConfig = Context.Parameters["CONFIG_EXISTS"]; // Always has "" as value
.... // Other code
What am I missing here? How to get in my custom action c# code the result of the search condition?
Your search is failing because it runs very early in the install, before TARGETDIR has been initialized (and keep in mind that TARGETDIR is also what can be changed by the dialog that offers an install folder). So you should redefine your search in terms of the standard Windows Installer folder properties, such as ProgramFilesFolder, CommonFilesFolder and so on, full list here:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa370905(v=vs.85).aspx#system_folder_properties
Currently, all the templates that we have created have source fields whose path is a string.
e.g. :
"sitecore/content/Test"
Now if I want to move the Test folder to
sitecore/content/Shared/Tags/Test
the links are broken.
If i manually change this to use the GUID (Using the build option), I get :
datasource={62CF8494-B148-4B2E-9D36-52EC4CD75E13}&database=master
If i now move the test folder around, my links remain as is.
I wanted to write a routine that runs through the tree and updates all the source fields for my templates (in a particular folder only), to contain the GUID and db name.
Is this possible?
I tried doing this in the Process method of a class that inherits from PublishItemProcessor and added the appropriate entry in the web.config. This method is called, but the Source property of the field is read only and cannpt be modified.
Any ideas?
Thanks in advance.
The best/most efficient option here would be to use Sitecore Powershell Extensions to modify the items.
This is a good reference point: https://sitecorepowershell.gitbooks.io/sitecore-powershell-extensions/content/working-with-items.html
You could also do this in code.
You need to write a routine (code or SPE) that starts with the /sitecore/templates/user defined or whatever your root folder is.
Recurse thru the tree and get all items that have the template: Template Field. Then you can check value of the the Source field. If it is the one you want to change, update the value and save the item.
Remember to publish the templates tree after updating all the values.
I have another problem with my FileSystemWatcher ;)
Simply, what I want to do:
I want to watch specific folders, in order to add the paths of the files to a List(Of String) and set an integer-variable for each new file.
So:
I create the file "Test.png" and it does that:
(List) categoryFilesList.Add(e.FullPath)
(Integer-variable) counter += 1
Well, this is working well.
Now, if a file is deleted again, it should remove the path from the list and set the integer-variable down again. (-= 1).
This is working well, too, but now I have a problem, I am not able to find a solution for.
I have IncludeSubdirectories set to true to scan the subdirectories for files. On adding this works fine, but on removing it does not.
That is because I do NOT add folders to the list, only files. So, if I delete a folder, I am not able to know what content it was containing and how much files it were, because it is already gone.
But I would want to know, how many files there were in and what their paths were to remove them again from the list and set the integer-variable down again with the amount of deleted files.
I though about saving it anyway on changing, but I do not know how exactly and if this is a good idea.
Is there a better solution?
If something is unclear, then I am sorry, ask then.
I also accept C#-answers, VB.NET is just because my friend wants that.
Thanks!
My solution to a similar problem is as follows:
Ensure your FileSystemWatcher is configured to track subfolders. Once you do that, you should get a Delete event any time a folder or file is deleted. In the case where a folder is deleted, simply iterate through your collection and remove any items that contain the path of the folder.
For example:
int numRemoved = categoryFilesList.RemoveAll(
delegate(String s)
{
return s.Contains(<DeletedDirectoryPath>);
}
);
This would remove any items in your list which contain . You can then use numRemoved to maintain your file count. Alternatively, you could simply use the count of the list instead of maintaining your own copy of that data via the Count property on your list.
Additionally you should be sure that you handle the cases where items and directories are renamed so that your list of filenames is always up to date.
I am afraid that this isn't really possible to do, because of the way the deletion of a directory works in a system. The directory is deleted, but the files recursively are just part of the directory.
This was quite well described here on MSDN forums :
I you observe the behavior of the trash on your desktop, you will see
that each time you delete a folder, you can see that folder in the
trash but you cannot see the elements within that dropped folder. The
only way to see those elements is to recover the folder from the
trash.
I think it happens the same thing with the FSW class. When you delete
a folder inside a watched directory you only have the event of the
deleted folder because the folder and its contents is not really
deleted but only moved to the trash. This is why you never receive the
deleted events for the included files because they are still somewhere
on your system
According to this question here on SO, the same problem occurs when a folder is moved into the structure (and the question also shows the workaround to the moving issue).
Possible workaround for the deletion problem would be browsing the structure in advance and saving the amount of files in the directories into a tree-like structure, but it will definitely be much more complex than this. It would look like this:
public class DirectoryFiles
{
public int Count {get; set;}
public string FullPath {get; set;}
public List<DirectoryFiles> Subdirectories {get; set; }
}
private DirectoryFiles Initialize(string fullPath)
{
if (Directory.Exists(fullPath))
{
var toReturn = new DirectoryFiles { Subdirectories = new List<DirectoryFiles>() };
foreach (string directory in Directory.GetDirectories(fullPath))
{
toReturn.Subdirectories.Add(this.Initialize(directory));
}
toReturn.Count = toReturn.Subdirectories.Sum(x => x.Count) + Directory.GetFiles(fullPath).Count();
return toReturn;
}
else
{
throw new DirectoryNotFoundException(String.Format("Directory {0} does not exist", fullPath));
}
}
and in the class where you are counting:
private int GetCountOfFiles(DirectoryFiles files, string fullPath)
{
if (files.FullPath.Equals(fullPath, StringComparison.InvariantCultureIgnoreCase))
{
return files.Count;
}
foreach (var subdir in files.Subdirectories)
{
if (this.GetCountOfFiles(subdir, fullPath) != -1)
{
return subdir.Count;
}
}
return -1;
}
This may need improvements for:
Permissions - it will throw an exception if you don't have access to the files,
The performance is not perfect, on every subfile or subdirectory deletion you'll need to rebuild the whole structure. There can be some optimizations created for that as well, but should work quite well for smaller subdirectories. If you need performance improvements, I'll leave that to you. For that, consider adding DirectoryFiles Parent to DirectoryFiles and recount the directories on the way up.
I am working on porting an iOS App to WP8. The iOS app is localized to 5 different languages. The localized strings are stored in different files. For example "app.strings" hold all strings unique for the app and "common.strings" hold strings that are used in other apps as well. Of course there are 5 versions of each .strings file (app.de.strings, app.en.strings, ....).
In the iOS code I can simple refere to "String_ID_123" and the system will automatically search for that string in all .strings files and display the correct value, no matter if this string can be found in app.strings, common.strings or elsewhere. Is this possible on WP8 as well?
VS automatically created two files to support localization:
AppResources.resx which holds the actual strings (with AppRessources.Designer.cs as code behind)
LocalizedStrings.cs which is a helper class to support binding
Of course I could add additional .resx files to the project and use them create the same structure as in the iOS project. But then I would have to add additional versions of LocalizedStrings.cs (e.g. LocalizedStrings_Common.cs) which refere to the correct .resx as well. Then I would have to explicitly use the correct Souce in XAML.
I would have to know where String_ID_123 is defines. Is there any way to let XAML/C# do that automatically?
I'm afraid VS isn't gonna help you with this. First, you cannot address String_ID_123 without specifying the resource file it resides in. Second, when you add additional resource files (e.g. CommonResources.resx) VS will not modify LocalizedStrings for you. You will have to do that yourself.
public class LocalizedStrings
{
private static AppResources _localizedResources = new AppResources();
private static CommonResources _locallizedCommonResources = new CommonResources();
public AppResources LocalizedResources { get { return _localizedResources; } }
public CommonResources LocalizedCommonResources { get { return _locallizedCommonResources; } }
And third, when you add new languages to your project, VS will generate AppResources.fr.resx for you but will not generate CommonResources.fr.resx. You'll have to add a copy your self.