C# Forms Powershell Set-Execution policy problem [duplicate] - c#

This question already has answers here:
How to execute a powershell script using c# and setting execution policy?
(1 answer)
How do I run a PowerShell Script from C# with System.Management.Automation
(1 answer)
PowerShell says "execution of scripts is disabled on this system."
(48 answers)
Closed 15 hours ago.
Im creating a tool to execute scripts from Forms application. The prom is that even if i set my execution policies to unrestricted or remote signed to problem still persist
File C:\AAE\Scripts\script1.ps1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies at https://go.microsoft.com/fwlink/?LinkID=135170.
File C:\AAE\Scripts\script2.ps1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies at https://go.microsoft.com/fwlink/?LinkID=135170.
these files are just some test scripts.
which contains
#scriptfile
function Show-process {
write-host "---------------------------------------------------------------------------------------------------------------------------------starting get process script"
Get-Process *
write-host "---------------------------------------------------------------------------------------------------------------------------------end get process script"
}
Show-process
this is the code in C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Windows.Forms;
namespace AAE___ICT_Support_Tool
{
public partial class AAE_ICT_Support_Tool_Form : Form
{
private Dictionary<string, string>[] scriptDicts = new Dictionary<string, string>[2];
public AAE_ICT_Support_Tool_Form()
{
InitializeComponent();
// Create dictionaries with scripts and manual paths for each tab page
scriptDicts[0] = new Dictionary<string, string>();
scriptDicts[0].Add("Script 1", "C:\\AAE\\Scripts\\script1.ps1");
scriptDicts[0].Add("Script 2", "C:\\AAE\\Scripts\\script2.ps1");
scriptDicts[1] = new Dictionary<string, string>();
scriptDicts[1].Add("Script 3", "C:\\AAE\\Scripts\\script3.ps1");
scriptDicts[1].Add("Script 4", "C:\\AAE\\Scripts\\script4.ps1");
// Create tab pages with names and add them to the tab control
TabPage tabPage1 = new TabPage("Scripts 1-2");
tabControl1.TabPages.Add(tabPage1);
int yPos = 10;
foreach (string scriptName in scriptDicts[0].Keys)
{
CheckBox cb = new CheckBox
{
Text = scriptName,
AutoSize = true,
Location = new Point(10, yPos)
};
tabPage1.Controls.Add(cb);
yPos += cb.Height + 5;
}
// Create tab pages with names and add them to the tab control
TabPage tabPage2 = new TabPage("Scripts 3-4");
tabControl1.TabPages.Add(tabPage2);
yPos = 10;
foreach (string scriptName in scriptDicts[1].Keys)
{
CheckBox cb = new CheckBox
{
Text = scriptName,
AutoSize = true,
Location = new Point(10, yPos)
};
tabPage2.Controls.Add(cb);
yPos += cb.Height + 5;
}
}
private void Run_Btn_Click(object sender, EventArgs e)
{
// Get the selected scripts and their paths from all tab pages
Dictionary<string, string> selectedScripts = new Dictionary<string, string>();
foreach (TabPage tabPage in tabControl1.TabPages)
{
Dictionary<string, string> scriptDict = null;
if (tabPage.Text == "Scripts 1-2")
{
scriptDict = scriptDicts[0];
}
else if (tabPage.Text == "Scripts 3-4")
{
scriptDict = scriptDicts[1];
}
if (scriptDict != null)
{
foreach (Control c in tabPage.Controls)
{
if (c is CheckBox && ((CheckBox)c).Checked)
{
string scriptName = ((CheckBox)c).Text;
if (scriptDict.ContainsKey(scriptName))
{
selectedScripts.Add(scriptName, scriptDict[scriptName]);
}
}
}
}
}
// Run the selected scripts
string output = "";
foreach (string scriptPath in selectedScripts.Values)
{
using (PowerShell ps = PowerShell.Create())
{
ps.AddScript(scriptPath);
ps.Runspace = RunspaceFactory.CreateRunspace();
ps.Runspace.Open();
ps.AddCommand("Set-ExecutionPolicy").AddParameter("Scope", "Process").AddParameter("ExecutionPolicy", "Bypass");
PSDataCollection<PSObject> results = new PSDataCollection<PSObject>();
results.DataAdded += new EventHandler<DataAddedEventArgs>(delegate (object sender1, DataAddedEventArgs e1)
{
PSDataCollection<PSObject> source = (PSDataCollection<PSObject>)sender1;
output += source[e1.Index].ToString() + Environment.NewLine;
});
ps.Streams.Error.DataAdded += new EventHandler<DataAddedEventArgs>(delegate (object sender1, DataAddedEventArgs e1)
{
PSDataCollection<ErrorRecord> source = (PSDataCollection<ErrorRecord>)sender1;
output += source[e1.Index].ToString() + Environment.NewLine;
});
ps.Invoke(null, results);
}
}
// Show the output in the textbox
txtOutput.Text = output;
}
}
}
I've tried to run it in PowerShell ISE and the terminal it works fine. is something wrong when i click the run button in the forms. it seems that it maybe invoked incorrectly. Maybe someone can explain what is going on. also tried to bypass in the code and same issue persists.
also changed the scope to CurrentUser same happens.
how can I possibly resolve this :S

