Using ClickOnce when i need to generate several large files? - c#

I'm building a digital signage application and I want to deploy it using ClickOnce. (I feel this is the best approach.) When I start the application from Visual Studio (VS) it works great. The application downloads a lot of images from my web service and saves them to disk:
string saveDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.FullName;
When I start my deployed application, it shows the splash screen and then disappears. The process keeps running, but the UI doesn't display. I'm wondering if my saveDir as shown above is giving me trouble?
How do I locate my installed application? (I need to make license files, etc.)

I'm not sure if this is the root of your problem, but I highly recommend you change the structure of how you store your application information.
When an application is installed through ClickOnce, the application is installed within the User's folder, and it's considerably obfuscated. Furthermore, locations may change with subsequent application updates, so you can not be guarantee than any cached, downloaded file will exist from update to update.
To solve this problem, ClickOnce does provide a Data directory, that is not obfuscated and can be used for caching local data. The only caveat is this directory is not available for non-ClickOnce instances of your application (like the version that is running within the VS debugger.)
To get around this, you should write a function that you can use to get your data directory, regardless of your method of distribution or execution. The following code is a sample of what the function should look like:
//This reference is necessary if you want to discover information regarding
// the current instance of a deployed application.
using System.Deployment.Application;
//Method to obtain your applications data directory
public static string GetAppDataDirectory()
{
//The static, IsNetworkDeployed property let's you know if
// an application has been deployed via ClickOnce.
if (ApplicationDeployment.IsNetworkDeployed)
//In case of a ClickOnce install, return the deployed apps data directory
// (This is located within the User's folder, but differs between
// versions of Windows.)
return ApplicationDeployment.CurrentDeployment.DataDirectory;
//Otherwise, return another location. (Application.StartupPath works well with debugging.)
else return Application.StartupPath;
}

Related

Invisible subfolder under AppData

