This post has been imported from the old blog and has not yet been converted to the new syntax yet.
As a follow up to my C# Script program, I've added an article on how to associate your program with every file by adding a registry key.

I also made a small article about including resources in your .exe as an answer to someone's question about Non-Rectangular Forms.

The Notepad bug issue still spooks around in my mind. So very strange...
 
This post has been imported from the old blog and has not yet been converted to the new syntax yet.
As a I reply on my Non-Rectangular Form article, someone asked if you couldn't include the bitmap in the file.

Well, you can. And that is what this little article will talk about. This very little article.

When do you want to include things? If they are small and are vital for your app to run. This bitmap is vital! You could also include icons for example...

How do you do it?

Include your bitmap in your project. Go to it's properties and for 'Build Action' choose: Embedded Resource. This will compile your file into your .exe as a resource.

This was what we had to load our bitmap:

[csharp]
FileStream imageStream = File.OpenRead("Glass.bmp");
Bitmap imageBackground = (Bitmap)Bitmap.FromStream(imageStream);
[/csharp]

This is now:

[csharp]
Bitmap imageBackground = new Bitmap(this.GetType(), "Glass.bmp");
[/csharp]

That's it. Wasn't that simple? Your Bitmap is inside the .exe and it's loading fine.

I've uploaded this version as well.

Note: You can drag the .exe around and press 'q' to close it (for the people who haven't read the Non-Rectangular Form article ;) imagine having to kill it)
 
This post has been imported from the old blog and has not yet been converted to the new syntax yet.
In my last article (C# Script) I spoke about adding it to the shell right click menu. Now it's time to implement that.

We'll create a class (ShellExtension) that allows us to associate our program with all files. So, we'll have one public method for that, which I'll call AssociateProgram.

But first, some info about this.

Where are those entries stored? They all reside in the Registry, global ones live in HKEY_LOCAL_MACHINE and per-user ones in HKEY_CURRENT_USER. Our class will only use it per user, as we don't want to spam the system.

Here is a sample registry file that will add a "test" entry which will simply open notepad:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Classes\*\shell\test\command]
@="notepad.exe"


Run it, and check it out. Not that useful, but it shows you the inner workings.

Let's get started. We need Microsoft.Win32 to access the Registry.

[csharp]
using System;
using Microsoft.Win32;

namespace ShellExtension {
public class ShellExtension {
public ShellExtension() { }

public bool AssociateProgram(string extensionName, string filePath) {

} /* AssociateProgram */
} /* ShellExtension */
} /* ShellExtension */
[/csharp]

We'll add two 'Helper' methods to read and create RegistryKeys. Someday we could do something when creating a key, like log it somewhere.

The true implies to open it for reading. Otherwise you'd get an UnAuthorizedException when creating our shell extension.

[csharp]
private RegistryKey GetKey(RegistryKey rootKey, string keyName) {
return rootKey.OpenSubKey(keyName, true);
} /* GetKey */

private bool CreateKey(RegistryKey rootKey, string keyName) {
return (!(rootKey.CreateSubKey(keyName) == null));
} /* CreateKey */
[/csharp]


Now, we have to create that key. Because almost nobody has '*' in his CURRENT_USER.

This is the 'biggest' method, after we've made sure our key is there we only have to put our shell extension in there.

[csharp]
private RegistryKey SetupKeys() {
RegistryKey returnKey = null;

RegistryKey currentUser = Registry.CurrentUser;
RegistryKey softwareKey = this.GetKey(currentUser, "Software");
if (softwareKey != null) {
RegistryKey softwareClasses = this.GetKey(softwareKey, "Classes");
if (softwareClasses != null) {
RegistryKey allFiles = this.GetKey(softwareClasses, "*");
if (allFiles != null) {
RegistryKey shellExtension = this.GetKey(allFiles, "shell");
if (shellExtension != null) {
returnKey = shellExtension;
} else {
if (this.CreateKey(allFiles, "shell")) { returnKey = this.GetKey(allFiles, "shell"); }
}
} else {
if (this.CreateKey(softwareClasses, "*")) {
allFiles = this.GetKey(softwareClasses, "*");
RegistryKey shellExtension = this.GetKey(allFiles, "shell");
if (shellExtension != null) {
returnKey = shellExtension;
} else {
if (this.CreateKey(allFiles, "shell")) { returnKey = this.GetKey(allFiles, "shell"); }
}
}
} // HKEY_CURRENT_USER\Software\Classes\*
} // HKEY_CURRENT_USER\Software\Classes
} // HKEY_CURRENT_USER\Software
return returnKey;
} /* SetupKeys */
[/csharp]


The only thing left now is to add our extension caption along with the 'command' key and the filepath.

We use "filepath" %1 to pass the filename along.

[csharp]
RegistryKey shellKey = this.SetupKeys();
if (shellKey != null) {
if (this.CreateKey(shellKey, extensionName + @"\command")) {
RegistryKey extPath = this.GetKey(shellKey, extensionName + @"\command");
extPath.SetValue("", "\"" + filePath + "\" %1");
}
}
[/csharp]

That's it. Compile your class, reference the .dll and call it like this:

[csharp]
ShellExtension.ShellExtension shellAdd = new ShellExtension.ShellExtension();
shellAdd.AssociateProgram("Notepad", "notepad.exe");
[/csharp]

I've uploaded the project again, the ShellExtensionDriver shows you how to associate Notepad with every file (which is very usefull!).
 
This post has been imported from the old blog and has not yet been converted to the new syntax yet.
I was playing around with the thought of being able to figure out some other possibilities for C#.

One thing I came up with was, why not make a possibility to quickly write something small in C# and run it. I'm talking really really really small here.

So I decided to write an article about it, maybe someone thinks of something and extends it. Before you tell me it's useless, let me say this is only something done to try out new things and to get ideas.

Here is my version of C#-Script ;)


One frustrating thing that keeps bothering me is. In code i specifically said to not create an executable, and what happens? It creates an executable. Anyone an idea of why it's doing this? Doesn't 'InMemory' mean in RAM, without files on your HD?

Hope you like it :)
 
