Tag Archives: Microsoft Office

Visio 2003 IFilter and MTA

I recently had a problem instantiating the Visio 2003 IFilter from C#….

The following code instantiates an IFilter using COM Interop. The CLSID
is obtained from the registry (method excluded for brevity) and then used to
obtain the COM object type, the activator then instantiates the object from
the type. I tested the code from a console C# application and it works
great. The CLSID is correctly obtained from the registry for a given
VSD extension and then the COM object is instantiated by the activator.

When I call this same code from a WinForm application the method throws
an invalid cast exception when casting an activated object to an
IFilter. Stepping through with the debugger shows me that the CLSID is
obtained correctly, the type is returned from Type.GetTypeFromCLSID,
and the call to the activator also succeeds.

private static void LoadIFilterFromSource(string fileName, ref IFilter filter)
{
// Get the extension.
string ext = Path.GetExtension(fileName);
if (null == ext || 0 == ext.Length)
throw new FilterException(“File does not have a known extension!”);

// Get the CLSID of the filter by extension.
string clsID = GetFilterCLSID(ext);
if (null != clsID && clsID.Length > 0)
{
// Get filter instance.
try
{
Type t = Type.GetTypeFromCLSID(new Guid(clsID));
if (null != t)
{
object filtObj = Activator.CreateInstance(t);
filter = (IFilter) filtObj;
// ^^^^^ Cast Exception thrown here!
}
}
catch (Exception ex)
{
throw new FilterException(“Failed to create IFilter instance!”, ex);
}
}

// Did we load the filter?
if (null == filter)
{
// No, attempt to load via COM.
try
{
IUnknown outer = null;
LoadIFilter(fileName, ref outer, ref filter);
}
catch (COMException ex)
{
throw new FilterException(“Failed to create IFilter instance!”, ex);
}
}
}

I read somewhere that the threading model for the COM component matters when calling from a multi threaded environment. So, I made sure the calling code to my method is running in a Single-Threaded-Apartment (STA) thread. This change from Multi threaded Apartment (MTA) to STA fixed the same problem with the Adobe PDF IFilter, but did nothing for the Visio IFilter.

I had just about given up when I decided to check the threading model in the registry. I noticed that the PDF IFilter’s threading model is set to Apartment, whereas the Visio threading model is blank. Setting the following registry REG_SZ value to Apartment fixes the problem with Visio also.

HKLMSOFTWAREClassesCLSID{FAEA5B46-761B-400E-B53E-E805A97A543E}InprocServer32ThreadingModel.

I’m not sure if this is an problem with the installation of the Visio IFilter or intended. When I reinstalled the IFilter the threading model was set back to empty. It’s not an elegant solution, but it seems to work.

I-Filters

Today Carved out a chunk of the day to work on I-Filters. I-Filters are COM
dynamic link libraries that convert known file types to text under
Windows XP/2K/2K3. The OS’s indexing service uses I-Filters to convert
PDF and Office file types to text so the indexer can tokenize words
contained in files.

I wrote a test application that calls an I-Filter
library given a file name and converts it to text. The correct filter is determined by
examining the file extension and querying the registry (I-Filters are
registered with associated file extensions). My code works great with
Office documents but barfs when using Adobe’s 6.0 I-Filter.

Below is a synopsis of the method that does the work of invoking the
filter (leave a comment if you want the rest of the code). The CLSID is
the class ID of the filter, read from the registry.

(Apologies for no syntax highlighting)

private static string ExecuteFilter(string clsID, string sourceFile)

{

  string result = String.Empty;

  // Some filters are not reentrant, such as Adobe PDF filter.

  lock(_lock)

  {

    object itfc = null;

    try

    {

      // Get the filter type from CLSID.

      Type t = Type.GetTypeFromCLSID(new Guid(clsID));

      if (null != t)

      {

        // Get filter instance.

        itfc = Activator.CreateInstance(t);

        // Cast to IPersistFile.

        IFilter ifilt = (IFilter)(itfc);

        System.Runtime.InteropServices.UCOMIPersistFile ipf =

           (System.Runtime.InteropServices.UCOMIPersistFile)(ifilt);

        // Load source.

        ipf.Load(sourceFile, 0);

        // Initialize.

        uint i = 0;

        int hr = 0;

        STAT_CHUNK chunk = new STAT_CHUNK();

        ifilt.Init(IFILTER_INIT.NONE, 0, null, ref i);

        // Read the in chunks.

        StringBuilder masterBuffer = new StringBuilder();

        while (0 == hr)

        {

          // Read next chunk structure.

          try

          {

            hr = ifilt.GetChunk(out chunk);

          }

          catch (COMException ex)

          {

            //
Get Chunk will throw an exception
            // when no more chunks to read – tsk.

            if (FILTER_E_END_OF_CHUNKS == ex.ErrorCode)

              hr = ex.ErrorCode;

            else

              throw ex;

          }

          // if chunk is text..

          if (0 == hr && CHUNKSTATE.CHUNK_TEXT == chunk.flags)

          {

            // Read text to buffer.

            uint bufferSize = CHUNK_SIZE;

            int hr2 = 0;

            while (FILTER_S_LAST_TEXT != hr2 || 0 == hr2)

            {

              bufferSize = CHUNK_SIZE;

             
StringBuilder buffer = new StringBuilder((int)bufferSize);

              hr2 = ifilt.GetText(ref bufferSize, buffer);

             
masterBuffer.Append(buffer.ToString(0, (int)bufferSize));

            }

            // Did we get an error?

            if
(FILTER_E_NO_MORE_TEXT != hr2 && FILTER_S_LAST_TEXT != hr2)

             
throw new Exception(“Failed reading data from chunk!”);

          }

        }

        // Assign result.

        result = masterBuffer.ToString();

      }

    }

    catch (Exception ex)

    {

      throw new FileLoadException(“Failed to read data from filter!”, ex);

    }

    finally

    {

      if (null != itfc)

        Marshal.ReleaseComObject(itfc);

    }

  }

return result;

}

Follow

Get every new post delivered to your Inbox.

Join 294 other followers

%d bloggers like this: