Criar assemblies satélites para aplicativos .NET
Arquivos de recurso desempenham um papel central em aplicativos localizados. Eles permitem que um aplicativo exiba cadeias de caracteres, imagens e outros dados no idioma e na cultura do usuário e forneça dados alternativos se esses recursos não estiverem disponíveis. O .NET Usa um modelo de hub e spoke para localizar e recuperar os recursos localizados. O hub é o principal assembly que contém o código executável não localizável e os recursos para uma única cultura, que é chamada de cultura neutra ou padrão. O padrão é a cultura de fallback para o aplicativo; ela é usada quando não há recursos localizados disponíveis. Você usa o atributo NeutralResourcesLanguageAttribute para designar a cultura da cultura padrão do aplicativo. Cada spoke conecta-se a um assembly satélite que contém os recursos para uma única cultura localizada, mas não contém nenhum código. Como os assemblies satélite não fazem parte do assembly principal, você pode facilmente atualizar ou substituir recursos que correspondem a uma cultura específica sem substituir o assembly principal do aplicativo.
Observação
Os recursos da cultura padrão de um aplicativo também podem ser armazenados em um assembly satélite. Para fazer isso, você atribui ao atributo NeutralResourcesLanguageAttribute um valor de UltimateResourceFallbackLocation.Satellite.
Nome e local do assembly satélite
Esse modelo hub e spoke requer que você coloque recursos em locais específicos, para que possam ser facilmente localizados e usados. Se você não compilar e nomear os recursos conforme esperado, ou se não colocá-los nos locais corretos, o CLR não poderá localizá-los e usará os recursos da cultura padrão. O gerenciador de recursos do .NET é representado pelo tipo ResourceManager e é usado para acessar automaticamente recursos localizados. O gerenciador de recursos requer o seguinte:
Um assembly satélite único deve incluir todos os recursos de uma cultura específica. Em outras palavras, você deve compilar vários arquivos .txt ou .resx em um arquivo binário .resources.
Deve haver um subdiretório separado no diretório do aplicativo para cada cultura localizada que armazena os recursos da cultura. O nome do subdiretório deve ser igual ao nome de cultura. Como alternativa, você pode armazenar seus assemblies satélites no cache de assembly global. Nesse caso, o componente de informações de cultura do nome forte do assembly deve indicar sua cultura. Para obter mais informações, consulte Instalar assemblies satélites no cache de assembly global.
Observação
Se o aplicativo incluir recursos para subculturas, coloque cada subcultura em um subdiretório separado no diretório do aplicativo. Não coloque subculturas em subdiretórios no diretório principal da sua cultura.
O assembly satélite deve ter o mesmo nome que o aplicativo e usar a extensão de nome de arquivo ".resources.dll". Por exemplo, se um aplicativo for denominado Example.exe, o nome de cada assembly satélite deverá ser Example.resources.dll. O nome do assembly satélite não indica a cultura dos seus arquivos de recursos. No entanto, o assembly satélite aparece em um diretório que especifica a cultura.
Informações sobre a cultura do assembly satélite devem ser incluídas nos metadados do assembly. Para armazenar o nome da cultura nos metadados do assembly satélite, você deve especificar a opção
/culture
quando usar o Assembly Linker para incorporar recursos no assembly satélite.
A ilustração a seguir mostra a estrutura e os requisitos de localização de um diretório de exemplo para os aplicativos que você não está instalando no cache de assembly global. Os itens com as extensões .txt e .resources não serão fornecidos com o aplicativo final. Esses são os arquivos de recurso intermediários usados para criar os assemblies satélite finais de recursos. Neste exemplo, você poderia substituir arquivos .resx por arquivos .txt. Para obter mais informações, consulte Pacote e implantar recursos.
A imagem a seguir mostra o diretório do assembly satélite:
Compilar assemblies satélite
Você usa o Gerador de arquivos de recurso (resgen.exe) para compilar arquivos de texto ou XML (.resx) que contenham arquivos binários .resources. Em seguida, usa o Assembly Linker (al.exe) para compilar arquivos .resources em assemblies satélite. O al.exe cria um conjunto de arquivos usando o .resources que você especificar. Assemblies satélites podem conter somente recursos; eles não podem conter nenhum código executável.
O comando a seguir al.exe cria um assembly satélite para o aplicativo Example
usando o arquivo de recursos alemão strings.de.resources.
al -target:lib -embed:strings.de.resources -culture:de -out:Example.resources.dll
O comando a seguir al.exe cria um assembly satélite para o aplicativo Example
usando o arquivo strings.de.resources. A opção /template faz com que o assembly satélite herde todos os metadados de assembly, exceto as informações de cultura do assembly pai (Example. dll).
al -target:lib -embed:strings.de.resources -culture:de -out:Example.resources.dll -template:Example.dll
A tabela a seguir descreve em detalhes as opções de al.exe usadas nesses comandos:
Opção | Descrição |
---|---|
-target:lib |
Especifica que o seu assembly satélite é compilado em um arquivo de biblioteca (. dll). Como um assembly satélite não contém código executável e não é um assembly principal do aplicativo, você precisa salvar assemblies satélites como DLLs. |
-embed:strings.de.resources |
Especifica o nome do arquivo de recurso incorporado quando o al.exe compila o assembly. Você pode incorporar vários arquivos .resources em um assembly satélite, mas se estiver seguindo o modelo hub e spoke, compile um assembly satélite para cada cultura. No entanto, você pode criar arquivos .resources separados para cadeias de caracteres e objetos. |
-culture:de |
Especifica a cultura do recurso para compilar. O Common Language Runtime usa essas informações ao procurar os recursos de uma cultura específica. Se você omitir esta opção, o al.exe ainda compilará o recurso, mas o runtime não será capaz de localizá-lo quando um usuário solicitá-lo. |
-out:Example.resources.dll |
Especifica o nome do arquivo de saída. O nome deve seguir o padrão de nomeação baseName.resources. extensão, onde baseName é o nome do assembly principal, e extension é uma extensão de nome de arquivo válido (como .dll). O runtime não é capaz de determinar a cultura de um assembly satélite com base em seu nome de arquivo de saída; você precisa usar a opção /culture para especificá-lo. |
-template:Example.dll |
Especifica o assembly do qual todos os metadados de assembly devem ser herdados, exceto o campo de cultura. Esta opção afetará assemblies satélites somente se você especificar um assembly com um nome forte. |
Para obter uma lista completa das opções disponíveis com al.exe, consulte Assembly Linker (al.exe).
Observação
Pode haver momentos em que você deseja usar a tarefa MSBuild do .NET Core para compilar assemblies satélites, mesmo que você esteja visando .NET Framework. Por exemplo, talvez você queira usar a opção determinística do compilador C# para poder comparar assemblies de diferentes builds. Nesse caso, defina GenerateSatelliteAssembliesForCoretrue
como no arquivo .csproj para gerar assemblies satélites usando csc.exe em vez de Al.exe (Assembly Linker).
<Project>
<PropertyGroup>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
</PropertyGroup>
</Project>
A tarefa MSBuild do .NET Core usa csc.exe em vez de al.exe para gerar assemblies satélites, por padrão. Para obter mais informações, consulte Facilitar a aceitação da geração de assembly satélite "Core".
Assemblies satélites: um exemplo
Este é um exemplo simples "Olá, Mundo", que exibe uma caixa de mensagem com uma saudação localizada. O exemplo inclui recursos para as culturas inglesas (Estados Unidos), francesas (França) e russas (Rússia), e sua cultura de fallback é o inglês. Para criar o exemplo, faça o seguinte:
Crie um arquivo de recurso chamado Greeting.resx ou Greeting.txt para conter o recurso para a cultura padrão. Armazene uma única cadeia de caracteres denominada
HelloString
, cujo valor é "Hello world!" neste arquivo.Para indicar que inglês (en) é a cultura padrão do aplicativo, adicione o seguinte atributo System.Resources.NeutralResourcesLanguageAttribute ao arquivo AssemblyInfo do aplicativo ou ao arquivo de código-fonte principal que será compilado no assembly principal do aplicativo.
[assembly: NeutralResourcesLanguage("en")]
<Assembly: NeutralResourcesLanguage("en")>
Adicione suporte a culturas adicionais (
en-US
,fr-FR
eru-RU
) ao aplicativo da seguinte maneira:Para oferecer suporte à cultura
en-US
ou inglesa (Estados Unidos) ou en-US, crie um arquivo de recurso chamado Greeting.en-US.resx ou Greeting.en-US.txt e armazene nele uma única cadeia de caracteres denominadaHelloString
, cujo valor é "Hi world!".Para oferecer suporte à cultura
fr-FR
ou francesa (França) ou fr-FR, crie um arquivo de recurso chamado Greeting.fr-FR.resx ou Greeting.fr-FR.txt e armazene nele uma única cadeia de caracteres denominadaHelloString
, cujo valor é "Salout tout le monde!".Para oferecer suporte à cultura
ru-RU
ou russa (Rússia) ou ru-RU, crie um arquivo de recurso chamado Greeting.ru-RU.resx ou Greeting.ru-RU.txt e armazene nele uma única cadeia de caracteres denominadaHelloString
, cujo valor é "Всем привет!".
Use resgen.exe para compilar cada texto ou um arquivo de recursos XML em um arquivo binário .resources. A saída é um conjunto de arquivos que têm o mesmo nome de arquivo raiz que os arquivos .resx ou .txt, mas uma extensão .resources. Se você criar o exemplo com o Visual Studio, o processo de compilação será manipulado automaticamente. Se você não estiver usando o Visual Studio, execute os seguintes comandos para compilar os arquivos .resx em arquivos .resources:
resgen Greeting.resx resgen Greeting.en-us.resx resgen Greeting.fr-FR.resx resgen Greeting.ru-RU.resx
Se os recursos estiverem em arquivos de texto em vez de arquivos XML, substitua a extensão .resx por .txt.
Compile o código-fonte abaixo com os recursos para a cultura padrão no assembly principal do aplicativo:
Importante
Se você estiver usando a linha de comando em vez do Visual Studio para criar o exemplo, deverá modificar a chamada para o construtor de classe ResourceManager para o seguinte:
ResourceManager rm = new ResourceManager("Greeting", typeof(Example).Assembly);
using System; using System.Globalization; using System.Reflection; using System.Resources; using System.Threading; using System.Windows.Forms; class Example { static void Main() { // Create array of supported cultures string[] cultures = {"en-CA", "en-US", "fr-FR", "ru-RU"}; Random rnd = new Random(); int cultureNdx = rnd.Next(0, cultures.Length); CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture; try { CultureInfo newCulture = new CultureInfo(cultures[cultureNdx]); Thread.CurrentThread.CurrentCulture = newCulture; Thread.CurrentThread.CurrentUICulture = newCulture; ResourceManager rm = new ResourceManager("Example.Greeting", typeof(Example).Assembly); string greeting = String.Format("The current culture is {0}.\n{1}", Thread.CurrentThread.CurrentUICulture.Name, rm.GetString("HelloString")); MessageBox.Show(greeting); } catch (CultureNotFoundException e) { Console.WriteLine("Unable to instantiate culture {0}", e.InvalidCultureName); } finally { Thread.CurrentThread.CurrentCulture = originalCulture; Thread.CurrentThread.CurrentUICulture = originalCulture; } } }
Imports System.Globalization Imports System.Resources Imports System.Threading Module Module1 Sub Main() ' Create array of supported cultures Dim cultures() As String = {"en-CA", "en-US", "fr-FR", "ru-RU"} Dim rnd As New Random() Dim cultureNdx As Integer = rnd.Next(0, cultures.Length) Dim originalCulture As CultureInfo = Thread.CurrentThread.CurrentCulture Try Dim newCulture As New CultureInfo(cultures(cultureNdx)) Thread.CurrentThread.CurrentCulture = newCulture Thread.CurrentThread.CurrentUICulture = newCulture Dim greeting As String = String.Format("The current culture is {0}.{1}{2}", Thread.CurrentThread.CurrentUICulture.Name, vbCrLf, My.Resources.Greetings.HelloString) MsgBox(greeting) Catch e As CultureNotFoundException Console.WriteLine("Unable to instantiate culture {0}", e.InvalidCultureName) Finally Thread.CurrentThread.CurrentCulture = originalCulture Thread.CurrentThread.CurrentUICulture = originalCulture End Try End Sub End Module
Se o aplicativo for chamado de Example e você estiver compilando na linha de comando, o comando para o compilador C# será:
csc Example.cs -res:Greeting.resources
O comando do compilador Visual Basic correspondente é:
vbc Example.vb -res:Greeting.resources
Crie um subdiretório no diretório do aplicativo principal para cada cultura localizada com suporte do aplicativo. Você deve criar um subdiretório en-US, um fr-FR e um ru-RU. O Visual Studio cria esses subdiretórios automaticamente como parte do processo de compilação.
Incorpore os arquivos individuais de culturas específicas .resources a assemblies satélites e os salve no diretório apropriado. O comando para fazer isso para cada arquivo .resources é:
al -target:lib -embed:Greeting.culture.resources -culture:culture -out:culture\Example.resources.dll
onde cultura é o nome da cultura cujos recursos o assembly satélite contém. O Visual Studio trata esse processo automaticamente.
Você pode executar o exemplo. Isso tornará aleatoriamente uma das culturas com suporte a cultura atual e exibirá uma saudação localizada.
Instalação de assemblies satélite no cache de assembly global
Em vez de instalar assemblies em um subdiretório do aplicativo local, você pode instalá-los no cache de assembly global. Isso é particularmente útil se você tiver bibliotecas de classes e assemblies de recursos de biblioteca de classe usados por vários aplicativos.
A instalação de assemblies no cache de assembly global exige que eles tenham nomes fortes. Assemblies com nomes fortes são assinados com um par de chaves públicas/privadas válido. Elas contêm informações de versão que o runtime usa para determinar qual assembly usar para atender a uma solicitação de associação. Para obter mais informações sobre nomes fortes e controle de versão, consulte Controle de versão do assembly. Para obter mais informações sobre nomes fortes, consulte Assemblies com nome forte.
Quando você estiver desenvolvendo um aplicativo, é improvável que tenha acesso ao par de chaves públicas/privadas final. Para instalar um assembly satélite no cache de assembly global e garantir que ele funcione conforme o esperado, você pode usar uma técnica chamada assinatura atrasada. Quando você atrasar a assinatura de um assembly, no momento da compilação, reserve espaço no arquivo para a assinatura de nome forte. A assinatura real é atrasada até mais tarde, quando o par de chaves públicas/privadas final ficar disponível. Para obter mais informações sobre o processo de assinatura com atraso, consulte Assinatura com atraso de um assembly.
Obter a chave pública
Para atrasar a assinatura de um assembly, você deve ter acesso à chave pública. Você pode obter a chave pública real da organização para sua empresa que fará a eventual assinatura, ou criar uma chave pública usando a Ferramenta de Nome Forte (sn.exe).
O comando a seguir Sn.exe cria um par de chaves públicas/privadas de teste. A opção –k especifica que Sn.exe deve criar um novo par de chaves e salvá-lo em um arquivo chamado TestKeyPair.snk.
sn –k TestKeyPair.snk
Você pode extrair a chave pública do arquivo que contém o par de chaves de teste. O comando a seguir extrai a chave pública de TestKeyPair.snk e a salva no PublicKey.snk:
sn –p TestKeyPair.snk PublicKey.snk
Atrasando a assinatura de um assembly
Depois de obter ou criar a chave pública, use oAssembly Linker (al.exe) para compilar o assembly e especificar a assinatura com atraso.
O comando a seguir al.exe cria um assembly satélite com nome forte para o aplicativo StringLibrary usando o arquivo strings.ja.resources:
al -target:lib -embed:strings.ja.resources -culture:ja -out:StringLibrary.resources.dll -delay+ -keyfile:PublicKey.snk
A opção -delay+ especifica que o Assembly Linker deve atrasar a assinatura do assembly. A opção -keyfile especifica o nome do arquivo da chave que contém a chave pública a ser usada para atrasar a assinatura do assembly.
Nova assinatura de um assembly com atraso
Antes de implantar seu aplicativo, você deve reassinar o assembly satélite com atraso com o par de chaves real. Você pode fazer isso usando Sn.exe.
O comando a seguir Sn.exe assina StringLibrary.resources.dll com o par de chaves armazenado no arquivo RealKeyPair.snk. A opção – R especifica que um assembly assinado anteriormente ou assinado com atraso deve ser assinado novamente.
sn –R StringLibrary.resources.dll RealKeyPair.snk
Instalação de assemblies satélite no Cache de Assembly Global (GAC)
Quando o runtime procura recursos no processo de fallback de recursos, ele examina o cache de assembly global primeiro. (Para obter mais informações, consulte a seção "Processo de Fallback de Recursos" do Pacote e implantar recursos.) Assim que um assembly satélite for assinado com um nome forte, ele poderá ser instalado no cache de assembly global usando a Ferramenta de cache de assembly global (gacutil.exe).
O comando a seguir Gacutil.exe instala o StringLibrary.resources.dll* no cache de assembly global:
gacutil -i:StringLibrary.resources.dll
A opção /i especifica que Gacutil.exe deve instalar o assembly especificado no cache de assembly global. Após o satélite assembly ser instalado no cache, os recursos que ele contém ficam disponíveis para todos os aplicativos que são projetados para usar o assembly satélite.
Recursos no Cache de Assembly Global: um exemplo
O exemplo a seguir usa um método em uma biblioteca de classes .NET para extrair e retornar uma saudação localizada de um arquivo de recurso. A biblioteca e seus recursos são registrados no cache de assembly global. O exemplo do inclui recursos para as culturas inglesa (Estados Unidos), francesa (França), russa (Rússia) e inglesas. Inglês é a cultura padrão; seus recursos são armazenados no assembly principal. O exemplo inicialmente atrasa a assinatura da biblioteca e seus assemblies satélites com uma chave pública. Em seguida, assina-os novamente com um par de chaves públicas/privadas. Para criar o exemplo, faça o seguinte:
Se você não estiver usando o Visual Studio, use o seguinte comando Ferramenta de Nome Forte (Sn.exe) para criar um par de chaves públicas/privadas denominado ResKey.snk:
sn –k ResKey.snk
Se você estiver usando o Visual Studio, use a guia Assinatura da caixa de diálogo Propriedades do projeto para gerar o arquivo de chave.
Use o seguinte comando Ferramenta de Nome Forte (Sn.exe) para criar um arquivo de chave pública denominado PublicKey.snk:
sn –p ResKey.snk PublicKey.snk
Crie um arquivo de recurso chamado Strings.resx para conter o recurso para a cultura padrão. Armazene uma única cadeia de caracteres denominada
Greeting
cujo valor é "How do you do?" nesse arquivo.Para indicar que “en” é a cultura padrão do aplicativo, adicione o seguinte atributo System.Resources.NeutralResourcesLanguageAttribute ao arquivo AssemblyInfo do aplicativo ou ao arquivo de código-fonte principal que será compilado no assembly principal do aplicativo:
[assembly:NeutralResourcesLanguageAttribute("en")]
<Assembly: NeutralResourcesLanguageAttribute("en")>
Adicione suporte a culturas adicionais (en-US, fr-FR e ru-RU) ao aplicativo da seguinte maneira:
Para oferecer suporte à cultura inglesa (Estados Unidos) ou “en-US”, crie um arquivo de recurso chamado Strings.en-US.resx ou Strings.en-US.txt e armazene nele uma única cadeia de caracteres denominada
Greeting
, cujo valor é "Olá!".Para oferecer suporte à cultura francesa (França) ou “fr-FR”, crie um arquivo de recurso chamado Strings.fr-FR.resx ou Strings.fr-FR.txt e armazene nele uma única cadeia de caracteres denominada
Greeting
, cujo valor é "Bon jour!".Para oferecer suporte à cultura russa (Rússia) ou “ru-RU”, crie um arquivo de recurso chamado Strings.ru-RU.resx ou Strings.ru-RU.txt e armazene nele uma única cadeia de caracteres denominada
Greeting
, cujo valor é "Привет!".
Use resgen.exe para compilar cada texto ou um arquivo de recursos XML em um arquivo binário .resources. A saída é um conjunto de arquivos que têm o mesmo nome de arquivo raiz que os arquivos .resx ou .txt, mas uma extensão .resources. Se você criar o exemplo com o Visual Studio, o processo de compilação será manipulado automaticamente. Se você não estiver usando o Visual Studio, execute o seguinte comando para compilar os arquivos .resx em arquivos .resources:
resgen filename
onde filename é o caminho opcional, o nome e a extensão do arquivo .resx ou texto.
Compile o seguinte código-fonte para StringLibrary.vb ou StringLibrary.cs com os recursos da cultura padrão em um assembly de biblioteca assinado com atraso chamado StringLibrary.dll:
Importante
Se você estiver usando a linha de comando em vez do Visual Studio para criar o exemplo, deverá modificar a chamada ao construtor da classe ResourceManager para
ResourceManager rm = new ResourceManager("Strings",
typeof(Example).Assembly);
.using System; using System.Globalization; using System.Reflection; using System.Resources; using System.Threading; [assembly:NeutralResourcesLanguageAttribute("en")] public class StringLibrary { public string GetGreeting() { ResourceManager rm = new ResourceManager("Strings", Assembly.GetAssembly(typeof(StringLibrary))); string greeting = rm.GetString("Greeting"); return greeting; } }
Imports System.Globalization Imports System.Reflection Imports System.Resources Imports System.Threading <Assembly: NeutralResourcesLanguageAttribute("en")> Public Class StringLibrary Public Function GetGreeting() As String Dim rm As New ResourceManager("Strings", _ Assembly.GetAssembly(GetType(StringLibrary))) Dim greeting As String = rm.GetString("Greeting") Return greeting End Function End Class
O comando para o compilador do C# é:
csc -t:library -resource:Strings.resources -delaysign+ -keyfile:publickey.snk StringLibrary.cs
O comando do compilador Visual Basic correspondente é:
vbc -t:library -resource:Strings.resources -delaysign+ -keyfile:publickey.snk StringLibrary.vb
Crie um subdiretório no diretório do aplicativo principal para cada cultura localizada com suporte do aplicativo. Você deve criar um subdiretório en-US, um fr-FR e um ru-RU. O Visual Studio cria esses subdiretórios automaticamente como parte do processo de compilação. Como todos os assemblies satélite têm o mesmo nome de arquivo, os subdiretórios são usados para armazenar assemblies satélite de cultura específica individuais até que eles sejam assinados com um par de chaves públicas/privadas.
Incorpore os arquivos individuais de culturas específicas .resources a assemblies satélites assinados com atraso e os salve no diretório apropriado. O comando para fazer isso para cada arquivo .resources é:
al -target:lib -embed:Strings.culture.resources -culture:culture -out:culture\StringLibrary.resources.dll -delay+ -keyfile:publickey.snk
onde culture é o nome de uma cultura. Neste exemplo, os nomes de cultura são ru-RU, en-US e fr-FR.
Assine novamente a StringLibrary.dll usando a Ferramenta de Nome Forte (Sn.exe) da seguinte maneira:
sn –R StringLibrary.dll RealKeyPair.snk
Assine novamente os assemblies satélite individuais. Para fazer isso, use a Ferramenta de Nome Forte (sn.exe) da seguinte forma para cada assembly satélite:
sn –R StringLibrary.resources.dll RealKeyPair.snk
Registre a StringLibrary.dll e cada um dos seus assemblies satélites no cache de assembly global usando o comando a seguir:
gacutil -i filename
onde filename é o nome do arquivo para registrar.
Se você estiver usando o Visual Studio, crie um novo projeto Aplicativo de Console chamado
Example
, adicione uma referência à StringLibrary.dll e o seguinte código-fonte a ela e compile.using System; using System.Globalization; using System.Threading; public class Example { public static void Main() { string[] cultureNames = { "en-GB", "en-US", "fr-FR", "ru-RU" }; Random rnd = new Random(); string cultureName = cultureNames[rnd.Next(0, cultureNames.Length)]; Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultureName); Console.WriteLine("The current UI culture is {0}", Thread.CurrentThread.CurrentUICulture.Name); StringLibrary strLib = new StringLibrary(); string greeting = strLib.GetGreeting(); Console.WriteLine(greeting); } }
Imports System.Globalization Imports System.Threading Module Example Public Sub Main() Dim cultureNames() As String = {"en-GB", "en-US", "fr-FR", "ru-RU"} Dim rnd As New Random() Dim cultureName As String = cultureNames(rnd.Next(0, cultureNames.Length)) Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultureName) Console.WriteLine("The current UI culture is {0}", Thread.CurrentThread.CurrentUICulture.Name) Dim strLib As New StringLibrary() Dim greeting As String = strLib.GetGreeting() Console.WriteLine(greeting) End Sub End Module
Para compilar da linha de comando, use o seguinte comando para o compilador do C#:
csc Example.cs -r:StringLibrary.dll
A linha de comando para o compilador do Visual Basic é:
vbc Example.vb -r:StringLibrary.dll
Execute Example.exe.