This post has been imported from the old blog and has not yet been converted to the new syntax yet.
There is VBScript, JScript and PHP. But there isn't anything like C#-Script. So, I decided to play around a bit, only as a matter of checking out the possibilities.

The plan:

Create a CS-Script.exe file that we can use to 'launch' a .cs file. Without having to add the file to a solution or anything.

A very basic tool, that can launch one .cs file. View it as some sort of batch scripting tool. Where you can quickly open notepad, write something and run it.

Our goal is to reach it through the right click menu in the shell. For every file! So we'll create a console app (CS-Script) that accepts one argument, the filename.

We want to let the user know when he fucks up and doesn't provide an argument, so we add a reference to System.Windows.Forms and display a MessageBox.

[csharp]
using System;
using System.Windows.Forms;

namespace CS_Script {
class CS_Script {
static void Main(string[] args) {
if (args.Length > 0) {

} else {
MessageBox.Show("You need to provide the filename as an argument!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} /* Main */
} /* CS_Script */
} /* CS_Script */
[/csharp]

Next, we want to check if the file exists. Otherwise, just hit them with another MessageBox.

[csharp]
if (File.Exists(args[0])) {

} else {
MessageBox.Show("You need to provide an existing file as an argument!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
[/csharp]

Now we read the file, just use an example from MSDN.

[csharp]
try {
using (StreamReader textReader = new StreamReader(args[0])) {
String textFile = textReader.ReadToEnd();
Console.WriteLine(textFile);
}
} catch (Exception e) {
MessageBox.Show("The file could not be read:\n" + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
[/csharp]

After we have read in the source we're going to compile it and run it. This is the hardest part :) There are some samples out there, but they all seemed to fail on my test data.

[csharp]
try {
ExecuteSource(textFile);
} catch (Exception e) {
MessageBox.Show("The file could not be executed:\n" + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
[/csharp]

The first thing we have to do is setup the compiler. Here I've put some restrictions. Every file should have a class CScript with a public void Main in it. If you want to extend this, be sure to get rid of that ;)

[csharp]
CSharpCodeProvider codeProvider = new CSharpCodeProvider();

ICodeCompiler compiler = codeProvider.CreateCompiler();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
parameters.OutputAssembly = "CS-Script-Tmp-Junk";
parameters.MainClass = "CScript.Main";
parameters.IncludeDebugInformation = false;
[/csharp]

Another important parameter is the assemblies we involve in compiling our source. A "good" thing would be to put the most important namespaces in your project with using. We're going to include all the ones our project uses.

[csharp]
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) {
parameters.ReferencedAssemblies.Add(asm.Location);
}
[/csharp]

And now it's time to compile our source, let's hope everything goes fine.

[csharp]
CompilerResults results = compiler.CompileAssemblyFromSource(parameters, sourceText);
[/csharp]

Of course not everything went fine. Someone messed up his source, let's give him another error.

[csharp]
if (results.Errors.Count > 0) {
string errors = "Compilation failed:\n";
foreach (CompilerError err in results.Errors) {
errors += err.ToString() + "\n";
}
MessageBox.Show(errors, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
[/csharp]

But some people don't make mistakes, and we run their code.

The compiler gave back an Assembly to use. It's very simply to invoke this one.

One strange issue, even though we have set GenerateInMemory, it does create a file on our harddisk (at least here), so we have to clean that up.

[csharp]
object o = results.CompiledAssembly.CreateInstance("CScript");
Type type = o.GetType();
MethodInfo m = type.GetMethod("Main");
m.Invoke(o, null);
if (File.Exists("CS-Script-Tmp-Junk")) { File.Delete("CS-Script-Tmp-Junk"); }
[/csharp]

If everything was fine, we now launched our program!

Here's a the little program I tested on:

[csharp]
using System;
using System.Windows.Forms;

class CScript {
public void Main() {
MessageBox.Show("I'm being loaded dynamicly!");
}
}
[/csharp]

We reached our goal of running code from a text file for now.

In my next article I'll talk about adding our CS-Script.exe file to the shell menu so we can use it on our files.

As always, I've uploaded the sources for this project.

Update: Here is the article about adding your menu to the shell: Associating your program with every file - ShellExtension