When I pass the value from the OpenFilePicker() method back to the button click method, I can utilize a debug string and ensure that the value is not null.
However, when I pass it to the GetCellValue() method, a 'FileNotFound' exception is thrown. Utilizing a debug statement here also shows that the value is not null and returns a valid file path of "C:\Test.xlsx".
Tried changing file permissions to RWX for all, attempted different folder locations. All permissions and folders seem to have the same issue.
public async void FileSelectButton_ClickAsync(object sender, RoutedEventArgs e)
{
string filePath = await openFilePicker();
//Debug.WriteLine("result:: " + filePath);
GetCellValue(filePath, "Sheet1", "A1");
}
public async Task<string> openFilePicker()
{
var archerReportPicker = new
Windows.Storage.Pickers.FileOpenPicker();
archerReportPicker.ViewMode =
Windows.Storage.Pickers.PickerViewMode.Thumbnail;
archerReportPicker.SuggestedStartLocation =
Windows.Storage.Pickers.PickerLocationId.Downloads;
archerReportPicker.FileTypeFilter.Add(".xlsx");
archerReportPicker.FileTypeFilter.Add(".xls"); // Default extensions
Windows.Storage.StorageFile archerReport = await archerReportPicker.PickSingleFileAsync(); //Get file
if (archerReport != null)
{
// Application now has read/write access to the picked file
this.fileTextBox.Text = archerReport.Name; // Load it up and throw the data in the textbox.
var filePath = archerReport.Path;
return filePath;
}
else
{
this.fileTextBox.Text = "";
return null;
}
}
public static string GetCellValue(string fileName, string sheetName, string addressName)
{
string value = null;
// Open the spreadsheet document for read-only access.
using (SpreadsheetDocument document = SpreadsheetDocument.Open(fileName, false)) //Line where exception is thrown
{...}
Throws System.IO.FileNotFound Exception as opposed to opening valid file path.
The issue also occurs when filePath or fileName is defined using const string '#c:\test.xlsx'
The short answer to this question is here:
https://blogs.msdn.microsoft.com/wsdevsol/2012/12/04/skip-the-path-stick-to-the-storagefile/
The gist of it is that in UWP, Storage Pickers return a non-filesystem bound Windows.Storage object. You can glean the filesystem path from the object, but because you are performing an operation on a secondary object, the fact that the user gave permissions for the first object does not apply to the second, resulting in an Access Denied condition when attempting to open the file - even if NTFS permissions allow 'Everyone' access.
This can be confirmed by monitoring the application using Process Monitor from SystemInternals.
If I discover a work-around to this issue, I will update this answer, but I will likely move away from UWP back towards a Windows Forms Application to avoid this issue entirely.
Related
I have a UWP application which perform to capture and process images from a camera. This project leverage Microsoft Cognitive Services Face Recognition API and I'm exploring the application's existing functionality for awhile now. My goal is that when the image of a person is identified by the camera (through Face Recognition API service), I want to show the associated image of that person.
With that, the images are captured and stored in a local directory of my machine. I want to retrieve the image file and render it on the screen once the person is identified.
The code below shows the async Task method ProcessCameraCapture
private async Task ProcessCameraCapture(ImageAnalyzer e)
{
if (e == null)
{
this.UpdateUIForNoFacesDetected();
this.isProcessingPhoto = false;
return;
}
DateTime start = DateTime.Now;
await e.DetectFacesAsync();
if (e.DetectedFaces.Any())
{
string names;
await e.IdentifyFacesAsync();
this.greetingTextBlock.Text = this.GetGreettingFromFaces(e, out names);
if (e.IdentifiedPersons.Any())
{
this.greetingTextBlock.Foreground = new SolidColorBrush(Windows.UI.Colors.GreenYellow);
this.greetingSymbol.Foreground = new SolidColorBrush(Windows.UI.Colors.GreenYellow);
this.greetingSymbol.Symbol = Symbol.Comment;
GetSavedFilePhoto(names);
}
else
{
this.greetingTextBlock.Foreground = new SolidColorBrush(Windows.UI.Colors.Yellow);
this.greetingSymbol.Foreground = new SolidColorBrush(Windows.UI.Colors.Yellow);
this.greetingSymbol.Symbol = Symbol.View;
}
}
else
{
this.UpdateUIForNoFacesDetected();
}
TimeSpan latency = DateTime.Now - start;
this.faceLantencyDebugText.Text = string.Format("Face API latency: {0}ms", (int)latency.TotalMilliseconds);
this.isProcessingPhoto = false;
}
In GetSavedFilePhoto, I passed the string names argument once the person is identified.
Code below for the GetSavedFilePhoto method
private void GetSavedFilePhoto(string personName)
{
if (string.IsNullOrWhiteSpace(personName)) return;
var directoryPath = #"D:\PersonImages";
var directories = Directory.GetDirectories(directoryPath);
var filePaths = Directory.GetFiles(directoryPath, "*.jpg", SearchOption.AllDirectories);
}
However, in GetSavedFilePhoto method the variable directories returned an empty string of array when using directoryPath string variable. Directory "D:\PersonImages" is a valid and existing folder in my machine and, it contains subfolders with images inside. I also tried Directory.GetFiles to retrieve the jpg images but still returned an empty string.
I think it should work because I have used Directory class several times but not inside an asyncTask method. Does using async caused the files not returned when using I/O operation?
Sorry for this stupid question, but I really don't understand.
Any help is greatly appreciated.
Using Directory.GetFiles or Directory.GetDirectories method can get the folder/file in the local folder of the Application by the following code. But it could not open D:\.
var directories = Directory.GetDirectories(ApplicationData.Current.LocalFolder.Path);
In UWP app you can only access two locations at default (local folder and install folder), others need capabilities setting or file open picker.Details please reference file access permission.
If you need access to all files in D:\, the user must manually pick the D:\ drive using the FolderPicker, then you have permissions to access to files in this drive.
var picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
picker.SuggestedStartLocation =
Windows.Storage.Pickers.PickerLocationId.ComputerFolder;
picker.FileTypeFilter.Add(".jpg");
picker.FileTypeFilter.Add(".jpeg");
picker.FileTypeFilter.Add(".png");
Windows.Storage.StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
// Application now has read/write access to the picked file
}
else
{
//do some stuff
}
Maybe someone knows a simple solution to my problem.
I do not know the entry of the file so it's not a static value.
It can be changed through the BizTalk gui and there we have a URI through the receiveport. But I do not believe it's accessible that easy. What I want to do is write out the full path as the filename. It works well with the messageID where the file is given a specific filepath name. But the Path-Name where the file was dropped is not working that well.
I keep getting this error :
Message:
Object reference not set to an instance of an object.
the message resource is present but the message is not found in the string/message table
-Does not say me much
Below you can see a snip from my code
internal static string UpdateMacroPathProperty(IBaseMessage baseMessage, string macroPathProperty, string macroDefsFile)
{
if (macroName == "MessageID")
{
contextPropertyValue = baseMessage.MessageID.ToString();
}
else if (macroName == "SourceFileName")
{
contextPropertyValue = Directory.GetCurrentDirectory();
}
}
This is an specific created pipeline. Has anyone encountered this problem or can point me in the right way.
I know that BizTalk has a built in function for this, BizTalk Server: List of Macros as the %SourceFileName% but I'm trying to save this as logs in a specific map structure so that it does not get processed.
It's adapter dependent; some adapters will use the FILE adapter's namespace even though they're not the file adapter, but this is the kind of logic that I've used in the past for this:
string adapterType = (string)pInMsg.Context.Read("InboundTransportType",
"http://schemas.microsoft.com/BizTalk/2003/system-properties");
string filePath = null;
if (adapterType != null)
{
if (adapterType == "FILE")
{
filePath = (string)pInMsg.Context.Read("ReceivedFileName",
"http://schemas.microsoft.com/BizTalk/2003/file-properties");
}
else if (adapterType.Contians("SFTP") && !adapterType.Contains("nsoftware"))
// nsoftware uses the FTP schema
{
filePath = (string)pInMsg.Context.Read("ReceivedFileName",
"http://schemas.microsoft.com/BizTalk/2012/Adapter/sftp-properties");
}
else if (adapterType.Contains("FTP"))
{
filePath = (string)pInMsg.Context.Read("ReceivedFileName",
"http://schemas.microsoft.com/BizTalk/2003/ftp-properties");
}
}
And then you can just fall back to the MessageID if you can't get the file path from any of these.
I'm running into some issues trying to move flat HTML files around on a server.
In short, the process says that it should take whatever file is currently in the newpath (if there is one) and move it to the backup folder. Then take the file in the old path and move it to the new path. Then it checks to see if it was successful (i.e. does the newpath exist) and if it wasn't, it replaces the backup. I've pasted the method below for your viewing pleasure.
public bool MoveContent(string oldPath, string newPath, string backupDirectoryPath, out string backupPath)
{
backupPath = String.Empty;
var oldFilePath = HttpContext.Current.Server.MapPath(oldPath);
var newFilePath = HttpContext.Current.Server.MapPath(newPath);
var newDirectory = Path.GetDirectoryName(newFilePath);
// If the file we're moving doesn't exist, fail.
if (!File.Exists(oldFilePath))
throw new InvalidPathException(oldFilePath);
// If no destination is found, fail.
if (string.IsNullOrWhiteSpace(newDirectory))
throw new InvalidPathException(newFilePath);
if (!Directory.Exists(newDirectory))
Directory.CreateDirectory(newDirectory);
var backupPhysicalPath = String.Empty;
// If there is a file in our destination, back that one up.
if (File.Exists(newFilePath) && !String.IsNullOrWhiteSpace(backupDirectoryPath))
{
var backupFilePath = HttpContext.Current.Server.MapPath(backupDirectoryPath);
var backupDirectory = Path.GetDirectoryName(backupFilePath);
// If the backup destination doesn't exist, fail.
if(string.IsNullOrWhiteSpace(backupDirectory))
throw new InvalidPathException(backupDirectory);
if(!Directory.Exists(backupDirectory))
Directory.CreateDirectory(backupDirectory);
var fileName = Path.GetFileNameWithoutExtension(newFilePath);
var currentDateTime = DateTime.Now.ToString(FileHelpers.TempFileDateFormat);
var fileExtension = Path.GetExtension(newFilePath);
// Example Result: hardware-2015-01-30-08-35-26-475.html
backupPath = backupDirectoryPath.Replace(fileName, fileName + "-" + currentDateTime);
backupPhysicalPath = String.Format("{0}\\{1}-{2}{3}", backupDirectory, fileName, currentDateTime, fileExtension);
// If there is already a file in our backup destination, fail.
if (File.Exists(backupPhysicalPath))
throw new InvalidPathException(backupPhysicalPath);
// Backup the file that currently exists in our new destination.
File.Move(newFilePath, backupPhysicalPath);
}
// Move our file to the new destination.
File.Move(oldFilePath, newFilePath);
// Return false if the new file doesn't exist.
if (!File.Exists(newFilePath))
{
// If we made a backup, return the backup to the original loction, since there's nothing in the destination.
if (!String.IsNullOrWhiteSpace(backupPhysicalPath) && File.Exists(backupPhysicalPath))
{
File.Move(backupPhysicalPath, newFilePath);
}
throw new Exception(String.Format("Failed to move content. OldPath: '{0}'; NewPath: '{1}'; BackupPath: '{2}'", oldFilePath, newFilePath, backupPhysicalPath));
}
return true;
}
Here's an example of the parameters being passed in:
oldPath: "/client/content/en/unpublished/Anpan.html"
newPath: "/client/content/en/Anpan.html"
backupDirectoryPath: "/client/content/en/backups/Anpan.html"
The problem that I'm running into is that sometimes the backup file will be made (it will move from newpath to backuppath), but it won't move from oldpath to newpath.
I've been unable to actually reproduce the issue as it happens so infrequently and without any exceptions being thrown, but the symptoms exist (I can see the files on the filesystem when the client reports the issue) and it's been reported multiple times.
I put some logging around it and wrapped the entire method in a try/catch. It never fails unexpectedly (except when I specifically throw the InvalidPathException). There is nothing in my logs when it happens.
Can anyone help me to diagnose this issue, or tell me if I'm doing something very wrong in my method that would cause the problem?
Thanks so much!
I am making an application that can potentially be used on multiple computers by the same user. I currently have implemented backup and restore functionality to keep the SQLite database synchronized. However, when I install a new copy of the application, I would like to prompt the user to select a backup file (if applicable).
Initially, I thought that I would just put the logic in MainPageViewModel.xaml in the OnNavigatedTo method. The problem with that is that it would run through that logic every time navigating back to MainView. So, I thought that it would make sense to put it in OnApplicationLaunchAsync to only run once when the application is launched.
Here is my code currently:
private async Task<StorageFile> SelectFileAsync()
{
StorageFile pickedFile = null;
var settings = Container.Resolve<ISettings>();
var picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".sqlite");
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
var file = await picker.PickSingleFileAsync();
if (file != null)
{
var pickedFileToken = StorageApplicationPermissions.FutureAccessList.Add(file);
settings.BackupFileToken = pickedFileToken;
pickedFile = file;
}
return pickedFile;
}
protected async override Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args)
{
var navigationService = Container.Resolve<INavigationService>();
var dialogService = Container.Resolve<IDialogService>();
var settings = Container.Resolve<ISettings>();
if (!string.IsNullOrEmpty(settings.BackupFileToken))
{
var backupFile =
await StorageApplicationPermissions.FutureAccessList.GetFileAsync(settings.BackupFileToken);
var properties = await backupFile.GetBasicPropertiesAsync();
dialogService.Show(string.Format("Backup file found: {0}\r\n" +
"Modified date: {1}", backupFile.Path, properties.DateModified), "");
navigationService.Navigate(Experiences.Main);
}
else
{
navigationService.Navigate(Experiences.Blank);
//dialogService.Show("No backup file specified. Please choose a backup file location.", "");
// TODO: load blank page, then select backup file location, then navigate to main page
await SelectFileAsync();
}
// do not need to return Task.FromResult when OnLaunchApplicationAsync() is async
//return Task.FromResult<object>(null);
}
As you can see, if the BackupFileToken exists, it will check the properties of the file and display a dialog with the file path and last modified date. My problem lies in the situation where it is a new installation/the backup token doesn't exist.
If I try to display a dialog before the FileOpenPicker then the application crashes. I thought perhaps adding a blank view to navigate to, then display the dialog and FileOpenPicker. It just seems like there should be a simpler way to accomplish this (without the need for extra views).
Can someone recommend a way to prompt the user to select a file when the application launches, but let them know the purpose of the FileOpenPicker?
I am aware that my await SelectFileAsync() is losing its return value, this is a temporary situation for testing purposes. I will be sure to assign the return value to the relevant variable once I have found a solution.
Maybe you can use the override
protected override void OnWindowCreated(WindowCreatedEventArgs args) in App.xaml.cs
which is also called once for the creation of the main window.
I have an application written in C#, and I am seeking to write some information to the hidden ProgramData in order to access the same connection string from both the application's front end and back end.
I am accessing the directory using path variables as follows:
private bool ProgramDataWriteFile(string contentToWrite)
{
try
{
string strProgramDataPath = "%PROGRAMDATA%";
string directoryPath = Environment.ExpandEnvironmentVariables(strProgramDataPath) + "\\MyApp\\";
string path = Environment.ExpandEnvironmentVariables(strProgramDataPath)+"\\MyApp\\ConnectionInfo.txt";
if (Directory.Exists(directoryPath))
{
System.IO.StreamWriter file = new System.IO.StreamWriter(path);
file.Write(contentToWrite);
file.Close();
}
else
{
Directory.CreateDirectory(directoryPath);
System.IO.StreamWriter file = new System.IO.StreamWriter(path);
file.Write(contentToWrite);
file.Close();
}
return true;
}
catch (Exception e)
{
}
return false;
}
This seems to work correctly. However, my question is, when I used this path variable: %AllUsersProfile%(%PROGRAMDATA%)
instead, it expanded into an illegal(and redundant) file path : C:\ProgramData(C:\ProgramData)\
However, I thought that the latter path variable was the correct full name. Was I just using it incorrectly? I need to ensure that this connection info will be accessible to all users, will just using %PROGRAMDATA% allow that? I am using Windows 7 in case that is relevant.
From here:
FOLDERID_ProgramData / System.Environment.SpecialFolder.CommonApplicationData
The user would never want to browse here in Explorer, and settings changed here should affect every user on the machine. The default location is %systemdrive%\ProgramData, which is a hidden folder, on an installation of Windows Vista. You'll want to create your directory and set the ACLs you need at install time.
So, just use %PROGRAMDATA%, or better still:
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)