How do I enumerate a list of Source Files with Pending Changes and get their server location? - visual-studio

I'm trying to write a macro that will generate a plain-text list of files changed based on the list of files in the Pending Changes pane but I can't figure out how to do it. The server location of a file is the property that is formatted like this:
$/TfsName/SomeSolution/Web/SomeFolder/SomeFile1.aspx
$/TfsName/SomeSolution/Web/SomeFolder/SomeFile2.aspx
The closest I can get is opening the properties of the selected item in the pane, which isn't very useful:
DTE.ExecuteCommand ("TeamFoundationContextMenus.SourceControlPendingChangesSourceFiles.TfsContextPendingCheckinsPendingCheckinsProperties")
Edit: here's the entire code for the macro I have so far, the TODOs are where I need help:
Public Class Pending
Public Shared Sub Pending()
OutputClear()
OutputWriteLine("Files Changed:")
Dim outInfo As String = ""
DTE.Windows.Item("{2456BD12-ECF7-4988-A4A6-67D49173F564}").Activate() 'Pending Changes - Source Files
'TODO: loop through each changed file
'TODO: get TFS server location of each file
outInfo &= "some file name"
OutputWriteLine(outInfo)
End Sub
' snip: other supporting functions
End Class

Well I haven't been able to figure out how to do it with a macro yet, but thanks to Bob Hardister on twitter, I can use this command to get what I'm looking for:
"C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\tf.exe" status $/ProjectName/SubDir/ /recursive
...but what works even better is a command-line app that uses this code:
const string TFSSERVER = "http://TfsServer:8080";
static void Main(string[] args)
{
//http://blogs.msdn.com/b/buckh/archive/2006/03/15/552288.aspx
//http://blogs.msdn.com/b/jmanning/archive/2005/12/01/499033.aspx
string projectName = args[0];
TeamFoundationServer tfs = new TeamFoundationServer(TFSSERVER);
VersionControlServer versionControl = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
PendingSet[] sets = versionControl.GetPendingSets(new String[] { "$/Projects/" + projectName }, RecursionType.Full);
Console.WriteLine(versionControl.AuthenticatedUser + " pending changes for " + projectName + ":");
foreach (PendingSet set in sets)
{
if (set.Type == PendingSetType.Workspace && set.OwnerName == versionControl.AuthenticatedUser)
{
foreach (PendingChange pc in set.PendingChanges)
{
Console.WriteLine(pc.ServerItem);
}
}
}
}
Then I just added the compiled EXE call to the External Tools menu and use it within VS there.
Bonus Edit: Here's the VSS version (not as nice):
const string SSDIR = #"\\VssServer\VssShare";
static void Main(string[] args)
{
string projectName = args[0];
string userName = "user";
VSSDatabaseClass vss = new VSSDatabaseClass();
vss.Open(SSDIR + #"\srcsafe.ini", userName, userName);
VSSItem sourceItem = vss.get_VSSItem("$/Projects/" + projectName, false);
Console.WriteLine(userName + " pending checkins for " + projectName + ":");
int total = GetItems(sourceItem);
Console.WriteLine(total.ToString() + " total changes.");
}
const int VSSFILE_CHECKEDOUT_ME = 2;
const int VSSITEM_PROJECT = 0;
const int VSSITEM_FILE = 1;
public static int GetItems(IVSSItem originalItem)
{
int total = 0;
foreach (IVSSItem subItem in originalItem.get_Items(false))
{
if (subItem.Type == VSSITEM_FILE && subItem.IsCheckedOut == VSSFILE_CHECKEDOUT_ME)
{
Console.WriteLine(subItem.Spec);
total++;
}
else if (subItem.Type == VSSITEM_PROJECT)
{
total += GetItems(subItem);
}
}
return total;
}

Related

How can I save and load textbox values, checkbox states, dropdown menu selections, etc., into a .txt file?

I am trying to implement a simple save-load function in my application, which would save the states of various GUI elements in my application (textboxes, checkboxes, dropdown menus, and so on) to a custom-named .txt file, and then load them back the next time user runs my application. I do not want to use My.Settings, because it is a portable application, and therefore the settings file has to be next to the executable. Also because my application is an editor, and the settings have to be bound by name to the current file the user is working with.
Write permissions is not an issue. I want to code this in a way so that I would only have to write down the names of the GUI elements to be mentioned once in my code, preferably in a list. Like this (pseudo-code):
'list
Dim ElementsToSave() as Object = {
Textbox1.text,
Checkbox1.Checked,
DropDownMenu1.SelectedItem,
.....
}
'save-load sub
Sub SaveLoad(Elements as Object, mode as string)
If mode = "save" then
For each Element in Elements
??? 'save each element state to .txt file
Next
If mode = "load" then
For each Element in Elements
??? 'load each element state from .txt file
Next
End if
End sub
'call
SaveLoad(ElementsToSave, "save")
'or
SaveLoad(ElementsToSave, "load")
I hope this conveys what I'm trying to achieve. Can anyone give any advice on how to make this work, please?
EDIT: I forgot to mention. It would be very nice if each value in the .txt file would be saved with a key that refers to a specific element, so that if I add more GUI elements in my application in the future, or re-arrange them, this save-load sub would always choose the correct value from the .txt file for a specific element.
using System.IO;
...
private enum ControlProperty
{
None = 0,
Text = 1,
Checked = 2,
SelectedValue = 3
}
private string GetSettingsFile()
{
FileInfo fi = new FileInfo(System.Reflection.Assembly.GetEntryAssembly().Location);
string path = Path.Combine(fi.Directory.FullName, "settings.txt");
return path;
}
private void test()
{
SaveSettings();
LoadSettings();
}
private void SaveSettings()
{
object[] vals = new object[] { this.Textbox1, ControlProperty.Text, this.Textbox1.Text, this.Checkbox1, ControlProperty.Checked, this.Checkbox1.Checked, this.Menu1, ControlProperty.SelectedValue, this.Menu1.SelectedValue };
string txt = "";
for (int i = 0; i < vals.Length; i += 3)
{
string controlID = (vals[i] as Control).ID;
ControlProperty property = (ControlProperty)vals[i + 1];
object state = vals[i + 2];
txt += controlID + ":" + property.ToString() + ":" + state.ToString() + "\n";
}
string file = GetSettingsFile();
File.WriteAllText(file, txt);
}
private void LoadSettings()
{
string file = GetSettingsFile();
string[] lines = File.ReadAllLines(file);
foreach (string s in lines)
{
string[] parts = s.Split(':');
if (parts.Length < 3) continue;
string id = parts[0];
var c = this.form1.FindControl(id);
ControlProperty prop = ControlProperty.None;
Enum.TryParse<ControlProperty>(parts[1], out prop);
string state = parts[2];
if (c is TextBox && prop == ControlProperty.Text)
{
TextBox t = c as TextBox;
t.Text = state;
}
else if (c is CheckBox && prop == ControlProperty.Checked)
{
CheckBox chk = c as CheckBox;
chk.Checked = state == "True";
}
else if (c is Menu && prop == ControlProperty.SelectedValue)
{
Menu m = c as Menu;
foreach (MenuItem menuItem in m.Items)
{
if (menuItem.Value == state)
{
menuItem.Selected = true;
}
}
}
}
}

How to display shared calendar in outlook using c#?

i want'to display shared calendar. i search on the web and i find this code:
static void Main(string[] args)
{
Outlook.Application objOLApp;
Outlook.MAPIFolder objFolder;
Outlook.Explorer objExplorer;
Outlook.MAPIFolder objSubFolder;
Outlook.AppointmentItem objCalenderItem;
Outlook.Folders objOutlookFolders;
int intFolderCtr;
int intSubFolderCtr;
int intAppointmentCtr;
// >> Initialize The Base Objects
objOLApp = new Outlook.Application();
objOutlookFolders = objOLApp.Session.Folders;
// >> Loop Through The PST Files Added n Outlook
for (intFolderCtr = 1; intFolderCtr <= objOutlookFolders.Count; intFolderCtr++)
{
objFolder = objOutlookFolders[intFolderCtr];
objExplorer = objFolder.GetExplorer();
// >> Loop Through The Folders In The PST File
for (intSubFolderCtr = 1; intSubFolderCtr <= objExplorer.CurrentFolder.Folders.Count; intSubFolderCtr++)
{
objSubFolder = objExplorer.CurrentFolder.Folders[intSubFolderCtr];
// >> Check if Folder Contains Appointment Items
if (objSubFolder.DefaultItemType == Outlook.OlItemType.olAppointmentItem)
{
// >> Loop Through Appointment Items
for (intAppointmentCtr = 1; intAppointmentCtr <= objSubFolder.Items.Count; intAppointmentCtr++)
{
// >> Get Teh Calender Item From The Calender Folder
objCalenderItem = objSubFolder.Items[intAppointmentCtr];
// >> Process Appointment Item Accordingly
Console.WriteLine(objCalenderItem.Subject);
Console.WriteLine(objCalenderItem.Location + "," + objCalenderItem.Start.ToLongDateString());
Console.WriteLine();
Console.WriteLine();
}
}
}
}
// >> Close Application
// objOLApp.Quit();
// >> Release COM Object
System.Runtime.InteropServices.Marshal.ReleaseComObject(objOLApp);
objOLApp = null;
Console.ReadLine();
}
but it's see only my appointment.
i want to see all shared calendar. because i set meeting but i don't know any idea about other person's busy or free. so how can i display other's calendar?
editing :
i changed this line
objOutlookFolders = objOLApp.Session.Folders;
with
objOutlookFolders = oNs.GetSharedDefaultFolder(oRep, OlDefaultFolders.olFolderCalendar).Folders;
and i add this
NameSpace oNs = objOLApp.GetNamespace("MAPI");
Recipient oRep = oNs.CreateRecipient(objOLApp.Session.CurrentUser.Name);
but i still get the error.
new version is
static void Main(string[] args)
{
Outlook.Application objOLApp;
Outlook.MAPIFolder objFolder;
Outlook.Explorer objExplorer;
Outlook.MAPIFolder objSubFolder;
Outlook.AppointmentItem objCalenderItem;
Outlook.Folders objOutlookFolders;
int intFolderCtr;
int intSubFolderCtr;
int intAppointmentCtr;
// >> Initialize The Base Objects
objOLApp = new Outlook.Application();
NameSpace oNs = objOLApp.GetNamespace("MAPI");
Recipient oRep = oNs.CreateRecipient(objOLApp.Session.CurrentUser.Name);
oRep.Resolve();
//if (oRep.Resolved)
objOutlookFolders = oNs.GetSharedDefaultFolder(oRep, OlDefaultFolders.olFolderCalendar).Folders;
//else
// objOutlookFolders = objOLApp.Session.Folders;
// >> Loop Through The PST Files Added n Outlook
for (intFolderCtr = 1; intFolderCtr <= objOutlookFolders.Count; intFolderCtr++)
{
objFolder = objOutlookFolders[intFolderCtr];
objExplorer = objFolder.GetExplorer();
// >> Loop Through The Folders In The PST File
for (intSubFolderCtr = 1; intSubFolderCtr <= objExplorer.CurrentFolder.Folders.Count; intSubFolderCtr++)
{
objSubFolder = objExplorer.CurrentFolder.Folders[intSubFolderCtr];
// >> Check if Folder Contains Appointment Items
if (objSubFolder.DefaultItemType == Outlook.OlItemType.olAppointmentItem)
{
// >> Loop Through Appointment Items
for (intAppointmentCtr = 1; intAppointmentCtr <= objSubFolder.Items.Count; intAppointmentCtr++)
{
// >> Get Teh Calender Item From The Calender Folder
objCalenderItem = objSubFolder.Items[intAppointmentCtr];
// >> Process Appointment Item Accordingly
Console.WriteLine(objCalenderItem.Subject);
Console.WriteLine(objCalenderItem.Location + "," + objCalenderItem.Start.ToLongDateString());
Console.WriteLine();
Console.WriteLine();
}
}
}
}
// >> Close Application
// objOLApp.Quit();
// >> Release COM Object
System.Runtime.InteropServices.Marshal.ReleaseComObject(objOLApp);
objOLApp = null;
Console.ReadLine();
}
please help me.. i'm still working. but it did not .
Use Namespace.GetSharedDefaultFolder to open other user's default Calendar folder.

analyzing zipped or any archive file

I was wondering if anyone can recommend a tool to analyze zipped or any archive file. I do not mean checking what is inside the archive but more about how it was compressed, with what compression method, etc.
Thanks!
For data compressed into a ZIP file, the command-line tool zipinfo is quite helpful, particularly when using the '-v' argument (for verbose mode). I learned of zipinfo from this zip-related question on SuperUser
I recently ran into an issue where the zip's being created by one tool would only open with certain programs and not others. The issue turned out to be that directories didn't have entries in the zip file, they were just implied by the presence of files in them. Also all the directory separators were backslashes instead of forward slashes.
zipinfo didn't really help with these bits. I needed to see the zip entries so I ended up writing this the following which allowed me diff the directory entries with a known good version
using System;
using System.IO;
using System.Text;
namespace ZipAnalysis
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("No filename specified");
Console.WriteLine("Press any key to exit");
Console.ReadKey(true);
return;
}
string fileName = args[0];
if (!File.Exists(fileName))
{
Console.WriteLine($"File not found: {fileName}");
Console.WriteLine("Press any key to exit");
Console.ReadKey(true);
return;
}
using (var file = File.OpenRead(fileName))
{
//First, find the End of central directory record
BinaryReader reader = new BinaryReader(file);
int entryCount = ReadEndOfCentralDirectory(reader);
if (entryCount > 0)
{
ReadCentralDirectory(reader, entryCount);
}
}
Console.WriteLine("Press any key to exit");
Console.ReadKey(true);
}
private static int ReadEndOfCentralDirectory(BinaryReader reader)
{
var b = reader.ReadByte();
int result = 0;
long fileSize = reader.BaseStream.Length;
while (result == 0 && reader.BaseStream.Position < fileSize)
{
while (b != 0x50)
{
if (reader.BaseStream.Position < fileSize)
b = reader.ReadByte();
else
break;
}
if (reader.BaseStream.Position >= fileSize)
{
break;
}
if (reader.ReadByte() == 0x4b && reader.ReadByte() == 0x05 && reader.ReadByte() == 0x06)
{
int diskNumber = reader.ReadInt16();
int centralDirectoryStartDiskNumber = reader.ReadInt16();
int centralDirectoryCount = reader.ReadInt16();
int centralDirectoryTotal = reader.ReadInt16();
result = centralDirectoryTotal;
int centralDirectorySize = reader.ReadInt32();
int centralDirectoryOffset = reader.ReadInt32();
int commentLength = reader.ReadInt16();
string comment = Encoding.ASCII.GetString(reader.ReadBytes(commentLength));
Console.WriteLine("EOCD Found");
Console.WriteLine($"Disk Number: {diskNumber}");
Console.WriteLine($"Central Directory Disk Number: {centralDirectoryStartDiskNumber}");
Console.WriteLine($"Central Directory Count: {centralDirectoryCount}");
Console.WriteLine($"Central Directory Total: {centralDirectoryTotal}");
Console.WriteLine($"Central Directory Size: {centralDirectorySize}");
Console.WriteLine($"Central Directory Offset: {centralDirectoryOffset}");
Console.WriteLine($"Comment: {comment}");
reader.BaseStream.Seek(centralDirectoryOffset, SeekOrigin.Begin);
}
b=0;
}
return result;
}
private static void ReadCentralDirectory(BinaryReader reader, int count)
{
for (int i = 0; i < count; i++)
{
var signature = reader.ReadInt32();
if (signature == 0x02014b50)
{
Console.WriteLine($"Version Made By: {reader.ReadInt16()}");
Console.WriteLine($"Minimum version to extract: {reader.ReadInt16()}");
Console.WriteLine($"Bit Flag: {reader.ReadInt16()}");
Console.WriteLine($"Compression Method: {reader.ReadInt16()}");
Console.WriteLine($"File Last Modification Time: {reader.ReadInt16()}");
Console.WriteLine($"File Last Modification Date: {reader.ReadInt16()}");
Console.WriteLine($"CRC: {reader.ReadInt32()}");
Console.WriteLine($"CompressedSize: {reader.ReadInt32()}");
Console.WriteLine($"UncompressedSize: {reader.ReadInt32()}");
var fileNameLength = reader.ReadInt16();
var extraFieldLength = reader.ReadInt16();
var fileCommentLength = reader.ReadInt16();
Console.WriteLine($"Disk number where file starts: {reader.ReadInt16()}");
Console.WriteLine($"Internal file attributes: {reader.ReadInt16()}");
Console.WriteLine($"External file attributes: {reader.ReadInt32()}");
Console.WriteLine($"Relative offset of local file header: {reader.ReadInt32()}");
string filename = Encoding.ASCII.GetString(reader.ReadBytes(fileNameLength));
string extraField = Encoding.ASCII.GetString(reader.ReadBytes(extraFieldLength));
string fileComment = Encoding.ASCII.GetString(reader.ReadBytes(fileCommentLength));
Console.WriteLine($"Filename: {filename}");
Console.WriteLine($"Extra Field: {extraField}");
Console.WriteLine($"File Comment: {fileComment}");
}
}
}
}
}

