Discovered a class for detecting directory changes but only seems to work on Mac Desktop, NOT Windows.
https://github.com/renz45/Actionscript/tree/master/Air/filesystem
The FileMonitor class only detects changes for single files I believe.
Does anyone know of a way to detect directory changes with AIR on Windows desktop?
The monitor checks for the modification time of folders only. On Windows the folder modification time update behaviour is very unreliable, try to add a new file, you'll probably see that it updates the folders modification time, while it doesn't when changing the contents of a file.
For a more reliable behaviour you would have to check the modification time of all files and subfolders.
Here's an example, a changed DirectoryMonitor.traverseDirectoryTree method which also includes the files. It's not thoroughly tested, and with a growing number of nested files and folders you might run into performance problems, but in the end it's just an example.
private function traverseDirectoryTree(dir:File):Vector.<File>
{
var list:Vector.<File> = new Vector.<File>;
list.push(dir);
for each (var file:File in dir.getDirectoryListing())
{
if(!file.isHidden)
{
list.push(file);
if(file.isDirectory)
{
list = list.concat(traverseDirectoryTree(file));
}
}
}
return list;
}
See also
Rules for "Date Modified" of folders in Windows Explorer
Folders last modified time stamp are not updated in Windows 7
Related
I'm really confused about how to properly copy files and grant permission to execute e.g. an AppleScript file from a sandboxed application. I've read several articles and threads but the more I read, the more it confuses me.
The Task
My app needs to run a very simple AppleScript from an .scpt file. To do so (if I got this right), I need to copy this file into Users/thisUser/Library/Application \Scripts/.com.developerName.appName/. Before I can interact with this folder the user needs to grant access to that folder. This can be done by showing the user an NSOpenPanel where he can select the path. After confirmation the app has access to that path and I can copy the file and later run the script (App Sandbox User Selected File must be read/write). So far so good.
The Problem(s)
I find presenting a Finder window with an empty folder to select very user unfriendly, so I was wondering if there is anything else I can do. The closest what I have found regarding this problem is drag & drop the folder "into the app" - details can be found here.
I guess I'm not the only person ever who created a (sandboxed) app which needs to run specific scripts and I can't believe that the above approach is the only possible solution!? Therefore,
can I not just have a single window with an OK button and some information above that the app needs permission to write into that folder without showing an entire Finder window?
When I was looking around for solutions I also came across several settings for the app itself. Unfortunately, the docs are very limited here and I could not really find out what the specific settings actually do and how I could test them (admittedly this is because this is my first ever app for OSX and I have basically no clue what I'm doing). One of which is the Copy Files option in the Build Phase settings of the app:
This did sound promising to me since I thought that if I install the app it will automatically copy the file to the Scripts destination (probably with some sort of user prompt) and I can use it. But it does nothing. There is no copy happening at any time, even if I deselect the Copy only when installing setting. I have also tried the different destination folders which are available in the dropdown
and unfortunately also here I
could not find out what the destinations are
nor the file has been copied to any of the destination folders on build.
I know that people here don't really like to answer questions like this in much detail since it is probably more a lack of knowledge on my side but I would really appreciate it if someone could at least help me getting into the right direction and direct me to some resources which tackle my problem!
Thanks!
Well, it seems like I have found a solution which (at least for me) seems to be more or less user friendly and within Apple's sandbox guidelines.
Again, I'm very new to app development using Xcode and SwiftUI so I'm not sure if this solution is 100% "the right way of doing it". But since it took me ages to find this out, maybe someone else can use it and speed up development!
Solution
Like I have mentioned in my question above, I was trying to get rid of the (in my opinion) pretty annoying NSOpenPanel Finder prompt, where the user is supposed to select the folder. I further asked about the Copy Files setting in the app's Build Phase tab - it turned out that this was the solution! Unfortunately, I still don't have any clue about the list of destination which are presented in the dropdown but choosing Absolute Path and inserting
Users/$USER/Library/Application Scripts/$PRODUCT_BUNDLE_IDENTIFIER
did the job! The file gets copied on every build into the app's Application Scripts directory, from which I can run scripts outside the sandbox. 🙌
The next step was to create a class which executes the script using NSUserScriptTask
import Foundation
class ExecuteAppleScript {
var status = ""
private let scriptfileUrl : URL?
init() {
do {
let destinationURL = try FileManager().url(
for: FileManager.SearchPathDirectory.applicationScriptsDirectory,
in: FileManager.SearchPathDomainMask.userDomainMask,
appropriateFor:nil,
create: true)
self.scriptfileUrl = destinationURL.appendingPathComponent("CreateMailSignature.scpt")
self.status = "Linking of scriptfile successful!"
} catch {
self.status = error.localizedDescription
self.scriptfileUrl = nil
}
}
func execute() -> String {
do {
let _: Void = try NSUserScriptTask(url: self.scriptfileUrl!).execute()
self.status = "Execution of AppleScript successful!"
} catch {
self.status = error.localizedDescription
}
return self.status
}
}
Then I have created a Button View which handles the request
import SwiftUI
struct GenerateSignatureButtonView: View {
#State var status = ""
var body: some View {
VStack(alignment: .center) {
Button(action: {
self.status = ExecuteAppleScript().execute()
})
{
Text("Generate Signature")
}
Text("\(self.status)")
}
}
}
struct GenerateSignatureButtonView_Previews: PreviewProvider {
static var previews: some View {
GenerateSignatureButtonView()
}
}
When clicking the button a window pops up that the app wants access to control (in my case) the Mail app.
This user prompt repeats every time the user closes the app, reopens it and clicks the button again. I guess this can be somehow managed with Security-Scoped-Bookmarks but I'm not yet sure how. Furthermore, the error handling is not really working in this example since the popup appears after the successful message appears in the status field. This is probably another big thing to figure out... Asynchronous?
Well, hope this helps!
I know that local application data is stored in a set location on disk for my application:
c:\Users\jonathan\AppData\Local\MyCompany\MyApp_In_Question_Url_fhjsu6dhsj673dkncsdhjfdf
This contains several sub-folders containing the application settings (user.config), which generally take the form "6.1.5944.23465".
We do a straight file copy over the install location, and running the new executable from the shortcut on the desktop for the first time usually creates a new sub-folder of the same form (i.e. 6.1.5966.34567), which requires us to call Settings.Upgrade
So far, so good (although this probably isn't good practice).
I have just recently upgraded the application from .NET 3.5 to .NET 4.6.2, and on some machines it creates a new folder entirely:
c:\Users\jonathan\AppData\Local\MyCompany\MyApp_In_Question_Url_aaabf35hhsjd4hkwn83kfcm
And a new sub-folder of the form 6.1.5977.10245.
This is an issue because the shortcut on the desktop no longer works, and the settings cannot be upgraded, and require re-entry by the end-user (who doesn't always know them).
Most machines (all the test and UAT ones) do not exhibit this behaviour.
My question is this:
What is the mechanism for deciding that an executable's settings should be stored in an entirely new location, and not a sub-folder?
As a corollary of this:
Can I set this so that it's definitely the same product, and the settings can continue to be upgraded?
I realised that sometimes the flow of code creates the first setting from a dll, and sometimes from the main executable, and I do some custom setting of My.Settings (in one of the VB chunks) which is a little messy.
In case you wondered: To recover the settings, I had to search through all the LocalApplicationData looking for my app's previous settings.
Private Shared Function FindFromPreviousSettings() As MigratableSettings
' Find the App_Data folder, and look to see if there are any other
' config settings which are valid, authenticated, etc.
Dim likelyConfigPath = New DirectoryInfo(
Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"My_Software_Company"
)
)
Dim allValidConfigs = SearchMyDirectory(likelyConfigPath, "user.config")
Dim ver As Func(Of FileInfo, Version)
ver = Function(fi)
Try
'Find out the version of this file. Bigger is better.
Return New Version(fi.Directory.Name)
Catch ex As Exception
Return New Version()
End Try
End Function
Dim latestValidFile = allValidConfigs.OrderByDescending(ver).FirstOrDefault()
Dim latestValidSettings = ReadSettingsFromConfigFile(latestValidFile)
Return latestValidSettings
End Function
I have an executable file that I have me and all my friends use. Every time I update it they have to re-download it and move it to the correct location and update the shortcut and etc. I want to make an installer that will create a shortcut for the program on the desktop and/or start menu, add it to the programs and features list with an option to uninstall it, and to update off of a file (or files) off of my website. I can't find and program that can do all of this.
P.S. The program is written in AutoHotKey and all my friends are running windows.
You have the ability to build your own launcher/installer/updater in AutoHotkey. It's not an easy task but it is doable. Below is an quick outline of the program I believe you wish to write. A lot of programmer's write their problems out in pseudo code to break down a large problem into smaller parts. I don't have time to code you a full solution but maybe this will help you get started:
Outline In Pseudo Code
Program Start:
Check for internet connectivity function()
If no Internet Skip Updater / Installer Functions Going straight to launcher
Check for .ini or Registry Entries (Where you store your installation info)
if Information Exists and no Internet MsgBox, "Cannot install"
Else if internet = True
Function Installer()
Launcher:
Run App
Updater:
Download Text info with MD5 Checksums
Compare MD5 checksums to installed files
If file different
function Installer(file)
Installer(file)
Download File
Copy file to appropriate Directory
Update ini or registry with MD5 of files
Md5(file, Info)
If File = Info
Continue
Uninstall:
Delete Programs / Files / Remove Directories etc
Below is a few functions to get you started.
Check Internet status:
connectedToInternet(flag=0x40) { ; Returns True or False
Return DllCall("Wininet.dll\InternetGetConnectedState", "Str", flag,"Int",0)
}
Usage:
If !connectedToInternet() {
curstatus = False
}
Else {
curstatus = True
}
URLDownloadToFiles
MD5 Checksum
Best of luck.
I created a monitoring utility that checks cpu, ram, drive space stats and emails if the usage goes above set threshold. It works great in the system tray but I realized that the exe will stop when I log out of windows server. That led me to believe that I needed to create a windows service. I would like to use the existing GUI Form to save data to application settings and use those settings in windows service. Here are the steps I took so far,
Added a Windows Service class.
Modified the original code to get rid of any interactive items that were related to GUI Form.
Added the code to this class.
Added a Service installer.
Added this code to it-->
public ProjectInstaller()
{
InitializeComponent();
ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
ServiceInstaller serviceInstaller = new ServiceInstaller();
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
serviceProcessInstaller.Username = null;
serviceProcessInstaller.Password = null;
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.ServiceName = "Server Monitoring";
this.Installers.Add(serviceProcessInstaller);
this.Installers.Add(serviceInstaller);
}
Change Start up object to Utility.Program.
When I try installing this through installUtil I get this error
System.IO.FileNotFoundException: Could not load file or assembly 'file:///C:\Use
rs\AdminUser\Desktop\Temp\Server' or one of its dependencies. The system cannot
find the file specified..
Thanks!
If you are saving these application settings into a file that is in the same directory as the Windows service, that is going to be your problem. All Windows Services are run in the C:/Windows directory (or a sub-directory in there) so when you access files you will need to do one of two things:
Change the executing directory
You can change the 'current directory' for the executing app back to the folder that contains the exe with the following line of code:
System.IO.Directory.SetCurrentDirectory(System.AppDomain.CurrentDomain.BaseDirectory);
This will make all relative files become relative to the executable location once again.
Make all file request full paths
This one is easier for some files than others. System files are the hardest. So if you are trying to get to a .config file, that's going to be a nightmare.
I have customer which claims that he has one application which updates the data in log file but that application does not change the time stamp of the log file.
I have question why would any application has such behavior.
It's a new FEATURE of Windows 2008 (R2), Windows 7 and up. Modified time is not updated anymore like it used to in Windows 2003.
http://blogs.technet.com/b/asiasupp/archive/2010/12/14/file-date-modified-property-are-not-updating-while-modifying-a-file-without-closing-it.aspx
And
http://social.technet.microsoft.com/Forums/en-US/winservergen/Thread/2B8BACA2-9C1B-4D80-80ED-87A3D6B1336F
There is a good workaround for build automation:
copy /b <Filename> +,,
I found this trick here: https://superuser.com/questions/292630/how-can-i-change-the-timestamp-on-a-file.
The strange is that "copy /?" does not say about /b option.
I know, this is a very poor C# workaround for some special cases. My server writes a log and I need to get the file changes using a FileSystemWatcher.
So, everytime my log file is updated I create a new empty file with the same name but the extention ".update".
try
{
string updateFlagFile = Path.ChangeExtension(myLogFilename, ".update");
using (File.Create(updateFlagFile))
{ }
}
catch (Exception)
{ }
My FileSystemWatchers tracks this file and I know that the log was updated.