views:

1192

answers:

2

I'm trying to use the EntLib 3.1 within .net code for a dll which is registered for COM interop. Where do I put the config file?

Alternatively, is there a way to specify within the dll code where it should get the entlib config from? Since my dll will be called from COM I don't always know what exe will be calling it.

I created a simple app which uses entlib Logging, with two classes: 'CallingApp' and 'MyComThing'. When I call a method of MyComThing from CallingApp it logs using the configuration in CallingApp's config file. When I call the method of MyComThing from a vbs script, ie through COM, I get an error "The configuration section for Logging cannot be found in the configuration source". My COMThing.dll.config file is in the same folder as the registered COMThing.dll, ie in the bin\debug\ folder.

thanks!

A: 

Check the related issue which i had faced . Maybe its of some help.

http://stackoverflow.com/questions/333595/how-to-include-com-components-on-a-published-net-site

Samiksha
thanks but I'm not having problems creating the COM obj, the problem is EntLib uses the exe's config file for configuration info. I have a COM object and want to use EntLib's Logging, but I don't know what exe's will be calling the COM object so want to specify a diff location for the config file.
Rory
+3  A: 

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}&#xD;&#xA;Message: {message}&#xD;&#xA;Category: {category}&#xD;&#xA;Priority: {priority}&#xD;&#xA;EventId: {eventid}&#xD;&#xA;Severity: {severity}&#xD;&#xA;Title:{title}&#xD;&#xA;Machine: {machine}&#xD;&#xA;Application Domain: {appDomain}&#xD;&#xA;Process Id: {processId}&#xD;&#xA;Process Name: {processName}&#xD;&#xA;Win32 Thread Id: {win32ThreadId}&#xD;&#xA;Thread Name: {threadName}&#xD;&#xA;Extended Properties: {dictionary({key} - {value}&#xD;&#xA;)}"
        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 &amp; 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.

Rory