I've very new to the TestStack (White) UI Automation library and I'm having a bit of an issue in terms of "hooking" the process. I'm trying to hook CCleaner, but I keep getting
An unhandled exception of type 'TestStack.White.AutomationException'
occurred in TestStack.White.dll
Additional information: Couldn't find window with title Piriform
CCleaner in process 1156, after waiting for 30 seconds:
My current code is:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using TestStack.White;
using TestStack.White.Factory;
using TestStack.White.UIItems.Finders;
using TestStack.White.InputDevices;
using TestStack.White.UIItems.WindowItems;
namespace NightWipe
{
class Program
{
private const string ExeSourceFile = #"C:\Program Files\CCleaner\CCleaner.exe";
private static TestStack.White.Application _application;
private static TestStack.White.UIItems.WindowItems.Window _mainWindow;
static void Main(string[] args)
{
clean();
}
public static string clean()
{
var psi = new ProcessStartInfo(ExeSourceFile);
_application = TestStack.White.Application.AttachOrLaunch(psi);
_mainWindow = _application.GetWindow("Piriform CCleaner");
_mainWindow.WaitWhileBusy();
return "";
}
}
}
I thought that maybe it was the name of the process since CCleaner starts another process (not CCleaner.exe) but CCleaner64.exe as seen here, which I can assume is for 64 bit operating systems maybe? Anyway I tried names including: "CCleaner", "CCleaner64"; but this threw the same exact exception.
I'm using Inspect by Microsoft and this is what it pulls for me (large image):
Inspect's information. Any idea what I'm doing wrong here?
The problem is that CCleaner is visible as WIN32 app. So GetWindow() doesn't work. You can try this code:
public void CCleanerSample()
{
var application = Application.AttachOrLaunch(new ProcessStartInfo(#"C:\Program Files\CCleaner\CCleaner.exe"));
AutomationElement ccleanerAutomationElement = null;
Console.Write("Waiting till WIN32 app is launching");
while (ccleanerAutomationElement == null)
{
ccleanerAutomationElement = AutomationElement.RootElement.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.NameProperty, "Piriform CCleaner"));
Thread.Sleep(1000);
Console.Write(".");
}
Console.WriteLine(" Done");
var mainWindow = new Win32Window(ccleanerAutomationElement, WindowFactory.Desktop, InitializeOption.NoCache,
new WindowSession(application.ApplicationSession, InitializeOption.NoCache));
}
Related
I am using the following code to find the length of a video file.
WindowsMediaPlayer windowsMediaPlayer = new WindowsMediaPlayer();
WindowsMediaPlayer player = windowsMediaPlayer;
var clip = player.newMedia(strPath);
WMPLength = $"{TimeSpan.FromSeconds(clip.duration)} ";
player.close();
The code returns what I expect but there are two problems.
One it crashes randomly. It crashes like its out of memory but while I do see memory usage go up it doesn't appear to be enough to crash the program
Two it is very slow
Am I missing something in cleaning up the code, a memory leak
or is there a better way to do this?
Thank you
First thing first: your method requires WMP installed on the computer, there are many editions without WMP installed oob.
This is an approach i am using and no problems so far (not sure why you need the media player) is it a specific requirement, extension ?
1-Install the following NuGet package, Microsoft.WindowsAPICodePack-Shell
https://www.nuget.org/packages/Microsoft.WindowsAPICodePack-Shell
2-Use the code snipet
using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VideoLenght
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine(GetVideoDuration(#"C:\videos\20190531_005611.mp4"));
Console.ReadLine();
}
public static TimeSpan GetVideoDuration(string filePath)
{
using (var shell = ShellObject.FromParsingName(filePath))
{
IShellProperty prop = shell.Properties.System.Media.Duration;
var t = (ulong)prop.ValueAsObject;
return TimeSpan.FromTicks((long)t);
}
}
}
}
I need to create an Windows Service that will capture images from camera. After serching the internet, i do not find any similar project. I decided to use Aforge.net but got stuck in how to capture image because the Bitmap is not supported in windows Service.
here is my code so far:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Deployment;
using System.Runtime.InteropServices;
using AForge.Video;
using AForge.Video.DirectShow;
using AForge.Imaging;
namespace PCSecurityCamera
{
partial class PCSecurityCamera : ServiceBase
{
System.Timers.Timer timeDelay;
string pixDrive = "", journalLoc = "", txnDate = "", txnTime = "", txnDate1 = "";
int retVal, timeFrame = 0, count = 0, txn_count = 0, retention = 0;
string picdirectory;
int i = 0;
string[] availableCameras = new string[5];
private FilterInfoCollection VideoCaptureDevices; //stores all available camera
private VideoCaptureDevice FinalVideoSource; //stores camera to be used
public PCSecurityCamera()
{
InitializeComponent();
timeDelay = new System.Timers.Timer();
timeDelay.Elapsed += new System.Timers.ElapsedEventHandler(WorkProcess);
}
public void WorkProcess(object sender, System.Timers.ElapsedEventArgs e)
{
}
protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
LogService("PCSecuritycamera Service is Started");
try
{
int camCount = 0;
Array.Clear(availableCameras,0,availableCameras.Length);
VideoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
foreach(FilterInfo VideoCaptureDevice in VideoCaptureDevices)
{
availableCameras[camCount] = VideoCaptureDevice.Name.ToString();
LogService(availableCameras[camCount]);
camCount++;
}
if (availableCameras[0] == "")
{
LogService("No Available Camera");
}
else
{
FinalVideoSource = new VideoCaptureDevice(VideoCaptureDevices[0].MonikerString);
LogService("Camera Selected: " + FinalVideoSource.ToString());
FinalVideoSource.NewFrame +=FinalVideoSource_NewFrame;
}
}
catch (Exception e)
{
LogService(e.ToString());
}
timeDelay.Enabled = true;
}
private void FinalVideoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
}
protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary to stop your service.
LogService("Service Stoped");
timeDelay.Enabled = false;
}
private void LogService(string content)
{
FileStream fs = new FileStream(#"C:\Users\talatj\Desktop\Me\ServiceLog.txt", FileMode.OpenOrCreate, FileAccess.Write);
StreamWriter sw = new StreamWriter(fs);
sw.BaseStream.Seek(0, SeekOrigin.End);
sw.WriteLine(content);
sw.Flush();
sw.Close();
}
}
}
my problem is how to capture the image in windows service.
Please help
System.Drawing Namespace
Classes within the System.Drawing namespace are not supported for use
within a Windows or ASP.NET service. Attempting to use these classes
from within one of these application types may produce unexpected
problems, such as diminished service performance and run-time
exceptions. For a supported alternative, see Windows Imaging
Components.
GDI+
GDI+ functions and classes are not supported for use within a Windows
service. Attempting to use these functions and classes from a Windows
service may produce unexpected problems, such as diminished service
performance and run-time exceptions or errors
HOWEVER!
System.Drawing does work in Services, it's just not supported. There can be issues with high load (running out of unmanaged resources), memory or resource leaks (badly implemented or called dispose patterns)
My suspicions is you have just not referenced the System.Drawing.dll
Note : You will just have to be wary and do this on a trial and error basis, though IMO saving bitmaps should be fine
I'm attempting to grab a device handle on the Synaptics Touchpad using the Synaptics SDK, specifically using methods in the SYNCTRLLib.
However, the SYNCTRL method failed to find it, returning -1.
Syn.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SYNCOMLib;
using SYNCTRLLib;
namespace TP_Test1
{
class Syn
{
SynAPICtrl SynTP_API = new SynAPICtrl();
SynDeviceCtrl SynTP_Dev = new SynDeviceCtrl();
SynPacketCtrl SynTP_Pack = new SynPacketCtrl();
int DeviceHandle;
//Constructor
public Syn ()
{
SynTP_API.Initialize();
SynTP_API.Activate();
//DeviceHandle == -1 ? Can't find device?
DeviceHandle = SynTP_API.FindDevice(new SynConnectionType(), new SynDeviceType(), 0);
//Below line causing Unhandled Exception
SynTP_Dev.Select(DeviceHandle);
SynTP_Dev.Activate();
SynTP_Dev.OnPacket += SynTP_Dev_OnPacket;
}
public void SynTP_Dev_OnPacket()
{
Console.WriteLine(SynTP_Pack.FingerState);
Console.WriteLine(SynTP_Pack.X);
Console.WriteLine(SynTP_Pack.Y);
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SYNCOMLib;
using SYNCTRLLib;
namespace TP_Test1
{
class Program
{
static void Main(string[] args)
{
Syn mySyn = new Syn();
mySyn.SynTP_Dev_OnPacket();
}
}
}
I see that you are using the C# wrappers for Synaptics SDK. Even though CPP code might be not trivial to you, you might want to take a look at the file Samples/ComTest.cpp. It contains some example logic in order to find devices, more specifically at lines 66-76:
// Find a device, preferentially a TouchPad or Styk.
ISynDevice *pDevice = 0;
long lHandle = -1;
if ((pAPI->FindDevice(SE_ConnectionAny, SE_DeviceTouchPad, &lHandle) &&
pAPI->FindDevice(SE_ConnectionAny, SE_DeviceStyk, &lHandle) &&
pAPI->FindDevice(SE_ConnectionAny, SE_DeviceAny, &lHandle)) ||
pAPI->CreateDevice(lHandle, &pDevice))
{
printf("Unable to find a Synaptics Device.\n");
exit(-1);
}
Also, make sure you have registered the dlls. According to the ReadSynSDK.txt file:
For certain purposes it may be necessary to register the dlls
that are provided with the SDK. This can be done with the windows regsvr32
utility.
I'm compiling code on-the-fly using System.CodeDom.Compiler. Everything inside the compiled source works well, whatever I'm putting inside this source. I know how to call my functions:
o = results.CompiledAssembly.CreateInstance("Foo.Bar");
MethodInfo mi = o.GetType().GetMethod("SayHello");
mi.Invoke(o, null);
But let's say I'm using a WebClient to retrieve a string asynchronously using WebClient.DownloadStringAsync. Or any other context where I want my compiled source to tell to the host "Hey, I got a nice string ready for you." For the example, I've used a WebBrowser. Basically, I know how to deal with each of the two instances: My hosting program and the compiled program, but I want my compiled program to communicate with the host. By the way, I'm not a super-experimented programmer, so no obvious method comes to my mind.
What I've tried:
1 . I don't really need to try it because it would work, but I could use a timer reading a strings stack or tasks queue inside the compiled source, but the purpose of my application is to have +- 60 scripts able to execute ponctual tasks, not continuous background processes, so it wouldn't be efficient on the CPU.
2 . I've passed the handler to the compiled source like if it was in the hosting app:
//In the hosting app
MethodInfo mi2 = o.GetType().GetMethod("attachCallbackToHost");
mi2.Invoke(o2, new object[] { new WebBrowserNavigatedEventHandler (wb_navigated) });
//... And the handler
public static void wb_navigated(object sender, WebBrowserNavigatedEventArgs e)
{
string browserHtmlFromCompiledSource = ((WebBrowser)sender).DocumentText;
MessageBox.Show(browserHtmlFromCompiledSource);
}
// Plain text from the compiled source code
public void attachCallbackToHost(WebBrowserNavigatedEventHandler handlerFromTheHost)
{
wb.Navigated += handlerFromTheHost;
}
And it did nothing.
3 . Maybe I could share a class or variable by passing it to the compiled assembly?
So, the question is either this or the other:
How to watch efficiently for change inside a specific variable or property inside the compiled program?
How to attach a callback to the host?
Ok. I got it: In order to access the host from the compiled source, the only thing required is to add the host assembly to the refered assemblies in the compiler parameters:
compilerParams.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
So no need for any special callback or INotifier.
Here's the full code that strictly answers my question and nothing more:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
namespace MamaProgram
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string source =
#"
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using MyMama = MamaProgram;
namespace Baby
{
public class Program
{
public WebBrowser wb = new WebBrowser();
public void navigateTo(string url)
{
wb.Navigated += wb_navigated;
wb.Navigate(url);
}
public void wb_navigated(object sender, WebBrowserNavigatedEventArgs e)
{
MyMama.Form1.getResult(wb.DocumentText);
}
}
}
";
Dictionary<string, string> providerOptions = new Dictionary<string, string>
{
{"CompilerVersion", "v3.5"}
};
CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);
CompilerParameters compilerParams = new CompilerParameters
{
GenerateInMemory = true,
GenerateExecutable = false,
TreatWarningsAsErrors = false
};
compilerParams.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
compilerParams.ReferencedAssemblies.Add("System.Data.dll");
compilerParams.ReferencedAssemblies.Add(typeof(System.Linq.Enumerable).Assembly.Location); // Trick to add assembly without knowing their name
compilerParams.ReferencedAssemblies.Add(typeof(System.ComponentModel.Component).Assembly.Location); // Trick to add assembly without knowing their name
compilerParams.ReferencedAssemblies.Add("System.Windows.Forms.dll");
CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source);
if (results.Errors.Count != 0)
throw new Exception("Compilation failed");
object o = results.CompiledAssembly.CreateInstance("Baby.Program");
MethodInfo mi2 = o.GetType().GetMethod("navigateTo");
mi2.Invoke(o, new object[] { "http://www.google.com" });
}
public static void getResult(string result)
{
MessageBox.Show(result);
}
}
}
i know i could search proccessId / name of running tasks and kill processes i need .
though till now i was not developing schedualed tasks / self executble Applications,
so i didn't need to know how to make the application close itself after execition
trying to close everything (including WebDriver) via Application.Exit + OR this.Close()
right after i have got what i was looking for. mission Complete .
please close ... no more work for you .
but mr . Program.cs still needs somthing from Form1.
saying somthing about
Cannot access a disposed object.
Object name: 'Form1'.
any combination of both was returning in some point an exeption error
(from program.cs ) even though mission complete . no more code was requested .(?) by me..atleast.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using OpenQA.Selenium;
using OpenQA.Selenium.IE;
using System.IO;
namespace HT_R_WbBrows2
{
public partial class Form1 : Form
{
public IeEnginGenerator Iengn = new IeEnginGenerator();
public Form1()
{
InitializeComponent();
//setLogView(View.Details);
string extractededVal = Iengn.ExtractPageValue(Iengn.itrfWebEng);
string flnm = #" the directory path to file --> \dolarRate.asp";
File.WriteAllText(fn, extractededVal);
this.Close();
Application.Exit();
}
public class IeEnginGenerator
{
private string directory = Environment.CurrentDirectory;///Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);
public IWebDriver IwebEngine;
public List<string> ListElementsInnerHtml = new List<string>();
public HtmlAgilityPack.HtmlDocument Dnetdoc = new HtmlAgilityPack.HtmlDocument();
#region <<=========== setupDriver ============>>
public string ExtractPageValue(IWebDriver DDriver, string url="")
{
if(string.IsNullOrEmpty(url))
url = #"http://www.boi.org.il/he/Markets/ExchangeRates/Pages/Default.aspx";
var service = InternetExplorerDriverService.CreateDefaultService(directory);
service.LogFile = directory + #"\seleniumlog.txt";
service.LoggingLevel = InternetExplorerDriverLogLevel.Trace;
var options = new InternetExplorerOptions();
options.IntroduceInstabilityByIgnoringProtectedModeSettings = true;
DDriver = new InternetExplorerDriver(service, options, TimeSpan.FromSeconds(60));
DDriver.Navigate().GoToUrl(url);
Dnetdoc.LoadHtml(DDriver.PageSource);
string Target = Dnetdoc.DocumentNode.SelectNodes("//table//tr")[1].ChildNodes[7].InnerText;
//.Select(tr => tr.Elements("td").Select(td => td.InnerText).ToList())
//.ToList();
return Math.Round(Convert.ToDouble(Target), 2).ToString();
//return "";//Math.Round(Convert.ToDouble( TempTxt.Split(' ')[10]),2).ToString();
}
#endregion
}
}
}
Why use a winform application? A Console application would probably suffice for what you are doing. Once Main() ends your app will close as well. Main() never ends in a winform app because of the applications runloop.
Edit:
Here would be the correct way to do this. You need to register to the forms Load event and run your code there, not in the constructor. You can't close a winform from inside a constructor.
Edit 2: Put this code in the Form1() constructor. Somewhere after InitializeComponent();
this.Load += (sender,args)=>{ /*do all your work here*/
string extractededVal = Iengn.ExtractPageValue(Iengn.itrfWebEng);
string flnm = #" the directory path to file --> \dolarRate.asp";
File.WriteAllText(fn, extractededVal);
Application.Exit();
};