Solved

Dynamic compiled Application Name.

Posted on 2009-06-28
27
446 Views
Last Modified: 2012-05-07
Hi,

Usually the compiled application name is set on project.

Is it possible to do it using code and using variable to change it?

Thank you.
0
Comment
Question by:emi_sastra
  • 16
  • 11
27 Comments
 
LVL 39

Expert Comment

by:abel
ID: 24731168
It is possible but only if you create a dynamic assembly with dynamic classes and dynamic methods. That's a lot of work, requires in-depth understanding of CLR and is hardly ever necessary.

The compiled application name is something that's returned from header information in your DLL or EXE. No code is run to get that information. As a result, it is not possible to make that dynamic. If you change it dynamically, you need to save the changes (or recompile) and reload it to see the changes.

There are major differences between compile time and runtime. This is a strictly compile time feature (compare it with the name of a file) and cannot be changed during runtime.

-- Abel --
0
 
LVL 1

Author Comment

by:emi_sastra
ID: 24731223
Hi Abel,

If you change it dynamically, you need to save the changes (or recompile) and reload it to see the changes.
Yes, it is what I need to.

Let me describe what I am facing.
I create an application which is used for several companies at the same server.
Each of the company use different database.
Thus I need to compiled the application based on the short name  of the company for example:

1. GL_ABC.exe
2. GL_IMM.exe
3. etc

How to solve this? Any other better way is welcome.

Thank you.

0
 
LVL 39

Expert Comment

by:abel
ID: 24731243
You can start your application as many times as you want, you don't need different names for that. You could put the company name on the commandline so that your application knows for what company it is working, assuming the applications are all equal:

GL_GENERIC.exe /company:ABC

GL = General Ledger?

-- Abel --
0
 
LVL 1

Author Comment

by:emi_sastra
ID: 24731346
Ok.   GL - General Ledger.

Another scenario:

The application is delivered to many clients, the clients could exchange data with the consultant using folder like:

1. Export To Consultant  <Client Code>.
2. Import From Consultant <Client Code>.

The Client Name should be flexible.
The MDI title could be "GL - Company Name"

Thus we need Company Code and Company Name for each of the company using the same application.

How to solve this problem?

Thank you.
0
 
LVL 39

Expert Comment

by:abel
ID: 24732717
I'm not sure, you got me lost a bit, I need a bit more info: Are we talking about many apps on one system? These winforms apps, how can many people use one app on one computer? How do they open the MDI form? If you have many MDI forms, then I assume you mean the forms inside one MDI app, right? It sounds to me that you are building a client-server application and that on the server application you want to monitor something, is that correct? Is the application already there and do you need to make changes or are you starting designing it?
0
 
LVL 1

Author Comment

by:emi_sastra
ID: 24732954
Let me describe it.

There is one Consultant Company and Several Companies Of Client. Not Server and Client as usually we understand.

I have develop 2 app, one for The Consultant and the other one is for The Clients.

The app for Client is solved already. The problem is the app for the Clients.
I need to compile it every time for each of the company, because I don't want them to change their Company Information by them self. The mdi Form of the app should display Company Name without interference from the user.


Thank you.


0
 
LVL 39

Expert Comment

by:abel
ID: 24748900
I don't like the approach of using separate compiles, it is time consuming and it is hard to put a checksum on your file for downloads or virus scanners. But anyway, I'm not here to judge your approach, it may suit your needs just fine. Here's a possible solution:

  1. Add a string resource to your assembly which contains the name
  2. Create a simple helper app that takes an assembly (Assembly type) and changes the resource
  3. Save the changed Assembly
that's all. Much easier than recompile and you can do as many renames as you like.

-- Abel --
0
 
LVL 1

Author Comment

by:emi_sastra
ID: 24751090
I am interested in your approach.

How should I do for the 3 points?

Would you please provide step for each of the point?

Thank you.
0
 
LVL 1

Author Comment

by:emi_sastra
ID: 24757830
Hi Abel,

Are your still there to help?

Thank you.
0
 
LVL 39

Expert Comment

by:abel
ID: 24759209
Yes, I'm still here. We're volunteers, you know, and do this next to our jobs, so we won't always be around...

