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

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

Related

winforms, help needed to create a dynamic tablelayoutpanel with buttons

I am trying to create a dynamic tablelayout which creates adds a skill and gives that skill 1 level and a increase decrease button. I am struggling with having the buttons access the level label. I thought about finding the location of the button that was clicked, but could figure out how to do that.
In advance, Thank you.
example:
1
this is what i have so far:
private void skilladded(object sender, EventArgs e)
{
int i = 1;
int[] position= { 0,0};
bool test = false;
//string select;
int k=0;
for (i=1;i<=skillstableLayoutPanel.RowCount;i++)
{
Control c= skillstableLayoutPanel.GetControlFromPosition(0,i);
if (c!=null&&addskillswin.selected==c.Text)
{
test = true;
k = i;
break;
}
else if(c==null)
{
k = i;
break;
}
}
if (test==false)
{
Label newskill = new Label();
Label newskilllvl = new Label();
TableLayoutPanel buttontable = new TableLayoutPanel();
Button up = new Button();
Button down = new Button();
buttontable.ColumnCount = 2;
buttontable.RowCount = 1;
buttontable.RowStyles.Add(new RowStyle(SizeType.Percent,100f));
buttontable.ColumnStyles.Add(new ColumnStyle(SizeType.Percent,50f));
buttontable.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50f));
buttontable.Margin = new Padding(0,0,0,0);
buttontable.Dock = DockStyle.Fill;
buttontable.Controls.Add(up, 0, 0);
buttontable.Controls.Add(down, 1, 0);
up.BackgroundImage = Properties.Resources.up;[enter image description here][2]
down.BackgroundImage = Properties.Resources.down;
up.BackgroundImageLayout=ImageLayout.Stretch;
down.BackgroundImageLayout = ImageLayout.Stretch;
newskill.Text = addskillswin.selected;
newskilllvl.Text = "1";
up.Margin = new Padding(0, 0, 0, 0);
down.Margin = new Padding(0, 0, 0, 0);
skillstableLayoutPanel.Controls.Add(newskill,0,k);
skillstableLayoutPanel.Controls.Add(newskilllvl, 1, k);
skillstableLayoutPanel.Controls.Add(buttontable, 2, k);
skillavaillabel.Text = (Convert.ToInt32(skillavaillabel.Text) - 1).ToString();
skillpointlvl = Convert.ToInt32(newskilllvl.Text);
up.MouseClick += new MouseEventHandler(skillup);
down.MouseClick += new MouseEventHandler(skilldown);
}
if (test==true)
{
skillstableLayoutPanel.GetControlFromPosition(1, k).Text = (Convert.ToInt32(skillstableLayoutPanel.GetControlFromPosition(1, k).Text) + 1).ToString();
skillavaillabel.Text = (Convert.ToInt32(skillavaillabel.Text) -1).ToString();
}
}
private void skillup(object sender, EventArgs e)
{
skillpointlvl++;
}
private void skilldown(object sender, EventArgs e)
{
skillpointlvl--;
}
Instead of this:
up.MouseClick += new MouseEventHandler(skillup);
down.MouseClick += new MouseEventHandler(skilldown);
Try something like this:
up.MouseClick += ((o, me) =>
{
int currentLevel = Int32.Parse(newskilllvl.Text);
currentLevel++;
newskilllvl.Text = currentLevel.ToString();
});
down.MouseClick += ((o, me) =>
{
int currentLevel = Int32.Parse(newskilllvl.Text);
currentLevel--;
newskilllvl.Text = currentLevel.ToString();
});
Your skillup or skilldown event handler could potentially process each sender and see from which button the click originated, but you'd have to uniquely name each button and do a lot of string comparisons to see which button corresponds to which skill level label. The code above adds an anonymous handler to each button, which allows us to essentially tie the button being clicked to its corresponding skill label.
Personally though, I would not store all these values in labels like you have done. I would either store them in properties on the form, or better yet in a class of their own. I would also put all this logic somewhere else beside the form, like in a Presenter or Controller (or class library), and follow a design pattern such as MVC or MVP. This will make maintaining and changing the application easier in the long run.
Also, consider using camel case and Pascal case for your variable names. All lowercase variables are harder for experienced programmers to read.

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;
}
}
}
}
}

EWS The server cannot service this request right now

