String values left out of PropertyInfo.GetValue - vb.net

I am not very well-versed in Reflection, but I have been working on this bit of code for a few days trying to obtain the values of class properties. I am using an API to find the values inside of cron jobs managed by the program VisualCron.
I'll explain the structure a bit. Each cron job has several tasks inside of it which have their own settings. The settings are stored in properties inside the TaskClass class that are declared like so:
Public Property <propertyname> As <classname>
Each property is tied to its own class, so for instance there is an Execute property inside TaskClass which is declared like this:
Public Property Execute As TaskExecuteClass
Inside TaskExecuteClass are the properties that hold the values I need. With the below block of code I have been able to retrieve the property values of all types EXCEPT strings. Coincidentally, the string values are the only values I need to get.
I know there must be something wrong with what I've written causing this because I can't find anyone with a similar issue after lots and lots of searching. Can anyone help me please?
Dim strAdd As String = ""
For Each t As VisualCronAPI.Server In vcClient.Servers.GetAll()
For Each f As VisualCron.JobClass In t.Jobs.GetAll
For Each s As VisualCron.TaskClass In f.Tasks
Dim propVal As Object
Dim propInfo As PropertyInfo() = s.GetType().GetProperties()
For i As Integer = 0 To propInfo.Length - 1
With propInfo(i)
If s.TaskType.ToString = propInfo(i).Name.ToString Then
Dim asm As Assembly = Assembly.Load("VisualCron")
Dim typeName As String = String.Format("VisualCron.{0}", propInfo(i).PropertyType.Name)
Dim tp As Type = asm.GetType(typeName)
Dim construct As ConstructorInfo = tp.GetConstructor(Type.EmptyTypes)
Dim classInst As Object = construct.Invoke(Nothing)
Dim classProps As PropertyInfo() = classInst.GetType().GetProperties()
For h As Integer = 0 To classProps.Length - 1
With classProps(h)
If .GetIndexParameters().Length = 0 Then
propVal = .GetValue(classInst, Nothing)
If Not propVal Is Nothing Then
strAdd = f.Name & " - " & s.Name & " - " & .Name & " - " & propVal.ToString
End If
End If
If strAdd <> "" Then
ListBox1.Items.Add(strAdd)
End If
End With
Next
End If
End With
Next
Next s
Next f
Next t