Anyway, what looked and sounded easy (and was inspired by Dynamic Assemblies and methods like GetMethodBody to get the IL of a body, I figured the same existed for assemblies) appears very hard in practice. Even something simple like changing a name is not trivial unless you know how to read and write PE or unless you dissect the assembly with reflection and re-emit it in a dynamic assembly and save that.

Sorry for pointing into a no-go area. I tried it, spent quite some time figuring out how to do it and ended up using RAIL: http://rail.dei.uc.pt/downloads.htm, which unfortunately does not work well together with newer versions of .NET and has not seen new development since 2005. From all I researched, RAIL seems the only library around that can read assemblies and modify existing assemblies.

Isn't it strange that it is real easy to create a new assembly, but that it is next to impossible to change an assembly? Maybe I'll research it once a bit more and come with some nice solution for it.

For now, you'll need another approach. What about using a resource-only assembly, which we can create dynamically? Then we don't have the problem of having to change a large existing assembly. You just create one assembly with a manifestresource which you can load dynamically. The resource just contains one string, the company name. Because it is an assembly (a dll) it is not likely that people can easily change it. You can put other info in the assembly as well. I'll try to create an example of how you can do that.

-- Abel --
0
 
LVL 1

Author Comment

by:emi_sastra
ID: 24759877
Hi Abel,

Glad to hear you again.

I am waiting for your great solution.

Thank you.
0
 
LVL 39

Expert Comment

by:abel
ID: 24759955
I decided to make a little project for you. I know you posted with VS 2005, I only have 2008, which I add here as a solution (remove ".txt"). It shouldn't be too hard to open the application: all you need is Form1.cs, actually. I had some trouble whilst making it because it *seemed* as though it didn't work, but that had to do with the wrong naming of the resource section (see comment in the CreateAssembly event).

I removed all rubbish, I think, and the remaining code is quite self-explanatory. See the screenshot: just add a few strings for testing and it will be generated in the DLL. When the application loads, the resource dll is loaded, so that you see the saved entries (and you can see how to dynamically read an assembly and its resources).

Have fun with it! ;-)

PS: when you load an assembly, it is locked and cannot be unloaded. That's why I copy the file in the Form_Load, otherwise you would not be able to save the file later on.

Dynamically-Create-Assemblies.ra.txt
0
 
LVL 39

Expert Comment

by:abel
ID: 24759964
oh, and the compiled executable in the Debug dir requires .NET 3.5. Not sure whether I accidentally used anything from 3.5, if you get some errors, use the .NET 2.0 equivalents... (high bedtime on this side of the earth, I'll check back later if you have any problems).
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 
LVL 1

Author Comment

by:emi_sastra
ID: 24760032
I'll be back to your later as soon as I've tested it.

Thank you.
0
 
LVL 39

Expert Comment

by:abel
ID: 24761857
I just noticed that there's something wrong with the filename, it ends on ".ra.txt" but should end on ".rar.txt". In case you have troubles: it is a RAR type of package, so rename it to "*.rar". You can get a free-to-use version of RAR at http://www.rarlab.com (but I'm sure you know).
0
 
LVL 1

Author Comment

by:emi_sastra
ID: 24761937
Yes, I've got it already when I saw "ra".
Unfortunately it is in C, I am really not familiar with it.
When I try to open it, I get message "It is not supported by this version of VS".

Would you please paste those code here (Code Snippet), thus I could learn it immediately from you.
May be I could translate it using translator.

Thank you.
0
 
LVL 39

Expert Comment

by:abel
ID: 24762060
> Unfortunately it is in C, I am really not familiar with it.

I didn't use C.... ;-)

> "It is not supported by this version of VS".

See my comment in my other text, explaining why. Just create a new project and open the *.cs file. It is C#, really, not C.

Of course I can copy the code if that makes things easier. Here you go. It will work if you add a btnCreateAssembly, btnAddField, btnDeleteField with Click events, plust a lstResourceItems with a SelectedChanged event on a form Form1. But I wanted to save you the trouble of doing that all yourself:

PS: I'm sure you already tried the executable. Was that about what you expected?

public partial class Form1 : Form

{

    Dictionary<string, string> fieldsDictionary = new Dictionary<string, string>();
 

    public Form1()

    {

        InitializeComponent();

    }
 

