I have a Compact Framework 3.5 application that is responsible for scanning barcodes. There are 3 different sounds it should play depending on the situation, so I created a wrapper class around the SoundPlayer object and call the Play method on that.
public static class SoundEffectsPlayer
{
private static readonly SoundEffect _alert;
static SoundEffectsPlayer()
{
// This will cause the SoundEffect class to throw an error that the Uri
// Format is not supported, when new SoundPlayer(location) is called.
_alert = new SoundEffect("SoundEffects/alert.wav");
}
public static SoundEffect Alert
{
get { return _alert; }
}
}
public class SoundEffect
{
private readonly SoundPlayer _sound;
public SoundEffect(string location)
{
_sound = new SoundPlayer(location);
_sound.Load();
}
public bool IsLoaded
{
get { return _sound.IsLoadCompleted; }
}
public void Play()
{
_sound.Play();
}
}
The idea was to not create a SoundPlayer each time a barcode needed to be scanned (which would be a few hundred times an hour). So I can just call Play on the already loaded file.
The alert.wav file is in a SoundEffects folder that is on the root of the library project that the application references, and it is set to be an Embedded Resource. What would I need to pass in to the SoundEffects class to load the wav file? Should the wav file even be embedded into the library project?
Also does anyone notice anything wrong with the way I'm handling the playing of the wav files? This is my first attempt at something like this, so I'm open to suggestions for improvement.
Hmmm. I haven't used SoundPlayer before, i'm guessing it is just a wrapper around the coredll function PlaySound. But anyhow, I would expect that it demands a file path as the argument.
Therefore to make it work with an embedded resource you would probably have to save the file to disk and then play it. It would probably be simpler to scrap the embedded resource idea and deploy it as a separate item. Include the .wav in your main project set it to: "Content" and "Copy if newer" and then reference it as a file in the sound player call.
Remember that in WindowsCE you always need FULL paths as well. A relative path from the currently running .exe wont work in CE.
If you need to find out "where you are" to create the path to the resource, see the answer to this question.
Related
this question follows my previous question.
I have a c# ASP.NET application and i want to provide support for plugins. Plugins can be custom c# classes, javascript, html, css, images, etc.
I see no problem as long as my application is extended with c# classes because all the user has to do is create a new "class library" project in visual studio and implement the interfaces, i provide. Then build a dll out of it and upload it to my server. The plugin-developer can add static files (html, js, css, etc.) into this project as well but i found some problems with that:
Every static file i add to the plugin project gets the build action "content" and it seems i cannot read those files from my server. (see my previously answered question). I have to manually select "Embedded Resource" on each file, so it is packed with the plugin dll.
I want to support Typescript for the plugins. The Typescript compiler generates javascript files in the same directory as the typescript-files. But the javascript files are not included in the project and therefore i have to include these in the plugin project and then set the correct build action. I don't want the plugin developers to do that all the time.
If the static files have the build action "enbedded resources", then the server can pickup these files by using the assembly.GetManifestResourceNames() method. This method returns the resources as a string. The path is not separated by \ or / but with a dot instead. So i am not able to distinguish between file path (this is relevant) or filename (also relevant to pickup the correct files), because the original filename can also have dots.
So i am starting to question the "class library" project type is right for my needs. Is there a way to get around of my issues or do i have to use another project type?
Thank you for any help!
Edit: Changed the question a little bit so it is better to understand.
You could make a zip package with the plugin dll and files. NuGet also uses this mechanism. A .nupkg is also just a zip file.
I would start by looking at MEF (Managed Extensibility Framework).
MSDN information can be found here: https://msdn.microsoft.com/en-us/library/dd460648(v=vs.110).aspx
From that link you can get more information and I believe there is a tutorial as well.
Oh, for me it seems very simple.
Let the developer create the plugin freestyle and put all the additional files in a directory, let's call it extras
To implement the your interface they will need your assembly so I guess you will ship it via nuget, or just some link. No matter what the case, provide them with some powershell script what will be required to run before the final build
The script would create zip archive from the extras directory and add it to the ClassLibrary project as EmbeddedResource.
As you mentioned earlier, you can access EmbeddedResource. So all you would do is to unpack it and you would have the exact directory tree.
The best idea would be to provide project template with script included, and also the empty zip archive added as embedded resource (it will be easier to just pack the files in the script and replace the file), and pre-build action set to run the script.
Am I missing something?
What about this.
In your web application, you could add a function that loop into your plugin directory and find DLL implementing an Iplugin (name is up to you) interface.
The interface is defined in a class library that both your web application and plugins have to implement.
You can use the Httpcontext Server mappath to read javascript and other files.
Here is a very basic implementation
First, you have the plugin interface (a class library implemented both by the web application and the individual plugins) I implemented sample properties and methods...
using System.Web;
public interface IPlugin
{
string Name { get; set; }
string Output { get; set; }
void Load(ref httpcontext Context);
void Dispose();
void Display();
}
Next, you have the Actual plugin class library we want to implement.
using System.Web;
using IPlugins;
public class AwesomePlugin : IPlugins.IPlugin
{
private string _Name = "AwesomePlugin";
private HttpContext _Context;
public string Name {
get { return _Name; }
set { _Name = value; }
}
public string Output {
get { return "Yay !!!"; }
set {
throw new NotImplementedException();
}
}
public void Display()
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public void Load(ref Web.HttpContext Context)
{
}
}
Finally, you dynamically load your plugins so you can use them in your application.
private Dictionary<string, IPlugins.IPlugin> _Plugins = new Dictionary<string, IPlugins.IPlugin>();
public void LoadPlugins()
{
lock (static_LoadPlugins_IpluginType_Init) {
try {
if (InitStaticVariableHelper(static_LoadPlugins_IpluginType_Init)) {
static_LoadPlugins_IpluginType = typeof(IPlugins.IPlugin);
}
} finally {
static_LoadPlugins_IpluginType_Init.State = 1;
}
}
string ServerPath = HttpContext.Current.Server.MapPath("~") + "Plugins";
dynamic Plugins = io.Directory.GetFiles(ServerPath);
foreach (string PluginPath in Plugins) {
dynamic Assembly = system.Reflection.Assembly.LoadFile(PluginPath);
Type PluginClass = Assembly.GetTypes.Where(T => T.GetInterface("IPlugin") != null).First;
IPlugins.IPlugin MyPlugin = Activator.CreateInstance(PluginClass);
MyPlugin.Load(httpcontext.Current);
_Plugins.#add(PluginClass.ToString, MyPlugin);
}
}
static bool InitStaticVariableHelper(Microsoft.VisualBasic.CompilerServices.StaticLocalInitFlag flag)
{
if (flag.State == 0) {
flag.State = 2;
return true;
} else if (flag.State == 2) {
throw new Microsoft.VisualBasic.CompilerServices.IncompleteInitialization();
} else {
return false;
}
}
That way, you can implement whatever you want in your plugin.
I believe you could load your plugins in a separate appdomain with restricted permissions to everything.
The files (Javascript / CSS / Html) should be available by accessing the full path of the file.
string ServerPath = HttpContext.Current.Server.MapPath("~") + "Plugins";
If the resources is embedded into the plugin DLL, you could read the stream from the loaded assembly or let the plugin manage its own embedded files.
For question Number 2, you can use
MS Build
to change the contenttype during build process.
You have to make yourself confident with MS Build
I have found myself having to rely on having to use some local folders. No problem normally, but the main issue is that if transfer the project to another machine, even during development the structure reports back an error.
The error is that is page can't be displayed on a local website and an IO error when I go to read from the file.
The directory is as follows
C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset
The folders in question are
Master Audio
Images
User Audio
The actual folder as they're used
private String folderPlay = #"C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset\Master Audio\";
private String folderRecord = #"C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset\User Audio\";
private String folderImages = #"C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset\Images\";
What I would like to do is be able to call those file relative to the code? Also if it's not too much trouble could a small note be attached so I can understand/
Use only the path in your project rather than whole physical path
private String folderPlay = #"~/Master Audio";
private String folderRecord = #"~/User Audio";
private String folderImages = #"~/Images";
and my suggestion is use path like Master_Audio, User_Audio. Don't put space between word
What I found useful in similar situation is to actually inject content root dir to the class that needs to know the location of particular resources:
E.g.
class Foo{
private string readonly _contentRoot;
public Foo (string contentRoot)
{
_contentRoot = contentRoot
}
public void DoSomethingWithImage()
{
string imgRelativePath = "/img/img1.png";
string imgPath = Path.Combine(_contentRoot, imgRelativePath);
// do something with imgPath
}
}
The benefit of this is that you can now write tests for your methods (which might be difficult otherwise). Sometimes unit testing framework copies libs to a temp location, but you still want to run the tests.
You can resolve root directory from several locations, depending how you run the code:
For unit tests, use Assembly.GetExecutingAssembly().GetName().CodeBase
For Web app HttpRuntime or HttpContext
For Windows app AppDomain.BaseDirectory or similar
Rule of thumb:
You should not have any hardcoded absolute paths (pain with testing, running on another machine, deployment)
Instead, use relative paths (usually relative to project root or root/content)
I'm new to C# and I have been trying to play a sound using SoundPlayer class. So in my solution in visual studio (2015 community), I created a folder called Music and drag'n'dropped a wav file there. In properties, I found the file path and then used in the SoundPlayer constructor. Right now, it's on the desktop.
My problem is that I'll be moving the actual program (it's just console app) to another computer (with different user name...which I don't know). So is there a way C# can determine the new location (directory) for the file so that I don't get an error?
SoundPlayer spPlayer = new SoundPlayer (#"C:\Users\tvavr\Desktop\Dango\mr_sandman.wav");
spPlayer.Play();
This is the code that works. Now, how am I supposed to change that path?
Thx for your answers.
use dynamic path as following :
SoundPlayer spPlayer = new SoundPlayer (Application.ExecutablePath +#"\mr_sandman.wav");
where Application.ExecutablePath will get your application folder dynamically
This is a design decision that only you can answer. When you write console applications that require you to load a file, you have several options
Option #1: Have the path specified in the argument list of the program when it is executed
Assume the name of your program is playsong, you run it like this:
playsong C:/path-to-music-file/song.wav
You get the name of the song file from the argument list of Main. The first item in args is the filename:
static void Main(string[] args) {
SoundPlayer spPlayer = new SoundPlayer(args[1]);
spPlayer.Play();
}
Option #2: Access the song file by hard coded path
static void Main() {
SoundPlayer spPlayer = new SoundPlayer("C:/playsong/songs/song.wav");
spPlayer.Play();
}
Option #3: Access the song file relative to the program location
static void Main() {
SoundPlayer spPlayer = new SoundPlayer(Application.ExecutablePath + "/songs/song.wav");
spPlayer.Play();
}
If you later want to change this into a Graphical User Interface (GUI) program, you would bring up the OpenFileDialog box which lets the user choose the file.
.NET has built-in tools to access locations like current user's desktop:
System.Environment.SpecialFolder.Desktop
I'm working on a plugin for a existing C# .NET Program. It's structured in a manner where you put your custom .dll file in Program Root/Plugins/your plugin name/your plugin name.dll
This is all working well, but now I'm trying to use NAudio in my project.
I've downloaded NAudio via Nuget, and that part works fine, but the problem is that it looks for the NAudio.dll in Program Root, and not in the folder of my plugin.
This makes it hard to distribute my plugin, because it would rely on users dropping the NAudio.dll in their Program Root in addition to putting the plugin into the "Plugins" folder.
Source:
SettingsView.xaml:
<Button HorizontalAlignment="Center"
Margin="0 5"
Width="120"
Command="{Binding SoundTestCommand,
Source={StaticResource SettingsViewModel}}"
Content="Sound Test" />
SettingsViewModel.cs:
using NAudio.Wave;
.
.
.
public void SoundTest()
{
IWavePlayer waveOutDevice;
WaveStream mainOutputStream;
WaveChannel32 inputStream;
waveOutDevice = new WaveOut();
mainOutputStream = new Mp3FileReader(#"E:\1.mp3");
inputStream = new WaveChannel32(mainOutputStream);
inputStream.Volume = 0.2F;
waveOutDevice.Init(mainOutputStream);
waveOutDevice.Play();
}
How can I get C# to look for NAudio in Program Root/Plugins/my plugin name/NAudio.dll instead of looking for it in Program Root/NAudio.dll ?
I'm using VS Express 2013, Target Framework is 4.5 and Output type is Class Library.
Edit:
I found 2 ways to make this work ( I'm not sure what the pros and cons of both methods are - if anyone knows I would appreciate additional information ).
Using the NuGet Package Costura.Fody.
After installing the NuGet package, I simply had to set all other References "Copy Local" to "False" and then set "Copy Local" for NAudio to "True".
Now when I build, the NAudio.dll is compressed and added to my own DLL.
Using the AssemblyResolver outlined below.
It didn't work right away though, so here is some additional information that may help anyone facing the same issue:
I put Corey's code as he posted it into the Helpers folder.
My entry point is Plugin.cs, the class is public class Plugin : IPlugin, INotifyPropertyChanged
In there, the entry method is public void Initialize(IPluginHost pluginHost), but simply putting PluginResolver.Init(path) did not work.
The host program uses WPF and is threaded and I had to use a dispatcher helper function of the host program to get it to work: DispatcherHelper.Invoke(() => Resolver.Init(path));
As mentioned, I'm currently unsure which method to use, but I'm glad I got it to work. Thanks Corey!
You can use the PATH environment variable to add additional folders to the search path. This works for native DLLs, but I haven't tried to use it for .NET assemblies.
Another option is to add a hook to the AssemblyResolve event on the current application domain and use a custom resolver to load the appropriate assembly from wherever you find it. This can be done at the assembly level - I use it in NAudio.Lame to load an assembly from a resource.
Something along these lines:
public static class PluginResolver
{
private static bool hooked = false;
public static string PluginBasePath { get; private set; }
public static void Init(string BasePath)
{
PluginBasePath = BasePath;
if (!hooked)
{
AppDomain.CurrentDomain.AssemblyResolve += ResolvePluginAssembly;
hooked = true;
}
}
static Assembly ResolvePluginAssembly(object sender, ResolveEventArgs args)
{
var asmName = new AssemblyName(args.Name).Name + ".dll";
var assemblyFiles = Directory.EnumerateFiles(PluginBasePath, "*.dll", SearchOption.AllDirectories);
var asmFile = assemblyFiles.FirstOrDefault(fn => string.Compare(Path.GetFileName(fn), asmName, true) == 0);
if (string.IsNullOrEmpty(asmFile))
return null;
return Assembly.LoadFile(asmFile);
}
}
(Usings for the above: System.IO, System.Reflection, System.Linq)
Call Init with the base path to your plugins folder. When you try to reference an assembly that isn't loaded yet it will search for the first file that matches the base name of the assembly with dll appended. For instance, the NAudio assembly will match the first file named NAudio.dll. It will then load and return the assembly.
No checking is done in the above code on the version, etc. and no preference is given to the current plugin's folder.
I'm writing a Windows service application in C# with FileSystemWatcher.
How can I add status icons to files and folders in Windows Explorer similar to how Dropbox or SVN do it?
You should develop an overlay icon handler and register it into the system.
Here you can find a partially working example written in C#.
Some MSDN documentation here and here.
I've never play with this, but I think it's the right way.
Custom Folder
First make the folder a System Folder, then create a Desktop.ini file and apply the change inside.
[.ShellClassInfo]
InfoTip=#Shell32.dll,-12690
IconFile=%SystemRoot%\system32\SHELL32.dll
IconIndex=-238
For anyone still interessted in this question:
Here is a codeproject link which describes the process in great detail.
It uses the SharpShell library, which can also be found on nuget.
Code, from the codeproject, looks something like that:
[ComVisible(true)]
public class ReadOnlyFileIconOverlayHandler : SharpIconOverlayHandler
{
protected override int GetPriority()
{
// The read only icon overlay is very low priority.
return 90;
}
protected override bool CanShowOverlay(string path, FILE_ATTRIBUTE attributes)
{
try
{
// Get the file attributes.
var fileAttributes = new FileInfo(path);
// Return true if the file is read only, meaning we'll show the overlay.
return fileAttributes.IsReadOnly;
}
catch (Exception) { return false; }
}
protected override System.Drawing.Icon GetOverlayIcon()
{
// Return the read only icon.
return Properties.Resources.ReadOnly;
}
}