Click here to Skip to main content
15,881,859 members
Articles / Programming Languages / C#
Tip/Trick

Converting Files to C# Source Code

Rate me:
Please Sign up or sign in to vote.
4.19/5 (9 votes)
15 Mar 2017Public Domain4 min read 27.3K   10   12
A trick you can do with enhanced C#

Introduction

One way you can include a file in a C# project is to embed it in a resx file. However, sometimes, you might prefer to embed the file in the source code itself, as either a string or a byte array.

How?

To do this, download the LeMP preprocessor. From here, you can either install it in Visual Studio or run it as a command-line tool.

The quickest approach is to run LeMP.exe --editor, which shows a demo window where you can play with LeMP. In that case, all you have to do is insert code like...

C#
byte[] file = includeFileBinary(@"C:\Path\To\BinaryFile.bin");
string code = includeFileText(@"C:\Path\To\Readme.txt"); 

...on the left side of the demo window. The output appears on the right.

includeFileBinary creates a new byte[] array, while includeFileText creates a string.

Adding LeMP to a C# Project

Image 1

  1. You can also install LeMP into Visual Studio so that it runs automatically whenever you save an ecs (Enhanced C#) file.

    Note: Visual Studio 2017 no longer supports the LeMP installer because it no longer uses the system registry. However, you can run LeMP as a pre-build event instead; see the end of step 5. Arguably, a pre-build event is actually better for the task of converting files to source code because the source code will be updated whenever you build your project. In contrast, if you use LeMP as a single-file generator, the output code is only updated when you save the ecs file in Visual Studio; it won't automatically notice when the included file changes.

  2. Create a C# file, let's call it Files.cs, that specifies the file(s) you want to include:
    C#
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace LeMPIncludeFileExample
    {
        class Files
        {
            public static readonly byte[] SmileGif = includeFileBinary(@"icon_smile.gif");
            public static readonly byte[] SadGif   = includeFileBinary(@"icon_sad.gif");
            public static readonly string Program  = includeFileText(@"Program.cs");
        }
    }

    The files need to exist in the same folder as the source code (or you can use a relative path).

  3. Rename the file to Files.ecs (Enhanced C#) to indicate it is not a standard C# file.
  4. Right-click Files.ecs in Solution Explorer and choose "Properties" to make sure the properties panel is visible.
  5. One of the properties is "Custom Tool". Set this property to "LeMP" (without the quotes).

    If LeMP is installed, then a new source file will appear in your project, Files.out.cs.

    Check the Error panel to see if any errors occurred. If not, you should see contents of your files in Files.out.cs:

    C#
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace LeMPIncludeFileExample
    {
        class Files
        {
            static readonly byte[] smileGif = new byte[] {
            0x47,0x49,0x46,0x38,0x39,0x61,0xF,0x0,0xF,0x0,0xF3,0x0,0x0,0xFF,0xEA,
            0x0,0x45,0x45,0x45,0x0,0x0,0x0,0xFF,0xCE,0x0,0xFF,0xC9,0x0,0xFF,0xB4,0x0,
            0xFE,0x9D,0x0,0xFF,0xFE,0x93,0xFF,0xFD,0x13,0xFF,0xFF,0xFF,0xFF,0xFF,0xC7,0x33,
            0x33,0x33,0xFF,0xFF,0xEB,0xFF,0xE5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0xF9,
            0x4,0x1,0x0,0x0,0xE,0x0,0x2C,0x0,0x0,0x0,0x0,0xF,0x0,0xF,0x0,0x0,
            0x4,0x5B,0xD0,0x49,0x19,0x6A,0x9D,0xD8,0x55,0xA6,0xCE,0x19,0x17,0x16,0x70,0x8,
            0x2,0x9C,0x83,0x11,0x4C,0x41,0x27,0x8,0x27,0xF0,0xA6,0xAB,0x76,0x98,0x71,0xE,
            0x6A,0xA,0xE,0x9F,0x3F,0x82,0x2A,0x70,0x3,0xC6,0x82,0x85,0xCA,0x21,0xC7,0x4,
            0x10,0x92,0x1,0xD3,0xA2,0x9,0x58,0x3C,0x2B,0x3,0xD9,0x6B,0xBB,0x15,0xAE,0x2,
            0x59,0x41,0x62,0x3C,0x16,0x10,0x76,0x9A,0xC1,0xA0,0xC1,0x35,0xD3,0x58,0x6A,0x82,
            0x5C,0xFE,0x16,0x5,0xA,0xF8,0xC2,0x30,0xC3,0xB2,0xD4,0x26,0x11,0x0,0x3B
            };
            static readonly byte[] sadGif = new byte[] {
            0x47,0x49,0x46,0x38,0x39,0x61,0xF,0x0,0xF,0x0,0xF3,0x0,0x0,0xFF,0xEA,
            0x0,0x45,0x45,0x45,0x0,0x0,0x0,0xFF,0xCE,0x0,0xFF,0xC9,0x0,0xFE,0x9D,0x0,
            0xFF,0xB4,0x0,0xFF,0xFE,0x93,0xFF,0xFD,0x13,0xFF,0xFF,0xC7,0xFF,0xE5,0x0,0xFF,
            0xFF,0xEB,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x21,0xF9,
            0x4,0x1,0x0,0x0,0xC,0x0,0x2C,0x0,0x0,0x0,0x0,0xF,0x0,0xF,0x0,0x0,
            0x4,0x58,0x90,0x49,0x19,0x6A,0x9D,0x98,0xD5,0x95,0xCE,0x19,0x17,0x16,0x70,0x8,
            0x2,0x9C,0x43,0x11,0x4C,0x41,0x27,0x9C,0xB0,0x90,0xAE,0xDA,0xF1,0xC2,0x31,0xA8,
            0x25,0x26,0x70,0xFB,0x27,0x82,0x2A,0x70,0xE8,0xFD,0x6E,0x4,0x43,0xE5,0x80,0x6B,
            0x2,0x92,0x95,0x9E,0x33,0xA8,0xC,0xC,0xA6,0x27,0x85,0x70,0x65,0x3D,0x9,0xBE,
            0xDF,0xA7,0x4E,0x33,0x18,0xFC,0x0,0xA,0x99,0x8A,0x55,0x26,0xB8,0xDD,0xB3,0x4C,
            0xC5,0x40,0x37,0xC,0x33,0x2C,0xB,0x6D,0x12,0x1,0x0,0x3B
            };
            static readonly string program = @"using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace LeMPIncludeFileExample
    {
        class Program
        {
            static void Main(string[] args)
            {
            }
        }
    }
    ";
        }
    }

Visual Studio will rebuild the file every time.

Note: You can also run LeMP on the command line or as a pre-build event. To take the latter approach, go to the project properties and, in the "Build Events" section, add a command like this to the "Pre-build event command line":

C:\LOCATION-OF-LEMP\LeMP.exe $(ProjectDir)Files.ecs --outext:out.cs

This should produce a Files.out.cs file that you can include in your project manually (the Show all files icon in Solution Explorer will allow you to see the file. Right-click and "Include in project".)

If you use this method, then "Custom Tool" should not be set to LeMP in the Properties window. It should be blank.

How Does It Work?

LeMP is a tool that lets you generate code by calling special methods called "macros" in your C# code. For example, the includeFileText method that was used in the example above is bundled with LeMP. Here is its source code:

C#
[LexicalMacro(@"includeFileText(""Filename"")",
    "Reads a UTF-8 text file into a string literal.
    Newlines become '\n'."
   +"The single argument is macro-preprocessed.")]
public static LNode includeFileText(LNode node, IMacroContext context)
{
    string filename;
    if (node.ArgCount == 1 &&
    (filename = context.PreProcess(node[0]).Value as string) != null) {
        var inputFolder = context.ScopedProperties.TryGetValue
                          ((Symbol)"#inputFolder", "").ToString();
        var path = System.IO.Path.Combine(inputFolder, filename);
        var text = File.ReadAllText(path, Encoding.UTF8);
        return F.Literal(text).SetBaseStyle(NodeStyle.TDQStringLiteral);
    }
    return null;
}

// elsewhere
static LNodeFactory F =
new LNodeFactory(new EmptySourceFile("StandardMacros.cs"))

The key part of this method is at the end, where it calls the standard File.ReadAllText method to load a file, then calls the factory method F.Literal(text) to convert it into a source code literal (for example, F.Literal("Hello") essentially converts "Hello" into a syntax tree, which will later be printed as C# code.)

To find out what else you can do with LeMP, check out the home page. If you'd like to write a macro of your own, please leave a message here and I will be delighted to help you! Basically, the steps are:

  1. Create a class library called MyMacros.dll to hold your macro
  2. Add the Loyc.Syntax NuGet package to it
  3. Write a public static method like the one above with a [LexicalMacro("","")] attribute on it (the two strings are documentary and have no effect)
  4. Put a [ContainsMacros] attribute on the containing class, which must be public
  5. Use LNodeFactory to generate some code
  6. Use the command-line argument --macros:MyMacros.dll to load the macros into LeMP
  7. Put #importMacros(MyMacros); at the top of the input file (where MyMacros is the namespace that contains your macros) to gain access to your macro (it's like a using directive for macros), and
  8. Call your macro! If you'd like to do something complicated, you might want to read this article in addition to contacting me.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Software Developer None
Canada Canada
Since I started programming when I was 11, I wrote the SNES emulator "SNEqr", the FastNav mapping component, the Enhanced C# programming language (in progress), the parser generator LLLPG, and LES, a syntax to help you start building programming languages, DSLs or build systems.

My overall focus is on the Language of your choice (Loyc) initiative, which is about investigating ways to improve interoperability between programming languages and putting more power in the hands of developers. I'm also seeking employment.

Comments and Discussions

 
Questionthats why Pin
BManfred17-Mar-17 1:43
BManfred17-Mar-17 1:43 
Questionwhy ? Pin
BillWoodruff16-Mar-17 21:53
professionalBillWoodruff16-Mar-17 21:53 
AnswerRe: why ? Pin
Pawel Sienko16-Mar-17 23:53
Pawel Sienko16-Mar-17 23:53 
GeneralRe: why ? Pin
Member 1297581217-Mar-17 5:00
Member 1297581217-Mar-17 5:00 
GeneralRe: why ? Pin
Pawel Sienko20-Mar-17 0:43
Pawel Sienko20-Mar-17 0:43 
QuestionEmbedded resources Pin
Frantisek Ruzicka16-Mar-17 11:25
professionalFrantisek Ruzicka16-Mar-17 11:25 
AnswerRe: Embedded resources Pin
sx200824-Mar-17 12:42
sx200824-Mar-17 12:42 
Embedded resources can be modified with a resource editor.
Modify the static data inside a assembly or executeable is much harder for a casual hacker.

GeneralRe: Embedded resources Pin
Frantisek Ruzicka25-Mar-17 14:36
professionalFrantisek Ruzicka25-Mar-17 14:36 
QuestionWhy Pin
johannesnestler16-Mar-17 4:12
johannesnestler16-Mar-17 4:12 
AnswerRe: Why Pin
Qwertie17-Mar-17 17:26
Qwertie17-Mar-17 17:26 
QuestionRe: Why Pin
sx200824-Mar-17 13:28
sx200824-Mar-17 13:28 
AnswerRe: Why Pin
Qwertie25-Mar-17 15:03
Qwertie25-Mar-17 15:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.