I'm working on a project that involves the rewriting of legacy Windows applications for a modern .NET environment. The structure of these applications requires that there is one "Master" application running locally on the user's machine that fetches the others from a network share, along with required DLLs, and then runs them locally. This is not a problem per se, but I'm running into issues when I try to use the current user's AppData folder structure for this, whether it's local or roaming.
Thing is though, I can create a subfolder without a problem, I can copy files over (via a call to System.IO.File.Copy) and run them (via creating and starting an instance of System.Diagnostics.Process), but still the applications complain that they can't find the needed DLLs. This probably is related to the fact that the subfolder doesn't show up in the file system at all, which leads me to believe that files are not being copied over there in a fully conventional sense, but rather exist in some sort of limbo that prevents them from being "aware" of each other. When I do the copying and running to a standard filesystem folder, it all works fine. I've been trying to Google this issue as I was sure that this was a known problem, but I'm having a hard time finding anything on this subject. Hence this question.
Any ideas as to what might be going on? Or is my approach of using AppData for this particular purpose perhaps misguided to begin with?
EDIT: Comments asked for a viable code sample that reproduces the problem. I did have some interesting results trying to reproduce the issue as simply as I could. I created a fresh console app and could not reproduce it there. I then tried a fresh WinUI 3 app (which is what I'm trying to work with for my project) and I reproduced it there - the code seems to create the directory and then the file, without complaints (I run without the subfolder being there), but they don't show up in Explorer. For comparison, I then tried a fresh WPF app that does the exact same thing but this behaves the same as the console app, no issue with the subfolder and file showing up after running the app. So my issue seems to be confined to WinUI only.
You should be able to reproduce by placing this code inside the main window of a fresh application (and creating a button called myButton if VS doesn't do that automatically) and comparing WinUI 3 with WPF.
public MainWindow()
{
InitializeComponent();
}
private void myButton_Click(object sender, RoutedEventArgs e)
{
// See https://aka.ms/new-console-template for more information
var destinationPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Subfolder");
string sourceFilename = "C:\\Data\\Textfile.txt";
CopyFileToLocalExecutionPath(sourceFilename, destinationPath);
}
private string CopyFileToLocalExecutionPath(string sourceFilename, string destinationPath)
{
// Make sure that destination path exists before copying file
if (!Directory.Exists(destinationPath))
Directory.CreateDirectory(destinationPath);
// Assign full destination filename - local path + same as source filename
var destinationFilename = System.IO.Path.Combine(destinationPath, System.IO.Path.GetFileName(sourceFilename));
// Do not overwrite file if it's already there
if (!File.Exists(destinationFilename))
File.Copy(sourceFilename, destinationFilename);
return destinationFilename;
}

Copy Files to the output directory on an Azure WebRole

We have an azure Webrole which we call as an API from other applications to convert webpages into a rendered byte array which we then attach as a pdf into emails. We do this using ABCpdf.
For our latest project we have to use a second engine from ABC (ABCGecko) in order to render our pages correctly. The ABCGecko engine requires manually copying to the output directory after build occurs, it doesn't happen automatically.
For a normal application this is no issue, I simply copy the required folder (XULRunner_38 for anyone who uses ABC) into the release after building but I can't figure out how to do this for an Azure WebRole and there doesn't seem to be anything much in the way of help from what I can see in google searches.
I'm assuming I either have to build the role and then adjust the package before I deploy, or deploy the role and then copy the folder across after. I can't figure out how to do this though.
If anyone has any ideas or has needed to manually copy files to an Azure Webrole in the past then I would greatly appreciate your help. Also I should mention that we use Visual Studio as our IDE and publish from within there in case that matters to anyone.
Azure web (and worker) roles allow you to define startup tasks, which allows you to call a script (e.g. powershell script, batch file) which can then perform actions such as copying files.
Oh, and if you don't want it to attempt copying in the event a role instance reboots, you'd need to do something like leaving yourself a "breadcrumb" somewhere to signal that you've already done your init work.
What you don't want to ever do is manual copying of content to your role instances. The moment those instances are updated (e.g. new Host OS update) or they're moved to another physical host, you'll lose any of the files you manually copied.
This is all independent of any IDE (aside from general support for the script language you're writing in), since your startup task is going to execute on each web role instance when it starts up.
More details about startup tasks are here.

how to load a picture to an <asp:Image> using a windows path

I have a little payments webApp, our customers can install it on their IIS and work with it. They can upload their own logotype.
We are using WyBuild to update this apps, but it replaces all files on the web folder with the new version, so the logotypes are deleted, that's why we placed the customer's files in program files, so the updater can't delete them.
the problem is that I can't load the images from the following path
C:\Program Files\MyApp\ImageFoder\logo.jpg
I don't know how to do it and I'm almost sure that is not possible to load
My web application is on
C:\inetpub\wwwroot\MyApp\
I can't have the images on the webFolder because wyBuild deletes them when I'm trying to update them, I already tried the paths like this: (the don't work)
///file:c:/program files/ .... etc
so, the question is
How can I load an image to an asp:image control using it's windows path ?
You need to configure an IIS Virtual Folder to point to the alternate location where the images are stored.
I wouldn't put them in Program Files, though, a sibling folder in wwwroot would be better.
Remember NTFS permissions are easy to mess up and it's easier to manage them in a single place.
Update - for locally installed, localhost-only sites Alternatively (and this is only a good idea if you have minimal amounts of traffic. NOT for public websites), you can serve files from an arbitrary location using a VirtualPathProvider. It sounds like this 'web app' is installed like a desktop app for some reason? If you want to store user data externally, the user's App Data folder would be appropriate, but ONLY if the web app refuses external connections, and can only be accessed from the machine.
Since you're dealing with images, I'd grab the imageresizing.net library and use the VirtualFolder plugin to serve the files dynamically. It's 200KB more in your project, but you get free dynamic image resizing and/or processing if you need it, and you save a few days making a VirtualPathProvider subclass work (they're a nightmare).
Wouldn't it be better to use isolated storage?
Added: I mean on the users machine, and upload them again if they are not found. This takes away your overhead completely.

Windows 7 Compatibility Issue in .NET

When we create a SetUp & Deployment project for our application in .net, the default folder for our application to be installed is being set as C:\Program Files.....
Our application will run perfectly if we are using a Windows XP machine. But if we are using a Windows Vista or Windows 7 machine, the application wont run perfectly, especially if we are performing any file operations in our application...
Can anyone find a solution for this problem? Is there any means to penetrate this User Account Control and File Access Controls of Windows 7? or can any1 give a choice to change the default installation folder from [ProgramFilesFolder]\[Manufacturer]\[ProductName] to some other folder?
If your application writes to any files under its install folder (i.e. under Program Files if default path is used), then it is badly written. You shouldn't try to circumvent OS security mechanisms to protect the user from badly written apps; you should rather fix your app so that it works correctly.
And it is, of course, not a .NET issue at all. Any application doing the same thing, no matter which language/framework it's written in, will have the same problem.
You should still install your application in Program Files folder. there are good reasons to have it there - a single copy for all users, in a well known locked place where nobody but an admin can tamper with your binaries.
However, any file operation you are doing should be in one of the standard Windows locations for user-writable files. There are several such folders, depending on the file usage model. You can read more about these in the following SO questions :
My winform app uses xml files to store data, where should I store them so Vista users can write to them?
Vista and ProgramData
Data shared among all users should go in C:\ProgramData (use Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) to find out where it actually lives -- it can be different between Windows versions).
Data specific to the user should be in SpecialFolder.ApplicationData. If the user has a roaming profile, this is shared between machines.
Data specific to the user that's also specific to the machine should be in SpecialFolder.LocalApplicationData.
If you really need to put something in your program's installation directory, you need to do this with Administrator privileges.
You should either do this by prompting for elevation the first time that the program is run. Preferably, you should do this during installation (because you're already elevated).
First of all, you should not set your app to install under C:\Program Files\..., you should instead set it to %PROGRAMFILES%\... While these will usually equate to the same thing, they can be different on a 64 bit system, or they can be wildly different if the system has been set that way.
As already mentioned, don't try to circumvent the UAC, it is there for a reason, and your program is no more special than any other program on the system. Instead what you should do is set your app manifest to demand administrative level permission upon launch (which if granted bypasses the file system virtualization, although the user can decline it or possibly not even have the rights to launch something as admin). The other thing you can do is set the ACLs on the folder that your app sits in, and give all users on the machine read/write access to that folder (this would have to be done at install time).
Or you can do things the proper way and store your data files in the %APPDATA% folder, which you have full rights to, although these folders are specific to each individual user of the system.
I'd suggest start here http://channel9.msdn.com/pdc2008/PC51/
That will give you a good foundation.

Why can't my C# app create files in Windows 7?

I have a C# app that creates a settings file for itself to store the current state of certain visual elements. This app works just fine on any machine that isn't running Windows 7, but on those machines we get an error that the settings file can't be created because the user doesn't have permission. Now, I could fix this issue by going to each computer, logging in as the administrator and giving the user read and write access on the program folder for the application that we've installed, but there has to be a better way.
It seems like in XP, you got write access on the folders you created by default, but that isn't the case anymore. Is there a setting I need in the setup package to make this work?
The point is that you shouldn't be storing settings files in the program folder. Microsoft have advised against this for a long time, but started making things stricter with Vista IIRC.
Use Environment.SpecialFolders.ApplicationData (etc) to find the most appropriate place to put settings. Or use the .NET settings infrastructure which does this automatically for you.
are you trying to create files in the installation folder? you should be using the user data folder for data and not the installation folders. Use the Environment.SpecialFolders.ApplicationData folder to get a folder you can write to.
You're probably running as an administrator on your non-Windows 7 machine which an write anywhere. Be sure to save any per user instance data in their AppData folder (roaming if it should follow them from computer to computer, or local if its a cache or local to taht machine only). If you need to share settings between users, use the C:\ProgramData folder with the appropriate permissions.
A program shouldn't try to store settings in its installation directory.
Be sure to use the SpecialFolders along with Environment.GetFolderPath to get the right locations needed. You should never hard code paths because they can be different between versions AND languages. (I know in the German version of XP it wasn't Program Files but Programme!)
this app works just fine on any machine that isn't running Windows 7
Wrong! It only works on those machines if you run as administrator. I think you'll find your program is broken on Windows XP as well if you try to run it on just about any business computer rather than a home computer.
Instead, this kind of information needs to go in one of the special Application Data folders.
This is a security flaw in your program because your program is writing information to the program directory (which is, and should be, protected.) If it's a situation of correcting the root cause, consider using the SpecialFolder enumeration or the static members on Application like like CommonAppDataPath to write your information to a more appropriate location.
Assuming the typical approach to writing a file via a path, this is a trivial fix and there's no good "expediency" reason to not correct the root cause. If you're not sure about how to manipulate the path, consider using Path.Combine(). It does it for you.
In general, you shouldn't be writing program data to any folder underneath Program Files (even if you created the folder). You should use the Environment.GetFolderPath(...) to figure out where to put your application specific data. You can pass in one of many enums defined here -- you probably want Environtment.SpecialFolder.CommonApplicationData
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
I do not see how any of this is an actaul answer. I need to be able to write a report and have it saved the users documents folder the same folder I used to read the xml files I am writing the report from.

Categories