I solved my own problem, albeit in a crappy way. This is probably incredibly inefficient, but speed isn't a necessity in my particular case. Here is the code that works:
Dim strAdd As String = ""
For Each t As VisualCronAPI.Server In vcClient.Servers.GetAll()
For Each f As VisualCron.JobClass In t.Jobs.GetAll
For Each s As VisualCron.TaskClass In f.Tasks
Dim propVal As Object
Dim propInfo As PropertyInfo() = s.GetType().GetProperties()
For i As Integer = 0 To propInfo.Length - 1
With propInfo(i)
If s.TaskType.ToString = propInfo(i).Name.ToString Then
Dim asm As Assembly = Assembly.Load("VisualCron")
Dim typeName As String = String.Format("VisualCron.{0}", propInfo(i).PropertyType.Name)
Dim tp As Type = asm.GetType(typeName)
Dim construct As ConstructorInfo = tp.GetConstructor(Type.EmptyTypes)
Dim classInst As Object = construct.Invoke(Nothing)
Dim classProps As PropertyInfo() = classInst.GetType().GetProperties()
For h As Integer = 0 To classProps.Length - 1
With classProps(h)
If .GetIndexParameters().Length = 0 Then
propVal = .GetValue(CallByName(s, propInfo(i).Name.ToString, [Get]), Nothing)
If Not propVal Is Nothing Then
If propVal.ToString.Contains("\\server\") Or propVal.ToString.Contains("\\SERVER\") Then
strAdd = f.Name & " - " & s.Name & " - " & .Name & " - " & propVal.ToString
ListBox1.Items.Add(strAdd)
End If
End If
End If
End With
Next
End If
End With
Next
Next s
Next f
Next t
The piece of code that made the difference was the
classProps(h).GetValue(CallByName(s, propInfo(i).Name.ToString, [Get]), Nothing)
line.
If there are any suggestions for improving this code - I'm assuming that I've still got a lot of mistakes in here - then please comment for the future viewers of this answer and so I can adjust my code and learn more about how all of this works.

Related

VB.NET extract data from API

I am a beginner in VB.NET and i am trying to extract data from an API and add it to a listview column but i don't know how to extract the data.
[This is the API][1]
[1]: https://tmnf.exchange/api/tracks?author=lolsport&count=40&fields=TrackId%2CTrackName
It is a API for downloading race tracks for Trackmania.
The data is shown as follows {"TrackId":9707620,"TrackName":"lolsport R444"},
Now what i need is the TrackIDs and TrackNames.
i have two columns in my program where i want to sort them into like so.
**TrackID** **TrackName**
9707620 lolsport R444
How can i do this? i googled a lot about regular expressions but i cant seem to find anything that works.
Dim Data As String = "{""TrackId"":9707620,""TrackName"":""lolsport R444""}"
Dim dataaray() As String = Data.Split(",")
Dim dataval() As String
Dim fileloc As String = Environment.CurrentDirectory & "\Test.txt"
If Not File.Exists(fileloc) Then
File.Create(fileloc).Dispose()
Else
File.Delete(fileloc)
File.Create(fileloc).Dispose()
End If
Dim objwriter As New StreamWriter(fileloc, True)
Dim i As Int32 = 0
Dim val As String
objwriter.WriteLine("**TrackID** **TrackName**")
For Each rw As String In dataaray
dataval = rw.Split(":")
val += dataval(1).Replace("""", "").Replace("}", "") & vbTab
If i = 1 Then
objwriter.WriteLine(val.TrimEnd())
val = String.Empty
i = 0
End If
i += 1
Next
objwriter.Close()
objwriter.Dispose()

¿How to get the exact address of a cell? (VB.NET/INTEROP.EXCEL)

Yes. I am using a library that almost nobody likes (COM / Interop). I am practicing doing a program that analize an Excel workbook, identify its columns and the user dials the type of each. Everything serves perfect, I can detect errors in the type of each column (for example if there is a string in a numeric column) but the only type that I am'm having problems is with dates. I asked a question here yesterday regarding dates (because I thought something) but I know from that question that dates are just numbers .... This is no problem because I can use Date.fromOADate.
Well, the situation I face is that if an Excel column contains information of dates and for example, you add a data string in that column of dates, when loading the Excel book in the program, that data string did not mark it as an error .. . but treats it as an empty cell (Thing that has surprised me).
this is the function that I wrote to mark the errors of each column
Protected Friend Function obtenerErroresColumna(ByVal column As String, ByVal page As String, ByVal tipe As String) As Integer
If (Not String.IsNullOrEmpty(column)) Then
Dim cmd As String = "Select [" & column & "] from [" & page & "$]"
Dim errors As Integer = 0
Dim table As New DataTable
Try
Dim adapter As New OleDbDataAdapter(cmd, conexion)
adapter.Fill(table)
adapter.Dispose()
For Each itm In table.Rows
If (tipe.Equals("String")) Then
If (Not IsDBNull(itm(0))) Then
If (IsNumeric(itm(0))) Then
errors += 1
setValueError = itm(0)
End If
End If
ElseIf (tipe.Equals("Numeric")) Then
If (Not IsDBNull(itm(0))) Then
If (Not IsNumeric(itm(0))) Then
errors += 1
setValueError = itm(0)
End If
End If
ElseIf (tipe.Equals("Date")) Then
If (Not IsDBNull(itm(0))) Then
If (Not IsDate(itm(0))) Then
errors += 1
setValueError = itm(0)
End If
End If
End If
Next
table.Dispose()
Return errors
Catch ex As Exception
boxMessage("Error", ex.Message, My.Resources._error).ShowDialog()
Return errors
End Try
Else
Return 0
End If
End Function
Ok, as I said the first two types is running good, the problem is when I start to compare date data type. I have this idea if the column is date type: If the program returns an empty cell (as I said earlier, the string data returns me as empty cells) then the program obtains the address of the cell to make a replacement. I have already written the method for substitution ... only as parameters would have to pass is today's date, the exact address of the cell and the column name.
I would like to check the adress of the current cell of the loop when the variable "itm" is Null (A4, B3, C50.... Etc)
I don't see any reference to Excel.Interop in your code. For the first 26 columns you can use Chr :
Dim adr = Function(col%, row%) Chr(64 + col) & row
Dim B3 = adr(2, 3) ' "B3"
Ok guys, I found the solution of this problem. I didnt use interop but I get what I wanted.
First I needed to get the letter according to the column name. I found the web a function that returns the column letters of excel by passing as a parameter one number
Private Function ColumnIndexToColumnLetter(colIndex As Integer) As String
Dim div As Integer = colIndex
Dim colLetter As String = String.Empty
Dim modnum As Integer = 0
While div > 0
modnum = (div - 1) Mod 26
colLetter = Chr(65 + modnum) & colLetter
div = CInt((div - modnum) \ 26)
End While
Return colLetter
End Function
I insert a counter in the function that detects errors, this counter would count the cells in the column while to get the column number, I create another function that carried the columns in a arrayList.
I take the indexOf function
Protected Friend Function obtenerErroresColumna(ByVal columna As String, ByVal hoja As String, ByVal tipo As String) As Integer
If (Not String.IsNullOrEmpty(columna)) Then
Dim cmd As String = "Select [" & columna & "] from [" & hoja & "$]"
Dim errores As Integer = 0
Dim tabla As New DataTable
Dim cell As Integer = 2
Dim column As New ArrayList
column = cargarMatrizColumnas(hoja)
Try
Dim adapter As New OleDbDataAdapter(cmd, conexion)
adapter.Fill(tabla)
adapter.Dispose()
For Each itm In tabla.Rows
If (tipo.Equals("Cadena")) Then
If (Not IsDBNull(itm(0))) Then
If (IsNumeric(itm(0))) Then
errores += 1
setValoresError = itm(0)
End If
End If
ElseIf (tipo.Equals("Numerico")) Then
If (Not IsDBNull(itm(0))) Then
If (Not IsNumeric(itm(0))) Then
errores += 1
setValoresError = itm(0)
End If
End If
ElseIf (tipo.Equals("Fecha")) Then
If (Not IsDBNull(itm(0))) Then
If (Not IsDate(itm(0))) Then
errores += 1
setValoresError = itm(0)
End If
Else
MsgBox("Direccion: " & ColumnIndexToColumnLetter(column.IndexOf(columna) + 1) & cell)
End If
cell += 1
End If
Next
tabla.Dispose()
Return errores
Catch ex As Exception
cajaMensaje("Error inesperado", ex.Message, My.Resources._error).ShowDialog()
PantallaPrincipal.lbldireccion.ForeColor = Color.Red
Return errores
End Try
Else
Return 0
End If
End Function
Source of the function: https://www.add-in-express.com/creating-addins-blog/2013/11/13/convert-excel-column-number-to-name/

Solidworks EPDM API IEdmEnumeratorVariable5::SetVar not working as expected

I'm trying to use IEdmEnumeratorVariable5::SetVar to update some file card variables based on user input into a windows form. My code executes, there are no error messages, the file is checked out and checked back in and the appropriate comment is added to the history; however the variables on the card are not updated.
I have verified by stepping through code at runtime that all variables are populated with the correct (as expected) data. The SetVar procedures all go off without a hitch, but the variables on the data card do not change value - even manually refreshing the folder view has no effect.
Below is my code.
This is an add-in application, written as a class-library project in VB using VS Community 2015, with target framework .NET 4.0.
In efforts to make this question more concise; immediately below I've included just the snippet of code doing the set variables work, then I've also included more code so you can get the whole picture if needed.
JUST THE TIP :
This is the code doing the set variables work:
Dim UserManager As IEdmUserMgr5 = .SourceVault
Dim User As IEdmUser5 = UserManager.GetLoggedInUser
CardComment = UserComment & CardComment
CardDate = Today().ToString("yyMMdd", Globalization.CultureInfo.InvariantCulture)
CardBy = User.Name
CardDisposition = UserDisposition
CardVariables.SetVar(DispositionVariable, "#", CardDisposition)
CardVariables.SetVar(CommentVariable, "#", CardComment)
CardVariables.SetVar(ByVariable, "#", CardBy)
CardVariables.SetVar(DateVariable, "#", CardDate)
CardVariables.Flush()
THE BROADER STROKES :
Class module level variables:
Private Structure CommandInfo
Dim SourceVault As IEdmVault11
Dim SourceCommand As EdmCmd
Dim SourceSelection As System.Array
Dim TargetTemplate As System.String
Dim VerifiedPaths As List(Of String)
End Structure
Private ReceivedCommand As CommandInfo
OnCmd procedure (caller):
Public Sub OnCmd(ByRef poCmd As EdmCmd,
ByRef ppoData As System.Array) Implements IEdmAddIn5.OnCmd
Dim CommandToRun As MenuCommand
Try
With ReceivedCommand
.SourceVault = poCmd.mpoVault
.SourceCommand = poCmd
.SourceSelection = ppoData
'Get the command structure for the command ID
Select Case poCmd.meCmdType
Case EdmCmdType.EdmCmd_Menu
CommandToRun = AvailableCommands(.SourceCommand.mlCmdID)
Case EdmCmdType.EdmCmd_CardButton
Select Case True
Case poCmd.mbsComment.ToString.ToUpper.Contains("DISPOSITION")
DispositionRequest()
Case Else : Exit Sub
End Select
Case Else : Exit Sub
End Select
'...... (End Try, End Sub, Etc.)
DispositionRequest procedure (callee):
Private Sub DispositionRequest()
Dim UserDisposition As String
Using Disposition As New DispositionForm
With Disposition
If Not .ShowDialog() = System.Windows.Forms.DialogResult.OK Then Exit Sub
Select Case True
Case .Approve.Checked
UserDisposition = "Approved"
Case .Reject.Checked
UserDisposition = "Rejected"
Case Else : Exit Sub
End Select
End With
End Using
Dim UserComment As String
Using Explanation As New DispositionExplanation
With Explanation
If Not .ShowDialog() = System.Windows.Forms.DialogResult.OK Then Exit Sub
If .ListView1.Items.Count > 0 Then
'do some stuff not relevant to this question...
End If
UserComment = .Comments.Text
End With
End Using
'This next procedure just gets a list of paths from ReceivedCommand.SourceSelection - which is just the ppoData argument from the OnCmd procedure - see code block above!
Dim RequestPaths As List(Of String) = GetSelectedFilePaths()
For Each Path As String In RequestPaths
With ReceivedCommand
Dim RequestFile As IEdmFile5 = .SourceVault.GetFileFromPath(Path)
Dim ParentFolder As IEdmFolder6 = .SourceVault.GetFolderFromPath(System.IO.Path.GetDirectoryName(Path))
Dim UnlockLater As Boolean = False
If Not RequestFile.IsLocked Then
UnlockLater = True
RequestFile.LockFile(ParentFolder.ID, .SourceCommand.mlParentWnd, CInt(EdmLockFlag.EdmLock_Simple))
End If
Dim CardVariables As IEdmEnumeratorVariable5 = RequestFile.GetEnumeratorVariable
'We allow users to re-disposition a request so we want to keep any previous disposition information so it is not lost
Dim CardComment As String = String.Empty
Dim CardBy As String = String.Empty
Dim CardDate As String = String.Empty
Dim CardDisposition As String = String.Empty
Dim Success As Boolean
Const CommentVariable As String = "DispComm"
Const ByVariable As String = "DisposedBy"
Const DateVariable As String = "DisposedDate"
Const DispositionVariable As String = "Disposition"
Success = CardVariables.GetVar(DispositionVariable, "#", CardDisposition)
If Success Then
Success = CardVariables.GetVar(CommentVariable, "#", CardComment)
If Success Then Success = CardVariables.GetVar(ByVariable, "#", CardBy)
If Success Then Success = CardVariables.GetVar(DateVariable, "#", CardDate)
If Success Then CardComment = "Previously dispositioned as: """ & CardDisposition & """ by: " & CardBy & " on: " & CardDate & vbNewLine &
"---------Previous disposition explanation---------" & vbNewLine & CardComment
End If
Dim UserManager As IEdmUserMgr5 = .SourceVault
Dim User As IEdmUser5 = UserManager.GetLoggedInUser
CardComment = UserComment & CardComment
CardDate = Today().ToString("yyMMdd", Globalization.CultureInfo.InvariantCulture)
CardBy = User.Name
CardDisposition = UserDisposition
CardVariables.SetVar(DispositionVariable, "#", CardDisposition)
CardVariables.SetVar(CommentVariable, "#", CardComment)
CardVariables.SetVar(ByVariable, "#", CardBy)
CardVariables.SetVar(DateVariable, "#", CardDate)
CardVariables.Flush()
If UnlockLater Then RequestFile.UnlockFile(lParentWnd:= .SourceCommand.mlParentWnd,
bsComment:="Dispositioned as " & CardDisposition,
lEdmUnlockFlags:=0)
.SourceVault.RefreshFolder(ParentFolder.LocalPath)
End With
Next
End Sub
From the documentation:
bsCfgName : Name of configuration or layout to which to store the variable value; empty string for folders and file types that do not support configurations
I was working with a virtual file, which did not support configurations.
I saw a C example working with a virtual file and they were passing null references, so I reread the documentation and saw that excerpt above, so I changed my code from "#" to String.Empty for the mboconfiguration argument and now it is working!
CardVariables.SetVar(DispositionVariable, String.Empty, CardDisposition)
CardVariables.SetVar(CommentVariable, String.Empty, CardComment)
CardVariables.SetVar(ByVariable, String.Empty, CardBy)
CardVariables.SetVar(DateVariable, String.Empty, CardDate)
CardVariables.Flush()

VB.NET Lambda expression in contextmenu getting wrong item

I'd be grateful if anybody could shed some light on a problem.
I have a form showing a sales order, this has a DGV (DGVDocs) showing a list of invoices against that order. I've populated a print button with the documents and for each a submenu with Print, Preview, PDF. The lambda expressions on the submenu always pick up the last entry on the menu.
Private Sub create_print_menu()
Dim i As Integer = 0
ms = New ContextMenuStrip
If dgvDocs.Rows.Count > 0 Then
Dim doctype As String = ""
Dim docno As Integer = 0
For i = 0 To dgvDocs.Rows.Count - 1
ms.Items.Add(RTrim(dgvDocs.Rows(i).Cells(0).Value) & " " & RTrim(dgvDocs.Rows(i).Cells(2).Value))
jc = ms.Items(ms.Items.Count - 1)
doctype = RTrim(dgvDocs.Rows(i).Cells(0).Value)
docno = RTrim(dgvDocs.Rows(i).Cells(2).Value)
jc.DropDownItems.Add("Preview", Nothing, Function(sender, e) docPreview(doctype, docno))
Next
End If
End Sub
Private Function docPreview(ByVal doctype As String, ByVal docno As Integer)
If doctype.ToUpper.Contains("DESPATCH NOTE") Then
Dim frm As New frmDespatchPreview
frm.delnote = docno
frm.ShowDialog()
ElseIf doctype.ToUpper.Contains("INVOICE") Then
Dim frm As New frmInvoicePreview
frm.invno = docno
frm.ShowDialog()
End If
Return True
Return True
End Function
when you pass the lambda in your loop:
Function(sender, e) docPreview(doctype, docno)
... you are not passing in a copy or snapshot of whatever value is in doctype and docno at the time of that specific loop iteration. You are actually passing in a reference to those variables.
So, by the end of the loop, all the lambdas will effectively have a reference to whatever the last value of doctype and docno is.
To avoid this problem, make sure each lambda refers to a different variable reference. This can be accomplished by declaring doctype and docno inside the loop, like this:
'Dim doctype As String = ""
'Dim docno As Integer = 0
For i = 0 To dgvDocs.Rows.Count - 1
ms.Items.Add(RTrim(dgvDocs.Rows(i).Cells(0).Value) & " " & RTrim(dgvDocs.Rows(i).Cells(2).Value))
jc = ms.Items(ms.Items.Count - 1)
Dim doctype As String = RTrim(dgvDocs.Rows(i).Cells(0).Value)
Dim docno As Integer = RTrim(dgvDocs.Rows(i).Cells(2).Value)
jc.DropDownItems.Add("Preview", Nothing, Function(sender, e) docPreview(doctype, docno))
Next
EDIT:
Relevant article: Closures in VB Part 5: Looping

How to obtain the macros defined in an Excel workbook

Is there any way, in either VBA or C# code, to get a list of the existing macros defined in a workbook?
Ideally, this list would have a method definition signatures, but just getting a list of the available macros would be great.
Is this possible?
I haven't done vba for Excel in a long time, but if I remember well, the object model for the code was inaccessible through scripting.
When you try to access it, you receive the following error.
Run-time error '1004':
Programmatic access to Visual Basic Project is not trusted
Try:
Tools | Macro | Security |Trusted Publisher Tab
[x] Trust access to Visual Basic Project
Now that you have access to the VB IDE, you could probably export the modules and make a text search in them, using vba / c#, using regular expressions to find sub and function declarations, then delete the exported modules.
I'm not sure if there is an other way to do this, but this should work.
You can take a look the following link, to get started with exporting the modules.
http://www.developersdex.com/vb/message.asp?p=2677&ID=%3C4FCD0AE9-5DCB-4A96-8B3C-F19C63CD3635%40microsoft.com%3E
This is where I got the information about giving thrusted access to the VB IDE.
Building on Martin's answer, after you trust access to the VBP, you can use this set of code to get an array of all the public subroutines in an Excel workbook's VB Project. You can modify it to only include subs, or just funcs, or just private or just public...
Private Sub TryGetArrayOfDecs()
Dim Decs() As String
DumpProcedureDecsToArray Decs
End Sub
Public Function DumpProcedureDecsToArray(ByRef Result() As String, Optional InDoc As Excel.Workbook) As Boolean
Dim VBProj As Object
Dim VBComp As Object
Dim VBMod As Object
If InDoc Is Nothing Then Set InDoc = ThisWorkbook
ReDim Result(1 To 1500, 1 To 4)
DumpProcedureDecsToArray = True
On Error GoTo PROC_ERR
Set VBProj = InDoc.VBProject
Dim FuncNum As Long
Dim FuncDec As String
For Each VBComp In VBProj.vbcomponents
Set VBMod = VBComp.CodeModule
For i = 1 To VBMod.countoflines
If IsSubroutineDeclaration(VBMod.Lines(i, 1)) Then
FuncDec = RemoveBlanksAndDecsFromSubDec(RemoveAsVariant(VBMod.Lines(i, 1)))
If LCase(Left(VBMod.Lines(i, 1), Len("private"))) <> "private" Then
FuncNum = FuncNum + 1
Result(FuncNum, 1) = FindToLeftOfString(InDoc.Name, ".") '
Result(FuncNum, 2) = VBMod.Name
Result(FuncNum, 3) = GetSubName(FuncDec)
Result(FuncNum, 4) = VBProj.Name
End If
End If
Next i
Next VBComp
PROC_END:
Exit Function
PROC_ERR:
GoTo PROC_END
End Function
Private Function RemoveCharFromLeftOfString(TheString As String, RemoveChar As String) As String
Dim Result As String
Result = TheString
While LCase(Left(Result, Len(RemoveChar))) = LCase(RemoveChar)
Result = Right(Result, Len(Result) - Len(RemoveChar))
Wend
RemoveCharFromLeftOfString = Result
End Function
Private Function RemoveBlanksAndDecsFromSubDec(TheLine As String) As String
Dim Result As String
Result = TheLine
Result = RemoveCharFromLeftOfString(Result, " ")
Result = RemoveCharFromLeftOfString(Result, " ")
Result = RemoveCharFromLeftOfString(Result, "Public ")
Result = RemoveCharFromLeftOfString(Result, "Private ")
Result = RemoveCharFromLeftOfString(Result, " ")
RemoveBlanksAndDecsFromSubDec = Result
End Function
Private Function RemoveAsVariant(TheLine As String) As String
Dim Result As String
Result = TheLine
Result = Replace(Result, "As Variant", "")
Result = Replace(Result, "As String", "")
Result = Replace(Result, "Function", "")
If InStr(1, Result, "( ") = 0 Then
Result = Replace(Result, "(", "( ")
End If
RemoveAsVariant = Result
End Function
Private Function IsSubroutineDeclaration(TheLine As String) As Boolean
If LCase(Left(RemoveBlanksAndDecsFromSubDec(TheLine), Len("Function "))) = "function " Or LCase(Left(RemoveBlanksAndDecsFromSubDec(TheLine), Len("sub "))) = "sub " Then
IsSubroutineDeclaration = True
End If
End Function
Private Function GetSubName(DecLine As String) As String
GetSubName = FindToRightOfString(FindToLeftOfString(DecLine, "("), " ")
End Function
Function FindToLeftOfString(FullString As String, ToFind As String) As String
If FullString = "" Then Exit Function
Dim Result As String, ToFindPos As Integer
ToFindPos = InStr(1, FullString, ToFind, vbTextCompare)
If ToFindPos > 0 Then
Result = Left(FullString, ToFindPos - 1)
Else
Result = FullString
End If
FindToLeftOfString = Result
End Function
Function FindToRightOfString(FullString As String, ToFind As String) As String
If FullString = "" Then Exit Function
Dim Result As String, ToFindPos As Integer
ToFindPos = InStr(1, FullString, ToFind, vbTextCompare)
Result = Right(FullString, Len(FullString) - ToFindPos + 1 - Len(ToFind))
If ToFindPos > 0 Then
FindToRightOfString = Result
Else
FindToRightOfString = FullString
End If
End Function