Related

C# DataGridView - Table is not populating

I'm trying to display data that I pull from text files within my program.cs to a DataGridView, but the table remains blank when I run the code.
Another problem I have is that when the form opens it stops running through the code.
Basically what the code does is it downloads .zip files from an sftp server, unzips a text file, reads through the file adding each line to an array and splits a certain line into an array. I'm trying to get the variables from that array to appear on a DataGridView in my form.
Here is my code:
class Machine
{
public string MacNum { get; set; }
public string CashCount { get; set; }
public string VendCount { get; set; }
}
static class Program
{
[STAThread]
static void Main()
{
string zipTemp = (#"C:\Users\mark\Desktop\Project Dex\zipTemp\");
string machineCashCount = ("");
string hostIP = ("0.0.0.0");
string userName = ("UN");
string passWord = ("PW");
string remotePath = (#"/home/dex/RESPONSE/PROCESSED");
string localPath = (#"C:\Users\mark\Desktop\Project Dex\Temp\PROCESSED\");
Application.Run(new Form1());
DataGridView dataGridView = new DataGridView();
IList<Machine> machines = new BindingList<Machine>();
dataGridView.DataSource = machines;
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
HostName = hostIP,
UserName = userName,
Password = passWord,
PortNumber = 22,
SshHostKeyFingerprint = "ssh-rsa 2048 96:48:96:52:8c:e7:de:c6:e1:00:08:7e:db:ad:e4:06"
};
using (Session session = new Session())
{
session.Open(sessionOptions);
TransferOptions transferOptions = new TransferOptions();
transferOptions.TransferMode = TransferMode.Binary;
session.GetFiles(remotePath, #"C:\Users\mark\Desktop\Project Dex\Temp\").Check();
}
DirectoryInfo directorySelected = new DirectoryInfo(#"C:\Users\mark\Desktop\Project Dex\Temp\PROCESSED\");
List<string> fileNames = new List<string>();
foreach (FileInfo fileInfo in directorySelected.GetFiles("*.zip"))
{
fileNames.Add(fileInfo.Name);
}
foreach (string fileName in fileNames)
{
string zipFilePath = localPath + fileName;
using (ZipFile zip1 = ZipFile.Read(zipFilePath))
{
var selection = (from e in zip1.Entries
where (e.FileName).StartsWith("01e")
select e);
Directory.CreateDirectory(zipTemp);
foreach (var e in selection)
{
e.Extract(zipTemp, ExtractExistingFileAction.OverwriteSilently);
}
}
DirectoryInfo dexDirect = new DirectoryInfo(#"C:\Users\mark\Desktop\Project Dex\zipTemp\");
List<string> dexName = new List<string>();
foreach (FileInfo dexInfo in dexDirect.GetFiles("*.dex"))
{
dexName.Add(dexInfo.Name);
}
foreach (string dexNames in dexName)
{
string dexFilePath = zipTemp + dexNames;
string[] lines = System.IO.File.ReadAllLines(dexFilePath);
foreach (string line in lines)
{
machineCashCount = Array.Find(lines,
element => element.StartsWith("VA1", StringComparison.Ordinal));
}
string[] MCC1 = machineCashCount.Split('*');
string[] nm = dexNames.Split('.');
int nam = int.Parse(nm[0], System.Globalization.NumberStyles.HexNumber);
// Console.WriteLine((nam + (":") + "Total cash count: ") + MCC1[1]);
// Console.WriteLine((nam + (":") + "Number of paid vends: ") + MCC1[2]);
Machine m = new Machine();
m.MacNum = nm[0];
m.CashCount = MCC1[1];
m.VendCount = MCC1[2];
machines.Add(m);
}
}
Application.Run(new Form1());
}
}
Form1:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void Form1_Load(object sender, EventArgs e)
{
}
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
}
}
1.Delete
Application.Run(new Form1());
DataGridView dataGridView = new DataGridView();
and
dataGridView.DataSource = machines;
2.At the end change
Application.Run(new Form1());
to
Application.Run(new Form1(machines));
3.Add constructor to Form1:
public Form1(IList<Machine> machines)
{
InitializeComponent();
dataGridView1.DataSource = machines;
}
What your program is basically doing is it runs the Form, downloads the zip file, processes the zip file and that's it.
You will need to do all this in the form's form_load event or a button click event instead of the Main().
remove the Application.Run(new form1) from the beginning of the code. Not required.
Move everything before the last Application.Run(new Form1) [Note this is with 'F' which is the correct one] to the a private function inside the form. Let's call it processFile()
Call this processFile() in the Form1_Load() event.
Assign m to the DataGridView's data property and call DataGridView.Bind() to bind the data to the datagrid control.
UPDATE:
Since you have a button and a Button_Click event, put the processFile() in the Button's click event. When the form is displayed, click the button to get the whole process running

Enumerate folder/subfolder contents to right-click context menu of system tray application in C#

I'm utilizing the template from https://alanbondo.wordpress.com/2008/06/22/creating-a-system-tray-app-with-c/ for creating a system tray icon that has a right-click context menu.
I'm able to have one of the buttons launch an explorer process that opens to the root of a directory using this function
private void MyApps(object sender, EventArgs e)
{
String currentUser = Environment.UserName.ToString();
Process explorer = new Process();
explorer.StartInfo.FileName = "explorer.exe";
explorer.StartInfo.Arguments = #"C:\Users\" + currentUser + #"\desktop\MyApps";
explorer.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
explorer.Start();
}
What I would rather do is have the system tray icon button, when clicked, expand into a sub-menu that contains the contents of the desired directory, which itself contains browseable sub-folders. Imagine the (pre-Windows 8) Start menu with nested menus and applications; that is the behavior I'd like to mimic.
What I have found thus far are multiple projects people have written to create their own customized Windows Explorer shell, do actually have to go that far in order to dynamically enumerate the contents of the desired folder into the right-click context menu of a system tray icon?
I'm much more comfortable using visual studio forms for .NET applications but from what I have read, there's no way to actually 'hide' the form at launch, so for now I'm using C#
Any advice or suggestions would be appreciated. Thanks!
Edit: Here's the updated code with the suggested method for recursively populating the menu item with the contents of the specified directory. I'm now getting an error that "System.Windows.Forms.MenuItem" does not contain a definition for DropDownItems
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
namespace MyTrayApp
{
public class SysTrayApp : Form
{
[STAThread]
public static void Main()
{
Application.Run(new SysTrayApp());
}
private NotifyIcon trayIcon;
private ContextMenu trayMenu;
public SysTrayApp()
{
trayMenu = new ContextMenu();
trayMenu.MenuItems.Add("Exit", OnExit);
trayMenu.MenuItems.Add("My Applications").Click += new EventHandler(MyApps);
trayIcon = new NotifyIcon();
trayIcon.Text = "MyTrayApp";
trayIcon.Icon = new Icon(SystemIcons.Application, 40, 40);
trayIcon.ContextMenu = trayMenu;
trayIcon.Visible = true;
}
protected override void OnLoad(EventArgs e)
{
Visible = false; // Hide form window.
ShowInTaskbar = false; // Remove from taskbar.
base.OnLoad(e);
}
private void OnExit(object sender, EventArgs e)
{
Application.Exit();
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
trayIcon.Dispose();
}
base.Dispose(isDisposing);
}
private void MyApps(object sender, EventArgs e)
{
String currentUser = Environment.UserName.ToString();
string[] directories = Directory.GetDirectories(#"C:\Users\" + currentUser + #"\desktop\My Applications");
foreach (string dir in directories)
{
string[] subdir = Directory.GetDirectories(dir);
this.trayMenu.MenuItems.Add(dir);
foreach (string sub in subdir)
{
(trayMenu.MenuItems[trayMenu.MenuItems.Count - 1] as MenuItem).DropDownItems.Add(sub);
}
string[] files = Directory.GetFiles(dir);
foreach (string file in files)
{
this.trayMenu.MenuItems.Add(file);
}
}
}
}
}
This is a simple quick test i've made, using a simple ContextMenuStrip. It of course should be recursive, but just to get you on the track:
string[] directories = Directory.GetDirectories(#"D:\descargas");
foreach (string dir in directories)
{
string[] subdir = Directory.GetDirectories(dir);
this.contextMenuStrip1.Items.Add(dir);
foreach(string sub in subdir)
{
(contextMenuStrip1.Items[contextMenuStrip1.Items.Count-1] as ToolStripMenuItem).DropDownItems.Add(sub);
}
string[] files = Directory.GetFiles(dir);
foreach(string file in files)
{
this.contextMenuStrip1.Items.Add(file);
}
}
Edit
As you are using ContextMenu, and using your provided code, you should do something like this:
private void MyApps(object sender, EventArgs e)
{
String currentUser = Environment.UserName.ToString();
string[] directories = Directory.GetDirectories(#"C:\Users\" + currentUser + #"\desktop\My Applications");
foreach (string dir in directories)
{
string[] subdir = Directory.GetDirectories(dir);
MenuItem mi=new MenuItem(dir);
foreach (string sub in subdir)
{
mi.MenuItems.Add(sub);
}
string[] files = Directory.GetFiles(dir);
foreach (string file in files)
{
mi.MenuItems.Add(file);
}
this.trayMenu.MenuItems.Add(mi);
}
}
I haven't tested it, but I think this would do more or less what you want

How to print Webview on a windows store app

Hey guys i have been trying to print my Webview using the tutorial Jerry Nixon gave on How do I print WebView content in a Windows Store App? and the tutorial from "windows 8 apps with xaml and c#" by Adam Nathan, i have attached the code below from his example:
using System;
using Windows.Graphics.Printing;
using Windows.Graphics.Printing.OptionDetails;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Printing;
namespace Chapter19
{
public sealed partial class MainPage : Page
{
// Supports printing pages, where each page is a UIElement
PrintDocument doc = new PrintDocument();
public MainPage()
{
InitializeComponent();
// Attach handlers to relevant events
doc.GetPreviewPage += OnGetPreviewPage;
doc.AddPages += OnAddPages;
PrintManager printManager = PrintManager.GetForCurrentView();
printManager.PrintTaskRequested += OnPrintTaskRequested;
}
// Prepare the print preview pages
void OnGetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
this.doc.SetPreviewPageCount(2, PreviewPageCountType.Final);
if (e.PageNumber == 1)
{
this.doc.SetPreviewPage(1, new Viewbox
{
Child = new Button
{
Content = "PAGE 1!",
Background = new SolidColorBrush(Colors.Red)
}
});
}
else
{
this.doc.SetPreviewPage(2, new Viewbox
{
Child = new Button
{
Content = "PAGE 2!",
Background = new SolidColorBrush(Colors.Red)
}
});
}
}
// Prepare the real pages
void OnAddPages(object sender, AddPagesEventArgs e)
{
this.doc.AddPage(new Viewbox
{
Child = new Button
{
Content = "PAGE 1!",
Background = new SolidColorBrush(Colors.Red)
}
});
this.doc.AddPage(new Viewbox
{
Child = new Button
{
Content = "PAGE 2!",
Background = new SolidColorBrush(Colors.Red)
}
});
this.doc.AddPagesComplete();
}
// Prepare and perform the printing
void OnPrintTaskRequested(PrintManager sender,
PrintTaskRequestedEventArgs args)
{
// This gets invoked as soon as the Devices pane is shown
PrintTask task = args.Request.CreatePrintTask("Document Title",
async (taskArgs) =>
{
// This is invoked on a background thread when the Print
// button is clicked
var deferral = taskArgs.GetDeferral();
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// This must run on the main thread
taskArgs.SetSource(doc.DocumentSource);
deferral.Complete();
});
});
// Show custom options
PrintTaskOptionDetails details =
PrintTaskOptionDetails.GetFromPrintTaskOptions(task.Options);
details.DisplayedOptions.Clear();
details.DisplayedOptions.Add(StandardPrintTaskOptions.MediaSize);
// A custom text option
PrintCustomTextOptionDetails option1 = details.CreateTextOption(
"CustomId1", "Header");
details.DisplayedOptions.Add("CustomId1");
// A custom list option
PrintCustomItemListOptionDetails option2 = details.CreateItemListOption(
"CustomId2", "Contents");
option2.AddItem("customItemId1", "As Seen on Screen");
option2.AddItem("customItemId2", "Summary View");
option2.AddItem("customItemId3", "Full Details");
option2.AddItem("customItemId4", "Multiple Columns");
details.DisplayedOptions.Add("CustomId2");
// Handle options changes
details.OptionChanged += OnOptionChanged;
}
void OnOptionChanged(PrintTaskOptionDetails sender, PrintTaskOptionChangedEventArgs args)
{
// TODO: Handle custom options
}
}
}
the problem i am having is that i am struggling to get "MywebviewPages" that jerry generated onto the
"this.doc.SetPreviewPage"
and
"this.doc.AddPage"
from Nathans example. Due to lack of PDF api's on winRT i am forced to use webview since its the only way to have tables. please help

Simple C# application eating memory

Alright so basicly I have this simple application running in system tray that has one timer. Every tick it performs a check to see if a given directory and file exists, and based on the result it changes its icon.
The problem is every single timer tick the memory for the application raises ~100kb. I currently have it running for about 5 mins and it already uses 40MB of memory, which is unacceptable for such "micro" application.
Here's my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
namespace Tray
{
public partial class Main : Form
{
string drive = "C:\\";
string file = "test.txt";
System.Drawing.Image imgRed = Image.FromFile("res\\icon-red.png");
System.Drawing.Image imgOrange = Image.FromFile("res\\icon-orange.png");
System.Drawing.Image imgGreen = Image.FromFile("res\\icon-green.png");
System.Drawing.Icon icoRed = System.Drawing.Icon.ExtractAssociatedIcon("res\\icon-red.ico");
System.Drawing.Icon icoOrange = System.Drawing.Icon.ExtractAssociatedIcon("res\\icon-orange.ico");
System.Drawing.Icon icoGreen = System.Drawing.Icon.ExtractAssociatedIcon("res\\icon-green.ico");
public Main()
{
InitializeComponent();
}
public static string ShowPrompt(string text, string caption)
{
Form prompt = new Form();
prompt.Width = 500;
prompt.Height = 150;
prompt.Text = caption;
Label textLabel = new Label() { Left = 50, Top = 20, Text = text };
TextBox textBox = new TextBox() { Left = 50, Top = 50, Width = 400 };
Button confirmation = new Button() { Text = "Ok", Left = 350, Width = 100, Top = 70 };
confirmation.Click += (sender, e) => { prompt.Close(); };
prompt.Controls.Add(confirmation);
prompt.Controls.Add(textLabel);
prompt.Controls.Add(textBox);
prompt.ShowDialog();
return textBox.Text;
}
public void updateInfo(){
this.statusDrive.Text = "Drive [" + drive + "]";
this.statusFile.Text = "File [" + drive + file + "]";
}
public void exec(){
int status = 0;
this.trayIcon.Text = "[Drive - ";
if (Directory.Exists(drive)){
this.statusDrive.Text += " - OK";
this.statusDrive.Image = imgGreen;
status++;
this.trayIcon.Text += "OK] ";
} else{
this.statusDrive.Text += " - FAIL";
this.statusDrive.Image = imgRed;
this.trayIcon.Text += "FAIL] ";
}
this.trayIcon.Text += "[File - ";
if (File.Exists(drive + file))
{
this.statusFile.Text += " - OK";
this.statusFile.Image = imgGreen;
status++;
this.trayIcon.Text += "OK] ";
}
else
{
this.statusFile.Text += " - FAIL";
this.statusFile.Image = imgRed;
this.trayIcon.Text += "FAIL] ";
}
switch (status)
{
case 2:
this.Icon = icoGreen;
this.trayIcon.Icon = icoGreen;
this.status.Image = imgGreen;
break;
case 1:
this.Icon = icoOrange;
this.trayIcon.Icon = icoOrange;
this.status.Image = imgOrange;
break;
case 0:
default:
this.Icon = icoRed;
this.trayIcon.Icon = icoRed;
this.status.Image = imgRed;
break;
}
}
private void Form1_Load(object sender, EventArgs e)
{
this.Hide();
}
private void timer1_Tick(object sender, EventArgs e)
{
updateInfo();
exec();
}
private void chDrive_Click(object sender, EventArgs e)
{
this.drive = ShowPrompt("Enter drive path", "Change drive");
}
private void chFile_Click(object sender, EventArgs e)
{
this.file = ShowPrompt("Enter new file path:", "Change file");
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Exit();
}
}
}
I already tried to optimize the app by preloading the icons and images into variables and assigning those to the appropriate properties, however this didn't solve my problem.
Also, note that I managed to hide my main window by doing this (in Program.cs):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace Tray
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Main mForm = new Main();
Application.Run();
}
}
}
UPDATE
I just noticed that the memory usage climbs up to 50MB and drops to 20MB afterwards (and goes up again). Is this something I can possibly address or is it a windows "issue"?
I'm going to take a stab at it being the string concatenations happening once a second. Consider using a StringBuilder. 40MB is nothing though really.
RE: Your update. The Garbage Collector is reclaiming the memory as it sees fit.
You never appear to be disposing your form correctly in ShowPrompt, so I'd imagine this is your problem.
Because a form displayed as a dialog box is not closed, you must call the Dispose method of the form when the form is no longer needed by your application.
ShowDialog
Some points that could cut down on memory usage:
Try to prebuild all those strings you're building in exec(). It looks like they're all runtime constants, but you build them every tick instead of building them once when the application starts. If this isn't possible, use StringBuilder instead of +=.
Only change properties on controls (icon, trayText, etc) if there has been a change. I.E. if tray text is already "[Drive C:\ - OK]", don't set its value again to "[Drive C:\ - OK]" next tick.
Garbage Collector does all the work of memory management for you. Temporary rise in memory doesn't always mean that there is a memory leak. It may come down when the GC collects memory. In case you suspect that there are memory leaks you need to do memory profiling which is no easy job. You need to read into this article for steps that you can take to find out the problem in your code. Alternatively, there are multiple tools avaiable in the market to do this job for you. You can use Ants Profiler of Red Gate, memprofiler amongst others.
One thing you might consider is rather than using a timer why not use the FileSystemWatcher and attach to events:
var watcher = new FileSystemWatcher("somepath");
watcher.Deleted += (sender, eventArgs) => { };
watcher.Changed += (sender, eventArgs) => { };
watcher.Error += (sender, eventArgs) => { };
watcher.Renamed += (sender, eventArgs) => { };
I also agree that you should be disposing of the forms once you're done with them.