I am seeing errors while exporting email in office 365 account using ews managed api, "The server cannot service this request right now. Try again later." Why is that error occurring and what can be done about it?
I am using the following code for that work:-
_GetEmail = (EmailMessage)item;
bool isread = _GetEmail.IsRead;
sub = _GetEmail.Subject;
fold = folder.DisplayName;
historicalDate = _GetEmail.DateTimeSent.Subtract(folder.Service.TimeZone.GetUtcOffset(_GetEmail.DateTimeSent));
props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(_source, item.Id, props);
bytes = new byte[email.MimeContent.Content.Length];
fs = new MemoryStream(bytes, 0, email.MimeContent.Content.Length, true);
fs.Write(email.MimeContent.Content, 0, email.MimeContent.Content.Length);
Demail = new EmailMessage(_destination);
Demail.MimeContent = new MimeContent("UTF-8", bytes);
// 'SetExtendedProperty' used to maintain historical date of items
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(57, MapiPropertyType.SystemTime), historicalDate);
// PR_MESSAGE_DELIVERY_TIME
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(3590, MapiPropertyType.SystemTime), historicalDate);
if (isread == false)
{
Demail.IsRead = isread;
}
if (_source.RequestedServerVersion == flagVersion && _destination.RequestedServerVersion == flagVersion)
{
Demail.Flag = _GetEmail.Flag;
}
_lstdestmail.Add(Demail);
_objtask = new TaskStatu();
_objtask.TaskId = _taskid;
_objtask.SubTaskId = subtaskid;
_objtask.FolderId = Convert.ToInt64(folderId);
_objtask.SourceItemId = Convert.ToString(_GetEmail.InternetMessageId.ToString());
_objtask.DestinationEmail = Convert.ToString(_fromEmail);
_objtask.CreatedOn = DateTime.UtcNow;
_objtask.IsSubFolder = false;
_objtask.FolderName = fold;
_objdbcontext.TaskStatus.Add(_objtask);
try
{
if (counter == countGroup)
{
Demails = new EmailMessage(_destination);
Demails.Service.CreateItems(_lstdestmail, _destinationFolder.Id, MessageDisposition.SaveOnly, SendInvitationsMode.SendToNone);
_objdbcontext.SaveChanges();
counter = 0;
_lstdestmail.Clear();
}
}
catch (Exception ex)
{
ClouldErrorLog.CreateError(_taskid, subtaskid, ex.Message + GetLineNumber(ex, _taskid, subtaskid), CreateInnerException(sub, fold, historicalDate));
counter = 0;
_lstdestmail.Clear();
continue;
}
This error occurs only if try to export in office 365 accounts and works fine in case of outlook 2010, 2013, 2016 etc..
Usually this is the case when exceed the EWS throttling in Exchange. It is explain in here.
Make sure you already knew throttling policies and your code comply with them.
You can find throttling policies using Get-ThrottlingPolicy if you have the server.
One way to solve the throttling issue you are experiencing is to implement paging instead of requesting all items in one go. You can refer to this link.
For instance:
using Microsoft.Exchange.WebServices.Data;
static void PageSearchItems(ExchangeService service, WellKnownFolderName folder)
{
int pageSize = 5;
int offset = 0;
// Request one more item than your actual pageSize.
// This will be used to detect a change to the result
// set while paging.
ItemView view = new ItemView(pageSize + 1, offset);
view.PropertySet = new PropertySet(ItemSchema.Subject);
view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Descending);
view.Traversal = ItemTraversal.Shallow;
bool moreItems = true;
ItemId anchorId = null;
while (moreItems)
{
try
{
FindItemsResults<Item> results = service.FindItems(folder, view);
moreItems = results.MoreAvailable;
if (moreItems && anchorId != null)
{
// Check the first result to make sure it matches
// the last result (anchor) from the previous page.
// If it doesn't, that means that something was added
// or deleted since you started the search.
if (results.Items.First<Item>().Id != anchorId)
{
Console.WriteLine("The collection has changed while paging. Some results may be missed.");
}
}
if (moreItems)
view.Offset += pageSize;
anchorId = results.Items.Last<Item>().Id;
// Because you’re including an additional item on the end of your results
// as an anchor, you don't want to display it.
// Set the number to loop as the smaller value between
// the number of items in the collection and the page size.
int displayCount = results.Items.Count > pageSize ? pageSize : results.Items.Count;
for (int i = 0; i < displayCount; i++)
{
Item item = results.Items[i];
Console.WriteLine("Subject: {0}", item.Subject);
Console.WriteLine("Id: {0}\n", item.Id.ToString());
}
}
catch (Exception ex)
{
Console.WriteLine("Exception while paging results: {0}", ex.Message);
}
}
}

In Selenium, how to compare images?