    private void Form1_Load(object sender, EventArgs e)

    {

        try

        {

            File.Copy(Path.GetFullPath("resource.dll"), Path.GetFullPath("resource.dll.copy"), true);

            Assembly assembly = Assembly.ReflectionOnlyLoadFrom(Path.GetFullPath("resource.dll.copy"));

            ResourceReader resourceReader = new ResourceReader(assembly.GetManifestResourceStream("CompanyInfo.resources"));

            foreach (DictionaryEntry resource in resourceReader)

            {

                fieldsDictionary.Add((string) resource.Key, (string) resource.Value);                    

            }

        } 

        catch(FileNotFoundException)

        {

            fieldsDictionary.Add("company-name", "My Very Cool Company");

            fieldsDictionary.Add("company-address", "Some street 204");

        }

        loadFields();

    }
 

    private void loadFields()

    {

        lstResourceItems.Items.Clear();

        foreach (string item in fieldsDictionary.Keys)

            lstResourceItems.Items.Add(item);

    }
 

    private void btnAddField_Click(object sender, EventArgs e)

    {

        fieldsDictionary[txtName.Text] = txtValue.Text;

        loadFields();

    }
 

    private void btnDeleteField_Click(object sender, EventArgs e)

    {

        if (fieldsDictionary.ContainsKey(txtName.Text))

            fieldsDictionary.Remove(txtName.Text);

        loadFields();

    }
 

    private void lstResourceItems_SelectedIndexChanged(object sender, EventArgs e)

    {

        string selected = (string) lstResourceItems.SelectedItem;

        if(fieldsDictionary.ContainsKey(selected))

        {

            txtName.Text = selected;

            txtValue.Text = fieldsDictionary[selected];

        }

    }
 

    private void btnCreateAssembly_Click(object sender, EventArgs e)

    {

        AssemblyName assemblyName = new AssemblyName("CompanyInfoResources");

        AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(

            assemblyName, 

            AssemblyBuilderAccess.Save);
 
 

        // contrary to what the references say, the FIRST parameter is vital

        // and MUST end on ".resources" to create a readable resource

        IResourceWriter resourceWriter = assemblyBuilder.DefineResource(

            "CompanyInfo.resources",

            "Company information",

            "CompanyInfoResources.CompanyInfo.resources");
 

        foreach (KeyValuePair<string, string> de in fieldsDictionary)

            resourceWriter.AddResource(de.Key, de.Value);
 

        assemblyBuilder.DefineVersionInfoResource("CompanyInfoResourceExample", "1:0:0:1", "Metacarpus", "(c) Metacarpus", "");

        assemblyBuilder.Save("resource.dll");
 

    }
 

}

Open in new window

ScreenShot416.png
0
 
LVL 1

Author Comment

by:emi_sastra
ID: 24762138
I just tried it, but It can not Create Assembly, because it is in used by other process.

I called MainApplication.exe.

I am not quite understand, there are several files that I don't know how to use it.

1. CompanyInfoResources.CompanyInfo.resources
2. resource.dll
3. resource.dll.copy

Which files should I copy to client computers and how my application use the file created by the app above?

Thank you.
0
 
LVL 39

Expert Comment

by:abel
ID: 24762305
In the screenshot it says "the assembly will be placed in the working dir as "resource.dll". That's what you need.

In my text above I tried to explain: "when you load an assembly, it is locked and cannot be unloaded. That's why I copy the file in the Form_Load, otherwise you would not be able to save the file later on."

In so many words that meant: to load the dll, I copy it, to prevent it from being locked. The copy it called "resource.dll.copy".

Nr 1 above can be ignored. It is an intermediate file that is created by the dynamic assembly creator.

Be aware that what you are doing here is considered by many as very complex: dynamic emit of type and assembly creation is usually about the last chapter in understanding everything you could possibly need to know about MSIL, IL compilers and CLI in general. If this all sounds like magic to you, don't be alarmed, to most programmers these things stay like magic their whole life.
0
 
LVL 39

Expert Comment

by:abel
ID: 24762326
> I just tried it, but It can not Create Assembly, because it is in used by other process.
what exactly did you try? If it is in use, then it has been loaded. That's not necessarily bad, but you can only create it when it is not loaded. My code creates a copy to prevent that. However, I placed that code in the form_load, because the loading-code will only work once in the lifetime of the application because it creates a lock on the copy-file. Close and restart to release that lock. I don't know of any workaround to that, but for your code to work properly you do not need to read back the contents of the DLL other then in your final application.
0
 
LVL 39

Expert Comment

by:abel
ID: 24762418
Ignore (parts of) my last two comments. The *.resources file is important, it apparently has to stick with the resource.dll. I hoped it would go inside it. Though it is a binary file, it is not embedded. I'll check whether that's possible (it is, but not sure how to do it dynamically).

The reason that the file is in use is that it opens the file resource.dll.copy and the *.resources file. The *.resources file is the problem, because that is locked when loaded. To prevent that, change my copy code to copy it to a new directory and copy both the dll and the resources file. No need to rename it.
0
 
LVL 1

Author Comment

by:emi_sastra
ID: 24762440
I see, correct me if I am wrong.

1. The resource.dll has the info what I want to store..
2. In my application I just read the ddl using :
     ResourceReader resourceReader = new ResourceReader(assembly.GetManifestResourceStream("CompanyInfo.resources"));
            foreach (DictionaryEntry resource in resourceReader)
            {
                fieldsDictionary.Add((string) resource.Key, (string) resource.Value);                    
            }

Thank you.
0
 
LVL 39

Accepted Solution

by:
abel earned 500 total points
ID: 24764700
No, you're not wrong. All you need in the current situation, with the approach above is the resource.dll and the *.resources file.

However, like I said in my last comment, I was looking for something to include the resource inside the resource.dll as embeded resource, not as linked resource. This proofed quite hard, because it was nowhere documented that this only works if you use the ModuleBuilder and then still only if you use the ModuleBuilder with exactly the same name as the AssemblyBuilder. In code, all you need to do is replace the btnCreateAssembly_Click code with the following. It will now create only one file: resource.dll

As a side effect, the locked resource problem you received earlier is gone with this one.

I think the code below is quite self-explanatory, but if you have questions, let me know.

-- Abel --

AssemblyName assemblyName = new AssemblyName("CompanyInfoResources");

AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(

    assemblyName, 

    AssemblyBuilderAccess.Save);
 

assemblyBuilder.DefineVersionInfoResource(

    "CompanyInfoResourceExample", 

    "1:0:0:1", 

    "Metacarpus", 

    "(c) Metacarpus", 

    "");
 

// without filename, the DefineManfistResource throws "Unable to add resource to transient module or transient assembly."

ModuleBuilder modBuilder = assemblyBuilder.DefineDynamicModule(

    "CompanyInfoResources", 

    "resource.dll");
 

IResourceWriter resourceWriter = modBuilder.DefineResource(

    "CompanyInfo.resources", 

    "Company information");
 

foreach (KeyValuePair<string, string> de in fieldsDictionary)

    resourceWriter.AddResource(de.Key, de.Value);
 

assemblyBuilder.Save("resource.dll");

Open in new window

0
 
LVL 39

Expert Comment

by:abel
ID: 24764732
Btw, after quite some research, the only reference that pointed to the fact that the names must be equal I found here: http://www.dotnet247.com/247reference/msgs/58/290731.aspx, so thanks to Nick Carter in that thread :)
0
 
LVL 1

Author Comment

by:emi_sastra
ID: 24769075
Hi Abel,

I have completed this project using VB Net Code.

Great help and explanation.

Thank you very much for your help.
0
 
LVL 39

Expert Comment

by:abel
ID: 24769081
Sorry, missed that you were on VB, I know you posted in that zone, but probably forgot.

Consider this tool, that does most of the trivial cases pretty well: http://www.developerfusion.com/tools/convert/csharp-to-vb/
0
 
LVL 39

Expert Comment

by:abel
ID: 24769086
Glad you completed it, btw and tx for the pts :-)
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

Article by: jpaulino
XML Literals are a great way to handle XML files and the community doesn’t use it as much as it should.  An XML Literal is like a String (http://msdn.microsoft.com/en-us/library/system.string.aspx) Literal, only instead of starting and ending with w…
If you need to start windows update installation remotely or as a scheduled task you will find this very helpful.
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

759 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now