Unlist a package from Nuget with all it's history versions

I'm looking for a simple solution, on how to Unlist (delete) a package from Nuget library with all of it's versions.
I'm asking this, because manually I can do this 1 version at a time for 1 package. And I have lots of packages with ~20+ versions each ..
Please help.
I know this is old, but the nuget "delete" command is still not implemented, so I needed a solution. Perhaps someone can still benefit from this. I created a Command-line program and added the Nuget package Headless. Then, the code is:
class Program
{
static void Main(string[] args)
{
List<PostEntry> parameters = new List<PostEntry>();
using (var browser = new Browser())
{
var loginPage = browser.GoTo<LoginPage>();
parameters.Clear();
parameters.Add(new PostEntry("SignIn.UserNameOrEmail", "yournugetusername"));
parameters.Add(new PostEntry("SignIn.Password", "yournugetpassword"));
parameters.Add(new PostEntry("ReturnUrl", "/"));
parameters.Add(new PostEntry("LinkingAccount", "false"));
parameters.Add(new PostEntry("__RequestVerificationToken", loginPage.Find<HtmlElement>().ById("signIn").Find<HtmlInput>().ByName("__RequestVerificationToken").Value));
browser.PostTo(parameters, new Uri("https://www.nuget.org/users/account/SignIn"));
Console.WriteLine("logged in");
var cont = true;
while (cont)
{
var packagesPage = browser.GoTo<PackagesPage>();
Console.WriteLine("at packages page");
var links = packagesPage.Find<HtmlElement>()
.ById("published")
.Find<HtmlLink>()
.AllByPredicate(o => o.GetAttribute("title") == "Delete"
&& (o.Href.Contains("/IdOfPackageIWantToDelete/")
|| o.Href.Contains("/IdOfAnotherPackageIWantToDelete/")));
cont = links.Count() >= 1;
if (links.Any())
{
var htmlLink = links.First();
var linkPage = htmlLink.Href;
Console.WriteLine(linkPage);
var deletePackagePage = htmlLink.Click();
parameters.Clear();
parameters.Add(new PostEntry("Listed", "false"));
parameters.Add(new PostEntry("__RequestVerificationToken", deletePackagePage.Find<HtmlElement>().ById("body").Find<HtmlInput>().ByName("__RequestVerificationToken").Value));
browser.PostTo(parameters, new Uri("https://www.nuget.org" + linkPage));
Console.WriteLine("delete submitted");
}
}
}
Console.WriteLine("Hit any key to quit");
Console.ReadKey();
}
public class LoginPage : HtmlPage
{
public override Uri TargetLocation
{
get { return new Uri("https://www.nuget.org/users/account/LogOn?returnUrl=%2F"); }
}
}
public class PackagesPage : HtmlPage
{
public override Uri TargetLocation
{
get { return new Uri("https://www.nuget.org/account/Packages"); }
}
}
}
It will take a little while to run, but it does the trick.
You could write a batch script to call nuget delete, iterating over all your packages - see http://docs.nuget.org/docs/reference/command-line-reference#Delete_Command
e.g.
nuget delete MyPackage 1.1 -NoPrompt
nuget delete MyPackage 1.2 -NoPrompt
etc. Pretty straightforward with grep or a search/replace in a text editor.
You can do this with PowerShell, here is a function:
function Remove-AllNuGetPackageVersions($PackageId, $ApiKey)
{
$lower = $PackageId.ToLowerInvariant();
$json = Invoke-WebRequest -Uri "https://api.nuget.org/v3-flatcontainer/$lower/index.json" | ConvertFrom-Json
foreach($version in $json.versions)
{
Write-Host "Unlisting $PackageId, Ver $version"
Invoke-Expression "nuget delete $PackageId $version $ApiKey -source https://api.nuget.org/v3/index.json -NonInteractive"
}
}
If you are hosting your own nuget gallery, you could execute the following SQL query against the nuget database:
select 'nuget delete ' + pr.Id + ' ' + p.Version + ' {ApiKey} -source {Source} -noninteractive' from Packages p
join PackageRegistrations pr
on p.PackageRegistrationKey = pr.[Key]
where pr.Id = '{PackageId}' AND
p.Listed = 1
Replace {ApiKey}, {Source} and {PackageId} with your own values, I then placed the results in a batch file adjacent to nuget.exe and executed.
You can use a combination of tools by creating a simple console application.
Create a new Console application and install the package "Nuget.Core".
Add the following method to get a list of all package versions:
private static IEnumerable<IPackage> GetListedPackages(string packageID)
{
var repo = PackageRepositoryFactory.Default.CreateRepository("https://packages.nuget.org/api/v2");
var packages = from package in repo.FindPackagesById(packageID)
where package.IsListed()
select package;
return packages;
}
Then copy nuget.exe from the ".nuget" folder in one of your projects into the console application project (add it in the solution explorer and make sure that it's copied to the output directory)
Next create a new method which uses nuget.exe to unlist the package version:
private static string UnlistPackage(IPackage package, string apiKey)
{
var arguments = $"delete {package.Id} {package.Version} -ApiKey {apiKey} -NonInteractive";
var psi = new ProcessStartInfo("nuget.exe", arguments)
{
RedirectStandardOutput = true,
WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory,
UseShellExecute = false
};
var process = Process.Start(psi);
process.WaitForExit();
return process.StandardOutput.ReadToEnd();
}
Finally change Main method so it gets a list and removes all versions:
private static void Main(string[] args)
{
var packageID = "yourPackageName";
var apiKey = "yourKeyFromNugetOrg";
var packages = GetListedPackages(packageID);
foreach (var package in packages)
{
Console.WriteLine("Unlisting package " + package);
var output = UnlistPackage(package, apiKey);
Console.WriteLine(output);
}
Console.Write("Completed. Press ENTER to quit.");
Console.ReadLine();
}
source: http://blog.gauffin.org/2016/09/how-to-remove-a-package-from-nuget-org/

Can I copy multiple rows from the Visual Studio "Find Symbol Results" window?

Does anyone know how to copy all the lines in the Visual Studio "Find Symbol Results" window onto the clipboard? You can copy a single line, but I want to copy them all.
I can't believe that I'm the first one to want to do this, but I can't even find a discussion about this apparently missing feature.
Here is some code that uses the .Net Automation library to copy all the text to the clipboard.
Start a new WinForms project and then add the following references:
WindowsBase
UIAutomationTypes
UIAutomationClient
System.Xaml
PresentationCore
PresentationFramework
System.Management
The code also explains how to setup a menu item in visual studio to copy the contents to the clipboard.
Edit: The UI Automation only returns visible tree view items. Thus, to copy all the items, the find symbol results window is set as foreground, and then a {PGDN} is sent, and the next batch of items is copied. This process is repeated until no new items are found. It would have been preferable to use the ScrollPattern, however it threw an Exception when trying to set the scroll.
Edit 2: Tried to improve the performance of AutomationElement FindAll by running on a separate thread. Seems to be slow in some cases.
Edit 3: Improved performance by making the TreeView window very large. Can copy about 400 items in about 10 seconds.
Edit 4: Dispose objects implementing IDisposable. Better message reporting. Better handling of process args. Put window back to its original size.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Management;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Forms;
namespace CopyFindSymbolResults {
// This program tries to find the 'Find Symbol Results' window in visual studio
// and copy all the text to the clipboard.
//
// The Find Symbol Results window uses a TreeView control that has the class name 'LiteTreeView32'
// In the future if this changes, then it's possible to pass in the class name as the first argument.
// Use TOOLS -> Spy++ to determine the class name.
//
// After compiling this code into an Exe, add a menu item (TOOLS -> Copy Find Symbol Results) in Visual Studio by:
// 1) TOOLS -> External Tools...
// (Note: in the 'Menu contents:' list, count which item the new item is, starting at base-1).
// Title: Copy Find Symbol Results
// Command: C:\<Path>\CopyFindSymbolResults.exe (e.g. C:\Windows\ is one option)
// 2) TOOLS -> Customize... -> Keyboard... (button)
// Show Commands Containing: tools.externalcommand
// Then select the n'th one, where n is the count from step 1).
//
static class Program {
enum Tabify {
No = 0,
Yes = 1,
Prompt = 2,
}
[STAThread]
static void Main(String[] args) {
String className = "LiteTreeView32";
Tabify tabify = Tabify.Prompt;
if (args.Length > 0) {
String arg0 = args[0].Trim();
if (arg0.Length > 0)
className = arg0;
if (args.Length > 1) {
int x = 0;
if (int.TryParse(args[1], out x))
tabify = (Tabify) x;
}
}
DateTime startTime = DateTime.Now;
Data data = new Data() { className = className };
Thread t = new Thread((o) => {
GetText((Data) o);
});
t.IsBackground = true;
t.Start(data);
lock(data) {
Monitor.Wait(data);
}
if (data.p == null || data.p.MainWindowHandle == IntPtr.Zero) {
System.Windows.Forms.MessageBox.Show("Cannot find Microsoft Visual Studio process.");
return;
}
try {
SimpleWindow owner = new SimpleWindow { Handle = data.MainWindowHandle };
if (data.appRoot == null) {
System.Windows.Forms.MessageBox.Show(owner, "Cannot find AutomationElement from process MainWindowHandle: " + data.MainWindowHandle);
return;
}
if (data.treeViewNotFound) {
System.Windows.Forms.MessageBox.Show(owner, "AutomationElement cannot find the tree view window with class name: " + data.className);
return;
}
String text = data.text;
if (text.Length == 0) { // otherwise Clipboard.SetText throws exception
System.Windows.Forms.MessageBox.Show(owner, "No text was found: " + data.p.MainWindowTitle);
return;
}
TimeSpan ts = DateTime.Now - startTime;
if (tabify == Tabify.Prompt) {
var dr = System.Windows.Forms.MessageBox.Show(owner, "Replace dashes and colons for easy pasting into Excel?", "Tabify", System.Windows.Forms.MessageBoxButtons.YesNo);
if (dr == System.Windows.Forms.DialogResult.Yes)
tabify = Tabify.Yes;
ts = TimeSpan.Zero; // prevent second prompt
}
if (tabify == Tabify.Yes) {
text = text.Replace(" - ", "\t");
text = text.Replace(" : ", "\t");
}
System.Windows.Forms.Clipboard.SetText(text);
String msg = "Data is ready on the clipboard.";
var icon = System.Windows.Forms.MessageBoxIcon.None;
if (data.lines != data.count) {
msg = String.Format("Only {0} of {1} rows copied.", data.lines, data.count);
icon = System.Windows.Forms.MessageBoxIcon.Error;
}
if (ts.TotalSeconds > 4 || data.lines != data.count)
System.Windows.Forms.MessageBox.Show(owner, msg, "", System.Windows.Forms.MessageBoxButtons.OK, icon);
} finally {
data.p.Dispose();
}
}
private class SimpleWindow : System.Windows.Forms.IWin32Window {
public IntPtr Handle { get; set; }
}
private const int TVM_GETCOUNT = 0x1100 + 5;
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int msg, int wparam, int lparam);
[DllImport("user32.dll", SetLastError = true)]
static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int Width, int Height, bool Repaint);
private class Data {
public int lines = 0;
public int count = 0;
public IntPtr MainWindowHandle = IntPtr.Zero;
public IntPtr TreeViewHandle = IntPtr.Zero;
public Process p;
public AutomationElement appRoot = null;
public String text = null;
public String className = null;
public bool treeViewNotFound = false;
}
private static void GetText(Data data) {
Process p = GetParentProcess();
data.p = p;
if (p == null || p.MainWindowHandle == IntPtr.Zero) {
data.text = "";
lock(data) { Monitor.Pulse(data); }
return;
}
data.MainWindowHandle = p.MainWindowHandle;
AutomationElement appRoot = AutomationElement.FromHandle(p.MainWindowHandle);
data.appRoot = appRoot;
if (appRoot == null) {
data.text = "";
lock(data) { Monitor.Pulse(data); }
return;
}
AutomationElement treeView = appRoot.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.ClassNameProperty, data.className));
if (treeView == null) {
data.text = "";
data.treeViewNotFound = true;
lock(data) { Monitor.Pulse(data); }
return;
}
data.TreeViewHandle = new IntPtr(treeView.Current.NativeWindowHandle);
data.count = SendMessage(data.TreeViewHandle, TVM_GETCOUNT, 0, 0);
RECT rect = new RECT();
GetWindowRect(data.TreeViewHandle, out rect);
// making the window really large makes it so less calls to FindAll are required
MoveWindow(data.TreeViewHandle, 0, 0, 800, 32767, false);
int TV_FIRST = 0x1100;
int TVM_SELECTITEM = (TV_FIRST + 11);
int TVGN_CARET = TVGN_CARET = 0x9;
// if a vertical scrollbar is detected, then scroll to the top sending a TVM_SELECTITEM command
var vbar = treeView.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, "Vertical Scroll Bar"));
if (vbar != null) {
SendMessage(data.TreeViewHandle, TVM_SELECTITEM, TVGN_CARET, 0); // select the first item
}
StringBuilder sb = new StringBuilder();
Hashtable ht = new Hashtable();
int chunk = 0;
while (true) {
bool foundNew = false;
AutomationElementCollection treeViewItems = treeView.FindAll(TreeScope.Subtree, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.TreeItem));
if (treeViewItems.Count == 0)
break;
if (ht.Count == 0) {
chunk = treeViewItems.Count - 1;
}
foreach (AutomationElement ele in treeViewItems) {
try {
String n = ele.Current.Name;
if (!ht.ContainsKey(n)) {
ht[n] = n;
foundNew = true;
data.lines++;
sb.AppendLine(n);
}
} catch {}
}
if (!foundNew || data.lines == data.count)
break;
int x = Math.Min(data.count-1, data.lines + chunk);
SendMessage(data.TreeViewHandle, TVM_SELECTITEM, TVGN_CARET, x);
}
data.text = sb.ToString();
MoveWindow(data.TreeViewHandle, rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top, false);
lock(data) { Monitor.Pulse(data); }
}
// this program expects to be launched from Visual Studio
// alternative approach is to look for "Microsoft Visual Studio" in main window title
// but there could be multiple instances running.
private static Process GetParentProcess() {
// from thread: http://stackoverflow.com/questions/2531837/how-can-i-get-the-pid-of-the-parent-process-of-my-application
int myId = 0;
using (Process current = Process.GetCurrentProcess())
myId = current.Id;
String query = String.Format("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {0}", myId);
using (var search = new ManagementObjectSearcher("root\\CIMV2", query)) {
using (ManagementObjectCollection list = search.Get()) {
using (ManagementObjectCollection.ManagementObjectEnumerator results = list.GetEnumerator()) {
if (!results.MoveNext()) return null;
using (var queryObj = results.Current) {
uint parentId = (uint) queryObj["ParentProcessId"];
return Process.GetProcessById((int) parentId);
}
}
}
}
}
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
private struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;
}
}
}
I solved this by using Macro Express. They have a free 30-day trial which I used because this is a one-off for me. I wrote a simple macro to copy all of the Find Symbol Results, one line at a time, over into a Notepad document.
Sequence:
* Repeat (x) times (however many symbol results you have)
* Activate Find Symbol Results window
* Delay .5 seconds
* Simulate keystrokes "Arrow Down"
* Clipboard Copy
* Activate Notepad window
* Delay .5 seconds
* Clipboard Paste
* Simulate keystrokes "ENTER"
* End Repeat
Had the same requirement and were solving this by using a screenshot tool named Hypersnap which also has some basic OCR functionality.
If you can encode your symbol as an expression for global Find, then copy-pasting all results from the Find Results window is easy.
eg finding all references of property 'foo' you might do a global find for '.foo'
I'm using Visual Studio 2017 (Version 15.6.4) and it has the functionality directly. Here's an example screenshot (I hit Ctrl+A to select all lines):
The text output for that example is this:
Status Code File Line Column Project
Console.WriteLine(xml); C:\Users\blah\Documents\ConsoleApp1\Program.cs 30 12 ConsoleApp1
static void Console.WriteLine(object)
Console.WriteLine(xml); C:\Users\blah\Documents\ConsoleApp1\Program.cs 18 12 ConsoleApp1
Console.WriteLine(xml); C:\Users\blah\Documents\ConsoleApp1\Program.cs 25 12 ConsoleApp1
I had the same problem. I had to make a list of all the occurences of a certain method and some of its overloaded version.
To solve my problem I used ReSharper. (ReSharper -> Find -> Find Usages Advanced).
It also has a very nice tabulated text export feature.
From my previous experience and a few tests I just did, there is no built in feature to do this.
Why do you want to do this? Why do you want to copy all of the references to the clipboard? As I understand it the speed of these features would make having a static copy of all the references would be relatively useless if you can generate a dynamic and complete copy quickly.
You can always extend visual studio to add this functionality, see
this post at egghead cafe.
Hey Somehow you can achieve this in another way,
Just 'FindAll' the selected text and you will be able to catch all the lines
Visual Studio Code works as a browser.
it is possible to open the developer tools and search for that part of the code
Menu: Help > Toggle Developer tools
write the following instructions to the developer tools console:
var elementos = document.getElementsByClassName("plain match")
console.log(elementos.length)
for(var i = 0; i<elementos.length;i++) console.log(elementos[i].title)
and you can see the match results.
Now if you can copy the results

Resources