As i have been asked to automate our Company's website using Selenium Automation tooL.
But i am new to Selenium tool to proceed with, but i have learnt the basics of Selenium IDE and RC. But i am very much confused with how to compare actual and original images as we usually do in other automation tools. How do we come to a result that there bug in the website? Its obviously through image comparison but i wonder as selenium is one of the very popular tools but it doesn't have image comparing option. On the other hand i doubt whether my way of proceeding with the automation process is correct! Could somebody please help me out..
Thanks in Advance!!
Sanjay S
I had simillar task. I needed to compare more than 3000 images on a WebPage.
First of all I scrolled page to load all images:
public void compareImage() throws InterruptedException {
driver.get(baseUrl);
driver.manage().window().maximize();
JavascriptExecutor executor = (JavascriptExecutor) driver;
Long previousHeight;
Long currentHeight;
do {
previousHeight = (Long) executor.executeScript("return document.documentElement.scrollHeight");
executor.executeScript("window.scrollBy(0, document.documentElement.scrollHeight)");
Thread.sleep(500);
currentHeight = (Long) executor.executeScript("return document.documentElement.scrollHeight");
} while (Long.compare(previousHeight, currentHeight) != 0);
after I compared size of all images with first image(or you can just write size):
List<WebElement> images = driver.findElements(By.cssSelector("img[class='playable']"));
List<String> errors = new LinkedList<>();
int imgWidth, imgHeight, elWidth, elHeight;
int imgNum = 0;
imgWidth = images.get(0).getSize().getWidth();
imgHeight = images.get(0).getSize().getHeight();
for (WebElement el : images) {
imgNum++;
elWidth = el.getSize().getWidth();
elHeight = el.getSize().getHeight();
if (imgWidth != elWidth || imgHeight != elHeight) {
errors.add(String.format("Picture # %d has incorrect size (%d : %d) px"
, imgNum, elWidth, elHeight));
}
}
for (String str : errors)
System.out.println(str);
if (errors.size() == 0)
System.out.println("All images have the same size");
}
Since you mention knowledge about Selenium RC, you can easily extend Selenium's capability using a library for your chosen programming language. For instance, in Java you can use the PixelGrabber class for comparing two images and assert their match.
imagemagick and imagediff are also two good tools to use for image matching. You would require Selenium RC and a programming language knowledge to work with it.
Image comparison on C#. To get exact results I recommend to disable anti aliasing browser feature before taking screenshots, otherwise pixels each time are a little bit different drawn. For example HTML canvas element options.AddArgument("disable-canvas-aa");
private static bool ImageCompare(Bitmap bmp1, Bitmap bmp2, Double TolerasnceInPercent)
{
bool equals = true;
bool flag = true; //Inner loop isn't broken
//Test to see if we have the same size of image
if (bmp1.Size == bmp2.Size)
{
for (int x = 0; x < bmp1.Width; ++x)
{
for (int y = 0; y < bmp1.Height; ++y)
{
Color Bitmap1 = bmp1.GetPixel(x, y);
Color Bitmap2 = bmp2.GetPixel(x, y);
if (Bitmap1.A != Bitmap2.A)
{
if (!CalculateTolerance(Bitmap1.A, Bitmap2.A, TolerasnceInPercent))
{
flag = false;
equals = false;
break;
}
}
if (Bitmap1.R != Bitmap2.R)
{
if (!CalculateTolerance(Bitmap1.R, Bitmap2.R, TolerasnceInPercent))
{
flag = false;
equals = false;
break;
}
}
if (Bitmap1.G != Bitmap2.G)
{
if (!CalculateTolerance(Bitmap1.G, Bitmap2.G, TolerasnceInPercent))
{
flag = false;
equals = false;
break;
}
}
if (Bitmap1.B != Bitmap2.B)
{
if (!CalculateTolerance(Bitmap1.B, Bitmap2.B, TolerasnceInPercent))
{
flag = false;
equals = false;
break;
}
}
}
if (!flag)
{
break;
}
}
}
else
{
equals = false;
}
return equals;
}
This C# function calculates tolerance
private static bool CalculateTolerance(Byte FirstImagePixel, Byte SecondImagePixel, Double TolerasnceInPercent)
{
double OneHundredPercent;
double DifferencesInPix;
double DifferencesPercentage;
if (FirstImagePixel > SecondImagePixel)
{
OneHundredPercent = FirstImagePixel;
}
else
{
OneHundredPercent = SecondImagePixel;
}
if (FirstImagePixel > SecondImagePixel)
{
DifferencesInPix = FirstImagePixel - SecondImagePixel;
}
else
{
DifferencesInPix = SecondImagePixel - FirstImagePixel;
}
DifferencesPercentage = (DifferencesInPix * 100) / OneHundredPercent;
DifferencesPercentage = Math.Round(DifferencesPercentage, 2);
if (DifferencesPercentage > TolerasnceInPercent)
{
return false;
}
return true;
}

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

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;
}

Resources