The answer is that Enterprise Library by default uses the exe's config file. If you're producing a dll, including COM, then for good reason you might not want to depend on the calling executable. One solution to this (there might be others) is to create the Enterprise Library objects yourself instead of using the default ones, and tell them where to get the configuration from. This isn't as scary as it seems and doesn't require recompiling entlib or anything like that.
Instead of simply using Logger.Write() I did the following:
a) Create the log writer, using the dll's config file:
string dllConfigFilename = Assembly.GetExecutingAssembly().Location + ".config";
FileConfigurationSource exceptionsSource = new FileConfigurationSource(dllConfigFilename);
LogWriterFactory writerFactory = new LogWriterFactory(exceptionsSource);
logWriter = writerFactory.Create();
b) Then use this log writer within your code:
LogEntry log = new LogEntry();
log.Message = message;
log.Categories = new string[] { "General" };
logWriter.Write(log);
Here's the full code for a sample object I created. The references were Microsoft.Practices.EnterpriseLibrary.Common, Microsoft.Practices.EnterpriseLibrary.Logging, Microsoft.Practices.ObjectBuilder, System, System.Data, System.Windows.Forms, System.Xml:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Reflection;
using System.IO;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
namespace COMThing
{
[ComVisible(true)]
public class MyComThing : MyComInterface
{
LogWriter logWriter;
public MyComThing()
{
string dllConfigFilename = Assembly.GetExecutingAssembly().Location + ".config";
FileConfigurationSource exceptionsSource = new FileConfigurationSource(dllConfigFilename);
LogWriterFactory writerFactory = new LogWriterFactory(exceptionsSource);
logWriter = writerFactory.Create();
}
public bool ProcessMessage(string message)
{
LogEntry log = new LogEntry();
log.Message = message;
log.Categories = new string[] { "General" };
logWriter.Write(log);
MessageBox.Show(message);
return true;
}
}
}
The project included a COMThing.dll.config file which i set 'Copy to Output Directory' to 'Copy always'. This is a trivial config that writes log information to the Application Event Log. The contents of the config file are:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</configSections>
<loggingConfiguration name="Logging Application Block" tracingEnabled="true"
defaultCategory="General" logWarningsWhenNoCategoriesMatch="true">
<listeners>
<add source="COMThing Logger" formatter="Text Formatter" log="Application"
machineName="" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FormattedEventLogTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
traceOutputOptions="None" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedEventLogTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
name="Formatted EventLog TraceListener" />
</listeners>
<formatters>
<add template="Timestamp: {timestamp}
Message: {message}
Category: {category}
Priority: {priority}
EventId: {eventid}
Severity: {severity}
Title:{title}
Machine: {machine}
Application Domain: {appDomain}
Process Id: {processId}
Process Name: {processName}
Win32 Thread Id: {win32ThreadId}
Thread Name: {threadName}
Extended Properties: {dictionary({key} - {value}
)}"
type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
name="Text Formatter" />
</formatters>
<categorySources>
<add switchValue="All" name="General">
<listeners>
<add name="Formatted EventLog TraceListener" />
</listeners>
</add>
</categorySources>
<specialSources>
<allEvents switchValue="All" name="All Events">
<listeners>
<add name="Formatted EventLog TraceListener" />
</listeners>
</allEvents>
<notProcessed switchValue="All" name="Unprocessed Category" />
<errors switchValue="All" name="Logging Errors & Warnings">
<listeners>
<add name="Formatted EventLog TraceListener" />
</listeners>
</errors>
</specialSources>
</loggingConfiguration>
</configuration>
In the project properties under Build check 'Register for COM interop'. Build the project, then create the following .vbs file:
Set obj = CreateObject("COMThing.MyComThing")
obj.ProcessMessage("called from com by vbs")
If you double-click this vbs file it should show a message box with the text 'called from com by vbs' and write an entry to your Application Event Log. This demonstrates that while the executing process is C:\WINDOWS\System32\WScript.exe (or similar), it's getting the config from your dll's config file.
I based this on the information here under 'Using Several ConfigurationSources'.
Note that the Logger class includes lots of nice helper methods with different arguments. Since we're using the LogWriter class we don't get this magic. Personally I'll be creating another class within my library to perform the same job, based on Logger.
The referenced article shows the same principle applied to Database and Exception application blocks. Presumably the same model can be applied to most/all of them.