I'd like to use TagLib in my Windows Store project.
The TagLib is imported as reference, with its dll ( taglib-sharp.dll )
I can access any file in my music folder since it is checked in the capabilities. However, when I call
TagLib.File file = TagLib.File.Create(soundFilePath, TagLib.ReadStyle.None);
it throws the following error:
System.UnauthorizedAccessException was unhandled by user code
HResult=-2147024891
Message=Access to the path 'C:\Users\Gabor\Music\_FIFA 2010 soundtracks\01. Meine Stadt - Auletta.mp3' is denied.
Source=taglib-sharp
StackTrace:
at TagLib.File.Create(IFileAbstraction abstraction, String mimetype, ReadStyle propertiesStyle)
at TagLib.File.Create(String path, String mimetype, ReadStyle propertiesStyle)
at TagLib.File.Create(String path, ReadStyle propertiesStyle)
You can use TagLibSharp to load tags by creating a StreamFileAbstraction and passing that to File.Create. This won't use any banned APIs.
public void ExampleCall(StorageFile storageFile)
{
IRandomAccessStreamWithContentType f = await storageFile.OpenReadAsync();
var file = File.Create(new StreamFileAbstraction(storageFile.Name, f.AsStream()));
}
public class StreamFileAbstraction : File.IFileAbstraction
{
public StreamFileAbstraction(string name, Stream stream)
{
Name = name;
ReadStream = stream;
WriteStream = stream;
}
public void CloseStream(Stream stream)
{
stream.Flush();
}
public string Name { get; private set; }
public Stream ReadStream { get; private set; }
public Stream WriteStream { get; private set; }
}
couldn't do it this way. used MusicProperties instead, and then used lastfm api to get the needed info.. shame its so difficult in Win8 c# what is easy in normal C#
For WinRT you need next:
var task = await StorageFile.GetFileFromApplicationUriAsync(uri);
var stream = await task.OpenStreamForReadAsync();
using (var info = File.Create(new StreamFileAbstraction(Path.GetFileName(uri.LocalPath), stream, stream)))
{
Album = info.Tag.Album;
Comment = info.Tag.Comment;
// more properties
}
and you need add NuGet Packages - TagLib# Portable
Related
I'm building an app for windows only, that needs to consume a docx file using .net MAUI.
I use the suggested class IFilePicker, implemented it, and worked fine while debugging (both in debug and release mode).
So, after finished a preview version, I want to deploy unpacked, "like a portable version", using:
MSBuild.exe D:\Workspace\dotNet\WordReplacer\WordReplacer.App\ /restore /t:Publish /p:TargetFramework=net6.0-windows10.0.19041 /p:configuration=release /p:WindowsAppSDKSelfContained=true /p:Platform=x64 /p:PublishSingleFile=true /p:WindowsPackageType=None /p:RuntimeIdentifier=win10-x64
Everthing works fine just as debug, except for the FilePicker that doesn't work and gives me the error:
Value does not fall within the expected range
This error doesn't happen if I install a published package with a certificate. So maybe I'm missing something in the msbuilder workaround to generate an unpacked app.
I'm using the communitytoolkit.MVVM and the method that I use to pick the file stays in my viewmodel:
private string _inputFilePath;
[ObservableProperty]
private string _inputFileNameText = "Select a input file";
[RelayCommand]
public async Task PickInputDocAsync()
{
try
{
var customFileType = new FilePickerFileType(
new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.WinUI, new[] { ".doc", ".docx" } },
});
PickOptions options = new()
{
PickerTitle = "Please select a document",
FileTypes = customFileType,
};
var result = await FilePicker.Default.PickAsync(options).ConfigureAwait(false);
if (result != null)
{
_inputFilePath = result.FullPath;
InputFileNameText = result.FileName;
}
}
catch (Exception ex)
{
ErrorMessage = $"{ex.InnerException?.Message} Error: {ex.Message}, InputFilePath: {_inputFilePath}, InputFileName: {InputFileNameText}";
}
Any clue how to fix it?
I've been trying to fix this error, but since I don't know much about msbuild itself, I take another approach.
So, I finally got it working when I implemented a FilePicker specifically for the Windows platform.
I take most of the code from this answer Folder Picker .NET MAUI, but, instead of folderpicker I use the filepicker. Check it out to know more about the implementation and the set up.
How I do it:
Create a Folder helper in the root folder app and create an interface ICustomPicker and a data transfer object(DTO).
public interface ICustomPicker
{
Task<FileDto> PickFileAsync();
}
public class FileDto
{
public string DisplayName { get; set; }
public string Name { get; set; }
public string FullPath { get; set; }
}
Inside the Plataforms/Windows folder I created CustomFilePicker.cs
public class CustomPicker : ICustomPicker
{
public async Task<FileDto> PickFileAsync()
{
try
{
var picker = new WindowsFilePicker();
picker.FileTypeFilter.Add(".docx");
picker.FileTypeFilter.Add(".doc");
// Get the current window's HWND by passing in the Window object
var windowHandle = ((MauiWinUIWindow)App.Current.Windows[0].Handler.PlatformView).WindowHandle;
// Associate the HWND - window handler, with the file picker
WinRT.Interop.InitializeWithWindow.Initialize(picker, windowHandle);
var storageFile = await picker.PickSingleFileAsync();
if (storageFile != null)
{
return new FileDto()
{
DisplayName = storageFile.DisplayName,
Name = storageFile.Name,
FullPath = storageFile.Path
};
}
}
catch
{
// Ignored
}
return null;
}
}
Register the DI in my MauiProgram.cs
#if WINDOWS
builder.Services.AddTransient<ICustomPicker, Platforms.Windows.CustomPicker>();
#endif
then in my view model I simply call it
private readonly ICustomPicker _customPicker;
public MainViewModel(ICustomPicker customPicker,)
{
_customPicker = customPicker;
}
[RelayCommand]
public async Task PickInputDocAsync(){
var file = await _customPicker.PickFileAsync().ConfigureAwait(false);
if (file != null)
{
//do something
}
}
Probably this problem will disappear (I hope) when MAUI releases a proper unpacked publish option.
Note: Right now, I just test this approach in Windows 11.
Hi I have an app that uses a json file to monitor run intervals of emails. I use it as a simple way to store the times I run an email notification. But I've run into an issue where the user can run concurrent instances of this app. What happens then is that two instances can read that json file and if they both read that an email is not sent, both instances will send an email leading to duplicate emails.
This is how the code is (sampled down for replicability)
public class SummaryEmailStatus
{
public DateTime DateSent { get; set; }
public string ToolType { get; set; }
public bool IsSent { get; set; }
}
public static class JsonUtil
{
public static Dictionary<string, SummaryEmailStatus> EmailStatusKVP = new Dictionary<string, SummaryEmailStatus>();
public static bool CreateEmailItemJasonFile(string jsonFileName)
{
var summCfgEmailStatus = new SummaryEmailStatus
{
DateSent = DateTime.Parse("2022-01-01"),
ToolType = "CIM",
IsSent = false
};
EmailStatusKVP.Add(SummaryEmailJsonObjs.ConfigError, summCfgEmailStatus);
string json = JsonConvert.SerializeObject(EmailStatusKVP, Formatting.Indented);
File.WriteAllText(jsonFileName, json);
}
public static Dictionary<string, SummaryEmailStatus> ReadEmailItemJsonFromFile(string jsonFileName)
{
string json = File.ReadAllText(jsonFileName);
EmailStatusKVP = JsonConvert.DeserializeObject<Dictionary<string, SummaryEmailStatus>>(json);
return EmailStatusKVP;
}
public static void WriteSummEmailStatusJsonToFile(string summaryEmailType, SummaryEmailStatus emailItem)
{
//string json = JsonConvert.SerializeObject(emailItem, Formatting.Indented);
EmailStatusKVP[summaryEmailType] = emailItem;
string json = JsonConvert.SerializeObject(EmailStatusKVP, Formatting.Indented);
File.WriteAllText(ParserConfigFilesConstants.SummaryEmailJsonFilePath, json);
}
}
The issue is I am using File.WritallText and ReadAllText. Is there a way to do what I am doing but in a way that locks the file each time the CreateEmailItemJasonFile or ReadEmailItemJsonFromFile or WriteSummEmailStatusJsonToFile is called?
I want only one instance for the console application to use this file. If the other instance tries to use it, it should get some "being used by another program" exception.
I saw this solution How to lock a file with C#? but with how new I am to C# I am not sure how to use it for my own needs.
I also thought about using a lock object around my File.Write and File.Read sections but I was under the impression that would only work if its another thread within the console application instance:
lock (fileReadLock)
{
string json = File.ReadAllText(jsonFileName);
}
I fixed it by using FileStream:
For reading I used:
FileStream fileStream = new FileStream(jsonFileName, FileMode.Open, FileAccess.Read, FileShare.None);
using (StreamReader reader = new StreamReader(fileStream))
{
json = reader.ReadToEnd();
}
And for Writing I used:
FileStream fs = new FileStream(ParserConfigFilesConstants.SummaryEmailJsonFilePath, FileMode.Create, FileAccess.Write, FileShare.None);
using (StreamWriter writer = new StreamWriter(fs))
{
writer.WriteLine(json);
writer.Flush();
}
This allows me to lock the file out whenever my app is using the file.
I'm trying to read the metadata of a mp3 file stored in IsolatedStorage using TagLib.
I know TagLib normally only take a file path as input but as WP uses a sandbox environment I need to use a stream.
Following this tutorial (http://www.geekchamp.com/articles/reading-and-writing-metadata-tags-with-taglib) I created a iFileAbstraction interface:
public class SimpleFile
{
public SimpleFile(string Name, Stream Stream)
{
this.Name = Name;
this.Stream = Stream;
}
public string Name { get; set; }
public Stream Stream { get; set; }
}
public class SimpleFileAbstraction : TagLib.File.IFileAbstraction
{
private SimpleFile file;
public SimpleFileAbstraction(SimpleFile file)
{
this.file = file;
}
public string Name
{
get { return file.Name; }
}
public System.IO.Stream ReadStream
{
get { return file.Stream; }
}
public System.IO.Stream WriteStream
{
get { return file.Stream; }
}
public void CloseStream(System.IO.Stream stream)
{
stream.Position = 0;
}
}
Normally I would now be able to do this:
using (IsolatedStorageFileStream filestream = new IsolatedStorageFileStream(name, FileMode.OpenOrCreate, FileAccess.ReadWrite, store))
{
filestream.Write(data, 0, data.Length);
// read id3 tags and add
SimpleFile newfile = new SimpleFile(name, filestream);
TagLib.Tag tags = TagLib.File.Create(newfile);
}
The problem is that TagLib.File.Create still doesn't want to accept the SimpleFile object.
How do I make this work?
Your code doesn't compile because TagLib.File.Create wants IFileAbstraction on input, and you're giving it SimpleFile instance which doesn't implement the interface. Here's one way to fix:
// read id3 tags and add
SimpleFile file1 = new SimpleFile( name, filestream );
SimpleFileAbstraction file2 = new SimpleFileAbstraction( file1 );
TagLib.Tag tags = TagLib.File.Create( file2 );
Don't ask me why we need SimpleFile class instead of passing name and stream into SimpleFileAbstraction - it was in your sample.
I have some Config files as part of my solution on Windows Mobile. I am porting the code to MonoForAndroid and MonoTouch, so I want to keep my code unchanged as much as possible.
When loading these xml files, on Windows Mobile works fine, in my last prototype it also worked on iOS, but the code does not work on MonForAndroid
I have these files
/solution folder
/My Documents/
/Business
App.Config
Settings.Config
I have these files build action set to Content and I can see that they are being copied to the /bin/Debug/ but When I try to read these files, I get the following exception:
System.IO.DirectoryNotFoundException
I see that there is a similar question in here, but they advised to use AndroidResources, which I do not want to do, there are many placed where these files are needed, so I do not want to change it in many places.
AndrodiResources, is out of the question, and if possible I would like to avoid using EmbededResources
ah and the way I am reading it, very straightforward xmDoc.Load(filePath) I also tried File.ReadAllText() I made sure that the filePath is correct, and I got the path generated using Path.Combine() to avoid any issues with the filePath/slashes
Here is how I construct my file path
var filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace(FileURIPrefix, ""), "My Documents", "Business");
filePath = Path.Combine(filePath, "App.Config");
And I can see in the debugger that the filePath is correct
Thanks for the help in advance
After searching all around, I could not get the MonoDroid to load (or include) my files when their build action is set to Content.
I had to create an entity called FileHelper which is implemented differently on Android, I then use that FileHelper.ReadAllText(string filename);
I will put my implementation here, hoping that it would benefit somebody else.
Windows Mobile and iOS
public class FileHelper
{
public static string ReadAllText(string filePath)
{
var path = filePath.GetFullPath();
if (!File.Exists(path))
{
Logging.LogHandler.LogError("File " + path + " does not exists");
return string.Empty;
}
using (var reader = new StreamReader(filePath))
{
return reader.ReadToEnd();
}
}
}
Android version
public class FileHelper : BaseFileHelper
{
public static string ReadAllText(string filePath)
{
var entryAssemblyPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace("file:", ""), "MyExecutableAssemblyName.dll");
// This is because Assembly.GetEntryAssembly() returns null on Android... Booohhh
var assembly = Assembly.LoadFrom(entryAssemblyPath);
using (var stream = assembly.GetManifestResourceStream(filePath.GetFullPath()))
{
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
}
I had a shared code for Constants and an extention method for paths as below
Constants.cs
public static Class Constants
{
private static string _RootPath;
private static string _iOSRootPath;
private static string _AndroidResourcePath;
public static string RootPath
{
get
{
if (string.IsNullOrEmpty(_RootPath))
{
_RootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace(FileURIPrefix, "") + "\\My Documents\\Business";
}
return _RootPath;
}
}
public static string iOSRootPath
{
get
{
if (!string.IsNullOrEmpty(_iOSRootPath))
{
_iOSRootPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace(FileURIPrefix, "").Replace("file:", ""), Path.Combine("My_Documents", "Business"));
}
return _iOSRootPath;
}
}
public static string AndroidResourcePath
{
get
{
if (string.IsNullOrEmpty(_AndroidResourcePath))
{
_AndroidResourcePath = "Leopard.Delivery.My_Documents.Business.";
}
return _AndroidResourcePath;
}
}
}
PathExtentions.cs
public static class PathExtensions
{
public static string GetFullPath(this string filePath)
{
if (Platform.IsAndroid) // platform is a class that I have to tell me which platfrom I am at :)
{
return Constants.AndroidResourcePath + filePath;
}
if (Platform.IsIOS)
{
return Path.Combine(Constants.iOSRootPath, filePath);
}
return Path.Combine(Constants.RootPath, filePath);
}
}
After setting this up, I am using my FileHelper just as easy as below
string configuratinContents = FileHelper.ReadAllText(configruationPath);
To whoever using this code, remember to set the build action to EmbededResources on Android, and to Content on iOS and Windows Mobile.
Windows Phone 7 App
The Goal of the application is a simple To Do list.
I have a class 'toditem' i add those objects to the Items object.
it seems to me I'm doing something really complicated and most likely no clean or decent code
But i have some serious problems with "IsolatedStorageFile"
public class ToDoItem
{
public string ToDoName { get; set; } // Add controle's enz.
public string ToDoDescription { get; set; }
internal Priority PriortiySelection { get; set; }
...
}
Items class (basicly a wrapper clas so i can acces it)
public class Items
{
public static List<ToDoItem> Itemslist = new List<ToDoItem>();
public static List<ToDoItem> GetList()
static methods here..
}
The code Belows returns the following exceptions :
"Attempt to access the method failed:
System.Io.streamreader..ctor
(System.String)"
and afterwards i get
Operation not permitted on IsolatedStorageFileSTream
if (store.FileExists(#"items.std"))
{
ToDoItem item = new ToDoItem();
try
{
IsolatedStorageFileStream save = new IsolatedStorageFileStream(#"items.std", FileMode.Open, store);
BinaryReader reader = new BinaryReader(save);
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
in public partial class NewToDo : PhoneApplicationPage
i added the following method. wich returns the above exceptions again i only assume that its allowd for some reason or i make some huge mistakes.
private void saveItem(ToDoItem toDoItem)
{
try
{
using (StreamWriter sw = new StreamWriter(store.OpenFile(#"items.std", FileMode.Append)))
{
sw.WriteLine(toDoItem.ToDoName);
sw.WriteLine(toDoItem.ToDoDescription);
sw.WriteLine(toDoItem.PriortiySelection.ToString());
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
Should u need more information I'm always glad to provide it, I'm currently a student at a Belgium college second year and I'm playing around with windows phone7 apps.
The following will read the contents of a file from isolated storage
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!store.FileExists(VIEW_MODEL_STORAGE_FILE))
{
return result;
}
using (var isfs = new IsolatedStorageFileStream(VIEW_MODEL_STORAGE_FILE, FileMode.Open, store))
{
using (var sr = new StreamReader(isfs))
{
string lineOfData;
while ((lineOfData = sr.ReadLine()) != null)
{
result += lineOfData;
}
}
}
}
The example builds a string of data (result). This is actually a serialized object which is actually a collection of other objects. This can then be deserialized back to the collection. This is probably preferable to what you were trying to do with writing out properties to a file one at a time.
Here's how to write the file:
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var isfs = new IsolatedStorageFileStream(VIEW_MODEL_STORAGE_FILE, FileMode.Create, store))
{
using (var sw = new StreamWriter(isfs))
{
sw.Write(serializedCollectionObject);
sw.Close();
}
}
}
Is it possible you're not disposing all your disposable objects and encountering a problem when you try to access a resource for a second time because it's still in use?
The using statement is a good way to handle this easily, more on that here.
Dispose with Using
A bit more background on the topic here where Jm47 was getting the same error message for this reason.
Problem opening a stream to an isolatedstorage image already the source on an image?