Is ExifLib usable in a WPF / XAML app? - c#

I want to extract exif data from jpg images. ExifLib seemed like a good choice to simplify this chore, and so I installed it via NuGet.
I then tried to get started using the sample code from here (commenting out the MessageBox code for now):
using (ExifReader reader = new ExifReader(#"C:\temp\testImage.jpg"))
{
// Extract the tag data using the ExifTags enumeration
DateTime datePictureTaken;
if (reader.GetTagValue<DateTime>(ExifTags.DateTimeDigitized, out datePictureTaken))
{
// Do whatever is required with the extracted information
//System.Windows.MessageBox.Show(this, string.Format("The picture was taken on {0}",
// datePictureTaken), "Image information"); //, MessageBoxButtons.OK);
}
}
but get an error:
The best overloaded method match for 'ExifLib.ExifReader.ExifReader(System.IO.Stream)' has some invalid arguments
and
Argument 1: cannot convert from 'string' to 'System.IO.Stream'
both on this line:
using (ExifReader reader = new ExifReader(#"C:\temp\testImage.jpg"))
Is this fixable, or is ExifLib not usable from a WPF / XAML app?
If ExifLib is not a viable solution for a WPF / XAML app, what alternatives exist?
Update:
With this code, from Simon McKenzie's answer:
private void btnLoadNewPhotoset_Click(object sender, RoutedEventArgs e)
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
using (var stream = store.OpenFile("testImage.jpg", FileMode.Open))
using (var reader = new ExifReader(stream))
{
// ...
}
}
I still get an error:
The type or namespace name 'IsolatedStorage' does not exist in the namespace 'System.IO' (are you missing an assembly reference?)
This is a Windows Store (C#) app created in Visual Studio 2013. The project's properties shows that it targets Windows 8.1, and Configuration Manager shows configuration == debug, platform = x64)
My project's displayed References are:
.NET for Windows Store apps
Bing.Maps.Xaml
ExifLib
Microsoft Visual C++ Runtime Package
Windows 8.1
What am I missing?
Update 2:
When I look in Reference Manager at Assemblies.Framework, it says, "All of the Framework assembles are already referenced..." I assume mscorlib.dll is supposed to be one of these (it doesn't list them)?
I searched my hard drive for "mscorlib.dll" and I've got a million of them, all different sizes and dates. Which one should I try to add as a reference? I've got everything from one in C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.5 dated 7/9/2012 with file size of 2,564,528 to one in C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5.1 to...you name it.
Thinking "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5.1" seemed the best bet, I tried to reference it via the Browse button, but when I did, I got scolded with:
In the interests of full disclosure, in Reference Manager for Windows 8.1, it says, "The Windows 8.1. SDK is already referenced."
For Windows 8.1.Extensions, it shows me:
Microsoft Visual C++ 2013 Runtime Package for Windows 12.0 (unchecked)
Microsoft Visual C++ 2013 Runtime Package 11.0 (checked)
Since this seems to be the cause of one of the warnings, I reversed their checkedness (checked 2013,
unchecked the other).
I also checked:
Behaviors SDK (XAML) 12.0
SQLite for Windows Runtime 3.8.6 (because I will eventually be using SQLite in this project)
Update 3:
I just found this: "Isolated storage is not available for Windows Store apps. Instead, use the application data classes in the Windows.Storage namespaces included in the Windows Runtime API to store local data and files." here.
Update 4:
I'm waiting for Simon's example, but I'm thinking it might be something like this:
using Windows.Storage;
using ExifLib;
. . .
private async void btnOpenImgFiles_Click(object sender, RoutedEventArgs e)
{
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".png");
IReadOnlyList<StorageFile> files = await openPicker.PickMultipleFilesAsync();
for (int i = 0; i < files.Count; i++)
{
using (var randomAccessStream = await files[i].OpenAsync(FileAccessMode.Read))
using (var stream = randomAccessStream.AsStream())
using (var exfrdr = new ExifReader(stream))
{
// ...exfrdr
}
}
}

this has nothing to do with WPF
try something like this
using (ExifReader reader = new ExifReader(File.Open(#"C:\temp\testImage.jpg",FileMode.Open)))

The string constructor is not available in Windows Phone/Windows Store apps because they aren't allowed direct filesystem access. You will instead need to pass in a stream containing your image. Here's an example using a FileOpenPicker. Note the use of AsStream(...) to convert the IRandomAccessStream into a Stream for use with an ExifReader.
using System;
using System.IO;
using Windows.Storage;
using Windows.Storage.Pickers;
// ...
var picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".jpg");
var file = await picker.PickSingleFileAsync();
using (var randomAccessStream = await file.OpenAsync(FileAccessMode.Read))
{
using (var stream = randomAccessStream.AsStream())
{
using (var reader = new ExifReader(stream))
{
string model;
if (reader.GetTagValue(ExifTags.Model, out model))
{
var dialog = new MessageDialog(model, "Camera Model");
dialog.ShowAsync();
}
}
}
}

Related

Type-safe way to get embedded resource content in .NET Core 3

I'm using .NET Core 3 preview 6 and Visual Studio 2019 16.2 for creating WinForms application.
In .NET Framework I used type-safe mechanism to load resources, something like that:
this.pictureBox1.BackgroundImage = global::MyNamespace.Properties.Resources.Image1;
this.textBox1.Text = global::MyNamespace.Properties.Resources.Script1;
But in .NET Core 3 I must write special helper class with several methods:
public static class EmbeddedResource
{
public static Image GetImage(String resourceName)
{
try
{
using (var stream = typeof(EmbeddedResource).GetTypeInfo().Assembly.GetManifestResourceStream(resourceName))
return Image.FromStream(stream);
}
catch(Exception exception)
{
throw new Exception($"Failed to read Embedded Resource {resourceName}");
}
}
public static String GetString(String resourceName)
{
try
{
using (var stream = typeof(EmbeddedResource).GetTypeInfo().Assembly.GetManifestResourceStream(resourceName))
using (var reader = new StreamReader(stream, Encoding.UTF8))
return reader.ReadToEnd();
}
catch(Exception exception)
{
throw new Exception($"Failed to read Embedded Resource {resourceName}");
}
}
}
And use it like that:
this.pictureBox1.BackgroundImage = EmbeddedResource.GetImage("MyNamespace.Image1.jpg");
this.textBox1.Text = EmbeddedResource.GetString("MyNamespace.Script1.sql");
Is there a better way (e. g. strictly-typed and resourceName error-safe) to do that?
Thank you in advance.
Visual Studio 2019 16.2 Has design-time support for Resx file for Windows Forms .NET Core Projects. It is the same feature that is supported in previous versions of Visual Studio for Windows Forms classic .NET projects.
It means you can:
Add New Item → Choose Resources File and set a name like Resources.Resx and press Add. The file will be opened in design mode. (Later to open it in design mode, just double click on it.)
Add an image to the designer by dragging an image file and dropping it into the designer. You can also add the image by click on Add Resource tool strip drop down button and choosing Add Existing File ....
Then the image will be accessible using a property which has the same name as the image. For example I created a Properties folder and created Resources.Resx under that folder, then added MyImage.jpg to the resource file, the I could use it this way:
this.BackgroundImage = Properties.Resources.MyImage;
Note - Create default Resource file for the project in Properties folder
Right click on Project → Choose Properties
In project properties window, choose Resources (left side, bottom of the list).
At the center, you will see a link This project does not contain a default resources file. Click here to create one. Click on the link and it will create a Resources.Resx file under Properties folder for your project.

How to fix this 'Missing assembly reference' error?

I am working on an application that captures real time pen strokes on a canvas using Wacom Bamboo Slate. The application is being developed for UWP platform using C#. After drawing on the canvas, save feature is to be implemented. I am using this for my reference. Below is the code and error message:
private async void BtnSave_Click(object sender, RoutedEventArgs e)
{
StorageFolder storageFolder = KnownFolders.SavedPictures;
var file = await storageFolder.CreateFileAsync("sample.jpg", CreationCollisionOption.ReplaceExisting);
CanvasDevice device = CanvasDevice.GetSharedDevice();
CanvasRenderTarget renderTarget = new CanvasRenderTarget(device, (int)inkCanvas.ActualWidth, (int)inkCanvas.ActualHeight, 96);
using (var ds = renderTarget.CreateDrawingSession())
{
ds.Clear(Colors.White);
ds.DrawInk(inkCanvas.InkPresenter.StrokeContainer.GetStrokes());
}
using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
await renderTarget.SaveAsync(fileStream, CanvasBitmapFileFormat.Jpeg, 1f);
}
}
CS1061 'InkCanvas' does not contain a definition for InkPresenter and no accessible extension method InkPresenter accepting a first argument of type InkCanvas could be found (are you missing a using directive or an assembly reference?)
Have you considered:
RenderTargetBitmap rtb = new RenderTargetBitmap((int)inkCanvas.Width, (int)inkCanvas.Height, 96d, 96d, PixelFormats.Default);
rtb.Render(inkCanvas);
After which you can then:
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
encoder.Save(fileStream);
The sample referred above can be found here.
The sample use CanvasDevice from the Microsoft.Graphics.Canvas namespace part of the package Win2D.UWP (version 1.6.0) from Microsoft. The UWP project sample targets build 10240 (minimum 10240) of Windows 10.
The package Win2D.UWP can be installed
using the menu "Project > Manage Nuget Packages", or
by selecting the context menu "References" in the UWP project of "Solution Explorer".
Select "Installed" and uninstall the current 2d graphics rendering package, if any.
Select "Browse", look for Win2D.UWP and install the one from Microsoft.
Please note that the latest version of Win2D.UWP updated 5/17/2018 version 1.23.0 requires target platform to be 17134.
For example, "Error List" might show the following error message after a build with version 1.23 of Win2D.UWP and target version set to 10240 in the UWP project properties:
This version of Win2D requires Windows SDK >= 10.0.17134.0, but TargetPlatformVersion is 10.0.10240.0.
Target version can be changed in the UWP project properties
select menu "Project > projectname Properties", or
by selecting the context menu "Properties" from the UWP project in "Solution Explorer".
PS: Add the following after InitializeComponent(); in MainPage.xaml.cs to enable drawing with a selection of input device types:
MyInkCanvas.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Touch;

Unable to convert PDF to any Image format in C# With Imagemagick

this is my first question here so please don't be to harsh on me :) Anyway, let's get right to it:
For an application I need to convert a PDF file to an Image file (Specific format doesn't matter but preferably png or jpg). To get this done, I try to use ImageMagick but when I try to convert anything, it will throw an error.
Now after some research I came to the conclusion I needed to have Ghostscript installed, which I tried to get from the NuGet package manager integrated in Visual Studio 2017. Anyway, when I try to install said package, it throws the following error:
Severity Code Description Project File Line Suppression >State
Error Failed to add reference to 'gsdll32'.
Please make sure that the file is accessible, and that it is a valid >assembly or COM component.
I'm trying to accomplish this using Visual Studio 2017 with C#. The API's I am using are:
+Magick.NET-Q16-AnyCPU V7.11.1
+GhostScriptSharp V1.3.1.4
+Ghostscript V9.2.0 (Throws error)
In case it is required to understand what I am trying, here is the code that I am trying to compile:
using ImageMagick.Configuration;
using ImageMagick.Defines;
using ImageMagick.ImageOptimizers;
using ImageMagick;
using GhostscriptSharp;
using GhostscriptSharp.Settings;
public MagickImageCollection PDFOutput;
public Image Current;
public org.pdfclown.documents.Page CurrentPage;
private void BtnConvert_Click(object sender, EventArgs e)
{
if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
try
{
ImageMagick.MagickNET.Initialize();
MagickReadSettings Settings = new MagickReadSettings();
Settings.Density = new Density(300, 300);
Settings.Format = MagickFormat.Jpg;
using (MagickImageCollection Images = new MagickImageCollection())
{
Images.Add(openFileDialog1.FileName);
int Page = 1;
int i = 0;
foreach(MagickImage Image in Images)
{
Image.Write("FilePage #" + Page);
PDFOutput[i] = Image;
Page++;
i++;
}
MessageBox.Show(PDFOutput.Count.ToString());
}
}
catch(Exception E)
{
MessageBox.Show(E.Message);
}
Am I missing something regarding the GhostScipt install? Does it only work when downloaded directly from the GhostScript website?
I hope that I have provided enough context to my problem and I'll be looking forward to any answers I may get on this.
Many thanks in advance!!
Kind Regards,
Melvin
Yes GhostScript is licensed in such a way, that people don't include it in their wrappers/nugets. You need to make sure you have the dll.
You generally have to download it (gsdll32.dll), add it to the project and output it to your output path (or anything similar, like install it) so that your application can find the gsdll32.dll and load it.
Also note you will need the appropriate bitness aswell

Windows.UI.Notifications is missing

I want to create simple toast notification to action center in windows 10 from this example. But I got problem on Step 2:
using Windows.UI.Notifications;
It`s missing. But I have spent a lot of time to find it and got no result. I really have no idea where I can find or at least download it.
What I tried:
After long search I found Windows.UI.dll in C:\Windows\System32 but when I try to add it as reference into project I got this error. Even after I tried to copy it and made this fully accessible nothing changed
I tried to reinstall .Net (I`m using 4.5.2)
Installed Windows 10 SDK
Tried to import with global
Added
<PropertyGroup>
<TargetPlatformVersion>10.0</TargetPlatformVersion>
</PropertyGroup>
Added System.Runtime.dll reference
Example code which probably is useless for you:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Toolkit.Uwp.Notifications;
using Microsoft.QueryStringDotNET;
using Windows.UI.Notifications;
namespace MessagerClient.Notifications {
class DefaultWindowsNotification {
public static void notificationTest() {
string title = "Andrew sent you a picture";
string content = "Check this out, Happy Canyon in Utah!";
string image = "http://blogs.msdn.com/something.jpg";
string logo = "ms-appdata:///local/Andrew.jpg";
ToastVisual visual = new ToastVisual() {
BindingGeneric = new ToastBindingGeneric() {
Children =
{
new AdaptiveText()
{
Text = title
},
new AdaptiveText()
{
Text = content
},
new AdaptiveImage()
{
Source = image
}
},
AppLogoOverride = new ToastGenericAppLogo() {
Source = logo,
HintCrop = ToastGenericAppLogoCrop.Circle
}
}
};
Console.WriteLine("NOTIFICATION");
//Can`t use because of Windows.UI library
ToastNotificationManager.CreateToastNotifier().Show(visual);
}
}
}
You have to fight Visual Studio pretty hard to use these UWP contracts in a Winforms app. You got off on the wrong foot right away with the wrong TargetPlatformVersion, pretty hard to recover from that. Full steps to take:
Edit the .csproj file with a text editor, Notepad will do. Insert this:
<PropertyGroup>
<TargetPlatformVersion>10.0.10586</TargetPlatformVersion>
</PropertyGroup>
Which assumes you have the 10586 SDK version installed on your machine. Current right now, these versions change quickly. Double-check by looking in the C:\Program Files (x86)\Windows Kits\10\Include with Explorer, you see the installed versions listed in that directory.
Open the Winforms project, use Project > Add Reference > Windows tab > tick the Windows.Data and the Windows.UI contract. Add Reference again and use the Browse tab to select System.Runtime. I picked the one in C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\ .NETFramework\v4.6.1\Facades. This reference displays with a warning icon, not sure what it is trying to say but it doesn't appear to have any side-effects.
Test it by dropping a button on the form, double-click to add the Click event handler. The most basic code:
using Windows.UI.Notifications;
...
private void button1_Click(object sender, EventArgs e) {
var xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01);
var text = xml.GetElementsByTagName("text");
text[0].AppendChild(xml.CreateTextNode("Hello world"));
var toast = new ToastNotification(xml);
ToastNotificationManager.CreateToastNotifier("anythinggoeshere").Show(toast);
}
Embellish by using a different ToastTemplateType to add an image or more lines of text. Do keep in mind that your program can only work on a Win10 machine.
If anyone should happen to stumble on this, see this similar but newer post -
Toast Notifications in Win Forms .NET 4.5
Read Stepan Hakobyan's comment at the bottom.
Essentially, I'm seeing the same thing. This code runs, I can step through it line by line with no exceptions but the toast notification is never shown within a Form app.

How to use LAME (lame_enc.dll) in my C# web site

I am trying to use the lame_enc.dll in my C# .NET website and I am stuck.
I am working with: .NET Framework 3.5 / Visual Web Developer 2008 Express Edition / (Anything else you'd need to know?)
The first thing I did was get the code from C# MP3 Compressor on The Code Project. One thing I noted is that this project/post is from January 2004 (so, it's old)
I put the folders "yeti.mmedia" and "yeti.mp3" in my "App_Code" directory and deleted the "Bin" and "obj" directories within each. Then I tried to build the project. When I got errors I ended up excluding from the project the following files:
yeti.mmedia/AssemblyInfo.cs
yeti.mmedia/EditWaveWriter.cs
yeti.mmedia/EditWaveWriter.resx
yeti.mmedia/InFormatEdit.cs
yeti.mmedia/InFormatEdit.resx
yeti.mmedia/NumericTextBox.cs
yeti.mmedia/NumericTextBox.resx
yeti.mmedia/Win32Functions.cs
yeti.mp3/AssemblyInfo.cs
yeti.mp3/EditMp3Writer.cs
yeti.mp3/EditMp3Writer.resx
These seem to me to be the code files related to the Windows UI (which I don't need sonce I'm doing this on the web).
I also put the file "lame_enc.dll" in the Bin directory.
I created a test page based on the example on the page linked above:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using WaveLib;
using Yeti.MMedia;
using Yeti.MMedia.Mp3;
public partial class Documents : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
WaveStream InStr = new WaveStream(Server.MapPath(#"Temp/SomeFile.wav"));
try {
Mp3Writer writer = new Mp3Writer(new FileStream(Server.MapPath(#"Temp/SomeFile.mp3"), FileMode.Create), InStr.Format);
try {
byte[] buff = new byte[writer.OptimalBufferSize];
int read = 0;
while ((read = InStr.Read(buff, 0, buff.Length)) > 0) {
writer.Write(buff, 0, read);
}
}
finally {
writer.Close();
}
}
finally {
InStr.Close();
}
}
}
So, then I load this page and the error I get is:
Unable to load DLL 'Lame_enc.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
(I can't add the DLL as a reference in my project because it says "...This is not a COM component.") I have also tried getting the latest and greatest (lame3.98.4) dll and had the same problems. So, I assume there is something different about using this code in a website rather than another type of project. What it is I don't know though.
My guess, having not used LAME, is you have to install in on the box in question. After that, you should be able to use the code project code successfully. If that does not work, it appears Lame_Enc.dll is a native component and you will have to PInvoke methods.

Categories