Tracking files of a particular extension - c#

I want to track the files which are opened by the user, and select them by one particular extension. If the opened file has that extension, then I want to assign it's file path to a variable for further processing. Example applications are very cpu demanding. Is there an easy, efficient way to do that?

System wide monitoring of file-->open events (including network drives, thumb drives, etc) would require you to write a FS filter driver.
Since you have access to the machine, and you definitely need system wide access, you could simply write a simple app that will be associated with the Powerpoint extensions, perform the copy, then open Powerpoint using the filepath as a command line argument. It would look similar to the following:
using System;
using System.Windows;
using System.Diagnostics;
using System.IO;
namespace WpfApplication1
{
internal class MainWindow : Window
{
public MainWindow()
{ }
[STAThread()]
static void Main(string[] args)
{
if (args.Length == 0)
{
// [ show error or print usage ]
return;
}
if (!File.Exists(args[0]))
{
// [ show error or print usage ]
return;
}
// Perform the copy
FileInfo target = new FileInfo(args[0]);
string destinationFilename = string.Format("X:\\ExistingFolder\\{0}", target.Name);
File.Copy(target.FullName, destinationFilename);
// You may need to place the filename in quotes if it contains spaces
string targetPath = string.Format("\"{0}\"", target.FullName);
string powerpointPath = "[FullPathToPowerpointExecutable]";
Process powerpointInstance = Process.Start(powerpointPath, targetPath);
// This solution is using a wpf windows app to avoid
// the flash of the console window. However if you did
// wish to display an accumulated list then you may choose
// to uncomment the following block to display your UI.
/*
Application app = new Application();
app.MainWindow = new MainWindow();
app.MainWindow.ShowDialog();
app.Shutdown(0);
*/
Environment.Exit(0);
}
}
}
Hope this helps.

Related

Revit IExternal Application Error- doesn't seems to run at all

