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: ' - - + +