diff --git a/DDMonorepo.sln b/DDMonorepo.sln
index 84f3fecd..e0cb67bf 100644
--- a/DDMonorepo.sln
+++ b/DDMonorepo.sln
@@ -146,6 +146,8 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Base", "Modules.Base\Base\B
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "MessageBoxEx", "MessageBoxEx\MessageBoxEx.vbproj", "{EF29F400-BE45-4283-9D18-CA7ACD9ACCC9}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FilterReader", "FilterReader\FilterReader.csproj", "{10C922FB-DD8D-4E0B-A50C-30EE658FBDDC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -392,6 +394,10 @@ Global
{EF29F400-BE45-4283-9D18-CA7ACD9ACCC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF29F400-BE45-4283-9D18-CA7ACD9ACCC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF29F400-BE45-4283-9D18-CA7ACD9ACCC9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10C922FB-DD8D-4E0B-A50C-30EE658FBDDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10C922FB-DD8D-4E0B-A50C-30EE658FBDDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10C922FB-DD8D-4E0B-A50C-30EE658FBDDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10C922FB-DD8D-4E0B-A50C-30EE658FBDDC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -457,6 +463,7 @@ Global
{E3DA65CA-964D-4896-991A-B5426884E215} = {7AF3F9C2-C939-4A08-95C1-0453207E298A}
{6EA0C51F-C2B1-4462-8198-3DE0B32B74F8} = {3E2008C8-27B1-41DD-9B1A-0C4029F6AECC}
{EF29F400-BE45-4283-9D18-CA7ACD9ACCC9} = {F98C0329-C004-417F-B2AB-7466E88D8220}
+ {10C922FB-DD8D-4E0B-A50C-30EE658FBDDC} = {3E2008C8-27B1-41DD-9B1A-0C4029F6AECC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C1BE4090-A0FD-48AF-86CB-39099D14B286}
diff --git a/FilterReader/ComHelper.cs b/FilterReader/ComHelper.cs
new file mode 100644
index 00000000..33e904b8
--- /dev/null
+++ b/FilterReader/ComHelper.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace DigitalData.Modules.FilterReader
+{
+ [ComVisible(false)]
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000001-0000-0000-C000-000000000046")]
+ internal interface IClassFactory
+ {
+ void CreateInstance([MarshalAs(UnmanagedType.Interface)] object pUnkOuter, ref Guid refiid, [MarshalAs(UnmanagedType.Interface)] out object ppunk);
+ void LockServer(bool fLock);
+ }
+
+ ///
+ /// Utility class to get a Class Factory for a certain Class ID
+ /// by loading the dll that implements that class
+ ///
+ internal static class ComHelper
+ {
+ //DllGetClassObject fuction pointer signature
+ private delegate int DllGetClassObject(ref Guid ClassId, ref Guid InterfaceId, [Out, MarshalAs(UnmanagedType.Interface)] out object ppunk);
+
+ //Some win32 methods to load\unload dlls and get a function pointer
+ private class Win32NativeMethods
+ {
+ [DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
+ public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
+
+ [DllImport("kernel32.dll")]
+ public static extern bool FreeLibrary(IntPtr hModule);
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr LoadLibrary(string lpFileName);
+ }
+
+ ///
+ /// Holds a list of dll handles and unloads the dlls
+ /// in the destructor
+ ///
+ private class DllList
+ {
+ private List _dllList = new List();
+ public void AddDllHandle(IntPtr dllHandle)
+ {
+ lock (_dllList)
+ {
+ _dllList.Add(dllHandle);
+ }
+ }
+
+ ~DllList()
+ {
+ foreach (IntPtr dllHandle in _dllList)
+ {
+ try
+ {
+ Win32NativeMethods.FreeLibrary(dllHandle);
+ }
+ catch { };
+ }
+ }
+ }
+
+ static DllList _dllList = new DllList();
+
+ ///
+ /// Gets a class factory for a specific COM Class ID.
+ ///
+ /// The dll where the COM class is implemented
+ /// The requested Class ID
+ /// IClassFactory instance used to create instances of that class
+ internal static IClassFactory GetClassFactory(string dllName, string filterPersistClass)
+ {
+ //Load the class factory from the dll
+ IClassFactory classFactory = GetClassFactoryFromDll(dllName, filterPersistClass);
+ return classFactory;
+ }
+
+ private static IClassFactory GetClassFactoryFromDll(string dllName, string filterPersistClass)
+ {
+ //Load the dll
+ IntPtr dllHandle = Win32NativeMethods.LoadLibrary(dllName);
+ if (dllHandle == IntPtr.Zero)
+ return null;
+
+ //Keep a reference to the dll until the process\AppDomain dies
+ _dllList.AddDllHandle(dllHandle);
+
+ //Get a pointer to the DllGetClassObject function
+ IntPtr dllGetClassObjectPtr = Win32NativeMethods.GetProcAddress(dllHandle, "DllGetClassObject");
+ if (dllGetClassObjectPtr == IntPtr.Zero)
+ return null;
+
+ //Convert the function pointer to a .net delegate
+ DllGetClassObject dllGetClassObject = (DllGetClassObject)Marshal.GetDelegateForFunctionPointer(dllGetClassObjectPtr, typeof(DllGetClassObject));
+
+ //Call the DllGetClassObject to retreive a class factory for out Filter class
+ Guid filterPersistGUID = new Guid(filterPersistClass);
+ Guid IClassFactoryGUID = new Guid("00000001-0000-0000-C000-000000000046"); //IClassFactory class id
+ Object unk;
+ if (dllGetClassObject(ref filterPersistGUID, ref IClassFactoryGUID, out unk) != 0)
+ return null;
+
+ //Yippie! cast the returned object to IClassFactory
+ return (unk as IClassFactory);
+ }
+ }
+}
diff --git a/FilterReader/FilterLoader.cs b/FilterReader/FilterLoader.cs
new file mode 100644
index 00000000..ab5b0688
--- /dev/null
+++ b/FilterReader/FilterLoader.cs
@@ -0,0 +1,225 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Win32;
+using System.IO;
+using System.Runtime.InteropServices.ComTypes;
+using System.Runtime.InteropServices;
+
+namespace DigitalData.Modules.FilterReader
+{
+ ///
+ /// FilterLoader finds the dll and ClassID of the COM object responsible
+ /// for filtering a specific file extension.
+ /// It then loads that dll, creates the appropriate COM object and returns
+ /// a pointer to an IFilter instance
+ ///
+ static class FilterLoader
+ {
+ #region CacheEntry
+ private class CacheEntry
+ {
+ public string DllName;
+ public string ClassName;
+
+ public CacheEntry(string dllName, string className)
+ {
+ DllName = dllName;
+ ClassName = className;
+ }
+ }
+ #endregion
+
+ static Dictionary _cache = new Dictionary();
+
+ #region Registry Read String helper
+ static string ReadStrFromHKLM(string key)
+ {
+ return ReadStrFromHKLM(key, null);
+ }
+ static string ReadStrFromHKLM(string key, string value)
+ {
+ RegistryKey rk = Registry.LocalMachine.OpenSubKey(key);
+ if (rk == null)
+ return null;
+
+ using (rk)
+ {
+ return (string)rk.GetValue(value);
+ }
+ }
+ #endregion
+
+ ///
+ /// finds an IFilter implementation for a file type
+ ///
+ /// The extension of the file
+ /// an IFilter instance used to retreive text from that file type
+ private static IFilter LoadIFilter(string ext)
+ {
+ //Find the dll and ClassID
+ if (GetFilterDllAndClass(ext, out string dllName, out string filterPersistClass))
+ {
+ //load the dll and return an IFilter instance.
+ return LoadFilterFromDll(dllName, filterPersistClass);
+ }
+ return null;
+ }
+
+ internal static IFilter LoadAndInitIFilter(string fileName)
+ {
+ return LoadAndInitIFilter(fileName, Path.GetExtension(fileName));
+ }
+
+ internal static IFilter LoadAndInitIFilter(string fileName, string extension)
+ {
+ IFilter filter = LoadIFilter(extension);
+ if (filter == null)
+ return null;
+
+ IPersistFile persistFile = (filter as IPersistFile);
+ if (persistFile != null)
+ {
+ persistFile.Load(fileName, 0);
+ IFILTER_FLAGS flags;
+ IFILTER_INIT iflags =
+ IFILTER_INIT.CANON_HYPHENS |
+ IFILTER_INIT.CANON_PARAGRAPHS |
+ IFILTER_INIT.CANON_SPACES |
+ IFILTER_INIT.APPLY_INDEX_ATTRIBUTES |
+ IFILTER_INIT.HARD_LINE_BREAKS |
+ IFILTER_INIT.FILTER_OWNED_VALUE_OK;
+
+ if (filter.Init(iflags, 0, IntPtr.Zero, out flags) == IFilterReturnCode.S_OK)
+ return filter;
+ }
+ //If we failed to retreive an IPersistFile interface or to initialize
+ //the filter, we release it and return null.
+ Marshal.ReleaseComObject(filter);
+ return null;
+ }
+
+ private static IFilter LoadFilterFromDll(string dllName, string filterPersistClass)
+ {
+ //Get a classFactory for our classID
+ IClassFactory classFactory = ComHelper.GetClassFactory(dllName, filterPersistClass);
+ if (classFactory == null)
+ return null;
+
+ //And create an IFilter instance using that class factory
+ Guid IFilterGUID = new Guid("89BCB740-6119-101A-BCB7-00DD010655AF");
+ Object obj;
+ classFactory.CreateInstance(null, ref IFilterGUID, out obj);
+ return (obj as IFilter);
+ }
+
+ private static bool GetFilterDllAndClass(string ext, out string dllName, out string filterPersistClass)
+ {
+ if (!GetFilterDllAndClassFromCache(ext, out dllName, out filterPersistClass))
+ {
+ string persistentHandlerClass;
+
+ persistentHandlerClass = GetPersistentHandlerClass(ext, true);
+ if (persistentHandlerClass != null)
+ {
+ GetFilterDllAndClassFromPersistentHandler(persistentHandlerClass,
+ out dllName, out filterPersistClass);
+ }
+ AddExtensionToCache(ext, dllName, filterPersistClass);
+ }
+ return (dllName != null && filterPersistClass != null);
+ }
+
+ private static void AddExtensionToCache(string ext, string dllName, string filterPersistClass)
+ {
+ lock (_cache)
+ {
+ _cache.Add(ext.ToLower(), new CacheEntry(dllName, filterPersistClass));
+ }
+ }
+
+ private static bool GetFilterDllAndClassFromPersistentHandler(string persistentHandlerClass, out string dllName, out string filterPersistClass)
+ {
+ dllName = null;
+ filterPersistClass = null;
+
+ //Read the CLASS ID of the IFilter persistent handler
+ filterPersistClass = ReadStrFromHKLM(@"Software\Classes\CLSID\" + persistentHandlerClass +
+ @"\PersistentAddinsRegistered\{89BCB740-6119-101A-BCB7-00DD010655AF}");
+ if (String.IsNullOrEmpty(filterPersistClass))
+ return false;
+
+ //Read the dll name
+ dllName = ReadStrFromHKLM(@"Software\Classes\CLSID\" + filterPersistClass +
+ @"\InprocServer32");
+ return (!String.IsNullOrEmpty(dllName));
+ }
+
+ private static string GetPersistentHandlerClass(string ext, bool searchContentType)
+ {
+ //Try getting the info from the file extension
+ string persistentHandlerClass = GetPersistentHandlerClassFromExtension(ext);
+ if (string.IsNullOrEmpty(persistentHandlerClass))
+ //try getting the info from the document type
+ persistentHandlerClass = GetPersistentHandlerClassFromDocumentType(ext);
+ if (searchContentType && string.IsNullOrEmpty(persistentHandlerClass))
+ //Try getting the info from the Content Type
+ persistentHandlerClass = GetPersistentHandlerClassFromContentType(ext);
+ return persistentHandlerClass;
+ }
+
+ private static string GetPersistentHandlerClassFromContentType(string ext)
+ {
+ string contentType = ReadStrFromHKLM(@"Software\Classes\" + ext, "Content Type");
+ if (string.IsNullOrEmpty(contentType))
+ return null;
+
+ string contentTypeExtension = ReadStrFromHKLM(@"Software\Classes\MIME\Database\Content Type\" + contentType,
+ "Extension");
+ if (ext.Equals(contentTypeExtension, StringComparison.CurrentCultureIgnoreCase))
+ return null; //No need to look further. This extension does not have any persistent handler
+
+ //We know the extension that is assciated with that content type. Simply try again with the new extension
+ return GetPersistentHandlerClass(contentTypeExtension, false); //Don't search content type this time.
+ }
+
+ private static string GetPersistentHandlerClassFromDocumentType(string ext)
+ {
+ //Get the DocumentType of this file extension
+ string docType = ReadStrFromHKLM(@"Software\Classes\" + ext);
+ if (String.IsNullOrEmpty(docType))
+ return null;
+
+ //Get the Class ID for this document type
+ string docClass = ReadStrFromHKLM(@"Software\Classes\" + docType + @"\CLSID");
+ if (String.IsNullOrEmpty(docType))
+ return null;
+
+ //Now get the PersistentHandler for that Class ID
+ return ReadStrFromHKLM(@"Software\Classes\CLSID\" + docClass + @"\PersistentHandler");
+ }
+
+ private static string GetPersistentHandlerClassFromExtension(string ext)
+ {
+ return ReadStrFromHKLM(@"Software\Classes\" + ext + @"\PersistentHandler");
+ }
+
+ private static bool GetFilterDllAndClassFromCache(string ext, out string dllName, out string filterPersistClass)
+ {
+ string lowerExt = ext.ToLower();
+ lock (_cache)
+ {
+ CacheEntry cacheEntry;
+ if (_cache.TryGetValue(lowerExt, out cacheEntry))
+ {
+ dllName = cacheEntry.DllName;
+ filterPersistClass = cacheEntry.ClassName;
+ return true;
+ }
+ }
+ dllName = null;
+ filterPersistClass = null;
+ return false;
+ }
+ }
+}
diff --git a/FilterReader/FilterReader.cs b/FilterReader/FilterReader.cs
new file mode 100644
index 00000000..07240d6c
--- /dev/null
+++ b/FilterReader/FilterReader.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace DigitalData.Modules.FilterReader
+{
+ ///
+ /// Implements a TextReader that reads from an IFilter.
+ ///
+ public class FilterReader : TextReader
+ {
+ IFilter _filter;
+ private bool _done;
+ private STAT_CHUNK _currentChunk;
+ private bool _currentChunkValid;
+ private char[] _charsLeftFromLastRead;
+
+ public override void Close()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ~FilterReader()
+ {
+ Dispose(false);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (_filter != null)
+ Marshal.ReleaseComObject(_filter);
+ }
+
+ public override int Read(char[] array, int offset, int count)
+ {
+ int endOfChunksCount = 0;
+ int charsRead = 0;
+
+ while (!_done && charsRead < count)
+ {
+ if (_charsLeftFromLastRead != null)
+ {
+ int charsToCopy = (_charsLeftFromLastRead.Length < count - charsRead) ? _charsLeftFromLastRead.Length : count - charsRead;
+ Array.Copy(_charsLeftFromLastRead, 0, array, offset + charsRead, charsToCopy);
+ charsRead += charsToCopy;
+ if (charsToCopy < _charsLeftFromLastRead.Length)
+ {
+ char[] tmp = new char[_charsLeftFromLastRead.Length - charsToCopy];
+ Array.Copy(_charsLeftFromLastRead, charsToCopy, tmp, 0, tmp.Length);
+ _charsLeftFromLastRead = tmp;
+ }
+ else
+ _charsLeftFromLastRead = null;
+ continue;
+ };
+
+ if (!_currentChunkValid)
+ {
+ IFilterReturnCode res = _filter.GetChunk(out _currentChunk);
+ _currentChunkValid = (res == IFilterReturnCode.S_OK) && ((_currentChunk.flags & CHUNKSTATE.CHUNK_TEXT) != 0);
+
+ if (res == IFilterReturnCode.FILTER_E_END_OF_CHUNKS)
+ endOfChunksCount++;
+
+ if (endOfChunksCount > 1)
+ _done = true; //That's it. no more chuncks available
+ }
+
+ if (_currentChunkValid)
+ {
+ uint bufLength = (uint)(count - charsRead);
+ if (bufLength < 8192)
+ bufLength = 8192; //Read ahead
+
+ char[] buffer = new char[bufLength];
+ IFilterReturnCode res = _filter.GetText(ref bufLength, buffer);
+ if (res == IFilterReturnCode.S_OK || res == IFilterReturnCode.FILTER_S_LAST_TEXT)
+ {
+ int cRead = (int)bufLength;
+ if (cRead + charsRead > count)
+ {
+ int charsLeft = (cRead + charsRead - count);
+ _charsLeftFromLastRead = new char[charsLeft];
+ Array.Copy(buffer, cRead - charsLeft, _charsLeftFromLastRead, 0, charsLeft);
+ cRead -= charsLeft;
+ }
+ else
+ _charsLeftFromLastRead = null;
+
+ Array.Copy(buffer, 0, array, offset + charsRead, cRead);
+ charsRead += cRead;
+ }
+
+ if (res == IFilterReturnCode.FILTER_S_LAST_TEXT || res == IFilterReturnCode.FILTER_E_NO_MORE_TEXT)
+ _currentChunkValid = false;
+ }
+ }
+ return charsRead;
+ }
+
+ public override string ReadToEnd()
+ {
+ IList chunks = new List();
+
+ // read all the chunks
+ IFilterReturnCode chunckResult = _filter.GetChunk(out _currentChunk);
+ while (chunckResult != IFilterReturnCode.FILTER_E_END_OF_CHUNKS)
+ {
+
+ // process only text type chunks
+ bool textChunk = (chunckResult == IFilterReturnCode.S_OK) && ((_currentChunk.flags & CHUNKSTATE.CHUNK_TEXT) != 0);
+ if (textChunk)
+ {
+
+ string chunkText = "";
+ uint bufLength = 8 * 1024;
+ char[] buffer = new char[bufLength];
+
+ // build chunk list of strings
+ IFilterReturnCode textResult = _filter.GetText(ref bufLength, buffer);
+ while (textResult == IFilterReturnCode.S_OK || textResult == IFilterReturnCode.FILTER_S_LAST_TEXT)
+ {
+ chunkText += new string(buffer).Replace("\0", "").Replace("\t", " ") + " ";
+
+ if (textResult == IFilterReturnCode.S_OK)
+ {
+
+ // read more text
+ buffer = new char[bufLength]; // get fresh buffer
+ bufLength = 8 * 1024;
+ textResult = _filter.GetText(ref bufLength, buffer);
+
+ if (textResult != IFilterReturnCode.S_OK)
+ {
+ chunkText = chunkText.Trim();
+ if (!string.IsNullOrEmpty(chunkText))
+ chunks.Add(chunkText);
+ }
+ }
+ else
+ {
+
+ // stop reading text
+ textResult = IFilterReturnCode.FILTER_E_NO_MORE_TEXT;
+ chunkText = chunkText.Trim();
+ if (!string.IsNullOrEmpty(chunkText))
+ chunks.Add(chunkText);
+ }
+ }
+ }
+
+ // get next chunk
+ chunckResult = _filter.GetChunk(out _currentChunk);
+ }
+
+ return string.Join("\r\n", chunks);
+ }
+
+ public FilterReader(string fileName)
+ {
+ _filter = FilterLoader.LoadAndInitIFilter(fileName);
+ if (_filter == null)
+ throw new ArgumentException("no filter defined for " + fileName);
+ }
+ }
+}
diff --git a/FilterReader/FilterReader.csproj b/FilterReader/FilterReader.csproj
new file mode 100644
index 00000000..b09c7e4c
--- /dev/null
+++ b/FilterReader/FilterReader.csproj
@@ -0,0 +1,51 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {10C922FB-DD8D-4E0B-A50C-30EE658FBDDC}
+ Library
+ Properties
+ DigitalData.Modules.FilterReader
+ DigitalData.Modules.FilterReader
+ v4.6.2
+ 512
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FilterReader/IFilter.cs b/FilterReader/IFilter.cs
new file mode 100644
index 00000000..99483595
--- /dev/null
+++ b/FilterReader/IFilter.cs
@@ -0,0 +1,435 @@
+using System;
+using System.Text;
+using System.Runtime.InteropServices;
+
+//Contains IFilter interface translation
+//Most translations are from PInvoke.net
+
+namespace DigitalData.Modules.FilterReader
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct FULLPROPSPEC
+ {
+ public Guid guidPropSet;
+ public PROPSPEC psProperty;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct FILTERREGION
+ {
+ public int idChunk;
+ public int cwcStart;
+ public int cwcExtent;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct PROPSPEC
+ {
+ [FieldOffset(0)] public int ulKind; // 0 - string used; 1 - PROPID
+ [FieldOffset(4)] public int propid;
+ [FieldOffset(4)] public IntPtr lpwstr;
+ }
+
+ [Flags]
+ internal enum IFILTER_FLAGS
+ {
+ ///
+ /// The caller should use the IPropertySetStorage and IPropertyStorage
+ /// interfaces to locate additional properties.
+ /// When this flag is set, properties available through COM
+ /// enumerators should not be returned from IFilter.
+ ///
+ IFILTER_FLAGS_OLE_PROPERTIES = 1
+ }
+
+ ///
+ /// Flags controlling the operation of the FileFilter
+ /// instance.
+ ///
+ [Flags]
+ internal enum IFILTER_INIT
+ {
+ NONE = 0,
+ ///
+ /// Paragraph breaks should be marked with the Unicode PARAGRAPH
+ /// SEPARATOR (0x2029)
+ ///
+ CANON_PARAGRAPHS = 1,
+
+ ///
+ /// Soft returns, such as the newline character in Microsoft Word, should
+ /// be replaced by hard returnsLINE SEPARATOR (0x2028). Existing hard
+ /// returns can be doubled. A carriage return (0x000D), line feed (0x000A),
+ /// or the carriage return and line feed in combination should be considered
+ /// a hard return. The intent is to enable pattern-expression matches that
+ /// match against observed line breaks.
+ ///
+ HARD_LINE_BREAKS = 2,
+
+ ///
+ /// Various word-processing programs have forms of hyphens that are not
+ /// represented in the host character set, such as optional hyphens
+ /// (appearing only at the end of a line) and nonbreaking hyphens. This flag
+ /// indicates that optional hyphens are to be converted to nulls, and
+ /// non-breaking hyphens are to be converted to normal hyphens (0x2010), or
+ /// HYPHEN-MINUSES (0x002D).
+ ///
+ CANON_HYPHENS = 4,
+
+ ///
+ /// Just as the CANON_HYPHENS flag standardizes hyphens,
+ /// this one standardizes spaces. All special space characters, such as
+ /// nonbreaking spaces, are converted to the standard space character
+ /// (0x0020).
+ ///
+ CANON_SPACES = 8,
+
+ ///
+ /// Indicates that the client wants text split into chunks representing
+ /// public value-type properties.
+ ///
+ APPLY_INDEX_ATTRIBUTES = 16,
+
+ ///
+ /// Indicates that the client wants text split into chunks representing
+ /// properties determined during the indexing process.
+ ///
+ APPLY_CRAWL_ATTRIBUTES = 256,
+
+ ///
+ /// Any properties not covered by the APPLY_INDEX_ATTRIBUTES
+ /// and APPLY_CRAWL_ATTRIBUTES flags should be emitted.
+ ///
+ APPLY_OTHER_ATTRIBUTES = 32,
+
+ ///
+ /// Optimizes IFilter for indexing because the client calls the
+ /// IFilter::Init method only once and does not call IFilter::BindRegion.
+ /// This eliminates the possibility of accessing a chunk both before and
+ /// after accessing another chunk.
+ ///
+ INDEXING_ONLY = 64,
+
+ ///
+ /// The text extraction process must recursively search all linked
+ /// objects within the document. If a link is unavailable, the
+ /// IFilter::GetChunk call that would have obtained the first chunk of the
+ /// link should return FILTER_E_LINK_UNAVAILABLE.
+ ///
+ SEARCH_LINKS = 128,
+
+ ///
+ /// The content indexing process can return property values set by the filter.
+ ///
+ FILTER_OWNED_VALUE_OK = 512
+ }
+
+ public struct STAT_CHUNK
+ {
+ ///
+ /// The chunk identifier. Chunk identifiers must be unique for the
+ /// current instance of the IFilter interface.
+ /// Chunk identifiers must be in ascending order. The order in which
+ /// chunks are numbered should correspond to the order in which they appear
+ /// in the source document. Some search engines can take advantage of the
+ /// proximity of chunks of various properties. If so, the order in which
+ /// chunks with different properties are emitted will be important to the
+ /// search engine.
+ ///
+ public int idChunk;
+
+ ///
+ /// The type of break that separates the previous chunk from the current
+ /// chunk. Values are from the CHUNK_BREAKTYPE enumeration.
+ ///
+ [MarshalAs(UnmanagedType.U4)]
+ public CHUNK_BREAKTYPE breakType;
+
+ ///
+ /// Flags indicate whether this chunk contains a text-type or a
+ /// value-type property.
+ /// Flag values are taken from the CHUNKSTATE enumeration. If the CHUNK_TEXT flag is set,
+ /// IFilter::GetText should be used to retrieve the contents of the chunk
+ /// as a series of words.
+ /// If the CHUNK_VALUE flag is set, IFilter::GetValue should be used to retrieve
+ /// the value and treat it as a single property value. If the filter dictates that the same
+ /// content be treated as both text and as a value, the chunk should be emitted twice in two
+ /// different chunks, each with one flag set.
+ ///
+ [MarshalAs(UnmanagedType.U4)]
+ public CHUNKSTATE flags;
+
+ ///
+ /// The language and sublanguage associated with a chunk of text. Chunk locale is used
+ /// by document indexers to perform proper word breaking of text. If the chunk is
+ /// neither text-type nor a value-type with data type VT_LPWSTR, VT_LPSTR or VT_BSTR,
+ /// this field is ignored.
+ ///
+ public int locale;
+
+ ///
+ /// The property to be applied to the chunk. If a filter requires that the same text
+ /// have more than one property, it needs to emit the text once for each property
+ /// in separate chunks.
+ ///
+ public FULLPROPSPEC attribute;
+
+ ///
+ /// The ID of the source of a chunk. The value of the idChunkSource member depends on the nature of the chunk:
+ /// If the chunk is a text-type property, the value of the idChunkSource member must be the same as the value of the idChunk member.
+ /// If the chunk is an public value-type property derived from textual content, the value of the idChunkSource member is the chunk ID for the
+ /// text-type chunk from which it is derived.
+ /// If the filter attributes specify to return only public value-type
+ /// properties, there is no content chunk from which to derive the current
+ /// public value-type property. In this case, the value of the
+ /// idChunkSource member must be set to zero, which is an invalid chunk.
+ ///
+ public int idChunkSource;
+
+ ///
+ /// The offset from which the source text for a derived chunk starts in
+ /// the source chunk.
+ ///
+ public int cwcStartSource;
+
+ ///
+ /// The length in characters of the source text from which the current
+ /// chunk was derived.
+ /// A zero value signifies character-by-character correspondence between
+ /// the source text and
+ /// the derived text. A nonzero value means that no such direct
+ /// correspondence exists
+ ///
+ public int cwcLenSource;
+ }
+
+ ///
+ /// Enumerates the different breaking types that occur between
+ /// chunks of text read out by the FileFilter.
+ ///
+ public enum CHUNK_BREAKTYPE
+ {
+ ///
+ /// No break is placed between the current chunk and the previous chunk.
+ /// The chunks are glued together.
+ ///
+ CHUNK_NO_BREAK = 0,
+ ///
+ /// A word break is placed between this chunk and the previous chunk that
+ /// had the same attribute.
+ /// Use of CHUNK_EOW should be minimized because the choice of word
+ /// breaks is language-dependent,
+ /// so determining word breaks is best left to the search engine.
+ ///
+ CHUNK_EOW = 1,
+ ///
+ /// A sentence break is placed between this chunk and the previous chunk
+ /// that had the same attribute.
+ ///
+ CHUNK_EOS = 2,
+ ///
+ /// A paragraph break is placed between this chunk and the previous chunk
+ /// that had the same attribute.
+ ///
+ CHUNK_EOP = 3,
+ ///
+ /// A chapter break is placed between this chunk and the previous chunk
+ /// that had the same attribute.
+ ///
+ CHUNK_EOC = 4
+ }
+
+
+ public enum CHUNKSTATE
+ {
+ ///
+ /// The current chunk is a text-type property.
+ ///
+ CHUNK_TEXT = 0x1,
+ ///
+ /// The current chunk is a value-type property.
+ ///
+ CHUNK_VALUE = 0x2,
+ ///
+ /// Reserved
+ ///
+ CHUNK_FILTER_OWNED_VALUE = 0x4
+ }
+
+ internal enum IFilterReturnCode : UInt32
+ {
+ ///
+ /// Success
+ ///
+ S_OK = 0,
+ ///
+ /// The function was denied access to the filter file.
+ ///
+ E_ACCESSDENIED = 0x80070005,
+ ///
+ /// The function encountered an invalid handle,
+ /// probably due to a low-memory situation.
+ ///
+ E_HANDLE = 0x80070006,
+ ///
+ /// The function received an invalid parameter.
+ ///
+ E_INVALIDARG = 0x80070057,
+ ///
+ /// Out of memory
+ ///
+ E_OUTOFMEMORY = 0x8007000E,
+ ///
+ /// Not implemented
+ ///
+ E_NOTIMPL = 0x80004001,
+ ///
+ /// Unknown error
+ ///
+ E_FAIL = 0x80000008,
+ ///
+ /// File not filtered due to password protection
+ ///
+ FILTER_E_PASSWORD = 0x8004170B,
+ ///
+ /// The document format is not recognised by the filter
+ ///
+ FILTER_E_UNKNOWNFORMAT = 0x8004170C,
+ ///
+ /// No text in current chunk
+ ///
+ FILTER_E_NO_TEXT = 0x80041705,
+ ///
+ /// No more chunks of text available in object
+ ///
+ FILTER_E_END_OF_CHUNKS = 0x80041700,
+ ///
+ /// No more text available in chunk
+ ///
+ FILTER_E_NO_MORE_TEXT = 0x80041701,
+ ///
+ /// No more property values available in chunk
+ ///
+ FILTER_E_NO_MORE_VALUES = 0x80041702,
+ ///
+ /// Unable to access object
+ ///
+ FILTER_E_ACCESS = 0x80041703,
+ ///
+ /// Moniker doesn't cover entire region
+ ///
+ FILTER_W_MONIKER_CLIPPED = 0x00041704,
+ ///
+ /// Unable to bind IFilter for embedded object
+ ///
+ FILTER_E_EMBEDDING_UNAVAILABLE = 0x80041707,
+ ///
+ /// Unable to bind IFilter for linked object
+ ///
+ FILTER_E_LINK_UNAVAILABLE = 0x80041708,
+ ///
+ /// This is the last text in the current chunk
+ ///
+ FILTER_S_LAST_TEXT = 0x00041709,
+ ///
+ /// This is the last value in the current chunk
+ ///
+ FILTER_S_LAST_VALUES = 0x0004170A
+ }
+
+ [ComImport, Guid("89BCB740-6119-101A-BCB7-00DD010655AF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IFilter
+ {
+ ///
+ /// The IFilter::Init method initializes a filtering session.
+ ///
+ [PreserveSig]
+ IFilterReturnCode Init(
+ //[in] Flag settings from the IFILTER_INIT enumeration for
+ // controlling text standardization, property output, embedding
+ // scope, and IFilter access patterns.
+ IFILTER_INIT grfFlags,
+
+ // [in] The size of the attributes array. When nonzero, cAttributes
+ // takes
+ // precedence over attributes specified in grfFlags. If no
+ // attribute flags
+ // are specified and cAttributes is zero, the default is given by
+ // the
+ // PSGUID_STORAGE storage property set, which contains the date and
+ // time
+ // of the last write to the file, size, and so on; and by the
+ // PID_STG_CONTENTS
+ // 'contents' property, which maps to the main contents of the
+ // file.
+ // For more information about properties and property sets, see
+ // Property Sets.
+ int cAttributes,
+
+ //[in] Array of pointers to FULLPROPSPEC structures for the
+ // requested properties.
+ // When cAttributes is nonzero, only the properties in aAttributes
+ // are returned.
+ IntPtr aAttributes,
+
+ // [out] Information about additional properties available to the
+ // caller; from the IFILTER_FLAGS enumeration.
+ out IFILTER_FLAGS pdwFlags);
+
+ ///
+ /// The IFilter::GetChunk method positions the filter at the beginning
+ /// of the next chunk,
+ /// or at the first chunk if this is the first call to the GetChunk
+ /// method, and returns a description of the current chunk.
+ ///
+ [PreserveSig]
+ IFilterReturnCode GetChunk(out STAT_CHUNK pStat);
+
+ ///
+ /// The IFilter::GetText method retrieves text (text-type properties)
+ /// from the current chunk,
+ /// which must have a CHUNKSTATE enumeration value of CHUNK_TEXT.
+ ///
+ [PreserveSig]
+ IFilterReturnCode GetText(
+ // [in/out] On entry, the size of awcBuffer array in wide/Unicode
+ // characters. On exit, the number of Unicode characters written to
+ // awcBuffer.
+ // Note that this value is not the number of bytes in the buffer.
+ ref uint pcwcBuffer,
+
+ // Text retrieved from the current chunk. Do not terminate the
+ // buffer with a character.
+ [Out(), MarshalAs(UnmanagedType.LPArray)]
+ char[] awcBuffer);
+
+ ///
+ /// The IFilter::GetValue method retrieves a value (public
+ /// value-type property) from a chunk,
+ /// which must have a CHUNKSTATE enumeration value of CHUNK_VALUE.
+ ///
+ [PreserveSig]
+ int GetValue(
+ // Allocate the PROPVARIANT structure with CoTaskMemAlloc. Some
+ // PROPVARIANT
+ // structures contain pointers, which can be freed by calling the
+ // PropVariantClear function.
+ // It is up to the caller of the GetValue method to call the
+ // PropVariantClear method.
+ // ref IntPtr ppPropValue
+ // [MarshalAs(UnmanagedType.Struct)]
+ ref IntPtr PropVal);
+
+ ///
+ /// The IFilter::BindRegion method retrieves an interface representing
+ /// the specified portion of the object.
+ /// Currently reserved for future use.
+ ///
+ [PreserveSig]
+ int BindRegion(ref FILTERREGION origPos,
+ ref Guid riid, ref object ppunk);
+ }
+
+
+}
diff --git a/FilterReader/Properties/AssemblyInfo.cs b/FilterReader/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..02492a83
--- /dev/null
+++ b/FilterReader/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("FilterReader")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("FilterReader")]
+[assembly: AssemblyCopyright("Copyright © 2022")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
+// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
+// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
+[assembly: ComVisible(false)]
+
+// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
+[assembly: Guid("10c922fb-dd8d-4e0b-a50c-30ee658fbddc")]
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
+// indem Sie "*" wie unten gezeigt eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/GUIs.GlobalIndexer/ControlCreator.vb b/GUIs.GlobalIndexer/ControlCreator.vb
index 54c57eb4..61a4d2ab 100644
--- a/GUIs.GlobalIndexer/ControlCreator.vb
+++ b/GUIs.GlobalIndexer/ControlCreator.vb
@@ -76,7 +76,11 @@ Public Class ControlCreator
If pDefaultValue.ToUpper = PLACEHOLDER_NULL Then
oPicker.EditValue = Nothing
ElseIf pDefaultValue IsNot Nothing Then
- oPicker.EditValue = pDefaultValue
+ Dim oDateValue As Date = Nothing
+
+ If Date.TryParse(pDefaultValue, oDateValue) Then
+ oPicker.EditValue = oDateValue
+ End If
End If
oPicker.Properties.AppearanceFocused.BackColor = HightlightColor
diff --git a/GUIs.GlobalIndexer/GlobalIndexer.vbproj b/GUIs.GlobalIndexer/GlobalIndexer.vbproj
index 4650cc1e..ba1813c4 100644
--- a/GUIs.GlobalIndexer/GlobalIndexer.vbproj
+++ b/GUIs.GlobalIndexer/GlobalIndexer.vbproj
@@ -46,19 +46,15 @@
False
-
False
-
False
-
False
-
diff --git a/GUIs.GlobalIndexer/My Project/AssemblyInfo.vb b/GUIs.GlobalIndexer/My Project/AssemblyInfo.vb
index 6486b2f6..6d27759d 100644
--- a/GUIs.GlobalIndexer/My Project/AssemblyInfo.vb
+++ b/GUIs.GlobalIndexer/My Project/AssemblyInfo.vb
@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
-
+
@@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices
' indem Sie "*" wie unten gezeigt eingeben:
'
-
-
+
+
diff --git a/GUIs.Test.TestGUI/TestGUI.vbproj b/GUIs.Test.TestGUI/TestGUI.vbproj
index 2852a563..c5b1fa0c 100644
--- a/GUIs.Test.TestGUI/TestGUI.vbproj
+++ b/GUIs.Test.TestGUI/TestGUI.vbproj
@@ -180,6 +180,12 @@
Form
+
+ frmFulltext.vb
+
+
+ Form
+
frmLookup.vb
@@ -256,6 +262,9 @@
frmFolderWatcher.vb
+
+ frmFulltext.vb
+
frmLookup.vb
@@ -312,6 +321,10 @@
{3e7bc8a9-91ef-49b8-8110-2c01f664c24a}
SQLEditor
+
+ {10c922fb-dd8d-4e0b-a50c-30ee658fbddc}
+ FilterReader
+
{D20A6BF2-C7C6-4A7A-B34D-FA27D775A049}
Common
diff --git a/GUIs.Test.TestGUI/frmFulltext.Designer.vb b/GUIs.Test.TestGUI/frmFulltext.Designer.vb
new file mode 100644
index 00000000..f2d9bb0d
--- /dev/null
+++ b/GUIs.Test.TestGUI/frmFulltext.Designer.vb
@@ -0,0 +1,71 @@
+ _
+Partial Class frmFulltext
+ Inherits System.Windows.Forms.Form
+
+ 'Das Formular überschreibt den Löschvorgang, um die Komponentenliste zu bereinigen.
+ _
+ Protected Overrides Sub Dispose(ByVal disposing As Boolean)
+ Try
+ If disposing AndAlso components IsNot Nothing Then
+ components.Dispose()
+ End If
+ Finally
+ MyBase.Dispose(disposing)
+ End Try
+ End Sub
+
+ 'Wird vom Windows Form-Designer benötigt.
+ Private components As System.ComponentModel.IContainer
+
+ 'Hinweis: Die folgende Prozedur ist für den Windows Form-Designer erforderlich.
+ 'Das Bearbeiten ist mit dem Windows Form-Designer möglich.
+ 'Das Bearbeiten mit dem Code-Editor ist nicht möglich.
+ _
+ Private Sub InitializeComponent()
+ Me.TextBox1 = New System.Windows.Forms.TextBox()
+ Me.OpenFileDialog1 = New System.Windows.Forms.OpenFileDialog()
+ Me.Button1 = New System.Windows.Forms.Button()
+ Me.SuspendLayout()
+ '
+ 'TextBox1
+ '
+ Me.TextBox1.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
+ Or System.Windows.Forms.AnchorStyles.Left) _
+ Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
+ Me.TextBox1.Location = New System.Drawing.Point(12, 45)
+ Me.TextBox1.Multiline = True
+ Me.TextBox1.Name = "TextBox1"
+ Me.TextBox1.Size = New System.Drawing.Size(776, 393)
+ Me.TextBox1.TabIndex = 0
+ '
+ 'OpenFileDialog1
+ '
+ Me.OpenFileDialog1.FileName = "OpenFileDialog1"
+ '
+ 'Button1
+ '
+ Me.Button1.Location = New System.Drawing.Point(12, 16)
+ Me.Button1.Name = "Button1"
+ Me.Button1.Size = New System.Drawing.Size(75, 23)
+ Me.Button1.TabIndex = 1
+ Me.Button1.Text = "Open"
+ Me.Button1.UseVisualStyleBackColor = True
+ '
+ 'frmFulltext
+ '
+ Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
+ Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
+ Me.ClientSize = New System.Drawing.Size(800, 450)
+ Me.Controls.Add(Me.Button1)
+ Me.Controls.Add(Me.TextBox1)
+ Me.Name = "frmFulltext"
+ Me.Text = "frmFulltext"
+ Me.ResumeLayout(False)
+ Me.PerformLayout()
+
+ End Sub
+
+ Friend WithEvents TextBox1 As TextBox
+ Friend WithEvents OpenFileDialog1 As OpenFileDialog
+ Friend WithEvents Button1 As Button
+End Class
diff --git a/GUIs.Test.TestGUI/frmFulltext.resx b/GUIs.Test.TestGUI/frmFulltext.resx
new file mode 100644
index 00000000..33c7f672
--- /dev/null
+++ b/GUIs.Test.TestGUI/frmFulltext.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/GUIs.Test.TestGUI/frmFulltext.vb b/GUIs.Test.TestGUI/frmFulltext.vb
new file mode 100644
index 00000000..eeafa994
--- /dev/null
+++ b/GUIs.Test.TestGUI/frmFulltext.vb
@@ -0,0 +1,25 @@
+Imports DigitalData.Modules.FilterReader
+
+Public Class frmFulltext
+ Private Sub frmFulltext_Load(sender As Object, e As EventArgs) Handles MyBase.Load
+
+ End Sub
+
+ Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
+ Dim oResult = OpenFileDialog1.ShowDialog()
+
+ If oResult = DialogResult.OK Then
+ Dim oFileName = OpenFileDialog1.FileName
+
+
+ Try
+ Using oReader As New FilterReader(oFileName)
+ Dim oContents = oReader.ReadToEnd()
+ TextBox1.Text = oContents
+ End Using
+ Catch ex As Exception
+ TextBox1.Text = $"Fulltext could not be extracted!{vbNewLine}{ex.Message}"
+ End Try
+ End If
+ End Sub
+End Class
\ No newline at end of file
diff --git a/GUIs.Test.TestGUI/frmStart.Designer.vb b/GUIs.Test.TestGUI/frmStart.Designer.vb
index e49647d7..79c7643a 100644
--- a/GUIs.Test.TestGUI/frmStart.Designer.vb
+++ b/GUIs.Test.TestGUI/frmStart.Designer.vb
@@ -32,6 +32,7 @@ Partial Class frmStart
Me.Button8 = New System.Windows.Forms.Button()
Me.Button9 = New System.Windows.Forms.Button()
Me.Button10 = New System.Windows.Forms.Button()
+ Me.Button11 = New System.Windows.Forms.Button()
Me.SuspendLayout()
'
'Button1
@@ -124,6 +125,15 @@ Partial Class frmStart
Me.Button10.Text = "MsgBox"
Me.Button10.UseVisualStyleBackColor = True
'
+ 'Button11
+ '
+ Me.Button11.Location = New System.Drawing.Point(254, 260)
+ Me.Button11.Name = "Button11"
+ Me.Button11.Size = New System.Drawing.Size(236, 60)
+ Me.Button11.TabIndex = 2
+ Me.Button11.Text = "Fulltext"
+ Me.Button11.UseVisualStyleBackColor = True
+ '
'frmStart
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
@@ -132,6 +142,7 @@ Partial Class frmStart
Me.Controls.Add(Me.Button10)
Me.Controls.Add(Me.Button9)
Me.Controls.Add(Me.Button6)
+ Me.Controls.Add(Me.Button11)
Me.Controls.Add(Me.Button5)
Me.Controls.Add(Me.Button4)
Me.Controls.Add(Me.Button3)
@@ -155,4 +166,5 @@ Partial Class frmStart
Friend WithEvents Button8 As Button
Friend WithEvents Button9 As Button
Friend WithEvents Button10 As Button
+ Friend WithEvents Button11 As Button
End Class
diff --git a/GUIs.Test.TestGUI/frmStart.vb b/GUIs.Test.TestGUI/frmStart.vb
index 684804d3..67d16229 100644
--- a/GUIs.Test.TestGUI/frmStart.vb
+++ b/GUIs.Test.TestGUI/frmStart.vb
@@ -55,4 +55,9 @@ Public Class frmStart
Dim oForm As New frmMsgBox(LogConfig, Database)
oForm.Show()
End Sub
+
+ Private Sub Button11_Click(sender As Object, e As EventArgs) Handles Button11.Click
+ Dim oform As New frmFulltext()
+ oform.Show()
+ End Sub
End Class
\ No newline at end of file
diff --git a/GUIs.ZooFlow/Administration/ClassDetailForm.vb b/GUIs.ZooFlow/Administration/ClassDetailForm.vb
index a33b0882..62cae0b1 100644
--- a/GUIs.ZooFlow/Administration/ClassDetailForm.vb
+++ b/GUIs.ZooFlow/Administration/ClassDetailForm.vb
@@ -1,6 +1,7 @@
Imports DigitalData.GUIs.ZooFlow.Administration.ClassConstants
Imports DigitalData.Modules.Base
Imports DigitalData.Modules.Language
+Imports DigitalData.Modules.Logging
Public Class ClassDetailForm
Inherits BaseClass
@@ -77,7 +78,7 @@ Public Class ClassDetailForm
}}
}
- Public Sub New(LogConfig As Modules.Logging.LogConfig)
+ Public Sub New(LogConfig As LogConfig)
MyBase.New(LogConfig)
End Sub
diff --git a/GUIs.ZooFlow/ClassConstants.vb b/GUIs.ZooFlow/ClassConstants.vb
index 35682897..1125fe88 100644
--- a/GUIs.ZooFlow/ClassConstants.vb
+++ b/GUIs.ZooFlow/ClassConstants.vb
@@ -16,6 +16,7 @@
Public Const MODULE_CLIPBOARDWATCHER = "CW"
Public Const MODULE_GLOBAL_INDEXER = "GLOBIX"
Public Const MODULE_ZOOFLOW = "ZOOFLOW"
+ Public Const MODULE_PROCESS_MANAGER = "PM"
Public Const ATTR_TYPE_STRING = "VARCHAR"
Public Const ATTR_TYPE_INTEGER = "BIG INTEGER"
diff --git a/GUIs.ZooFlow/ClassModules.vb b/GUIs.ZooFlow/ClassModules.vb
new file mode 100644
index 00000000..33dc972c
--- /dev/null
+++ b/GUIs.ZooFlow/ClassModules.vb
@@ -0,0 +1,116 @@
+Imports DigitalData.Modules.Base
+Imports DigitalData.Modules.Logging
+Imports DigitalData.Modules.Base.ECM
+Imports Microsoft.Win32
+
+Public Class ClassModules
+ Inherits BaseClass
+
+ Private Const PM_REGNODE = "Process Manager"
+ Private Const PM_EXENAME = "DD_ProcessManager.exe"
+
+ Private Const CW_REGNODE = "Clipboard Watcher"
+ Private Const CW_EXENAME = "DD_Clipboard_Watcher.exe"
+
+ Private Const GLOBIX_REGNODE = "Global Indexer"
+ Private Const GLOBIX_EXENAME = "Global_Indexer.exe"
+
+ Private Const REGNODE_MANUFACTURER = "Digital Data"
+ Private Const REGPATH_BASE = "SOFTWARE\\WOW6432Node"
+
+ Private Config As ClassConfig
+
+ Public Sub New(pLogConfig As LogConfig, pConfig As ClassConfig)
+ MyBase.New(pLogConfig)
+ Config = pConfig
+ End Sub
+
+ Public Function GetProductProgramPath(pProduct As Product) As String
+ Dim oApplicationName As String = Nothing
+ Dim oPath = GetProductPath(pProduct)
+
+ If oPath Is Nothing Then
+ Return Nothing
+ End If
+
+ Select Case pProduct
+ Case Product.GlobalIndexer
+ oApplicationName = PM_EXENAME
+
+ Case Product.ClipboardWatcher
+ oApplicationName = CW_EXENAME
+
+ Case Product.ProcessManager
+ oApplicationName = GLOBIX_EXENAME
+
+ End Select
+
+ If oApplicationName Is Nothing Then
+ Return Nothing
+ End If
+
+ Return IO.Path.Combine(oPath, oApplicationName)
+ End Function
+
+ Public Function GetProductPath(pProduct As Product) As String
+ Select Case pProduct
+ Case Product.ProcessManager
+ Return GetProductPathFor(pProduct, PM_REGNODE)
+
+ Case Product.GlobalIndexer
+ Return GetProductPathFor(pProduct, GLOBIX_REGNODE)
+
+ Case Product.ClipboardWatcher
+ Return GetProductPathFor(pProduct, CW_REGNODE)
+
+ Case Else
+ Return Nothing
+ End Select
+ End Function
+
+ Private Function GetProductPathFor(pProduct As Product, pSubKey As String) As String
+ Dim oPathFromRegistry = GetProductPathFromRegistryFor(pSubKey)
+
+ If oPathFromRegistry IsNot Nothing Then
+ Return pSubKey
+ Else
+ Return GetProductPathFromConfig(pProduct)
+ End If
+ End Function
+
+ Private Function GetProductPathFromConfig(pProduct As Product)
+ Select Case pProduct
+ Case Product.ProcessManager
+ Return Config.ProductPaths.ProcessManagerPath
+
+ Case Product.GlobalIndexer
+ Return Config.ProductPaths.GlobalIndexerPath
+
+ Case Product.ClipboardWatcher
+ Return Config.ProductPaths.ClipboardWatcherPath
+
+ Case Else
+ Return Nothing
+ End Select
+ End Function
+
+ Private Function GetProductPathFromRegistryFor(pSubKey As String) As String
+ Try
+ Dim oPathParts As New List(Of String) From {REGPATH_BASE, REGNODE_MANUFACTURER, pSubKey}
+ Dim oRegistryPath = String.Join("\\", oPathParts)
+ Dim oRoot = Registry.LocalMachine
+ Dim oProduct = oRoot.OpenSubKey(oRegistryPath, RegistryKeyPermissionCheck.ReadSubTree)
+
+ If oProduct Is Nothing Then
+ Return Nothing
+ End If
+
+ Return oProduct.GetValue("Path")
+ Catch ex As Exception
+ Logger.Warn("Could not read [Process Manager] path from registry!")
+ Logger.Error(ex)
+ Return Nothing
+ End Try
+ End Function
+
+End Class
diff --git a/GUIs.ZooFlow/Config/ClassConfig.vb b/GUIs.ZooFlow/Config/ClassConfig.vb
index 8dbda2aa..3093ff17 100644
--- a/GUIs.ZooFlow/Config/ClassConfig.vb
+++ b/GUIs.ZooFlow/Config/ClassConfig.vb
@@ -42,11 +42,22 @@ Public Class ClassConfig
Public Property AppServerConfig As String = String.Empty
+
Public Property ConnectionStringAppServer As String = ""
+
' === Logging Configuration
Public Property LogDebug As Boolean = False
' === User Configuration ===
Public Property UserLanguage As String = "de-DE"
+
+ ' === Product Configuration ===
+ Public Property ProductPaths As ClassProductPaths
+
+ Public Class ClassProductPaths
+ Public ProcessManagerPath As String
+ Public GlobalIndexerPath As String
+ Public ClipboardWatcherPath As String
+ End Class
End Class
diff --git a/GUIs.ZooFlow/Modules/Globix/ClassUserFiles.vb b/GUIs.ZooFlow/Modules/Globix/ClassUserFiles.vb
index ff612404..6dc90677 100644
--- a/GUIs.ZooFlow/Modules/Globix/ClassUserFiles.vb
+++ b/GUIs.ZooFlow/Modules/Globix/ClassUserFiles.vb
@@ -6,7 +6,7 @@ Imports File = DigitalData.Modules.Filesystem.File
Public Class ClassUserFiles
Inherits BaseClass
- Private Property FILESYSTEM As Modules.Filesystem.File
+ Private Property FILESYSTEM As File
Public Sub New(pLogConfig As LogConfig)
MyBase.New(pLogConfig)
FILESYSTEM = New File(pLogConfig)
diff --git a/GUIs.ZooFlow/ZooFlow.vbproj b/GUIs.ZooFlow/ZooFlow.vbproj
index baed3d0a..50235113 100644
--- a/GUIs.ZooFlow/ZooFlow.vbproj
+++ b/GUIs.ZooFlow/ZooFlow.vbproj
@@ -227,6 +227,7 @@
+
diff --git a/GUIs.ZooFlow/frmFlowForm.vb b/GUIs.ZooFlow/frmFlowForm.vb
index f01c401e..c3f0637e 100644
--- a/GUIs.ZooFlow/frmFlowForm.vb
+++ b/GUIs.ZooFlow/frmFlowForm.vb
@@ -157,10 +157,11 @@ Public Class frmFlowForm
Private DTIDB_SEARCHES As DataTable
' Common Helpers Classes
+ Private Logger As Logger
Private Init As ClassInit
Private FileEx As Filesystem.File
Private ErrorHandler As BaseErrorHandler
- Private Logger As Logger
+ Private Modules As ClassModules
' Globix Helper Classes
Private FileDropNew As FileDrop
@@ -219,7 +220,7 @@ Public Class frmFlowForm
Logger = My.LogConfig.GetLogger()
Environment = My.Application.GetEnvironment()
ErrorHandler = New BaseErrorHandler(My.LogConfig, Logger, Me)
-
+ Modules = New ClassModules(My.LogConfig, My.SystemConfig)
FileEx = New Filesystem.File(My.LogConfig)
' === Initialize Theming ===
@@ -321,6 +322,16 @@ Public Class frmFlowForm
End If
+ Dim oPMPath = Modules.GetProductPath(DigitalData.Modules.Base.ECM.Product.ProcessManager)
+
+
+ ' TODO: This needs an update of the function FNZF_GET_MODULE_INFO
+ If My.Application.ModulesActive.Contains(MODULE_PROCESS_MANAGER) Then
+
+ End If
+
+
+
If IsNothing(My.Tables.DTIDB_CATALOG_USER) Then
Exit Function
End If
@@ -1045,7 +1056,7 @@ Public Class frmFlowForm
Dim oFormTitle = GetResultWindowString(oState.CurrentClipboardContents)
' For now we assume these are document results instead of data results
- Dim oForm = Await oProfileSearches.GetDocResultForm(oProfile, oFormTitle, Modules.ZooFlow.Constants.OperationMode.ZooFlow)
+ Dim oForm = Await oProfileSearches.GetDocResultForm(oProfile, oFormTitle, DigitalData.Modules.ZooFlow.Constants.OperationMode.ZooFlow)
AddHandler oForm.NeedsRefresh, AddressOf ProfileResultForm_NeedsRefresh
oForm.Show()
@@ -1057,7 +1068,7 @@ Public Class frmFlowForm
.ClipboardContents = oState.CurrentClipboardContents,
.MatchingProfiles = oProfiles,
.MatchTreeView = oState.MatchTreeView,
- .OperationModeOverride = Modules.ZooFlow.Constants.OperationMode.ZooFlow
+ .OperationModeOverride = DigitalData.Modules.ZooFlow.Constants.OperationMode.ZooFlow
}
Dim oForm As New frmMatch(My.LogConfig, oEnvironment, oParams)
@@ -1130,7 +1141,7 @@ Public Class frmFlowForm
Dim oParams = New DocumentResultList.Params() With {
.WindowGuid = "QuickFlowSearch1",
.WindowTitle = GetResultWindowString(pSearchText),
- .OperationModeOverride = Modules.ZooFlow.Constants.OperationMode.ZooFlow,
+ .OperationModeOverride = DigitalData.Modules.ZooFlow.Constants.OperationMode.ZooFlow,
.ProfileGuid = 354521
}
diff --git a/Modules.Base/Base/Base.vbproj b/Modules.Base/Base/Base.vbproj
index 48349b26..9a6697eb 100644
--- a/Modules.Base/Base/Base.vbproj
+++ b/Modules.Base/Base/Base.vbproj
@@ -66,6 +66,7 @@
+
diff --git a/Modules.Base/Base/ECM.vb b/Modules.Base/Base/ECM.vb
new file mode 100644
index 00000000..bf04d74b
--- /dev/null
+++ b/Modules.Base/Base/ECM.vb
@@ -0,0 +1,7 @@
+Public Class ECM
+ Public Enum Product
+ ProcessManager
+ GlobalIndexer
+ ClipboardWatcher
+ End Enum
+End Class
diff --git a/Windows/FileDrop.vb b/Windows/FileDrop.vb
index 0c0e38f6..51fce442 100644
--- a/Windows/FileDrop.vb
+++ b/Windows/FileDrop.vb
@@ -54,7 +54,11 @@ Partial Public Class FileDrop
End Function
Public Function GetFileFormat(pEvent As DragEventArgs) As FileFormat
+ Logger.Debug("Determining FileFormat")
+
If IsThunderbird(pEvent) Then
+ Logger.Debug("File is a Thunderbird File")
+
If IsThunderbirdAttachment(pEvent) Then
Return FileFormat.ThunderbirdAttachment
@@ -67,6 +71,8 @@ Partial Public Class FileDrop
End If
If IsOutlook(pEvent) Then
+ Logger.Debug("File is an Outlook File")
+
If IsOutlookAttachment(pEvent) Then
Return FileFormat.OutlookAttachment
@@ -79,6 +85,8 @@ Partial Public Class FileDrop
End If
If IsNormalFile(pEvent) Then
+ Logger.Debug("File is a normal File")
+
Return FileFormat.LocalFile
Else
@@ -88,7 +96,13 @@ Partial Public Class FileDrop
Public Function GetFiles(pEvent As DragEventArgs) As List(Of DroppedFile)
Try
+ Dim oFormats As List(Of String) = GetFileFormats(pEvent)
+ Dim oFormatString = String.Join(", ", oFormats)
+ Logger.Debug("Available Formats: [{0}]", oFormatString)
+
Dim oFormat = GetFileFormat(pEvent)
+ Logger.Debug("Format: [{0}]", oFormat.ToString)
+
Dim oFiles As New List(Of DroppedFile)
Select Case oFormat
@@ -115,9 +129,12 @@ Partial Public Class FileDrop
End Select
+ Logger.Debug("Handled [{0}] dropped files.", oFiles.Count)
+
Return oFiles
Catch ex As Exception
+ Logger.Warn("Error while handling dropped files.")
Logger.Error(ex)
Return Nothing
@@ -165,10 +182,15 @@ Partial Public Class FileDrop
End While
End Using
- Return oBuilder.ToString
+ Dim oFileName As String = oBuilder.ToString
+ Logger.Debug("Outlook filename is [{0}]", oFileName)
+
+ Return oFileName
End Function
Private Function GetOutlookFileContents_FromDragEvent(pEvent As DragEventArgs) As List(Of Byte())
+ Logger.Debug("Getting file contents")
+
Using oStream As IO.MemoryStream = pEvent.Data.GetData("FileContents", True)
If oStream Is Nothing Then
Return Nothing
@@ -185,6 +207,9 @@ Partial Public Class FileDrop
End Function
Private Function GetOutlookFileContents_FromInterop(pEvent As DragEventArgs) As List(Of Byte())
+ Logger.Debug("Getting file contents")
+ Logger.Debug("Creating Outlook Application")
+
Dim oApp As Outlook.Application
Try
oApp = New Outlook.Application()
@@ -197,6 +222,8 @@ Partial Public Class FileDrop
Dim oMailItem As Outlook.MailItem
For oIndex As Integer = 1 To oApp.ActiveExplorer.Selection.Count
Try
+ Logger.Debug("Fetching mail [{0}]")
+
oMailItem = oApp.ActiveExplorer.Selection.Item(oIndex)
Dim oSubject As String = ConvertTextToSlug(oMailItem.Subject)
@@ -229,8 +256,11 @@ Partial Public Class FileDrop
Return oResults
Catch ex As Exception
Logger.Error(ex)
+
End Try
Next
+
+ Return Nothing
End Function
Public Function GetFormat(pEvent As DragEventArgs, pFormat As String, pAutoConvert As Boolean) As Object
@@ -250,10 +280,7 @@ Partial Public Class FileDrop
Dim oFormats = pEvent.Data.GetFormats(pAutoConvert).ToList()
Dim oFormatExists = oFormats.Any(Function(format) format = pFormat)
- ' This does not work with locally created outlook mails
- 'Dim oFormatExists = pEvent.Data.GetDataPresent(pFormat)
-
- Logger.Debug("Format exists: [{0}]/[{1}]", pFormat, oFormatExists)
+ Logger.Debug("Format [{0}] exists: [{1}]", pFormat, oFormatExists)
Return oFormatExists
End Function
@@ -266,9 +293,11 @@ Partial Public Class FileDrop
End Function
Public Function IsOutlook(e As DragEventArgs) As Boolean
+ Logger.Debug("Checking for Outlook")
Return CheckFor(e, "FileGroupDescriptor") AndAlso CheckFor(e, "FileContents")
End Function
Public Function IsThunderbird(e As DragEventArgs) As Boolean
+ Logger.Debug("Checking for Thunderbird")
Return CheckFor(e, "text/x-moz-url") AndAlso CheckFor(e, "FileDrop")
End Function
@@ -304,22 +333,24 @@ Partial Public Class FileDrop
#Region "Thunderbird"
Private Function IsOutlookMail(e As DragEventArgs) As Boolean
+ Logger.Debug("Checking for Outlook Mail")
Return Not IsOutlookAttachment(e) AndAlso CheckFor(e, "RenPrivateSourceFolder")
End Function
Private Function IsOutlookAttachment(e As DragEventArgs) As Boolean
- Return IsOutlook(e) AndAlso
- CheckFor(e, "RenPrivateItem") AndAlso
- CheckFor(e, "ZoneIdentifier")
+ Logger.Debug("Checking for Outlook Attachment")
+ Return IsOutlook(e) AndAlso CheckFor(e, "RenPrivateItem")
End Function
#End Region
#Region "Outlook"
Private Function IsThunderbirdMail(e As DragEventArgs) As Boolean
+ Logger.Debug("Checking for Thunderbird Mail")
Return Not IsThunderbirdAttachment(e)
End Function
Private Function IsThunderbirdAttachment(e As DragEventArgs) As Boolean
+ Logger.Debug("Checking for Thunderbird Attachment")
Return IsThunderbird(e) AndAlso
CheckFor(e, "text/x-moz-url-data") AndAlso
CheckFor(e, "text/x-moz-url-desc")
diff --git a/Windows/My Project/AssemblyInfo.vb b/Windows/My Project/AssemblyInfo.vb
index 8744b2d5..c1c4e43e 100644
--- a/Windows/My Project/AssemblyInfo.vb
+++ b/Windows/My Project/AssemblyInfo.vb
@@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices
' übernehmen, indem Sie "*" eingeben:
'
-
-
+
+