Creating assemblies with custom extension instead of .dll

A

Anton Shepelev

Hello all,

I am working on a plugin host whose plugins are .NET
assemblies implementing a certain interface. It
would be beautiful if I could give the plugins a
custom extension (say, .plu) to make them visibly
different from ordinary (non-plugin) library assem-
blies which are not part of the host and which those
plugins may use. Do you know of a way to do it in
Visual Studio?

P.S.: I hope that my question is not entirely off-
topic.
 
A

Arne Vajhøj

Hello all,

I am working on a plugin host whose plugins are .NET
assemblies implementing a certain interface. It
would be beautiful if I could give the plugins a
custom extension (say, .plu) to make them visibly
different from ordinary (non-plugin) library assem-
blies which are not part of the host and which those
plugins may use. Do you know of a way to do it in
Visual Studio?

With MEF it is relative simple:

C:\Work\plugin>type Common.cs
namespace E
{
public interface IHelloWorld
{
void Say();
}
}

C:\Work\plugin>csc /t:library Common.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\plugin>type A.cs
using System;
using System.ComponentModel.Composition;

namespace E
{
[Export(typeof(IHelloWorld))]
public class A : IHelloWorld
{
public void Say()
{
Console.WriteLine("Hello world from A");
}
}
}

C:\Work\plugin>csc /t:library /r:Common.dll
/r:System.ComponentModel.Composition.dll /out:A.plu A.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\plugin>type B.cs
using System;
using System.ComponentModel.Composition;

namespace E
{
[Export(typeof(IHelloWorld))]
public class B : IHelloWorld
{
public void Say()
{
Console.WriteLine("Hello world from B");
}
}
}

C:\Work\plugin>csc /t:library /r:Common.dll
/r:System.ComponentModel.Composition.dll /out:B.plu B.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\plugin>type Test.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;

namespace E
{
public class Test
{
[ImportMany]
private List<IHelloWorld> plugins = null;
private void DoAll()
{
foreach(IHelloWorld plugin in plugins)
{
plugin.Say();
}
}
public static void Main(string[] args)
{
Test o = new Test();
AggregateCatalog cat = new AggregateCatalog();
cat.Catalogs.Add(new DirectoryCatalog(@"C:\work\plugin",
"*.plu"));
CompositionContainer container = new CompositionContainer(cat);
container.SatisfyImportsOnce(o);
o.DoAll();
}
}
}

C:\Work\plugin>csc /t:exe /r:Common.dll
/r:System.ComponentModel.Composition.dll
Test.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\plugin>Test
Hello world from A
Hello world from B
P.S.: I hope that my question is not entirely off-
topic.

I think it is on-topic. Questions about .NET are usually
considered on-topic here as long as code examples are in C#.

Arne
 
A

Anton Shepelev

Arne Vajhoj:
Anton Shepelev:
I am working on a plugin host whose plugins are
.NET assemblies implementing a certain inter-
face. It would be beautiful if I could give the
plugins a custom extension (say, .plu) to make
them visibly different from ordinary (non-plug-
in) library assemblies which are not part of the
host and which those plugins may use. Do you
know of a way to do it in Visual Studio?

P.S.: I hope that my question is not entirely
off-topic.

With MEF it is relative simple:

C:\Work\plugin>type Common.cs
namespace E
{
public interface IHelloWorld
{
void Say();
}
}

C:\Work\plugin>csc /t:library Common.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\plugin>type A.cs
using System;
using System.ComponentModel.Composition;

namespace E
{
[Export(typeof(IHelloWorld))]
public class A : IHelloWorld
{
public void Say()
{
Console.WriteLine("Hello world from A");
}
}
}

C:\Work\plugin>csc /t:library /r:Common.dll
/r:System.ComponentModel.Composition.dll /out:A.plu A.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\plugin>type B.cs
using System;
using System.ComponentModel.Composition;

namespace E
{
[Export(typeof(IHelloWorld))]
public class B : IHelloWorld
{
public void Say()
{
Console.WriteLine("Hello world from B");
}
}
}

C:\Work\plugin>csc /t:library /r:Common.dll
/r:System.ComponentModel.Composition.dll /out:B.plu B.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\plugin>type Test.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;

namespace E
{
public class Test
{
[ImportMany]
private List<IHelloWorld> plugins = null;
private void DoAll()
{
foreach(IHelloWorld plugin in plugins)
{
plugin.Say();
}
}
public static void Main(string[] args)
{
Test o = new Test();
AggregateCatalog cat = new AggregateCatalog();
cat.Catalogs.Add(new DirectoryCatalog(@"C:\work\plugin", "*.plu"));
CompositionContainer container = new CompositionContainer(cat);
container.SatisfyImportsOnce(o);
o.DoAll();
}
}
}

C:\Work\plugin>csc /t:exe /r:Common.dll
/r:System.ComponentModel.Composition.dll
Test.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\plugin>Test
Hello world from A
Hello world from B

Thank you, Arne, for the very detailed example :) I
am not ready to switch to .NET 4.0 and rewrite my
system with MeF, but I'll consider your advice dur-
ing our planned switch to a newer Visual Studio.
Meanwhile, I'll have to make do with something like
*.plu.dll.

Note that your newsreader broke a long line in the
example, but I have fixed it in the quotation.
 
A

Arne Vajhøj

Arne Vajhoj:
Anton Shepelev:
I am working on a plugin host whose plugins are
.NET assemblies implementing a certain inter-
face. It would be beautiful if I could give the
plugins a custom extension (say, .plu) to make
them visibly different from ordinary (non-plug-
in) library assemblies which are not part of the
host and which those plugins may use. Do you
know of a way to do it in Visual Studio?

P.S.: I hope that my question is not entirely
off-topic.

With MEF it is relative simple:

C:\Work\plugin>type Common.cs
namespace E
{
public interface IHelloWorld
{
void Say();
}
}

C:\Work\plugin>csc /t:library Common.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\plugin>type A.cs
using System;
using System.ComponentModel.Composition;

namespace E
{
[Export(typeof(IHelloWorld))]
public class A : IHelloWorld
{
public void Say()
{
Console.WriteLine("Hello world from A");
}
}
}

C:\Work\plugin>csc /t:library /r:Common.dll
/r:System.ComponentModel.Composition.dll /out:A.plu A.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\plugin>type B.cs
using System;
using System.ComponentModel.Composition;

namespace E
{
[Export(typeof(IHelloWorld))]
public class B : IHelloWorld
{
public void Say()
{
Console.WriteLine("Hello world from B");
}
}
}

C:\Work\plugin>csc /t:library /r:Common.dll
/r:System.ComponentModel.Composition.dll /out:B.plu B.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\plugin>type Test.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;

namespace E
{
public class Test
{
[ImportMany]
private List<IHelloWorld> plugins = null;
private void DoAll()
{
foreach(IHelloWorld plugin in plugins)
{
plugin.Say();
}
}
public static void Main(string[] args)
{
Test o = new Test();
AggregateCatalog cat = new AggregateCatalog();
cat.Catalogs.Add(new DirectoryCatalog(@"C:\work\plugin", "*.plu"));
CompositionContainer container = new CompositionContainer(cat);
container.SatisfyImportsOnce(o);
o.DoAll();
}
}
}

C:\Work\plugin>csc /t:exe /r:Common.dll
/r:System.ComponentModel.Composition.dll
Test.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\plugin>Test
Hello world from A
Hello world from B

Thank you, Arne, for the very detailed example :) I
am not ready to switch to .NET 4.0 and rewrite my
system with MeF, but I'll consider your advice dur-
ing our planned switch to a newer Visual Studio.
Meanwhile, I'll have to make do with something like
*.plu.dll.

You can also achieve the same without MEF by writing a bit more code.

C:\Work\plugin>type Test35.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace E
{
public class Test
{
private List<IHelloWorld> plugins = new List<IHelloWorld>();
private void Setup()
{
foreach(string fnm in Directory.GetFiles(@"C:\work\plugin",
"*.plu"))
{
foreach(Type typ in Assembly.LoadFrom(fnm).GetTypes())
{
if(typeof(IHelloWorld).IsAssignableFrom(typ))
{
plugins.Add((IHelloWorld)typ.GetConstructor(new
Type[0]).Invoke(new object[0]));
}
}
}
}
private void DoAll()
{
foreach(IHelloWorld plugin in plugins)
{
plugin.Say();
}
}
public static void Main(string[] args)
{
Test o = new Test();
o.Setup();
o.DoAll();
}
}
}

C:\Work\plugin>csc /t:exe /r:Common.dll Test35.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Work\plugin>Test35
Hello world from A
Hello world from B

Arne
 
A

Anton Shepelev

Arne Vajhoj:
Thank you, Arne, for the very detailed example
:) I am not ready to switch to .NET 4.0 and
rewrite my system with MeF, but I'll consider
your advice during our planned switch to a newer
Visual Studio. Meanwhile, I'll have to make do
with something like *.plu.dll.

You can also achieve the same without MEF by writ-
ing a bit more code.
[...]

Thanks, I misunderstood you. My problem is that I
can't configure Visual Studio 2008 to produce a .plu
file instead of a .dll, which you did via csc's
/out: parameter. I don't want to switch to the com-
mand line every time I need to complile a library,
and neither do I want to rename the .dll's after the
build, which I am afraid will make them impossible
to debug.
 
