Self updating application question - c#

I've been looking at a simple machanism for self-updating executable files. (I can't use Click-Once due to the nature of the application - trust me on this)
I've noticed that an in-flight assembly can move itself to another location on disk, presumably because the executing assembly is actually an in-memory copy), and that the original location of the file can be overwritten. This is proven with the following proof-of-concept snippet...
string assemblyStart = System.Reflection.Assembly.GetExecutingAssembly().Location;
Console.WriteLine(assemblyStart);
if(File.Exists(#"C:\ANewExe.exe"))
{
File.Delete(#"C:\ANewExe.exe");
}
File.Move(assemblyStart,#"C:\ANewExe.exe");
string assemblyMoved = System.Reflection.Assembly.GetExecutingAssembly().Location;
Console.WriteLine(assemblyMoved); // still reports as the original location
File.WriteAllText(assemblyStart,"some text");
On my XP development system, this results in the assembly file being moved to c:\ANewExe.exe, and the original file location being filled with the text "some text".
I guess my question here is, is this a safe and/or reliable method to use across versions of Windows from XP up), or are there other ways to achieve this functionality? Will this be detected as possible virus behaviour by AV systems that I don't have access to to test?
TIA

Notepad++ uses GUP - http://gup-w32.sourceforge.net/ . Worth a look.
Is the nature of the application such that it MUST stay running? It seems far easier to have an updater download a new EXE, overwrite the current one, and then prompt to restart the app.

Related

AppDomainSetup.PrivateBinPath vs Environment.SetEnvironmentVariable

I just need my app to know where to look for some unmanaged dlls. I am using SetEnvironmentVariable and it is working great. I know that there is also a property AppDomainSetup.PrivateBinPath. What is the practical difference between them?
Currently I am doing it as below:
var dllDirectory = #"C:\some\path";
Environment.SetEnvironmentVariable("PATH", Environment.GetEnvironmentVariable("PATH") + ";" + dllDirectory)
Edit:
I noticed that Environment.SetEnvironmentVariable does not actually change the PATH variable, it seams to affect only the app that have called it.
PrivateBinPath is where the CLR will look for assemblies.
Which is not where Windows will look for DLLs, it doesn't know anything about CLR configuration. It uses the regular Windows search rules, which usually behaves like this:
same directory as where the EXE is stored
the directory specified in a Set/AddDllDirectory() call, if any
the Windows system directory (normally c:\windows\system32)
the Windows install directory (normally c:\windows)
the current default directory (Environment.CurrentDirectory)
the directories listed in the PATH environment variable.
Several quirks to this, it has been tinkered with a lot. Particularly bullet 5 is a security problem and can be abused to get a program to load a rogue DLL. But close enough to what you can expect in the wild.
Setting the PATH environment variable in your code is okayish, it is not exactly reliable. It being on the bottom of the list is of course an issue, you might get the wrong DLL loaded. And the PATH environment variable itself is troublesome, it can easily be corrupted on a machine and may be already too long to allow you to append another directory to it. Very hard to diagnose problems.
You should always, always, always favor bullet 1. Simply copy the native DLLs into the same directory as your EXE. Always works, always reliable, never a surprise, no special config needed. Nobody cares that this directory is a bit full, not your customer, not the file system, not the operating system.
If you have to then always favor bullet 2, pinvoke SetDllDirectory(). It is not completely reliable, you'll have trouble if one of the DLLs you load is using it too. But you quickly find that out. Using AddDllDiretory() is better but it isn't supported on enough Windows versions yet to be relied upon.
AppDomainSetup.PrivateBinPath is set of folders under application base directory that are probed for private assemblies during appdomain setup. Env var PATH will not be necessarily pointing to folders under application base directory. PATH will contain any arbitrary folder path.

How to use reuse softlinks created on Mac in Windows 8

I have few softlinks, says 1000 images which i have created in MacBook Pro which i am using in my iOS Apps.
Now i am porting the same app in Windows 8 phone app, so i want to reuse the same Softlink in Windows phone 8 apps as well, so how can i use that ?
I have tried to open the softlink in Windows 8 machine, but it says that the "File format is not supported".
I have both the original file and the softlink in my Windows machine.
Is there anyother way that i can reuse the same soft link ? if NOT what is the best approach that i can follow.
EDIT
Ok, here is some more info on this :
In MacBook Pro
I have a folder in desktop which has physical paths (actual images), now i have created softlinks using a script and these softlinks are placed in some different folder.
Now i am using these soflinks in my iOS app.
In Windows 8
I have copied the folder which has soflink as well as the folder which has actual files in it from Mac.
Now i have pasted actual files folder on my desktop and soflinks folder in some D: drive now if i go my soflink folder in D drive and when i check those images it shows blank, because its not pointing to the actual files.
I have both actual files folder and also the soflink folder.
One more point is that when you create a soflink, in MacBook Pro it shows this icon :
But on Windows 8 its blank nothing like that.
Your question is missing a couple of details so I'm going to have to make a guess about your situation. The problem is:
You created some symlinks using OS X on a file system and now you are
having problems accessing those symlinks in Windows.
Unless you did something tricky, like installing 3rd party file system drivers, then the only file system that both Windows and OS X can read/write to natively is FAT based. So I'm guessing your situation is:
You created some symlinks using OS X on a FAT32 file system and now
you are having problems accessing those symlinks in Windows.
Assuming the above situation, the problem is that there are no symlinks in FAT32 because the file system doesn't support them. OS X is tricking you because it "just works". What is really happening is that OS X is creating an ASCII text file that contains the line "XSym" along with the name of the file it is "linking" to, plus some file system information. You can confirm this by opening your softlinks on your Windows system in notepad. Normally you would see binary code if you were opening an actual image in notepad, but instead you should see the text from these fake symlinks.
So, what do you do? I see a couple of options:
You could use a file system that supports soft links. This could mean using HFS+ (OS X file system) which would require you to install HFS+ drivers on your Windows system so that it can read/write to the file system. Or it could mean going in the other direction and using NTFS (Windows file system) which would require you to install NTFS drivers on your Mac. Note that most recent versions of OS X can read NTFS file systems, they just can't write to them.
You could use the fake symlinks that OS X is creating. This would require writing a parser to interpret the links or finding a library that does this for you. I don't have a copy, but I believe the XSym format is covered in the "OS X Internals" book.
You could rethink the approach to your problem so that it doesn't require you to use symlinks.
If this didn't solve your problem, then please provide more details because I had to make some guesses about your situation.
==EDIT==
Take a look at the subversion documentation on symbolic links here.
The relevant quote from the doc is:
Versioning Symbolic Links
On non-Windows platforms, Subversion is able to version files of the
special type symbolic link (or “symlink”). A symlink is a file that
acts as a sort of transparent reference to some other object in the
filesystem, allowing programs to read and write to those objects
indirectly by way of performing operations on the symlink itself.
When a symlink is committed into a Subversion repository, Subversion
remembers that the file was in fact a symlink, as well as the object
to which the symlink “points.” When that symlink is checked out to
another working copy on a non-Windows system, Subversion reconstructs
a real filesystem-level symbolic link from the versioned symlink. But
that doesn't in any way limit the usability of working copies on
systems such as Windows that do not support symlinks. On such systems,
Subversion simply creates a regular text file whose contents are the
path to which to the original symlink pointed. While that file can't
be used as a symlink on a Windows system, it also won't prevent
Windows users from performing their other Subversion-related
activities.
Basically, it says something similar to what I mentioned earlier, which is that symlinks are not supported that well if at all on Windows systems. Subversion just creates text files with the contents of the link so you can choose to either figure out how to parse these text files yourself or try to find a library that will parse them for you.
Maybe the problem is that there are so many links in one directory
There is a maximum of 31 reparse points (and therefore symbolic links)
allowed in a particular path.
See also
Programming Considerations
I know I am late in this, but I hope that others may benefit from my answer, even though the asker may long have moved on.
Some background
Symbolic link semantics differ considerably between unixoid systems and Windows. As was stated before, Windows uses reparse points to implement symbolic links and junction points (some deduplication features on the Server editions also seem to use it).
Now, a reparse point contains extra data as a hint to the I/O manager and object manager. Essentially, based on a reparse point tag (a GUID) the type of reparse point can be determined and then a file system filter driver handles the details. You can find a moderately detailed description of this in the 6th edition of "Windows Internals" in chapter 9 or in a recent Windows Driver Kit or on MSDN under REPARSE_GUID_DATA_BUFFER (and related topics).
On unixoid systems the file system metadata also contains a clue that the (text file) is a symlink. If you use ls -l that clue is visible in the form of a leading l, e.g. in:
lrwxrwxrwx 1 user group 38 2015-10-12 11:51
The actual contents of symlinks are system-specific as well, on Linux for example they contain merely the target path.
What the Windows and *nix symlinks share is that the target needn't exist at the time of creation. Also on Windows a symlink can point to a network location, which is special because on Windows network paths differ from local paths.
Possible compatibility
Assuming a symlink was created on the OSX or Linux side, we can imagine certain levels of compatibility. If the file system driver on the Windows side would now present symlinks as reparse points and some party (either said file system driver or a file system filter) would handle these reparse points, it would be possible to interpret the target path of a symlink in some way.
Converting forward slashes to backward slashes is the least concern, however.
In this answer I already outlined a few cases where there would be no meaningful translation possible.
Essentially the only type of symlinks for which I would see a potential for compatibility are relative symlinks. But even for those is is necessary to point out that the target path may not point outside of the folder hierarchy that is visible on the Windows side. That is, if your symlink on the OSX or Linux side resides inside /var/www/html and points to ../../../something it becomes meaningless in a case where /var is the mounted volume on Windows.
If, however, such symlink /var/www/html/foobar and pointed to ../html1/foo/bar chances are that if /var was the mounted volume on OSX or Linux and now on Windows, the relative target path still makes sense (after adjustments such as forward to backward slashes etc).
For any absolute target paths, the file system driver or the file system filter driver would have to get some hints on how to translate the source form of a symlink into the target form.
E.g. if a symlink pointed to /home/foo/bar the /home part might translate to a specific mounted volume.
But you can already see that this requires a lot of user intervention, which is probably why most people would consider it futile to even attempt a meaningful translation.
Possible workaround for SVN
A possible workaround for you could be to use SVN externals. It depends on the exact scenario, but since you are using SVN they come to mind.
You can think of SVN externals as Subversion's native symlinks. I have used them this way and I know of several others who have, but I don't know how widespread that train of thought and subsequent usage is.
Attention: externals pointing to files were only introduced in SVN 1.6, so this may or may not be an issue in your scenario.
SVN externals come in several flavors. You can set them for folders or files (files only with 1.6 and newer).
And an external can point to:
an external repo (schema://server/path)
relative to the same repo (^/path)
relative to the schema (//server/path) or
relative to the parent directory
You'll probably want 2 or 4 from that list. Most likely you'll want 4, though, because file externals must point to the same repository.
Long story short
If your images are in a folder such as trunk/images and you have a folder trunk/platforms/windows/images you can either set the the svn:externals property on trunk/platforms/windows to have an external named images pointing to ../../images (i.e. directory external) or, assuming you wanted to use a different hierarchy or different names underneath trunk/platforms/windows/images you could create file externals like so (images subdirectory must exist in WC):
cd trunk/platforms/windows
svn propedit svn:externals images
and add individual externals like this:
../../../images/filename.jpeg other-filename.jpeg
Please note that the target directories need to exist in the repository and the working copy, so for an external like this:
../../../images/filename.jpeg foo/other-filename.jpeg
the subdirectory trunk/platforms/windows/images/foo must exist.
Updating your working copy will result in those externals to manifest as versioned files inside the working copy. So they are a type of symlinks that exists in SVN and manifests as proper files in the working copy, which means all platforms can handle them equally.

Couldn't write file to Application.StartupPath in Windows 7

I created a winform application and then created a setup of that application. this application records some info at Application.StartupPath in a file. unfortunately i got exception when i try to write the file 'Access to is denied'. Please guide me how can i get rid of that..
Thanks
You should never assume that the application startup path is writable by anyone besides system administrators, especially on modern Windows systems.
Instead of storing your file there, I'd suggest you use the folder returned by Environment.GetFolderPath(SpecialFolder.ApplicationData). That folder is guaranteed to be writable by the current user.
You can find the Microsoft guidelines about this issue here.
That's typical - you shouldn't be writing to the "Program" area of your application. You should be writing to a data area of the file system - perhaps the user's settings area, or a common application settings area.
Basically the policy was toughened up (in Vista, I believe) to try to discourage programs from doing exactly what you're currently doing. The best approach isn't to work round it - it's to change where your application stores its settings.

C# Cannot write to Application.ExecutablePath, some boxes i can some i cannot? uninstall from this

So I have been writing to
Environment.SpecialFolder.ApplicationData
this data file, that upon uninstall needs to be deleted. I am using Innos Setup to build my installer. It works great for me. So my data file hangs out in the above path and I do that cause when I used to try to write it to
Application.ExecutablePath
certain boxes I tested it on would throw a nasty error at me trying to write data there. I do research and somehow its not always writable and its how i came up with the Environment.SpecialFolder.ApplicationData
That is why my data file now resides in the SpecialFolder.ApplicationData. Trouble is if the user uninstalls and reinstalls I need that file gone. It might be a short coming of my knowledge of Innos but I cannot figure out how to know where that file will be to tell innos that.
So then I thought I had a clever solution: Innos can run a file when its done uninstalling, so I had my program create this file "uninstallData.bat" that says:
del "the file in my special folder application data path"
and I wrote it out to drumroll
Application.ExecutablePath
(yes it was a while in development and I had forgot it was't doable.)
So of course I am back to square one, I need to write a file to a path Innos knows about {app} and I need it to be able to delete my data file in the SpecialFolder... i don't care how I do it i just need that file gone.
Are there other Environment. or Application. approches I have missed? Maybe somewhere that is viewable by an uninstaller AND can be written to?
As an aside, I am not sure why my box I develop on can write to the application folder no issue, but it cannot on other boxes... weird.
Any input would be great sorta lost as to how to crack this nut.
The environment location is in the user profile. If there are multiple users on the machine, and they all run the application then a copy of the file will be in each profile.
The path also depends on the OS.
Regardless, the current user's app data location is pointed to by %APPDATA% and %LOCALAPPDATA%. These Windows environment variables should be available within Innos.
Appliccation.ExecutablePath is not writable per standard defintions - the program files folder should never be manipulated by running applications. Ther area number of special folders for that. Nice that you finally found.... what is properly documented by Microsoft for a LONG time now (minimum 10 years).
I suggest you get a proper installer - WIX comes to my mind. Your problem is totally unrelated to C# - it seems to be totally a "crappy installer" issue. Or provide a PROGRAM (not bat file) to run at uninstall. What exatly is your problem there?

.NET FileInfo.LastWriteTime & FileInfo.LastAccessTime are wrong

When I call FileInfo(path).LastAccessTime or FileInfo(path).LastWriteTime on a file that is in the process of being written it returns the time that the file was created, not the last time it was written to (ie. now).
Is there a way to get this information?
Edit: To all the responses so far. I hadn't tried Refresh() but that does not do it either. I am returned the time that the file was started to be written to. The same goes for the static method, and creating a new instance of FileInfo.
Codymanix might have the answer, but I'm not running Windows Server (using Windows 7), and I don't know where the setting is to test.
Edit 2: Nobody finds it interesting that this function doesn't seem to work?
The FileInfo values are only loaded once and then cached. To get the current value, call Refresh() before getting a property:
f.Refresh();
t = f.LastAccessTime;
Another way to get the current value is by using the static methods on the File class:
t = File.GetLastAccessTime(path);
Starting in Windows Vista, last access time is not updated by default. This is to improve file system performance. You can find details here:
http://blogs.technet.com/b/filecab/archive/2006/11/07/disabling-last-access-time-in-windows-vista-to-improve-ntfs-performance.aspx
To reenable last access time on the computer, you can run the following command:
fsutil behavior set disablelastaccess 0
As James has pointed out LastAccessTime is not updated.
The LastWriteTime has also undergone a twist since Vista. When the process has the file still open and another process checks the LastWriteTime it will not see the new write time for a long time -- until the process has closed the file.
As a workaround you can open and close the file from your external process. After you have done that you can try to read the LastWriteTime again which is then the up to date value.
File System Tunneling:
If an application implements something like a rolling logger which closes the file and then renames it to a different file name you will also run into issues since the creation time and file size of the "old" file is remembered by the OS although you did create a new file. This includes wrong reports of the file size even if you did recreate log.txt from scratch which is still 0 bytes in size. This feature is called OS File System Tunneling which is still present on Windows 8.1 . An example how to work around this issue check out RollingFlatFileTracelistener from Enterprise Library.
You can see the effects of file system tunneling on your own machine from the cmd shell.
echo test > file1.txt
ren file1.txt file2.txt
Wait one minute
echo test > file1.txt
dir /tc file*.txt
...
05.07.2015 19:26 7 file1.txt
05.07.2015 19:26 7 file2.txt
The file system is a state machine. Keeping states correctly synchronized is hard if you care about performance and correctness.
This strange tunneling syndrome is obviously still used by application which do e.g. autosave a file and move it to a save location and then recreate the file again at the same location. For these applications it makes to sense to give the file a new creation date because it was only copied around. Some installers do also such tricks to move files temporarily to a different location and write the contents back later to get past some file exists check for some install hooks.
Have you tried calling Refresh() just before accessing the property (to avoid getting a cached value)? If that doesn't work, have you looked at what Explorer shows at the same time? If Explorer is showing the wrong information, then it's probably something you can't really address - it might be that the information is only updated when the file handle is closed, for example.
There is a setting in windows which is sometimes set especially on server systems so that modified and accessed times for files are not set for better performance.
From MSDN:
When first called, FileSystemInfo
calls Refresh and returns the
cached information on APIs to get
attributes and so on. On subsequent
calls, you must call Refresh to get
the latest copy of the information.
FileSystemInfo.Refresh()
If you're application is the one doing the writing, I think you are going to have to "touch" the file by setting the LastWriteTime property your self between each buffer of data you write. Some psuedocode:
while(bytesWritten < totalBytes)
{
bytesWritten += br.Write(buffer);
myFileInfo.LastWriteTime = DateTime.Now;
}
I'm not sure how severely this will affect write performance.
Tommy Carlier's answer got me thinking....
A good way to visualise the differences is seperately running the two snippets (I just used LinqPAD) simliar to below while also running sysinternals Process Monitor.
while(true)
File.GetLastAccessTime([file path here]);
and
FileInfo bob = new FileInfo(path);
while(true){
string accessed = bob.LastAccessTime.ToString();
}
If you look at Process Monitor while running the first snippet you will see repeated and constant access attempts to the file for the LinqPAD process. The second snippet will do an initial access of the file, for which you will see activity in process monitor, and then very little afterwards.
However if you go and modify the file (I just opened the text file I was monitoring using FileInfo and added a character and saved) you will see a series of access attempts by the LinqPAD process to the file in process monitor.
This illustrates the non-cached and cached behaviour of the two different approachs respectively.
Will the non-cached approach wear a hole in the hard drive?!
EDIT
I went away feeling all clever over my testing and then used the caching behaviour of FileInfo in my windows service (basically to sit in a loop and say 'Has-file-changed-has-file-changed...' before doing processing)
While this approach worked on my dev box, it did not work in the production environment, ie the process just kept running regardless if the file had changed or not. I ended up changing my approach to checking and just used GetLastAccessTime as part of it. Don't know why it would behave differently on production server....but I am not too concerned at this point.

Categories

Resources