Should we have something with the External application to properly register the event?
I also tried putting two breakpoints one inside the start module and other inside the Export module.
the first responded and waited for me to continue and the next didn't respond(hope did not run the line)
Also,I had manually tried coping the addin file to the addin location to avoid any post build event error but still doesnt seem to work.
could you tell me what I am I doing wrong here.
Here is the code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.UI.Events;
using Autodesk.Revit.DB.Events;
using System.IO;
namespace UserDataSheet
{
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class UserDataSheetclass : IExternalApplication
{
public Result OnShutdown(UIControlledApplication application)
{
return Result.Succeeded;
}
public Result OnStartup(UIControlledApplication application)
{
try
{
// Register event.
application.ControlledApplication.DocumentOpened += new EventHandler<Autodesk.Revit.DB.Events.DocumentOpenedEventArgs>(ExportLog);
return Result.Succeeded;
}
catch (Exception)
{
return Result.Failed;
}
}
public void ExportLog(object sender, DocumentOpenedEventArgs args)
{
var doc = sender as Document;
var isFamilyDoc = doc.IsFamilyDocument;
// variables to use
string RevitUserName = "";
DateTime OpenTime = new DateTime();
string localUserName = "";
string filename = "";
string filepath = "";
string content = "";
if (isFamilyDoc == false)
{
var IsloggedIn = Autodesk.Revit.ApplicationServices.Application.IsLoggedIn;
if (IsloggedIn == true )//&& doc.IsModelInCloud == true)
{
// use variables
filepath = doc.PathName;
filename = doc.Title;
RevitUserName = doc.Application.Username;
OpenTime = DateTime.Now;
localUserName = System.Environment.MachineName;
}
content = string.Format("Date and time : {0} \n Revit UserName : {1} \n Local PC UserName : {2} \n FileName : {3} \n FilePath : {4} "
, OpenTime.ToString(), RevitUserName, localUserName, filename, filepath);
TaskDialog.Show("Model Open Writer info", "user and file details : \n " + content);
}
var writefilepath = Path.GetTempPath();
var Writefile = writefilepath + "//records.txt";
FileStream fs = new FileStream(Writefile, FileMode.Append);
StreamWriter writer = new StreamWriter(fs);
writer.Write(content);
writer.Close();
File.OpenRead(Writefile);
}
}
}
First of all, you can completely remove the TransactionMode and RegenerationOption. The latter is completely obsolete and does nothing at all anywhere whatsoever. The former is only useful when declaring an external command. It is useless and ignored in the context of an external application.
Secondly, to address your question: you can set a breakpoint in the beginning of OnStartup. If the breakpoint is not hit, your add-in is not being loaded at all. Probably something is wrong with your add-in registration, e.g., in the add-in manifest *.addin file.
Go back to square one, i.e., work through the getting started material and the developer guide instructions on registering and loading a Revit add-in.
If the breakpoint in OnStartup is hit, then your add-in is loading correctly, which means that the add-in manifest *addin file is OK. So, you do not need to worry about that. The VisibilityMode tag is not used for external applications, by the way.
Thanks, Jeremy It worked
Firstly I apologies for adding this as answer( I don't know how to add codes in comment)
It worked when I deleted my Addin file and recreated it may be I had made some mistake in it.
meanwhile I have copied the following code from examples and used it,honestly I did't understand this line of the code.
"public void ExportLog(object sender, DocumentOpenedEventArgs args)"
can you point to a right source that explains this part. I have three questions here :
what object type is sender and args are they of type application?
How do I add a 3rd parameter to this method say I want the user to input a string to name the file the data is copied to.
Can I do this
var newEvent = new EventHandler<Autodesk.Revit.DB.Events.DocumentOpenedEventArgs>(ExportLog);
instead of
application.ControlledApplication.DocumentOpened += new EventHandler<Autodesk.Revit.DB.Events.DocumentOpenedEventArgs>(ExportLog);
why does all example use += is this to register the event every time a new instance of Revit is opened?
Thanks for your help.
You can see the class of sender yourself by setting a breakpoint at the beginning of ExportLog and looking in the debugger.
No, you cannot modify the signature of the event handler. It is predetermined by the Revit API.
Yes.
It sounds to me as if you might save some time and effort for yourself by learning a bit more about the basics of C# and .NET programming in general before continuing to tackle this task.

Xamarin/Android - Issue reading locally stored photo from camera - no read access

I'm trying to create a simple test app to take photos in Android, using Xamarin. When I get this app working (or so I hope), i'll use the code in a real app that I'm working on. I'm using the following recipe from Xamarin as my basis:
http://docs.xamarin.com/recipes/android/other_ux/camera_intent/take_a_picture_and_save_using_camera_app/
The major difference is that I need to store images locally, and not on the SD card. I'm able to successfully take a picture (with the Android simulator). I can see the file in the file structure using ADB and can successfully copy and open the file on my PC. However, I'm unsuccessfull in accessing the file in the app, probably due to user rights.
Please note that I was successfull in creating my own .txt files, and reading them back using either System.IO and Java.IO.
Please review the following code. My app crashes when using "System.IO.File.ReadAllText" and gives me "Access to the path "/data/data/CameraAppDemo.CameraAppDemo/files/photo.jpg" is denied.". And whatever I try (absolute, relative paths, uri's), objBitmap is always null.
ADB says that "photo.jpg" has -rwxrwx--- rights, and though I'm not entirely sure, I think that should be more than sufficient
On the other hand, maybe the intent still has a lock on "photo.jpg"? Or something else is going on...
And one final note, I'm using System.IO.File.ReadAllText just for testing purposes. I experimented with stream readers as well, but with the same result. Also, though I believe this step is unnecessary, I enabled "WriteExternalStore" in the Manifest
namespace CameraAppDemo
{
using System;
using System.Collections.Generic;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics;
using Android.OS;
using Android.Provider;
using Android.Widget;
using Java.IO;
using Environment = Android.OS.Environment;
using Uri = Android.Net.Uri;
[Activity(Label = "Camera App Demo", MainLauncher = true)]
public class MainActivity : Activity
{
private File _file;
private string _basePath;
private ImageView _imageView;
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
var objBitmap = BitmapFactory.DecodeFile(_file.AbsolutePath) ;
Console.WriteLine ("objBitmap = null : " + (objBitmap == null).ToString ());
var strOutput = System.IO.File.ReadAllText (FileManager.BasePath + "/photo.jpg");
Console.WriteLine (strOutput);
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
FileManager.SetupFolderStructure();
if (IsThereAnAppToTakePictures())
{
Button button = FindViewById<Button>(Resource.Id.myButton);
_imageView = FindViewById<ImageView>(Resource.Id.imageView1);
button.Click += TakeAPicture;
}
}
private bool IsThereAnAppToTakePictures()
{
Intent intent = new Intent(MediaStore.ActionImageCapture);
IList<ResolveInfo> availableActivities = PackageManager.QueryIntentActivities(intent, PackageInfoFlags.MatchDefaultOnly);
return availableActivities != null && availableActivities.Count > 0;
}
private void TakeAPicture(object sender, EventArgs eventArgs)
{
System.IO.Directory.Delete (FileManager.BasePath, true);
_basePath = FileManager.BasePath;
_file = new Java.IO.File (_basePath, "photo.jpg");
Intent intent = new Intent(MediaStore.ActionImageCapture);
intent.PutExtra(MediaStore.ExtraOutput, Uri.FromFile(_file));
StartActivityForResult(intent, 0);
}
}
}
//Part of the FileManager class:
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Android.Graphics;
namespace CameraAppDemo
{
public class FileManager
{
public static string BasePath {
get {
var libraryPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
if (Directory.Exists (libraryPath) == false) {
Directory.CreateDirectory (libraryPath);
}
return libraryPath;
}
}
}
}
==== Edit ====
It seems that I'm simply not able to read the file. As an ex-webdeveloper, I'm fairly new to programming for mobile, let alone the combo of C# and Java and I'm still learning a lot.
Anyway, I added the following lines:
Console.WriteLine("Setting file :" + _file.SetReadable (true));
Console.WriteLine("Can read :" + _file.CanRead());
Both cases return False. I can't read the file, and I am unable to give read access.
So, any ideas? Is this by design? Can I tell the Intent for taking images to give me read access, or is there another workaround?
If everything fails, I'm hoping to workaround the problem by saving to the SD card first and then copying the file to the local filesystem. But that's something I rather would not do; I can't guarantee that the end users have an SD card, and the pictures should not be deleted by accident.

How to get the current directory path of application's shortcut

I want to get the current directory path but not of the application location but of it's shortcut location.
I tried these but they return the application's location.
Directory.GetCurrentDirectory();
Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase);
Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]);
According to the process API reference in MSDN, the process STARTUPINFO struct for a given process contains the information about the shortcut .lnk file in the title member. There is a flag present in the dwFlags struct member that is set when this is the case - so it appears that this is not always set (im guessing if you ran the exe directly)
From MSDN:
STARTF_TITLEISLINKNAME: 0x00000800
The lpTitle member contains the path of the shortcut file (.lnk) that the user invoked
to start this process. This is typically set by the shell when a .lnk file pointing to
the launched application is invoked. Most applications will not need to set this value.
This flag cannot be used with STARTF_TITLEISAPPID.
Reference here.
If adding a COM Object reference is not a problem , Add COM Object Reference - Windows Script Host Object Model
i ran this code in my desktop folder and it did work. for current folder use - Environment.CurrentDirectory
using System;
using System.IO;
using IWshRuntimeLibrary; //COM object -Windows Script Host Object Model
namespace csCon
{
class Program
{
static void Main(string[] args)
{
// Folder is set to Desktop
string dir = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
var di = new DirectoryInfo(dir);
FileInfo[] fis = di.GetFiles();
if (fis.Length > 0)
{
foreach (FileInfo fi in fis)
{
if (fi.FullName.EndsWith("lnk"))
{
IWshShell shell = new WshShell();
var lnk = shell.CreateShortcut(fi.FullName) as IWshShortcut;
if (lnk != null)
{
Console.WriteLine("Link name: {0}", lnk.FullName);
Console.WriteLine("link target: {0}", lnk.TargetPath);
Console.WriteLine("link working: {0}", lnk.WorkingDirectory);
Console.WriteLine("description: {0}", lnk.Description);
}
}
}
}
}
}
}
Code Reference from Forum : http://www.neowin.net/forum/topic/658928-c%23-resolve-lnk-files/
Try this:
Environment.CurrentDirectory
From MSDN:
Gets or sets the fully qualified path of the current working directory.
I think you will need to use COM and add a reference to "Microsoft Shell Control And Automation", as described in this blog post:
Here's an example using the code provided there:
namespace Shortcut
{
using System;
using System.Diagnostics;
using System.IO;
using Shell32;
class Program
{
public static string GetShortcutTargetFile(string shortcutFilename)
{
string pathOnly = System.IO.Path.GetDirectoryName(shortcutFilename);
string filenameOnly = System.IO.Path.GetFileName(shortcutFilename);
Shell shell = new Shell();
Folder folder = shell.NameSpace(pathOnly);
FolderItem folderItem = folder.ParseName(filenameOnly);
if (folderItem != null)
{
Shell32.ShellLinkObject link = (Shell32.ShellLinkObject)folderItem.GetLink;
return link.Path;
}
return string.Empty;
}
static void Main(string[] args)
{
const string path = #"C:\link to foobar.lnk";
Console.WriteLine(GetShortcutTargetFile(path));
}
}
}
using System.Reflection;
string currentAssemblyDirectoryName = Path.GetDirectoryName(
Assembly.GetExecutingAssembly()
.Location
);
Also for webapplications you can use:
Web Applications:
Request.PhysicalApplicationPath
http://msdn.microsoft.com/en-us/library/system.web.httprequest.physicalapplicationpath.aspx
to grap the applicationpath :)
Since creating the shortcut is part of the workflow, just set the working directory to "%cd%" for the shortcut then, in the app, use:
Environment.CurrentDirectory
Obviously, you would want to capture this value before any code your app calls can change it.
When creating a shortcut using Windows Explorer, you don't have the option of setting the working directory. So, after creating it, open its property page by right-clicking on it and selecting Properties, then set the Start in field to %cd%. After creating such a shortcut, you can move or copy it to the folders in which you want the app to run.
If you do not want to use a COM object as suggested in the other answers
You can open the file as a regular text file, split on the \x00 0 char, and inspect the resulting string array. One of them will be obviously the link target: something like "C:\path\to\file" or in case of UNC "\\computers\path\to\file".
string lnkFilePath "...";
var file = System.IO.File.ReadAllText(lnkFilePath);
var contents = Regex.Split(file, "[\x00]+");
var paths = contents.Where(line => Regex.IsMatch(line, #"^([A-Z]:\\|^\\\\)\S+.*?"));
Use GetStartupInfoW, it will tell you the .lnk file that was launched to start the program.

prevent a c# application from running more than one instance

I wrote a program in c#
now I would like to know what is the proper way to prevent the program from starting if it is already running?
so if it is already running, and double-click on the program it will not start because it is already running.
I can do that, but I was thinking of a standard and proper way.
The recommended way to do this is with a system mutex.
bool createdNew;
using(var mutex = new System.Threading.Mutex(true, "MyAppName", out createdNew))
{
if (createdNew)
// first instance
Application.Run();
else
MessageBox.Show("There is already an instace running");
}
The first parameter to the Mutex ctor tells it to give create a system wide mutex for this thread. If the Mutex already exists it will return out false through the 3rd parameter.
Update
Where to put this?
I'd put this in program.cs. If you put it in form_load you'll need to keep the mutex for the life time of the application (have the mutex as a member on the form), and manually release it in the form unload.
The earlier you call this the better, before the other app opens DB connections etc. and before resources are put created for forms / controlls etc.
Quick way I did in one of the applications .. You can look at the list of running processes to see whether the current application is already running and not start the application again.
Process[] lprcTestApp = Process.GetProcessesByName("TestApplication");
if (lprcTestApp.Length > 0)
{
// The TestApplication is already running, don't run it again
}
I think enumerating the process list could potentially be slow. You could also create a Mutex using the System.Threading.Mutex class and check to see if it's already created when the process starts. However, this would require calling into Win32 system code so wouldn't be completely platform agnostic.
Take a look at Scotts blog post and don't be foolished by the assembly name. It's just a file name of a standard file in the .Net framework.
Here are more informations direct out of MSDN for the WindowsFormsApplicationBase.
You can use a system-wide Semaphore, using the Semaphore Constructor (Int32, Int32, String, Boolean%) constructor and a fairly unique name.
Cheers, Matthias
If your application produces/consumes files, then you're better registering a system wide communication mechanism (e.g. a remoting or WCF endpoint, or even a socket). Then, if the second instance of the application is being launched from double clicking one of your files, you can send the file information across to the running instance.
Otherwise, if it's a standalone program, then as others have said, a Mutex or Semaphore would server equally well.
solution in Windows form application Prohibit again run application(reopen application).
1- first add Class RunAlready.cs
2-Call method processIsRunning() with Name Process from RunAlready.cs in Program.cs
Program.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Tirage.MainStand
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
PublicClass.Class.RunAlready RunAPP = new PublicClass.Class.RunAlready();
string outApp = RunAPP.processIsRunning("Tirage.MainStand");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainStand_FrmLogin fLogin = new MainStand_FrmLogin();
if (outApp.Length == 0)
{
if (fLogin.ShowDialog() == DialogResult.OK)
{
Application.Run(new MainStand_masterFrm());
}
}
else MessageBox.Show( "Instance already running");
}
}
}
class RunAlready:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PublicClass.Class
{
public class RunAlready
{
public string processIsRunning(string process)
{
string xdescription = "";
System.Diagnostics.Process[] processes =
System.Diagnostics.Process.GetProcessesByName(process);
foreach (System.Diagnostics.Process proc in processes)
{
var iddd = System.Diagnostics.Process.GetCurrentProcess().Id;
if (proc.Id != System.Diagnostics.Process.GetCurrentProcess().Id)
{
xdescription = "Application Run At time:" + proc.StartTime.ToString() + System.Environment.NewLine;
xdescription += "Current physical memory : " + proc.WorkingSet64.ToString() + System.Environment.NewLine;
xdescription += "Total processor time : " + proc.TotalProcessorTime.ToString() + System.Environment.NewLine;
xdescription += "Virtual memory size : " + proc.VirtualMemorySize64.ToString() + System.Environment.NewLine;
}
}
return xdescription;
}
}
}

C# IO Reading and Writing file in use error

I have a library that handles reading and writing a cache file. This library is used by a Windows Service and several instances of a console application on the same machine. The console application runs when a user logs in.
I am getting occasional IO errors saying the cache file is in use by another process. I assume that collisions are occurring between the different application instances and service trying to read and write at the same time.
Is there a way to lock the file when it is in use and force all other requests to "wait in line" to access the file?
private void SaveCacheToDisk(WindowsUser user) {
string serializedCache = SerializeCache(_cache);
//encryt
serializedCache = AES.Encrypt(serializedCache);
string path = user == null ? ApplicationHelper.CacheDiskPath() :
_registry.GetCachePath(user);
string appdata = user == null ? ApplicationHelper.ClientApplicationDataFolder() :
_registry.GetApplicationDataPath(user);
if (Directory.Exists(appdata) == false) {
Directory.CreateDirectory(appdata);
}
if (File.Exists(path) == false) {
using (FileStream stream = File.Create(path)) { }
}
using (FileStream stream = File.Open(path, FileMode.Truncate)) {
using (StreamWriter writer = new StreamWriter(stream)) {
writer.Write(serializedCache);
}
}
}
private string ReadCacheFromDisk(WindowsUser user) {
//cache file path
string path = user == null ? ApplicationHelper.CacheDiskPath() :
_registry.GetCachePath(user);
using (FileStream stream = File.Open(path, FileMode.Open)) {
using (StreamReader reader = new StreamReader(stream)) {
string serializedCache = reader.ReadToEnd();
//decrypt
serializedCache = AES.Decrypt(serializedCache);
return serializedCache;
}
}
}
Sure, you could use a mutex and permit access only when holding the mutex.
You could use a cross-process EventWaitHandle. This lets you create and use a WaitHandle that's identified across processes by name. A thread is notified when it's its turn, does some work, and then indicates it's done allowing another thread to proceed.
Note that this only works if every process/thread is referring to the same named WaitHandle.
The EventWaitHandle constructors with strings in their signature create named system synchronization events.
One option you could consider is having the console applications route their file access through the service, that way there's only one process accessing the file and you can synchronise access to it there.
One way of implementing this is by remoting across an IPC channel (and here's another example from weblogs.asp.net). We used this technique in a project for the company I work for and it works well, with our specific case providing a way for a .net WebService to talk to a Windows Service running on the same machine.
Sample based on the weblogs.asp.net example
Basically what you need to do with the code below is create a Solution, add two Console Apps (one called "Server" and the other called "Client" and one Library to it. Add a reference to the Library to both console apps, paste the code below in and add a reference to System.Runtime.Remoting to both Server & Console.
Run the Server app, then run the client app. Observe the fact that the server app has a message passed to it by the client. You can extend this to any number of messages/tasks
// Server:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
namespace RemotingSample
{
public class Server
{
public Server()
{
}
public static int Main(string[] args)
{
IpcChannel chan = new IpcChannel("Server");
//register channel
ChannelServices.RegisterChannel(chan, false);
//register remote object
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(RemotingSample.RemoteObject),
"RemotingServer",
WellKnownObjectMode.SingleCall);
Console.WriteLine("Server Activated");
Console.ReadLine();
return 0;
}
}
}
// Client:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using RemotingSample;
namespace RemotingSample
{
public class Client
{
public Client()
{
}
public static int Main(string[] args)
{
IpcChannel chan = new IpcChannel("Client");
ChannelServices.RegisterChannel(chan);
RemoteObject remObject = (RemoteObject)Activator.GetObject(
typeof(RemotingSample.RemoteObject),
"ipc://Server/RemotingServer");
if (remObject == null)
{
Console.WriteLine("cannot locate server");
}
else
{
remObject.ReplyMessage("You there?");
}
return 0;
}
}
}
// Shared Library:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
namespace RemotingSample
{
public class RemoteObject : MarshalByRefObject
{
public RemoteObject()
{
Console.WriteLine("Remote object activated");
}
public String ReplyMessage(String msg)
{
Console.WriteLine("Client : " + msg);//print given message on console
return "Server : I'm alive !";
}
}
}
Check out the TextWriter.Synchronized method.
http://msdn.microsoft.com/en-us/library/system.io.textwriter.synchronized.aspx
This should let you do this:
TextWriter.Synchronized(writer).Write(serializedCache);

Categories

Resources