detect selected program opened using openas_rundll in c#

I am opening a file using openfile dialog with the help of openas_rundll in c#.
Process.Start("rundll32.exe", string.Format("shell32.dll,OpenAs_RunDLL \"{0}\"", tempFilePath));
Now I want to detect which program is used to open the file. I want to trace the process.
My goal is to delete the file when user close the program.
You can try to catch the moment when actual app is closed by finding it py parent process id. If you found it, you can wait it to close as long as it is acceptable. Thanks jeremy-murray for GetAllProcessParentPids method:
public void StartProcessAndWathTillTerminated(string tempFilePath)
{
// Show app selection dialog to user
Process rundll32 = Process.Start("rundll32.exe", string.Format("shell32.dll,OpenAs_RunDLL {0}", tempFilePath));
int rundll32id = rundll32.Id;
// Wait till dialog is closed
while (!rundll32.HasExited)
{
System.Threading.Thread.Sleep(50);
}
// Get all running processes with parent id
Dictionary<int, int> allprocparents = GetAllProcessParentPids();
int openedAppId = 0;
// Loop throu all processes
foreach (var allprocparent in allprocparents)
{
// Found child process, started by our rundll32.exe instance
if (allprocparent.Value == rundll32id)
{
openedAppId = allprocparent.Key;
break;
}
}
// Check if we actually found any process. It can not be found in two situations:
// 1) Process was closed too soon, while we was looking for it
// 2) User clicked Cancel and no application was opened
// Also it is possible that chesen application is already running. In this
// case new instance will be opened by rundll32.exe for a very short period of
//time needed to pass file path to running instance. Anyway, this case falls into case 1).
//If we ca not find process explicitly, we can try to find it by file lock, if one exists:
//I'm using here a code snippet from https://stackoverflow.com/a/1263609/880156,
//which assumes that there are possible more than one lock on this file.
//I just take first.
if (openedAppId==0)
{
Process handleExe = new Process();
handleExe.StartInfo.FileName = "handle.exe";
handleExe.StartInfo.Arguments = tempFilePath;
handleExe.StartInfo.UseShellExecute = false;
handleExe.StartInfo.RedirectStandardOutput = true;
handleExe.Start();
handleExe.WaitForExit();
string outputhandleExe = handleExe.StandardOutput.ReadToEnd();
string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach(Match match in Regex.Matches(outputhandleExe, matchPattern))
{
openedAppId = int.Parse(match.Value);
break;
}
}
if (openedAppId != 0)
{
Process openedApp = Process.GetProcessById(openedAppId);
while (!openedApp.HasExited)
{
System.Threading.Thread.Sleep(50);
}
}
// When we reach this position, App is already closed or was never started.
}
public static Dictionary<int, int> GetAllProcessParentPids()
{
var childPidToParentPid = new Dictionary<int, int>();
var processCounters = new SortedDictionary<string, PerformanceCounter[]>();
var category = new PerformanceCounterCategory("Process");
// As the base system always has more than one process running,
// don't special case a single instance return.
var instanceNames = category.GetInstanceNames();
foreach(string t in instanceNames)
{
try
{
processCounters[t] = category.GetCounters(t);
}
catch (InvalidOperationException)
{
// Transient processes may no longer exist between
// GetInstanceNames and when the counters are queried.
}
}
foreach (var kvp in processCounters)
{
int childPid = -1;
int parentPid = -1;
foreach (var counter in kvp.Value)
{
if ("ID Process".CompareTo(counter.CounterName) == 0)
{
childPid = (int)(counter.NextValue());
}
else if ("Creating Process ID".CompareTo(counter.CounterName) == 0)
{
parentPid = (int)(counter.NextValue());
}
}
if (childPid != -1 && parentPid != -1)
{
childPidToParentPid[childPid] = parentPid;
}
}
return childPidToParentPid;
}
Update
It seems that there is no solution with 100% guarantee of success due to many reasons.
I think that finding a process started by rundll32.exe is most solid among all other. If it fails, you still able to complete it with some other methods to determine process id.
As far as i know, there are several other ways to find that file is still used. Winword.exe, for example, creates some temp files in same directory and removes them when it closes. So if you able to catch a moment of temp files deleting then you may assume that program been closed.
Other programs may hold your file open by setting a lock on it. If so, you can find that program by finding lock owner. I used a solution with external program handle.exe from this answer https://stackoverflow.com/a/1263609/880156, so take a look.
I have to mention, that there may be no permanent file lock at all. It depend on program architecture. For example, if you open html file with Firefox, it reads file as fast as it can and closes it and does not leave file locked no more. In this case, even if you somehow find process name (e.g. "firefox.exe"), you will not able to find a moment when user closes a tab with your file.
If i were you, i would implement this solution, that still not ideal, and i would updgrade it later if it is necessary.
Just a simple helper class which provides you with a method to open a file with the OpenWithDialog of windows and monitors the started processes with WMI to identify the choosen application.
for WMI, add System.Management.dll as reference
NOTICE: It doesn't recognice windows photo viewer
- which is a dllhost.exe
Example call for your situation:
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Filter = "All files(*.*)|*.*";
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
using (Win.OpenWithDialogHelper helper = new Win.OpenWithDialogHelper())
{
helper.OpenFileAndWaitForExit(ofd.FileName);
File.Delete(helper.Filepath);
}
}
}
The class:
namespace Win
{
using System.Management;
using System.Threading;
using System.Diagnostics;
using System.IO;
public class OpenWithDialogHelper : IDisposable
{
#region members
private Process openWithProcess;
private ManagementEventWatcher monitor;
public string Filepath { get; set; }
public Process AppProcess { get; private set; }
#endregion
#region .ctor
public OpenWithDialogHelper()
{
}
public OpenWithDialogHelper(string filepath)
{
this.Filepath = filepath;
}
#endregion
#region methods
public void OpenFileAndWaitForExit(int milliseconds = 0)
{
OpenFileAndWaitForExit(this.Filepath, milliseconds);
}
public void OpenFileAndWaitForExit(string filepath, int milliseconds = 0)
{
this.Filepath = filepath;
this.openWithProcess = new Process();
this.openWithProcess.StartInfo.FileName = "rundll32.exe";
this.openWithProcess.StartInfo.Arguments = String.Format("shell32.dll,OpenAs_RunDLL \"{0}\"", filepath);
this.openWithProcess.Start();
//using WMI, remarks to add System.Management.dll as reference!
this.monitor = new ManagementEventWatcher(new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
this.monitor.EventArrived += new EventArrivedEventHandler(start_EventArrived);
this.monitor.Start();
this.openWithProcess.WaitForExit();
//catching the app process...
//it can't catched when the process was closed too soon
//or the user clicked Cancel and no application was opened
Thread.Sleep(1000);
int i = 0;
//wait max 5 secs...
while (this.AppProcess == null && i < 3000)
{
Thread.Sleep(100); i++;
}
if (this.AppProcess != null)
{
if (milliseconds > 0)
this.AppProcess.WaitForExit(milliseconds);
else
this.AppProcess.WaitForExit();
}
}
public void Dispose()
{
if (this.monitor != null)
{
this.monitor.EventArrived -= new EventArrivedEventHandler(start_EventArrived);
this.monitor.Dispose();
}
if(this.openWithProcess != null)
this.openWithProcess.Dispose();
if (this.AppProcess != null)
this.AppProcess.Dispose();
}
#endregion
#region events
private void start_EventArrived(object sender, EventArrivedEventArgs e)
{
int parentProcessID = Convert.ToInt32(e.NewEvent.Properties["ParentProcessID"].Value);
//The ParentProcessID of the started process must be the OpenAs_RunDLL process
//NOTICE: It doesn't recognice windows photo viewer
// - which is a dllhost.exe that doesn't have the ParentProcessID
if (parentProcessID == this.openWithProcess.Id)
{
this.AppProcess = Process.GetProcessById(Convert.ToInt32(
e.NewEvent.Properties["ProcessID"].Value));
if (!this.AppProcess.HasExited)
{
this.AppProcess.EnableRaisingEvents = true;
}
}
}
#endregion
}
}

Categories

Resources