Imports System.IO Imports System.IO.Path Imports System.Text.RegularExpressions Imports DigitalData.Modules.Base Imports DigitalData.Modules.Logging Imports WINDREAMLib Imports WINDREAMLib.WMCOMEvent Imports WINDREAMLib.WMEntity Imports WINDREAMLib.WMObjectEditMode Imports WMCNNCTDLLLib Imports WMOBRWSLib Imports WMOSRCHLib Imports WMOTOOLLib ''' Windream ''' 0.0.0.2 ''' 23.10.2018 ''' ''' Module that provides methods to access the Windream ECM ''' ''' ''' NLog, >= 4.5.8 ''' ''' ''' LogConfig, DigitalData.Modules.Logging.LogConfig ''' The LogFactory containing the current log config. Used to instanciate the class logger for this and any dependent class ''' ''' ClientDriveLetter, String ''' Drive Letter of the Windream Drive, should default to `W` ''' ''' ClientSupport64Bit, Boolean ''' Should this session support 64bit methods/functionality? ''' ''' SessionReconnect, Boolean ''' Should the session reconnect automatically when the connection to the server is lost? ''' ''' SessionServerName, String ''' Name of the server used in the connection. If this is `Nothing`, the current server defined in the client is used ''' ''' SessionUserName, String ''' Name of the user that is used in the connection. If this is `Nothing`, the currently signed in user is used ''' ''' SessionPassword, String ''' User-password that is used in the connection. If this is `Nothing`, the currently signed in user is used ''' ''' SessionDomain, String ''' User-domain that is used in the connection. If this is `Nothing`, the currently signed in user is used ''' ''' ''' ClientDriveLetter, String (readonly) ''' ClientSupports64Bit, Boolean (readonly) ''' Session, IWMSession2 (readonly) ''' SessionLoggedin, Boolean (readonly) ''' SessionReconnect, Boolean (readonly) ''' SessionServername, String (readonly) ''' Objecttypes, List(Of String) (readonly) ''' ''' ''' _windream = New ConnectionBuilder(LogConfig). ''' WithDriveLetter("W"). ''' WithSessionReconnect(). ''' With64BitSupport(). ''' WithServerName("sdd-vmx02-aps01"). ''' Connect() ''' ''' ''' This class should not be instanciated directly. Instead, ConnectionBuilder should be used. ''' Public Class Windream #Region "+++++ Konstanten +++++" Protected Const WMObjectEditModeObject = &H1F Protected Const WMObjectStreamOpenModeReadWrite = 2 Protected Const WMEntityObjectType = 10 Protected Const WMEntityDocument = 1 Public Const WMObjectVariableValueTypeUndefined = 0 Public Const WMObjectVariableValueTypeString = 1 Public Const WMObjectVariableValueTypeInteger = 2 Public Const WMObjectVariableValueTypeFloat = 3 Public Const WMObjectVariableValueTypeBoolean = 4 Public Const WMObjectVariableValueTypeDate = 5 Public Const WMObjectVariableValueTypeFixedPoint = 6 Public Const WMObjectVariableValueTypeTimeStamp = 7 Public Const WMObjectVariableValueTypeCurrency = 8 Public Const WMObjectVariableValueTypeTime = 9 Public Const WMObjectVariableValueTypeVariant = 10 Public Const WMObjectVariableValueTypeMask = &HFFF Public Const WMObjectVariableValueFlagMask = &HFFFFF000 Public Const WMObjectVariableValueTypeVector = 4115 Public Const WMObjectVariableValueTypeFulltext = &H2000 Public Const WMObjectVariableValueTypeDefaultValue = &H4000 Public Const WMObjectEditModeIndexEdit = &H3DA #End Region #Region "Private Properties" Private ReadOnly _logger As Logger Private ReadOnly _logConfig As LogConfig Private ReadOnly _fileSystem As FilesystemEx Private ReadOnly _sessionDomain As String Private ReadOnly _sessionPassword As String Private ReadOnly _sessionUsername As String #End Region #Region "Public Properties" Public ReadOnly Property ClientDriveLetter As String Public ReadOnly Property ClientBasePath As String Public ReadOnly Property ClientSupports64Bit As Boolean Public ReadOnly Property Session As IWMSession2 Public ReadOnly Property SessionLoggedin As Boolean = False Public ReadOnly Property SessionReconnect As Boolean Public ReadOnly Property SessionServername As String Public ReadOnly Property UsesDriveLetter As Boolean = True Public Property NewDocumentID As Int32 = 0 Public Property CurrentWMObject As IWMObject6 ''' A list of object types that are available Public ReadOnly Property ObjectTypes As List(Of String) Get Dim types As WMObjects = GetObjectTypes() Dim list As New List(Of String) For Each type As WMObject In types list.Add(type.aName) Next Return list End Get End Property #End Region ''' ''' Creates a new Windream object and connects to a server with the provided options and credentials ''' ''' ''' ''' ''' ''' ''' ''' ''' ''' Public Sub New(LogConfig As LogConfig, SessionReconnect As Boolean, ClientDriveLetter As String, BasePath As String, ClientSupport64Bit As Boolean, SessionServerName As String, SessionUserName As String, SessionPassword As String, SessionDomain As String) ' Create logger and save LogFactory for dependent classes _logger = LogConfig.GetLogger() _logConfig = LogConfig _fileSystem = New FilesystemEx(LogConfig) ' Create a session Dim oSession As IWMSession2 = NewSession(SessionServerName, SessionUserName, SessionPassword, SessionDomain) ' If no session could be created, exit If oSession Is Nothing Then Throw New Exceptions.SessionException() End If ' Set properties of currently established session Session = oSession SessionLoggedin = True Me.SessionReconnect = SessionReconnect Me.ClientDriveLetter = ClientDriveLetter Me.ClientSupports64Bit = ClientSupport64Bit Me.SessionServername = SessionServerName Me.ClientBasePath = GetNormalizedBasePath(BasePath) _logger.Debug("ClientBasePath: [{0}]", ClientBasePath) _logger.Debug("ClientDriveLetter: [{0}]", ClientDriveLetter) _logger.Debug("SessionServername: [{0}]", SessionServerName) _logger.Debug("SessionUserName: [{0}]", SessionUserName) _logger.Debug("SessionDomain: [{0}]", SessionDomain) If ClientDriveLetter = String.Empty Then UsesDriveLetter = False End If _sessionUsername = SessionUserName _sessionPassword = SessionPassword _sessionDomain = SessionDomain End Sub Public Function GetCleanedPath(Path As String) As String Return Regex.Replace(Path, Constants.REGEX_CLEAN_FILENAME, String.Empty) End Function Public Function GetChoiceListItems(ChoiceListName As String) As List(Of String) Dim oItems As New List(Of String) Dim oChoicelist As WMObject If TestSessionLoggedIn() = False Then Return oItems End If Try oChoicelist = Session.GetWMObjectByName(WMEntityChoiceList, ChoiceListName) Catch ex As Exception _logger.Error(ex, "Could not get choice list") Return oItems End Try Try Dim oChoiceListItems As Object = oChoicelist.GetVariableValue("vItems") If oChoiceListItems Is Nothing Then Return oItems End If For Each oChoiceListItem In oChoiceListItems oItems.Add(oChoiceListItem) Next Return oItems Catch ex As Exception _logger.Error(ex, "Could not get choice list items") Return oItems End Try End Function Public Function GetChoiceLists() As List(Of String) Dim oItems As New List(Of String) If TestSessionLoggedIn() = False Then Return oItems End If Try Dim oChoiceLists As WMObjects Dim oChoiceList As IWMObject2 'load list of choicelists oChoiceLists = Session.GetAllObjects(WMEntityChoiceList) For Each oChoiceList In oChoiceLists oItems.Add(oChoiceList.aName) Next Return oItems Catch ex As Exception _logger.Error(ex) Return oItems End Try End Function Public Function CheckFileExistsinWM(pPath As String) As Boolean If TestSessionLoggedIn() = False Then Return Nothing End If pPath = GetNormalizedPath(pPath, False) _logger.Info($"CheckFileExistsinWM: {pPath} ...") Dim oWMObject As WMObject Try oWMObject = Session.GetWMObjectByPath(WMEntityDocument, pPath) Return True Catch ex As Exception _logger.Info($"Unexpected Error in windream.CheckFileExistsinWM: {ex.Message}") _logger.Error(ex) Return False End Try End Function Public Function GetFileByPath(pPath As String) As WMObject If TestSessionLoggedIn() = False Then Return Nothing End If pPath = GetNormalizedPath(pPath, False) Dim oWMObject As WMObject Try oWMObject = Session.GetWMObjectByPath(WMEntityDocument, pPath) Return oWMObject Catch ex As Exception _logger.Info($"Unexpected Error in windream.GetFileByPath: {ex.Message}") _logger.Error(ex) Return Nothing End Try End Function Public Function GetFileByPathObj6(pPath As String) As IWMObject6 If TestSessionLoggedIn() = False Then Return Nothing End If pPath = GetNormalizedPath(pPath, False) Dim oWMObject As IWMObject6 Try oWMObject = Session.GetWMObjectByPath(WMEntityDocument, pPath) Return oWMObject Catch ex As Exception _logger.Error(ex) Return Nothing End Try End Function Public Function GetIndexType(IndexName As String) As Integer If TestSessionLoggedIn() = False Then Return Nothing End If Try Dim oAttribute = Session.GetWMObjectByName(WMEntityAttribute, IndexName) Dim oType = oAttribute.GetVariableValue("dwAttrType") Return oType Catch ex As Exception _logger.Warn($"Error in Windream.GetIndexType for Indexname [{IndexName}]") _logger.Error(ex) Return Nothing End Try End Function Public Function GetIndexValue(Path As String, IndexName As String) As List(Of String) Dim oResult As New List(Of String) If TestSessionLoggedIn() = False Then Return oResult End If Try Path = GetNormalizedPath(Path, False) Dim oWMObject As WMObject = Session.GetWMObjectByPath(WMEntityDocument, Path) If oWMObject Is Nothing Then Return oResult End If Dim oValues = oWMObject.GetVariableValue(IndexName) If oValues Is Nothing Then Return oResult End If If TypeOf oValues Is IEnumerable Then For Each oValue In oValues oResult.Add(oValue) Next Else oResult.Add(oValues) End If Return oResult Catch ex As Exception _logger.Error(ex) Return oResult End Try End Function Public Function GetIndiciesByObjecttype(ObjectTypeName As String) As List(Of String) If TestSessionLoggedIn() = False Then Return Nothing End If Dim oObjectType As WMObject Dim oIndexAttributes As WMObjectRelation Dim oIndexAttribute As WMObject Dim oIndex As WMObject Dim oRelProperties As WMObjectRelation Dim oTempSession As IWMSession2 Dim oIndicies As New List(Of String) Try ' den Objekttyp laden oTempSession = DirectCast(Session, IWMSession2) oObjectType = Session.GetWMObjectByName(WMEntityObjectType, ObjectTypeName) ' Beziehung zu Indizes des Objekttyp auslesen oIndexAttributes = oObjectType.GetWMObjectRelationByName("TypeAttributes") ' alle Indizes durchlaufen For j As Integer = 0 To oIndexAttributes.Count - 1 ' aktuellen Index auslesen oIndexAttribute = oIndexAttributes.Item(j) ' Eigenschaften des Index auslesen oRelProperties = oIndexAttribute.GetWMObjectRelationByName("Attribute") ' Index aus den Eigenschaften auslesen oIndex = oRelProperties.Item(0) ' Indexname speichern 'aIndexNames(j) = oIndex.aName oIndicies.Add(oIndex.aName) Next ' Indexarray zurückgeben oIndicies.Sort() 'Return aIndexNames Return oIndicies Catch ex As Exception _logger.Error(ex) Return oIndicies End Try End Function Public Function GetTypeIndiciesByObjecttype(ObjectTypeName As String, Optional SystemIndicies As Boolean = False) As List(Of String) If TestSessionLoggedIn() = False Then Return Nothing End If Dim oObjectType As IWMObject6 Dim oTempSession As IWMSession2 Dim oIndicies As New List(Of String) Dim oAttributeFlag As Integer = 0 Try ' den Objekttyp laden oTempSession = DirectCast(Session, IWMSession2) oObjectType = Session.GetWMObjectByName(WMEntityObjectType, ObjectTypeName) If SystemIndicies = True Then oAttributeFlag = Constants.ATTRIBUTE_TYPE_SYSTEMINDEX Else oAttributeFlag = Constants.ATTRIBUTE_TYPE_TYPEINDEX End If Dim oVariableNames As WMObjectVariableNames = oObjectType.GetVariableNames(oAttributeFlag, False) For oIndex = 0 To oVariableNames.Count - 1 oIndicies.Add(oVariableNames.Item(oIndex)) Next Return oIndicies Catch ex As Exception _logger.Warn("Type Indicies could not be read") _logger.Error(ex) Return Nothing End Try End Function Public Function NewSession(Optional ServerName As String = Nothing, Optional UserName As String = Nothing, Optional Password As String = Nothing, Optional Domain As String = Nothing) As IWMSession2 Dim oBrowser As ServerBrowser Dim oConnect As IWMConnect2 Dim oSession As IWMSession2 Dim oCredentials As WMUserIdentity Dim oImpersonation As Boolean Dim oServerNameFromClient As Boolean ' Create initial windream objects Try oBrowser = New ServerBrowser() oConnect = New WMConnect() _logger.Info("Successfully created windream objects") Catch ex As Exception _logger.Error(ex, "Error while creating windream objects") Return Nothing End Try ' If no server was supplied, try to get the current server set in the client Try If ServerName Is Nothing OrElse ServerName.Length = 0 Then ServerName = oBrowser.GetCurrentServer oServerNameFromClient = True Else oServerNameFromClient = False End If Catch ex As Exception _logger.Error(ex, "Error while getting Servername") Return Nothing End Try _logger.Info("Servername: {0}", ServerName) _logger.Info("Servername aquired from client: {0}", oServerNameFromClient) 'Test connection to windream server Try Dim response = My.Computer.Network.Ping(ServerName) If response = False Then _logger.Warn("Windream Server {0} refused connection", ServerName) Return Nothing End If Catch ex As Exception _logger.Error(ex) Return Nothing End Try ' If username, password and domain are set, login with impersonation ' Else, login with current credentials oImpersonation = False If UserName IsNot Nothing And Password IsNot Nothing And Domain IsNot Nothing Then If UserName <> String.Empty And Password <> String.Empty And Domain <> String.Empty Then oImpersonation = True _logger.Info("Impersonated Login: {0}", oImpersonation) _logger.Info("Username: {0}", IIf(UserName IsNot Nothing, UserName, Environment.UserName)) _logger.Info("Domain: {0}", IIf(Domain IsNot Nothing, Domain, Environment.UserDomainName)) oCredentials = New WMUserIdentity() With { .aServerName = ServerName, .aUserName = UserName, .aPassword = Password, .aDomain = Domain } oConnect.ModuleId = 9 End If End If If oImpersonation = False Then oCredentials = New WMUserIdentity() With { .aServerName = ServerName } End If Try _logger.Debug("Trying to create a session...") oSession = oConnect.Login(oCredentials) _logger.Info("Connected..Session created") Catch ex As Exception _logger.Error(ex, "Error while logging in") Return Nothing End Try Try ' Standardmässig hinterlegen dass abgelegte Dateien keine Indexmaske öffnet oSession.SwitchEvents(WMCOMEventWMSessionNeedIndex, False) Catch ex As Exception _logger.Error(ex, "Could not SwitchEvents") Return Nothing End Try If oSession.aLoggedin = False Then _logger.Warn("Session created but user {0} could not be logged in", Environment.UserName) Return Nothing End If _logger.Info("Connection to {0} established!", ServerName) Return oSession End Function #Region "Public Methods" ''' ''' changes the archive end date ''' ''' WM Filepath ''' number/count of period (if ''' date_unity (d,m,y or day(s),month(s),years(s) ''' dateFrom_value ''' Returns true when date was set, false if not ''' Public Function NewLifecycle_Period(ByVal wmfilepath As String, ByVal dateFrom_value As Date, ByVal date_period As Double, ByVal date_unit As String) Dim oWMObject As WMObject Try oWMObject = GetFileByPath(wmfilepath) _logger.Info($"Changing the archive end-date for file '{oWMObject.aName}', Items: {dateFrom_value},{date_period},{date_unit}") Try ' die Datei sperren oWMObject.LockFor(WMObjectEditModeLifeCycleEdit) _logger.Debug(">> object locked") Catch ex As Exception _logger.Error(ex) ' nichts tun (Datei ist bereits gesperrt) End Try Dim oObjectLifecycle = oWMObject.aWMLifeCycle Dim oIntervalType As DateInterval If date_unit.ToLower = "d" Or date_unit.ToLower = "day(s)" Then oIntervalType = DateInterval.Day ElseIf date_unit.ToLower = "m" Or date_unit.ToLower = "month(s)" Then oIntervalType = DateInterval.Month ElseIf date_unit.ToLower = "y" Or date_unit.ToLower = "year(s)" Then oIntervalType = DateInterval.Year End If Dim oArchUntil = DateAdd(oIntervalType, date_period, dateFrom_value) _logger.Debug("New date shall be: " & oArchUntil.ToString) oObjectLifecycle.SetPeriodEndDate(2, oArchUntil) _logger.Info("Archive end-date has been changed!") oWMObject.Save() oWMObject.unlock() Return SetArchive_Active(oWMObject) Catch ex As Exception _logger.Warn($"Unexpected Error in NewLifecycle_Period {ex.Message}") If Not IsNothing(oWMObject) Then If oWMObject.aLocked = True Then oWMObject.unlock() End If End If Return False End Try End Function ''' ''' changes the archive end date ''' ''' WM Filepath ''' number/count of period (if ''' Returns true when date was set, false if not ''' Public Function NewLifecycle_PeriodTEST(ByVal wmfilepath As String, ByVal oArchUntil As String) Dim oWMObject As WMObject Try oWMObject = GetFileByPath(wmfilepath) _logger.Info($"Changing the archive end-date for file '{oWMObject.aName}', Items: {oArchUntil}") Try ' die Datei sperren oWMObject.LockFor(WMObjectEditModeLifeCycleEdit) _logger.Debug(">> object locked") Catch ex As Exception _logger.Error(ex) ' nichts tun (Datei ist bereits gesperrt) End Try Dim oObjectLifecycle = oWMObject.aWMLifeCycle ' Dim oDate = CDate(oArchUntil) ' Dim omMyFormattedDate = oDate.ToString("dd.MM.yyyy") If oArchUntil <> String.Empty Then If IsDate(oArchUntil) Then If CDate(oArchUntil) < CDate(Now) Then _logger.Debug("oArchUntil < today so direct move to archivepool") Else _logger.Debug("New date shall be: " & oArchUntil) oObjectLifecycle.SetPeriodEndDate(2, oArchUntil) _logger.Info("Archive end-date has been changed!") End If End If Else _logger.Debug("oArchUntil is empty so direct move to archivepool") End If Dim result = SetArchive_Active(oWMObject) oWMObject.Save() oWMObject.unlock() Return result Catch ex As Exception _logger.Warn($"Unexpected Error in NewLifecycle_PeriodTEST {ex.Message}") If Not IsNothing(oWMObject) Then If oWMObject.aLocked = True Then oWMObject.unlock() End If End If Return False End Try End Function ''' ''' Archives windream object immediately ''' ''' Returns true when archiving was set, false if not ''' Private Function SetArchive_Active(oWMObject As WMObject) Try oWMObject.MoveToArchivePool() _logger.Info("oWMObject has been archived!") Return True Catch ex As Exception _logger.Warn($"Unexpected Error in SetArchive_Active {ex.Message}") Return False End Try End Function Public Function NewFolder(Path As String) As Boolean If Not TestSessionLoggedIn() Then Return False End If Try Path = GetNormalizedPath(Path, True) Dim oFolders As List(Of String) = Path.Split("\").ToList() Dim oFolderObject As WMObject Dim oCurrentPath As String = String.Empty For Each oFolder In oFolders If oFolder = String.Empty Then Continue For End If oCurrentPath = Combine(oCurrentPath, oFolder) If TestFolderExists(oCurrentPath) = False Then oFolderObject = Session.GetNewWMObjectFS(WMEntityFolder, oCurrentPath, WMObjectEditModeNoEdit) _logger.Info($"new Folder [{oCurrentPath}] has been created!") End If Next Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function Public Function NewFileVersion(Path As String, Comment As String) As Boolean If Not TestSessionLoggedIn() Then Return False End If Try Path = GetNormalizedPath(Path, False) Dim oFileObject As IWMObject6 oFileObject = GetObjectByPath(Path, WMEntityDocument) oFileObject.CreateVersion2(False, Constants.HISTORY_NEW_FROM_VERSION, Comment) Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function Public Function NewFileStream(ByVal FilenameSource As String, ByVal FilenameTarget As String, ByVal WMObjecttypeName As String) As Boolean NewDocumentID = 0 If Not TestSessionLoggedIn() Then Return False End If Dim oTargetDrive As String = Path.GetDirectoryName(FilenameTarget) FilenameTarget = GetNormalizedPath(FilenameTarget, True) _logger.Debug($"Preparing to stream file from {FilenameSource} to {FilenameTarget}") Dim oWMObject As IWMObject6 = Nothing Dim oFileIO As WMFileIO Dim oWMStream As WMStream NewFolder(oTargetDrive) 'Indexierungsdialog der Session unterdrücken Session.SwitchEvents(Constants.COM_EVENT_SESSION_NEED_INDEX, False) Try ' GetNewWMObjectFS already locks the WMObject _logger.Debug("Creating WMObject for file {0}", FilenameTarget) oWMObject = Session.GetNewWMObjectFS(WMEntityDocument, FilenameTarget, WMObjectEditModeObject) Catch ex As Exception _logger.Error(ex, "WMObject could not be created") Return False End Try If oWMObject Is Nothing Then _logger.Warn("Document {0} could not be found", FilenameTarget) Return False End If Try _logger.Debug("Opening stream for {0}", FilenameTarget) oWMStream = oWMObject.OpenStream(Constants.STREAM_BINARY_OBJECT, Constants.STREAM_OPEN_MODE_READ_WRITE) Catch ex As Exception _logger.Error(ex) If UnlockObject(oWMObject) Then RemoveFile(FilenameTarget) Return False End Try Try _logger.Debug("Creating FileIO", FilenameTarget) oFileIO = New WMFileIO With { .bstrOriginalFileName = FilenameSource, .aWMStream = oWMStream } Catch ex As Exception If UnlockObject(oWMObject) Then RemoveFile(FilenameTarget) _logger.Error(ex, "Error while creating FileIO object") Return False End Try Try _logger.Debug("Streaming file...") oFileIO.ImportOriginal(True) _logger.Debug("Content of file was transferred!") Catch ex As Exception If UnlockObject(oWMObject) Then RemoveFile(FilenameTarget) _logger.Error(ex, "Error while streaming file") Return False End Try Try _logger.Debug("Closing Stream") oWMStream.Close() _logger.Debug("Saving new object") oWMObject.aObjectType = GetObjectByName(WMObjecttypeName, WMEntityObjectType) oWMObject.Save() _logger.Debug("Unlocking new object") oWMObject.unlock() Catch ex As Exception RemoveFile(FilenameTarget) Return False End Try _logger.Info($"File '{FilenameTarget}' has been successfully streamed to windream!") Dim oDocid = GetIndexValue(FilenameTarget, "Dokument-ID") If Not IsNothing(oDocid) Then NewDocumentID = oDocid(0) CurrentWMObject = oWMObject End If Return True End Function Public Function LockObject(WMObject As WMObject, Optional EditMode As WMObjectEditMode = WMObjectEditModeNoEdit) As Boolean Try WMObject.LockFor(EditMode) Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function Public Function UnlockObject(WMObject As WMObject) As Boolean Try WMObject.unlock() Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function Public Function GetNormalizedPath(pPath As String, pCleanPath As Boolean) As String _logger.Debug("Normalizing Path: [{0}]", pPath) Dim oNormalizedPath As String = pPath If pCleanPath = True Then oNormalizedPath = StringEx.RemoveInvalidCharacters(pPath) _logger.Debug("Path after RemoveInvalidCharacters: [{0}]", oNormalizedPath) End If Try ' Remove Driveletter, eg. W:\ If oNormalizedPath.StartsWith($"{ClientDriveLetter}:\") Then oNormalizedPath = oNormalizedPath.Substring(ClientDriveLetter.Length + 2) _logger.Debug($"Path after replaced ClientDriveLetter: [{oNormalizedPath}]") End If ' Remove Windream Base Path, eg. \\windream\objects\ If oNormalizedPath.ToLower.StartsWith(ClientBasePath.ToLower) Then _logger.Debug($"Replacing ClientBasePath: [{ClientBasePath}]") oNormalizedPath = oNormalizedPath.Substring(ClientBasePath.Length) End If ' Handle misconfigured drive-letter If oNormalizedPath.Contains(":") Then _logger.Warn($"NormalizedPath [{oNormalizedPath}] still contains a drive name!!") _logger.Warn($"Check Your config ClientDriveLetter [{ClientDriveLetter}] // ClientBasePath [{ClientBasePath}]") oNormalizedPath = oNormalizedPath.Substring(3) End If ' Convert any forward slashes / and double slashes \\ into backslashes \ ' See: https://stackoverflow.com/questions/3144492/how-do-i-get-nets-path-combine-to-convert-forward-slashes-to-backslashes If IsPathRooted(oNormalizedPath) Then ' This breaks because it converts the path "\SomeFolder" into "C:\SomeFolder" LOL 'oNormalizedPath = GetFullPath(oNormalizedPath) ' If path is a UNC path, exclude the first double backslashes while replacing If oNormalizedPath.StartsWith("\\") Then _logger.Debug("Path looks like a UNC Path") oNormalizedPath = "\\" & oNormalizedPath.Substring(2).Replace("\\", "\") Else oNormalizedPath = oNormalizedPath.Replace("\\", "\") End If ' Replace forward slashes oNormalizedPath = oNormalizedPath.Replace("/", "\") _logger.Debug("Path after converting slashes: [{0}]", oNormalizedPath) End If If oNormalizedPath.StartsWith("\") = False Then oNormalizedPath = $"\{oNormalizedPath}" End If _logger.Debug($"NormalizedPath: [{oNormalizedPath}]") Return oNormalizedPath Catch ex As Exception _logger.Warn($"Unexpected error in GetNormalizedPath - NormalizedPath [{oNormalizedPath}] - Error: [{ex.Message}]") Return "" End Try End Function Public Function GetAbsolutePath(pPath As String) As String Dim oNormalizedPath = GetNormalizedPath(pPath, False) Return $"\\windream\objects{oNormalizedPath}" End Function ''' ''' Returns the result of a search file ''' ''' Path of a search file (*.wdf) ''' Index containing the Document-ID ''' A datatable of the results with columns PATH and DOCID Public Function GetSearchDocuments(SearchFilePath As String, Optional DocIdIndexName As String = "") As DataTable Dim oDatatable As New DataTable oDatatable.Columns.Add("PATH", GetType(String)) oDatatable.Columns.Add("DOCID", GetType(Integer)) If DocIdIndexName = "" Then DocIdIndexName = "Dokument-ID" End If If TestSessionLoggedIn() = False Then Return oDatatable End If If TestFileExists(SearchFilePath) = False Then Return oDatatable End If Try Dim oFileInfo = New FileInfo(SearchFilePath) Dim oProfileName = oFileInfo.Name Dim oProfilePath = oFileInfo.DirectoryName Dim oSearchController As New WMOSearchController() oSearchController.CheckSearchProfile(SearchFilePath) Dim oSearchType = oSearchController.SearchProfileTargetProgID Dim oSearchProfileExSetttings As Integer = oSearchController.SearchProfileExSettings If oSearchProfileExSetttings = 0 Then oSearchProfileExSetttings = 7 End If Dim oSearch As WMSearch Select Case oSearchType.ToUpper() Case Constants.SEARCH_TYPE_QUICK_SEARCH Dim oQuickSearch As New WMQuickSearch With { .WMSession = Session, .SearchProfilePath = oProfilePath } oQuickSearch.ClearSearch() oQuickSearch.LoadSearchProfile(oProfileName) oSearch = oQuickSearch.GetSearch() Case Constants.SEARCH_TYPE_INDEX_SEARCH Dim oIndexSearch As New WMIndexSearch With { .WMSession = Session, .SearchProfilePath = oProfilePath } oIndexSearch.ClearSearch() oIndexSearch.LoadSearchProfile(oProfileName) oSearch = oIndexSearch.GetSearch() Case Constants.SEARCH_TYPE_OBJECTTYPE_SEARCH Dim oObjecttypeSearch As New WMObjectTypeSearch With { .WMSession = Session, .SearchProfilePath = oProfilePath } oObjecttypeSearch.ClearSearch() oObjecttypeSearch.LoadSearchProfile(oProfileName) oSearch = oObjecttypeSearch.GetSearch() Case Else _logger.Warn("{0} is not a valid search type", oSearchType) Return oDatatable End Select Dim oSearchResults As WMObjects = oSearch.Execute() If oSearchResults.Count = 0 Then Return oDatatable End If For Each oSearchResult As WMObject In oSearchResults Dim path As String = oSearchResult.aPath Dim docId As Integer = oSearchResult.GetVariableValue(DocIdIndexName) oDatatable.Rows.Add(path, docId) Next oDatatable.AcceptChanges() Return oDatatable Catch ex As Exception _logger.Error(ex) Return oDatatable End Try End Function Public Function GetVectorData(FilePath As String, IndexName As String, NewValues As Object, CheckDuplicates As Boolean) As String() Try Dim oWMObject = GetFileByPath(FilePath) Dim oObjectArray As Object() = GetVektorData_Combined(oWMObject, IndexName, NewValues, CheckDuplicates) Dim oStringArray As New List(Of String) For Each oObjectValue In oObjectArray oStringArray.Add(oObjectValue.ToString) Next Return oStringArray.ToArray Catch ex As Exception _logger.Error(ex) Return Nothing End Try End Function ''' ''' Gets an array of the actual vektorvalues of index, collated with the passed values ''' ''' windream-file as WMObject ''' Indexname as String ''' The new values as Array ''' True if duplicates shall be prevented ''' Public Function GetVektorData_Combined(ByVal WindreamObject As WMObject, IndexName As String, NewValues As Object(), CheckDuplikat As Boolean) Try Dim oAnzahl As Integer = 0 Dim oValueArray() 'Jeden Wert des Vektorfeldes durchlaufen Dim oWMValue = WindreamObject.GetVariableValue(IndexName) If oWMValue Is Nothing = False Then 'Nochmals prüfen ob wirklich Array If oWMValue.GetType.ToString.Contains("System.Object") Then 'Keine Duplikatprüfung also einfach neues Array füllen If CheckDuplikat = False Then For Each ovalue As Object In oWMValue 'Das Array anpassen ReDim Preserve oValueArray(oAnzahl) 'Den Wert im Array speichern oValueArray(oAnzahl) = ovalue.ToString oAnzahl += 1 Next 'Und jetzt den/die Neuen Wert(e) anfügen For Each oNewValue As Object In NewValues If oNewValue Is Nothing = False Then 'Das Array anpassen ReDim Preserve oValueArray(oAnzahl) 'Den Wert im Array speichern oValueArray(oAnzahl) = oNewValue.ToString oAnzahl += 1 End If Next Else _logger.Debug("Duplicates shall be checked...") 'Duplikat Prüfung an, also nur anhängen wenn Wert <> For Each oValue As Object In oWMValue If oValue Is Nothing = False Then 'Erst einmal die ALten Werte schreiben ReDim Preserve oValueArray(oAnzahl) 'Den Wert im Array speichern oValueArray(oAnzahl) = oValue.ToString _logger.Debug("Value (" & oAnzahl & ") " & oValue.ToString) oAnzahl += 1 End If Next 'Jetzt die Neuen Werte auf Duplikate überprüfen For Each NewValue As Object In NewValues If NewValue Is Nothing = False Then If oValueArray.Contains(NewValue) = False Then _logger.Debug("New Value (" & oAnzahl & ") " & NewValue.ToString) 'Das Array anpassen ReDim Preserve oValueArray(oAnzahl) 'Den Wert im Array speichern oValueArray(oAnzahl) = NewValue.ToString oAnzahl += 1 Else _logger.Debug("Value '" & NewValue.ToString & "' bereits in Vektorfeld enthalten") End If End If Next End If End If Else _logger.Debug("Vektorfeld ist noch leer....") 'Den/die Neuen Wert(e) anfügen For Each oNewValue As Object In NewValues If oNewValue Is Nothing = False Then If CheckDuplikat = True Then If oValueArray Is Nothing = False Then If oValueArray.Contains(oNewValue) = False Then 'Das Array anpassen ReDim Preserve oValueArray(oAnzahl) 'Den Wert im Array speichern oValueArray(oAnzahl) = oNewValue.ToString oAnzahl += 1 Else _logger.Debug("Value '" & oNewValue.ToString & "' bereits in Array enthalten") End If Else 'Dererste Wert, also hinzufügen 'Das Array anpassen ReDim Preserve oValueArray(oAnzahl) 'Den Wert im Array speichern oValueArray(oAnzahl) = oNewValue.ToString oAnzahl += 1 End If Else 'Das Array anpassen ReDim Preserve oValueArray(oAnzahl) 'Den Wert im Array speichern oValueArray(oAnzahl) = oNewValue.ToString oAnzahl += 1 End If End If Next End If _logger.Debug("Return ValueArray: length " & oValueArray.Length) Return oValueArray Catch ex As Exception _logger.Error(ex) Return Nothing End Try End Function ''' ''' Sets objecttype of a folder ''' ''' ''' ''' Public Function SetFolderObjecttype(FolderPath As String, Objecttype As String) As Boolean If TestSessionLoggedIn() = False Then Return False End If Try FolderPath = GetNormalizedPath(FolderPath, False) If TestFolderExists(FolderPath) = False Then _logger.Warn("Folder {0} does not exist!", FolderPath) Return False End If Dim oWMFolder As WMObject = GetObjectByPath(FolderPath, WMEntityFolder) If LockObject(oWMFolder) = False Then _logger.Warn("Folder {0} could not be locked", FolderPath) End If If oWMFolder.aObjectType.aName <> Constants.OBJECT_TYPE_DEFAULT Then _logger.Warn("Objecttype for folder {0} has already been set!", FolderPath) End If Dim oObjecttype As WMObject = GetObjectByName(Objecttype, WMEntityObjectType) If oObjecttype Is Nothing Then _logger.Warn("Objecttype {0} does not exist!", Objecttype) Return False End If oWMFolder.aObjectType = oObjecttype oWMFolder.Save() If UnlockObject(oWMFolder) Then _logger.Warn("Folder {0} could not be unlocked", FolderPath) End If Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function Public Function RemoveFile(Path As String) As Boolean If TestSessionLoggedIn() = False Then _logger.Warn("TestSession = False!") Return Nothing End If Try Dim oWMObject As WMObject = GetFileByPath(Path) If oWMObject Is Nothing Then _logger.Debug("File so far not existing in WM") Return True End If _logger.Info($"Deleting WMObject [{Path}]") oWMObject.Delete() _logger.Info($"[RemoveFile] - File [{Path}] has been deleted!") Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function Public Function RemoveVectorIndexValue(Path As String, IndexName As String, ValueToDelete As String) As Boolean If TestSessionLoggedIn() = False Then Return False End If Try Dim oWMObject As WMObject = Session.GetWMObjectByName(WMEntityAttribute, Path) If oWMObject Is Nothing Then _logger.Warn("Could not find document {0}", Path) Return False End If Dim oVectorValues = oWMObject.GetVariableValue(IndexName) Dim oType As Integer = GetIndexType(IndexName) If Helpers.IsVectorIndex(oType) = False Then _logger.Warn("Index {0} is not a vector index", IndexName) Return False End If If oVectorValues Is Nothing Then _logger.Warn("Could not values of index {0}", IndexName) Return False End If Dim oNewValues As New List(Of Object) oNewValues = oVectorValues.Except(New List(Of Object) From {ValueToDelete}).ToList() ' BEGIN WRITE INDEX If LockObject(oWMObject, WMObjectEditModeIndexEdit) = False Then _logger.Warn("File {0} could not be locked") Return False End If oWMObject.SetVariableValue(IndexName, oNewValues.ToArray()) oWMObject.Save() UnlockObject(oWMObject) Return True ' END WRITE INDEX Catch ex As Exception _logger.Error(ex) Return False End Try End Function Public Function SetFileIndex(Path As String, pIndexName As String, pStringValue As String, pObjectType As String) As Boolean If TestSessionLoggedIn() = False Then Return False End If If TestFileExists(Path) = False Then _logger.Warn("File '{0}' does not exist", Path) Return False End If Dim oWMObject As IWMObject6 = GetFileByPath(Path) If LockObject(oWMObject, WMObjectEditModeIndexEdit) = False Then _logger.Warn("File {0} could not be locked") Return False End If If oWMObject.aObjectType.aName = Constants.OBJECT_TYPE_DEFAULT Then oWMObject.aObjectType = GetObjectByName(pObjectType, WMEntityObjectType) End If Try Dim oType As Integer = GetIndexType(pIndexName) If Not IsNothing(oType) Then Dim oConvertedValue As Object = Helpers.ConvertIndexValue(oType, pStringValue) oWMObject.SetVariableValue(pIndexName, oConvertedValue) oWMObject.Save() If UnlockObject(oWMObject) = False Then _logger.Warn("File {0} could not be unlocked", Path) End If Return True Else Return False End If Catch ex As Exception _logger.Error(ex) UnlockObject(oWMObject) Return False End Try End Function Public Function SetFileIndexLoS(pPath As String, pIndexName As String, pAttributeValues As List(Of String), pObjectType As String) As Boolean If TestSessionLoggedIn() = False Then Return False End If If TestFileExists(pPath) = False Then _logger.Warn("File '{0}' does not exist", pPath) Return False End If Dim oWMObject As IWMObject6 Try 'Create the windream-Object via path oWMObject = GetFileByPath(pPath) Catch ex As Exception _logger.Warn("Could not create a WMObject for path '{0}'!!", pPath) _logger.Warn(ex.Message) Return False End Try 'Lock WMObject for changes If LockObject(oWMObject, WMObjectEditModeIndexEdit) = False Then _logger.Warn("File {0} could not be locked") Return False End If If oWMObject.aObjectType.aName = Constants.OBJECT_TYPE_DEFAULT Then oWMObject.aObjectType = GetObjectByName(pObjectType, WMEntityObjectType) End If Try Dim oType As Integer = GetIndexType(pIndexName) Dim oConvertedValues As New List(Of String) Dim oArray As Object _logger.Debug("SetFileIndexLoS '{0}' - Indextype: {1} - Values.Count: {2} ... ", pIndexName, oType, pAttributeValues.Count) ReDim oArray(pAttributeValues.Count - 1) For oIndex = 0 To pAttributeValues.Count - 1 Dim oValue As Object = Helpers.ConvertAttrValue(oType, pAttributeValues.Item(oIndex)) _logger.Debug("Converted Value: {0}", oValue.ToString) oArray(oIndex) = oValue Next oWMObject.SetVariableValue(pIndexName, oArray) oWMObject.Save() If UnlockObject(oWMObject) = False Then _logger.Warn("File {0} could not be unlocked", pPath) End If Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function Public Function ExportFile(WMObject As WMObject, ExportPath As String) As Boolean Try Dim oWMObject As IWMObject6 = DirectCast(WMObject, IWMObject6) Dim oFilenameFull As String = oWMObject.aName Dim oFilenameExport As String Dim oSplitIndex = oFilenameFull.LastIndexOf(".") Dim oVersion = 1 Dim oFilename = oFilenameFull.Substring(0, oSplitIndex) Dim oExtension = oFilenameFull.Substring(oSplitIndex) _logger.Debug("Preparing export of file {0}..", oFilenameFull) _logger.Debug("Filename: {0}", oFilename) _logger.Debug("Extension: {0}", oExtension) ' build the file path in case the exported file doesn't already exist oFilenameExport = BuildExportPath(ExportPath, oFilename, oExtension) ' Add version until we find the version that does NOT exist Do While File.Exists(oFilenameExport) oVersion += 1 oFilenameExport = BuildExportPath(ExportPath, oFilename, oExtension, oVersion) Loop _logger.Debug("File will be exported to {0}", oFilenameExport) _logger.Debug("Opening file stream..") Dim oStream As WMStream = oWMObject.OpenStream("BinaryObject", WMObjectStreamOpenMode.WMObjectStreamOpenModeRead) Dim oWMFileIO As New WMFileIO() With { .aWMStreamEx = oStream, .aWMStream = oStream, .bstrOriginalFileName = oFilenameExport } _logger.Debug("Exporting file..") oWMFileIO.ExportOriginal(True) _logger.Debug("Cleaning up..") oStream.Flush() oStream.Close() _logger.Debug("File exported to {0}", oFilenameExport) Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function Private Function BuildExportPath(BasePath As String, FilenameWithoutExtension As String, Extension As String, Optional Version As Integer = 1) Dim oFilename If Version = 1 Then oFilename = FilenameWithoutExtension & Extension Else oFilename = FilenameWithoutExtension & "_" & Version & Extension End If Return Path.Combine(BasePath, oFilename) End Function Public Function Export_WMFile(WMPath As String, Exportpath As String) Try If Not Exportpath.EndsWith("\") Then Exportpath &= "\" End If Dim oWMObject As Object = GetFileByPath(WMPath) If IsNothing(oWMObject) Then Return False '_logger.Debug("GetFileByPath failed - Trying GetFileByPathObj6...") 'oWMObject = GetFileByPathObj6(WMPath) 'If IsNothing(oWMObject) Then ' Return False 'End If End If _logger.Debug("(Export_WMFile) Working on file: " & oWMObject.aName) Dim ExportFileIO = New WMOTOOLLib.WMFileIO ' CreateObject("WMOTOOLLib.WMFileIO") ' New WMOTOOLLib.WMFileIO _logger.Debug("(Export_WMFile) ExportFileIO created...") ' Stream Interface bereitstellen oWMObject.LockFor(WMObjectEditModeFileSystem) Try If Not oWMObject.aLocked Then oWMObject.lock() End If Catch ex As Exception _logger.Error(ex) Return False End Try Dim oWMStream = oWMObject.OpenStream("BinaryObject", WMObjectEditModeTypedData) '### VERSIONIERUNG ### Dim version As Integer = 2 'Dim Stammname As String = System.IO.Path.GetFileNameWithoutExtension(Quelle) Dim Filename = oWMObject.aName.Substring(0, oWMObject.aName.LastIndexOf(".")) Dim Extension = oWMObject.aName.Substring(oWMObject.aName.LastIndexOf(".")) Dim tempFilename As String = Exportpath & Filename & Extension 'Überprüfen ob File existiert Do While IO.File.Exists(tempFilename) = True tempFilename = Exportpath & Filename & "_" & version & Extension version = version + 1 Loop _logger.Debug("Exportfilename is: " & tempFilename) ' den Dateiinhalt der neuen Datei zuweisen ExportFileIO.aWMStream = oWMStream ExportFileIO.bstrOriginalFileName = tempFilename 'Das eigentliche kopieren ExportFileIO.ExportOriginal(True) ' close the windream file stream oWMStream.Close() oWMObject.Save() oWMObject.unlock() _logger.Info($"WMFile has been exported to {tempFilename} ") Return tempFilename Catch ex As Exception _logger.Error(ex) Return False End Try End Function Public Function Export_WMFile_DocID(WMPath As String, Exportpath As String, DocId As Integer) Try If Not Exportpath.EndsWith("\") Then Exportpath &= "\" End If Dim Extension = WMPath.Substring(WMPath.LastIndexOf(".")) Dim oTempFilename As String = Exportpath & DocId & Extension _logger.Info($"(Export_WMFile_DocID)Working on file [{WMPath}] ") If IO.File.Exists(oTempFilename) Then _logger.Info($"(Export_WMFile_DocID) DocID [{DocId}] already existing!! No action necessary!") Return True End If Dim oWMObject As WMObject Try oWMObject = GetFileByPath(WMPath) Catch ex As Exception _logger.Error(ex) _logger.Warn("No object created: " & WMPath) Return False End Try Dim ExportFileIO = New WMOTOOLLib.WMFileIO ' CreateObject("WMOTOOLLib.WMFileIO") ' New WMOTOOLLib.WMFileIO _logger.Debug("(Export_WMFile_DocID)ExportFileIO created...") Dim oWMStream = oWMObject.OpenStream("BinaryObject", 1) '### VERSIONIERUNG ### Dim version As Integer = 2 _logger.Debug($"Checking (FileExists) on [{oTempFilename}]... ") 'Überprüfen ob File existiert Do While IO.File.Exists(oTempFilename) = True oTempFilename = Exportpath & DocId & "_" & version & Extension version = version + 1 Loop _logger.Debug($"(Export_WMFile_DocID)Exportfilename is [{oTempFilename}] ") ' den Dateiinhalt der neuen Datei zuweisen ExportFileIO.aWMStream = oWMStream ExportFileIO.bstrOriginalFileName = oTempFilename 'Das eigentliche kopieren ExportFileIO.ExportOriginal(True) Dim oCounter = 0 Dim oError As Boolean = False Do While IO.File.Exists(oTempFilename) = False oCounter += 1 If oCounter = 30000 Then _logger.Warn($"WMStream took too long...Check the file [{WMPath}]") oError = True Exit Do End If Loop If oError = True Then Return False End If 'close the windream file stream oWMStream.Close() _logger.Info($"WMFile DocID [{DocId}] has been exported to [{oTempFilename}]") Return True Catch ex As Exception _logger.Error(ex) _logger.Info("Unexpected error in Export_WMFile: " & ex.Message) Return False End Try End Function Public Function VersionWMFilename(pPath As String, pExt As String) Return _fileSystem.GetVersionedFilenameWithFilecheck(pPath, AddressOf TestFileExists) End Function Public Function TestFolderExists(Path As String) As Boolean Return TestObjectExists(GetNormalizedPath(Path, False), WMEntityFolder) End Function Public Function TestFileExists(Path As String) As Boolean Return TestObjectExists(GetNormalizedPath(Path, False), WMEntityDocument) End Function Public Function TestUserExists(Username As String) As Boolean Return TestObjectExists(Username, WMEntityUser) End Function Public Function TestGroupExists(Groupname As String) As Boolean Return TestObjectExists(Groupname, WMEntityGroups) End Function Public Function TestIndexTypeIsVectorIndex(IndexType As Integer) As Boolean Return Helpers.IsVectorIndex(IndexType) End Function Public Function TestIndexNameIsVectorIndex(IndexName As String) As Boolean Dim oIndexType As Integer = GetIndexType(IndexName) Return Helpers.IsVectorIndex(oIndexType) End Function #End Region #Region "Private Methods" Private Function GetNormalizedBasePath(BasePath As String) As String If BasePath.EndsWith("\") Then Return BasePath Else Return BasePath & "\" End If End Function Private Function GetObjectTypes() As WMObjects Dim oObjectTypes As WMObjects Try oObjectTypes = Session.GetWMObjectTypes(WMEntityDocument) Return oObjectTypes Catch ex As Exception _logger.Error(ex) Return Nothing End Try End Function Private Function GetObjectByName(ObjectName As String, ObjectType As WMEntity) As WMObject If TestSessionLoggedIn() = False Then Return Nothing End If If TestObjectExists(ObjectName, ObjectType) = False Then _logger.Warn("GetObjectByName: Object {0} does not exist!", ObjectName) Return Nothing End If Try Dim oWMObject As WMObject = Session.GetWMObjectByName(ObjectType, ObjectName) Return oWMObject Catch ex As Exception _logger.Error(ex) Return Nothing End Try End Function Private Function GetObjectByPath(ObjectPath As String, ObjectType As WMEntity) As WMObject If TestSessionLoggedIn() = False Then Return Nothing End If If TestObjectExists(ObjectPath, ObjectType) = False Then _logger.Warn("GetObjectByPath: Object {0} does not exist!", ObjectPath) Return Nothing End If Try Dim oNormalizedPath = GetNormalizedPath(ObjectPath, False) Dim oWMObject As WMObject = Session.GetWMObjectByPath(ObjectType, oNormalizedPath) Return oWMObject Catch ex As Exception _logger.Error(ex) Return Nothing End Try End Function Private Function TestSessionLoggedIn() As Boolean Try If Session.aLoggedin Then Return True Else _logger.Warn("There is no active WM-Session for user {0}", _sessionUsername) Return False End If Catch ex As Exception _logger.Error(ex) Return False End Try End Function Private Function TestObjectExists(ObjectName As String, ObjectType As WMEntity) As Boolean If TestSessionLoggedIn() = False Then Return False End If Try Dim oObjectId = 0 Dim oObjectDbId = 0 Return Session.WMObjectExists(ObjectType, ObjectName, oObjectId, oObjectDbId) Catch ex As Exception _logger.Error(ex, "Error while checking existence of WMObject {0} of type {1}", ObjectName, ObjectType.ToString) Return False End Try End Function #End Region End Class