A

Arne Vajhøj

Arne Vajhoj:
Thank you, Arne, for the very detailed example
:) I am not ready to switch to .NET 4.0 and
rewrite my system with MeF, but I'll consider
your advice during our planned switch to a newer
Visual Studio. Meanwhile, I'll have to make do
with something like *.plu.dll.

You can also achieve the same without MEF by writ-
ing a bit more code.
[...]

Thanks, I misunderstood you. My problem is that I
can't configure Visual Studio 2008 to produce a .plu
file instead of a .dll, which you did via csc's
/out: parameter. I don't want to switch to the com-
mand line every time I need to complile a library,
and neither do I want to rename the .dll's after the
build, which I am afraid will make them impossible
to debug.

Visual Studio allows you to add a "post build event
command line" where you could rename or copy.

You could copy and use the DLL for debugging
and deploy the PLU.

Anyway I do not consider it good practice to do production
builds via VS. A very clean build system and a build using
msbuild or nant preferably driven by a CI tool produce
much more reproducable builds.

Arne
 
B

Bjarke L

Date: Mon, 28 Oct 2013 08:43:02 From: Anton Shepelev
<anton.txt@g{oogle}mail.com> Newsgroups:
microsoft.public.dotnet.languages.csharp Subject: Re: Creating
assemblies with custom extension instead of .dll

Thanks, I misunderstood you. My problem is that I can't configure
Visual Studio 2008 to produce a .plu file instead of a .dll, which you
did via csc's /out: parameter. I don't want to switch to the com- mand
line every time I need to complile a library, and neither do I want to
rename the .dll's after the build, which I am afraid will make them
impossible to debug.

In a property group inside your .csproj file add the following:

<TargetExt>.plu</TargetExt>

/Bjarke
 
B

Bjarke L

Clarification: Visual Studio still compiles it as
.dll .

That's funny. But then again - You're using an quite old version of Visual
Studio, so I checked in the
%windir%\Microsoft.NET\Framework\v3.5\Microsoft.Common.targets

It defines the <TargetExt> as:

<PropertyGroup>
<TargetExt Condition="'$(OutputType)'=='exe'">.exe</TargetExt>
<TargetExt Condition="'$(OutputType)'=='winexe'">.exe</TargetExt>
<TargetExt Condition="'$(OutputType)'=='library'">.dll</TargetExt>
<TargetExt
Condition="'$(OutputType)'=='module'">.netmodule</TargetExt>
</PropertyGroup>

While %windir%\Microsoft.NET\Framework\v3.0.30319\Microsoft.Common.targets
defines it as:

<PropertyGroup>
<TargetExt Condition="'$(TargetExt)' == '' and
'$(OutputType)'=='exe'">.exe</TargetExt>
<TargetExt Condition="'$(TargetExt)' == '' and
'$(OutputType)'=='winexe'">.exe</TargetExt>
<TargetExt Condition="'$(TargetExt)' == '' and
'$(OutputType)'=='appcontainerexe'">.exe</TargetExt>
<TargetExt Condition="'$(TargetExt)' == '' and
'$(OutputType)'=='library'">.dll</TargetExt>
<TargetExt Condition="'$(TargetExt)' == '' and
'$(OutputType)'=='module'">.netmodule</TargetExt>
<TargetExt Condition="'$(TargetExt)' == '' and
'$(OutputType)'=='winmdobj'">.winmdobj</TargetExt>
</PropertyGroup>

The thing is that the 3.5 version does not check if the TargetExt is set
before it sets it. If you want to fix it you have two options; either edit
the Microsfot.Common.targets file (not recommended - and it will only work
on your machine) or put the <TargetExt> inside a property group after the
Microsoft.CSharp.targets file is included in your .csproj file.

I can't remember how the VS 2008 csproj msbuild file looks, but look for a
line like this:
<Import
Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).CSharp.targets"
/>

and after this line add:

<PropertyGroup>
<TargetExt>.plu</TargetExt>
</PropertyGroup>

It should work - but I haven't tested my self.

/Bjarke
 
A

Anton Shepelev

Bjarke L:
The thing is that the 3.5 version does not check
if the TargetExt is set before it sets it. If you
want to fix it you have two options; either edit
the Microsfot.Common.targets file (not recommend-
ed -- and it will only work on your machine) or
put the <TargetExt> inside a property group after
the Microsoft.CSharp.targets file is included in
your .csproj file.

Ah, thanks for the investigation :) You were
right -- I moved my <TargetExt> definition to after
the <Import> and it worked. I had first tried to
move the <Import> to the beginning, but that hadn't
cut it, producing an error about incorrect or un-
specified build path.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top