In VBA and VB6 I can assign something to mid for Example mid(str,1,1)="A" in VBS this doesn't work.
I need this because String concatenation is freakin' slow
Here is the actual code i hacked together real quick
Function fastXMLencode(str)
Dim strlen
strlen = Len(str)
Dim buf
Dim varptr
Dim i
Dim j
Dim charlen
varptr = 1
buf = Space(strlen * 7)
Dim char
For i = 1 To strlen
char = CStr(Asc(Mid(str, i, 1)))
charlen = Len(char)
Mid(buf, varptr, 2) = "&#"
varptr = varptr + 2
Mid(buf, varptr, charlen) = char
varptr = varptr + charlen
Mid(buf, varptr, 1) = ";"
varptr = varptr + 1
Next
fastXMLencode = Trim(buf)
End Function
How can i get this to work in VBS?
Authoritative source explicitly stating it's not available in VBScript:
Visual Basic for Applications Features Not In VBScript:
Strings: Fixed-length strings LSet, RSet Mid Statement StrConv
VBA has both a Mid Statement and a Mid Function. VBScript only has the Mid Function.
The one other option you have if you are stuck doing this in VBScript is to make API calls. Since you are already comfortable working directly with the string buffer this might not be too big a jump for you. This page should get you started: String Functions
Sorry, it looks like API calls are out, too: Rube Goldberg Memorial Scripting Page: Direct API Calls Unless you want to write an ActiveX wrapper for your calls, but we're starting to get into an awful lot of work (and additional maintenance requirements) now.
You can't do that. You will have to rebuild the string from scratch.
This is not possible, mid(str,1,1) is a function which just returns a number (str is not passed 'by reference' and is not altered).
It's never too late to suggest an answer, even to a decade-old question...
One great thing about VBScript is that even though it's a subset of VB/VBA it still lets you declare (and use) classes. And having classes implies them having properties, with getters and/or setters... if you can guess where I'm going...
So if you wrap a class around a VBA-compatible implementation of Mid(), it's actually possible to emulate Mid() statements with a Let property. Consider the following code:
Class CVBACompat
' VB6/VBA-Like Mid() Statement
Public Property Let LetMid(ByRef Expression, ByVal StartPos, ByVal Length, ByRef NewValue) ' ( As String, As Long, As Long, As String)
Dim sInsert ' As String
sInsert = Mid(NewValue, 1, Length)
Expression = Mid(Expression, 1, StartPos - 1) & sInsert & Mid(Expression, StartPos + Len(sInsert))
End Property
' VB6/VBA-Like IIf() Function
Public Function IIf(Expression, TruePart, FalsePart)
If CBool(Expression) Then
IIf = TruePart
Else
IIf = FalsePart
End If
End Function
End Class: Public VBACompat: Set VBACompat = New CVBACompat: Public Function IIf(X, Y, Z): IIf = VBACompat.IIf(X, Y, Z): End Function
VBA's language specification says that when Mid( <string-expression>, <start> [, <length> ] ) is used as a statement, what's to be replaced in the source string <string-expression> is the lowest number of characters given the entire RHS expression or as limited by the <lenght> argument, when provided. Now, you can't have optional arguments in VBScript, so if you want to be able to also use statements of the sort Mid( <string-expression>, <start> ) = <expression> you'll have to have two separate implementations.
The implementation proposed above can be checked with the following code (not including checks for error-throwing conditions, which are the same as for the Mid() function anyway) :
Dim f ' As String
Dim g ' As String
f = "1234567890"
g = f
' VB6 / VBA: Mid(f, 5, 2) = "abc"
VBACompat.LetMid(f, 5, 2) = "abc"
' VB6 / VBA: Mid(g, 5, 4) = "abc"
VBACompat.LetMid(g, 5, 4) = "abc"
WScript.Echo "First call " & IIf(f = "1234ab7890", "passes", "fails")
WScript.Echo "Second call " & IIf(g = "1234abc890", "passes", "fails")
In any case, since the proposed solution is just an emulation of otherwise native code implementations (in VB6, VBA), and still implies string concatenation anyway with the overhead of class instantiation + VTable translations, the OP'd be better off creating and making available an ActiveX component (OCX) to do concatenations in series.
Related
i m working on translating code from VB to C#, though there are lots of great conversion websites, i still find the code ambiguous to me, since the documentation is really poor it approaches to useless, i thought about posting it here to see if i can get it clearer.
i need some elaboration on the code given below, and whats the use of the Buffer[] in the method below:
Function hexToBin(ByVal str As String, ByRef Buffer() As Byte)
Dim strRemain As String
Dim firstChar As Boolean
Dim i, count, inputLen, remainLen As Integer
i = 0
count = 0
firstChar = True
strRemain = str
While Len(strRemain) > 0
If Mid(strRemain, 1, 1) = " " Then
firstChar = True
strRemain = Mid(strRemain, 2)
ElseIf firstChar = True Then
If Len(strRemain) = 1 Then
Buffer(count) = myVal(strRemain)
ElseIf Len(strRemain) >= 2 Then
Buffer(count) = myVal(Mid(strRemain, 1, 1)) * 16 + myVal(Mid(strRemain, 2, 1))
strRemain = Mid(strRemain, 3)
End If
count = count + 1
firstChar = False
Else
strRemain = Mid(strRemain, 2)
End If
Wend
hexToBin = count
End Function
see, i know this code converts from hex to binary as the name suggests, yet i cant really figure the use of the Buffer[] in the context, i looked up the Mid function in string VB, but still can't figure out the use of the Buffer[] in this function, i would appreciate if someone explained the use of the buffer.
In the code above, the parentheses are used to access elements of an array. So, Buffer(i) refers to the ith element of the array Buffer.
You can learn more about arrays in VB from any text book, or indeed from MSDN: http://msdn.microsoft.com/en-us/library/wak0wfyt.aspx
As for why the array Buffer is used in the first place, well that's to store the output of the function. The function takes a hex string as input and populates the byte array Buffer with the binary equivalent.
This does seem to be rather inefficient code though. And it presents a somewhat clumsy interface because it asks the caller to allocate the array. Rather than translating it, I think I would start here: How can I convert a hex string to a byte array?
I have a string output from user interface as below,
strFormula ="gridControlName.Rows(i).cells("C1").value *
gridControlName.Rows(i).cells("C2").value"
if i write code like
dblRes=gridControlName.Rows(i).cells("C1").value *
gridControlName.Rows(i).cells("C2").value
it will give result.. but since its a string i could not get result
How can I remove the double quotes from the above string and get the values entered in the grid cells to be multiplied?
I don't think there's an 'easy' way to do this, since VB.Net doesn't have an "eval()" like some other languages. However, it does support run-time compilation. Here are a couple articles which may help you:
Using .NET Languages to make your Application Scriptable (VB.Net example)
Runtime Compilation (A .NET eval statement) (C# example)
Both are intended to be a bit more robust than just executing single lines of code, allowing users to input entire textboxes of their own code for example, but should give you some direction. Both include sample projects.
Hi guys thanks for your updates.. I wrote my own function by using your concepts and some other code snippets .I am posting the result
Function generate(ByVal alg As String, ByVal intRow As Integer) As String Dim algSplit As String() = alg.Split(" "c)
For index As Int32 = 0 To algSplit.Length - 1
'algSplit(index) = algSplit(index).Replace("#"c, "Number")
If algSplit(index).Contains("[") Then
Dim i As Integer = algSplit(index).IndexOf("[")
Dim f As String = algSplit(index).Substring(i + 1, algSplit(index).IndexOf("]", i + 1) - i - 1)
Dim grdCell As Infragistics.Win.UltraWinGrid.UltraGridCell = dgExcelEstimate.Rows(intRow).Cells(f)
Dim dblVal As Double = grdCell.Value
algSplit(index) = dblVal
End If
Next
Dim result As String = String.Join("", algSplit)
'Dim dblRes As Double = Convert.ToDouble(result)
Return result
End Function
Thanks again every one.. expecting same in future
I'm working with VBA. I wrote a user define function that takes a string, process it and return a cleaned string. I am not sure what is wrong with it. I am not able to call it and ask it to process my string and return it. I am thinking there are a mistake in the way I am defining or returning it.
Public Function ProcessString(input_string As String) As String
' The temp string used throughout the function
Dim temp_string As String
For i = 1 To Len(input_string)
temp_string = Mid(input_string, i, 1)
If temp_string Like "[A-Z, a-z, 0-9, :, -]" Then
return_string = return_string & temp_string
End If
Next i
return_string = Mid(return_string, 1, (Len(return_string) - 1))
ProcessString = return_string & ", "
End Function
And I use this function like this
Worksheets(data_sheet).Range("C2").Value = ProcessString(last_name)
Last name is a string variable, usually looks like this Lastname*****, and I am trying to remove all the stars behind it. Have it return Lastname without the stars.
I received Compile error: ByRef arugment type mismatch when I tried to run this. I am using Windows XP with Office 2003.
EDIT: I added the basic struction of the code I have, I have about 20 lines of the similar code. Doing the same thing for each field I need.
Private Sub CommandButton2_Click()
' In my original production code I have a chain of these
' Like this Dim last_name, first_name, street, apt, city, state, zip As String
Dim last_name As String
' I get the last name from a fixed position of my file. Because I am
' processing it from another source which I copied and pasted into excel
last_name = Mid(Range("A4").Value, 20, 13)
' Insert the data into the corresponding fields in the database worksheet
Worksheets(data_sheet).Range("C2").Value = ProcessString(last_name)
I suspect you haven't set up last_name properly in the caller.
With the statement Worksheets(data_sheet).Range("C2").Value = ProcessString(last_name)
this will only work if last_name is a string, i.e.
Dim last_name as String
appears in the caller somewhere.
The reason for this is that VBA passes in variables by reference by default which means that the data types have to match exactly between caller and callee.
Two fixes:
1) Force ByVal -- Change your function to pass variable ByVal: Public Function ProcessString(ByVal input_string As String) As String, or
2) Dim varname -- put Dim last_name As String in the caller before you use it.
(1) works because for ByVal, a copy of input_string is taken when passing to the function which will coerce it into the correct data type. It also leads to better program stability since the function cannot modify the variable in the caller.
I don't know why, but it is very important to declare the variables separately if you want to pass variables (as variables) into other procedure or function.
For example there is a procedure which make some manipulation with data: based on ID returns Part Number and Quantity information. ID as constant value, other two arguments are variables.
Public Sub GetPNQty(ByVal ID As String, PartNumber As String, Quantity As Long)
the next main code gives me a "ByRef argument mismatch":
Sub KittingScan()
Dim BoxPN As String
Dim BoxQty, BoxKitQty As Long
Call GetPNQty(InputBox("Enter ID:"), BoxPN, BoxQty)
End sub
and the next one is working as well:
Sub KittingScan()
Dim BoxPN As String
Dim BoxQty As Long
Dim BoxKitQty As Long
Call GetPNQty(InputBox("Enter ID:"), BoxPN, BoxQty)
End sub
I changed a few things to work with Option Explicit, and the code ran fine against a cell containing "abc.123", which returned "abc.12,". There were no compile errors.
Option Explicit ' This is new
Public Function ProcessString(input_string As String) As String
' The temp string used throughout the function
Dim temp_string As String
Dim i As Integer ' This is new
Dim return_string As String ' This is new
For i = 1 To Len(input_string)
temp_string = Mid(input_string, i, 1)
If temp_string Like "[A-Z, a-z, 0-9, :, -]" Then
return_string = return_string & temp_string
End If
Next i
return_string = Mid(return_string, 1, (Len(return_string) - 1))
ProcessString = return_string & ", "
End Function
I'll suggest you post more of your relevant code (that calls this function). You've stated that last_name is a String, but it appears that may not be the case. Step through your code line by line and ensure that this is actually the case.
While looping through your string one character at a time is a viable method, there's no need. VBA has built-in functions for this kind of thing:
Public Function ProcessString(input_string As String) As String
ProcessString=Replace(input_string,"*","")
End Function
Something is wrong with that string try like this:
Worksheets(data_sheet).Range("C2").Value = ProcessString(CStr(last_name))
It looks like ByRef needs to know the size of the parameter. A declaration of
Dim last_name as string
doesn't specify the size of the string so it takes it as an error. Before using
Worksheets(data_sheet).Range("C2").Value = ProcessString(last_name)
The last_name has to be declared as
Dim last_name as string *10 ' size of string is up to you but must be a fix length
No need to change the function. Function doesn't take a fix length declaration.
For me the problem here was that I was declaring multiple variables in a row instead of separate rows.
For example, I was trying to pass i as an integer to my function.
Dim i,j as integer - gets me the error
Dim i as integer - doesn't get the error
I'm simply wondering what symbol/character I can use to define any character in a string...
Basically I have a number of records with RR 2, RR#2, RR1, RR 1, etc. and I want to use a symbol that will define anything after the RR and replace it with nothing "". I know in SQL it's the "%" symbol, but not sure in VBA.
I am using the Replace function in ArcGIS field calculator.
I tried searching but cannot come up with the right question to find the answer I'm looking for.
Any ideas?
Since it's unclear if you want VBA or VB.Net,
Here's a VBA answer just use the ChopString function using the format shown in the Test sub:
Function ChopString(str As String, after As String, Optional caseInsensitive As Boolean = True) As String
Dim x As Long
If caseInsensitive Then
x = InStr(1, str, after, vbTextCompare)
Else
x = InStr(1, str, after, vbBinaryCompare)
End If
If x Then
str = Left(str, x + Len(after) - 1)
End If
ChopString = str
End Function
Sub Test()
Dim OriginalString As String
Dim choppedString As String
OriginalString = "1234RR this will be chopped"
choppedString = ChopString(OriginalString, "RR")
MsgBox choppedString
End Sub
Sadly the .net REPLACE() function doesn't support wildcard characters, you can use a function as described here but it's a bit long winded.
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
Which features of the VBA language are either poorly documented, or simply not often used?
This trick only works in Access VBA, Excel and others won't allow it. But you can make a Standard Module hidden from the object browser by prefixing the Module name with an underscore. The module will then only be visible if you change the object browser to show hidden objects.
This trick works with Enums in all vb6 based version of VBA. You can create a hidden member of an Enum by encasing it's name in brackets, then prefixing it with an underscore. Example:
Public Enum MyEnum
meDefault = 0
meThing1 = 1
meThing2 = 2
meThing3 = 3
[_Min] = meDefault
[_Max] = meThing3
End Enum
Public Function IsValidOption(ByVal myOption As MyEnum) As Boolean
If myOption >= MyEnum.[_Min] Then IsValidOption myOption <= MyEnum.[_Max]
End Function
In Excel-VBA you can reference cells by enclosing them in brackets, the brackets also function as an evaluate command allowing you to evaluate formula syntax:
Public Sub Example()
[A1] = "Foo"
MsgBox [VLOOKUP(A1,A1,1,0)]
End Sub
Also you can pass around raw data without using MemCopy (RtlMoveMemory) by combining LSet with User Defined Types of the same size:
Public Sub Example()
Dim b() As Byte
b = LongToByteArray(8675309)
MsgBox b(1)
End Sub
Private Function LongToByteArray(ByVal value As Long) As Byte()
Dim tl As TypedLong
Dim bl As ByteLong
tl.value = value
LSet bl = tl
LongToByteArray = bl.value
End Function
Octal & Hex Literals are actually unsigned types, these will both output -32768:
Public Sub Example()
Debug.Print &H8000
Debug.Print &O100000
End Sub
As mentioned, passing a variable inside parenthesis causes it to be passed ByVal:
Sub PredictTheOutput()
Dim i&, j&, k&
i = 10: j = i: k = i
MySub (i)
MySub j
MySub k + 20
MsgBox Join(Array(i, j, k), vbNewLine), vbQuestion, "Did You Get It Right?"
End Sub
Public Sub MySub(ByRef foo As Long)
foo = 5
End Sub
You can assign a string directly into a byte array and vice-versa:
Public Sub Example()
Dim myString As String
Dim myBytArr() As Byte
myBytArr = "I am a string."
myString = myBytArr
MsgBox myString
End Sub
"Mid" is also an operator. Using it you overwrite specific portions of strings without VBA's notoriously slow string concatenation:
Public Sub Example1()
''// This takes about 47% of time Example2 does:
Dim myString As String
myString = "I liek pie."
Mid(myString, 5, 2) = "ke"
Mid(myString, 11, 1) = "!"
MsgBox myString
End Sub
Public Sub Example2()
Dim myString As String
myString = "I liek pie."
myString = "I li" & "ke" & " pie" & "!"
MsgBox myString
End Sub
There is an important but almost always missed feature of the Mid() statement. That is where Mid() appears on the left hand side of an assignment as opposed to the Mid() function that appears in the right hand side or in an expression.
The rule is that if the if the target string is not a string literal, and this is the only reference to the target string, and the length of segment being inserted matches the length of the segment being replaced, then the string will be treated as mutable for the operation.
What does that mean? It means that if your building up a large report or a huge list of strings into a single string value, then exploiting this will make your string processing much faster.
Here is a simple class that benefits from this. It gives your VBA the same StringBuilder capability that .Net has.
' Class: StringBuilder
Option Explicit
Private Const initialLength As Long = 32
Private totalLength As Long ' Length of the buffer
Private curLength As Long ' Length of the string value within the buffer
Private buffer As String ' The buffer
Private Sub Class_Initialize()
' We set the buffer up to it's initial size and the string value ""
totalLength = initialLength
buffer = Space(totalLength)
curLength = 0
End Sub
Public Sub Append(Text As String)
Dim incLen As Long ' The length that the value will be increased by
Dim newLen As Long ' The length of the value after being appended
incLen = Len(Text)
newLen = curLength + incLen
' Will the new value fit in the remaining free space within the current buffer
If newLen <= totalLength Then
' Buffer has room so just insert the new value
Mid(buffer, curLength + 1, incLen) = Text
Else
' Buffer does not have enough room so
' first calculate the new buffer size by doubling until its big enough
' then build the new buffer
While totalLength < newLen
totalLength = totalLength + totalLength
Wend
buffer = Left(buffer, curLength) & Text & Space(totalLength - newLen)
End If
curLength = newLen
End Sub
Public Property Get Length() As Integer
Length = curLength
End Property
Public Property Get Text() As String
Text = Left(buffer, curLength)
End Property
Public Sub Clear()
totalLength = initialLength
buffer = Space(totalLength)
curLength = 0
End Sub
And here is an example on how to use it:
Dim i As Long
Dim sb As StringBuilder
Dim result As String
Set sb = New StringBuilder
For i = 1 to 100000
sb.Append CStr( i)
Next i
result = sb.Text
VBA itself seems to be a hidden feature. Folks I know who've used Office products for years have no idea it's even a part of the suite.
I've posted this on multiple questions here, but the Object Browser is my secret weapon. If I need to ninja code something real quick, but am not familiar with the dll's, Object Browser saves my life. It makes it much easier to learn the class structures than MSDN.
The Locals Window is great for debugging as well. Put a pause in your code and it will show you all the variables, their names, and their current values and types within the current namespace.
And who could forget our good friend Immediate Window? Not only is it great for Debug.Print standard output, but you can enter in commands into it as well. Need to know what VariableX is?
?VariableX
Need to know what color that cell is?
?Application.ActiveCell.Interior.Color
In fact all those windows are great tools to be productive with VBA.
It's not a feature, but a thing I have seen wrong so many times in VBA (and VB6): Parenthesis added on method calls where it will change semantics:
Sub Foo()
Dim str As String
str = "Hello"
Bar (str)
Debug.Print str 'prints "Hello" because str is evaluated and a copy is passed
Bar str 'or Call Bar(str)
Debug.Print str 'prints "Hello World"
End Sub
Sub Bar(ByRef param As String)
param = param + " World"
End Sub
Hidden Features
Although it is "Basic", you can use OOP - classes and objects
You can make API calls
Possibly the least documented features in VBA are those you can only expose by selecting "Show Hidden Members" on the VBA Object Browser. Hidden members are those functions that are in VBA, but are unsupported. You can use them, but microsoft might eliminate them at any time. None of them has any documentation provided, but you can find some on the web. Possibly the most talked about of these hidden features provides access to pointers in VBA. For a decent writeup, check out; Not So Lightweight - Shlwapi.dll
Documented, but perhaps more obscure (in excel anyways) is using ExecuteExcel4Macro to access a hidden global namespace that belongs to the entire Excel application instance as opposed to a specific workbook.
You can implement interfaces with the Implements keyword.
Dictionaries. VBA is practically worthless without them!
Reference the Microsoft Scripting Runtime, use Scripting.Dictionary for any sufficiently complicated task, and live happily ever after.
The Scripting Runtime also gives you the FileSystemObject, which also comes highly recommended.
Start here, then dig around a bit...
http://msdn.microsoft.com/en-us/library/aa164509%28office.10%29.aspx
Typing VBA. will bring up an intellisense listing of all the built-in functions and constants.
With a little work, you can iterate over custom collections like this:
' Write some text in Word first.'
Sub test()
Dim c As New clsMyCollection
c.AddItems ActiveDocument.Characters(1), _
ActiveDocument.Characters(2), _
ActiveDocument.Characters(3), _
ActiveDocument.Characters(4)
Dim el As Range
For Each el In c
Debug.Print el.Text
Next
Set c = Nothing
End Sub
Your custom collection code (in a class called clsMyCollection):
Option Explicit
Dim m_myCollection As Collection
Public Property Get NewEnum() As IUnknown
' This property allows you to enumerate
' this collection with the For...Each syntax
' Put the following line in the exported module
' file (.cls)!'
'Attribute NewEnum.VB_UserMemId = -4
Set NewEnum = m_myCollection.[_NewEnum]
End Property
Public Sub AddItems(ParamArray items() As Variant)
Dim i As Variant
On Error Resume Next
For Each i In items
m_myCollection.Add i
Next
On Error GoTo 0
End Sub
Private Sub Class_Initialize()
Set m_myCollection = New Collection
End Sub
Save 4 whole keystrokes by typing debug.? xxx instead of debug.print xxx.
Crash it by adding: enum foo: me=0: end enum to the top of a module containing any other code.
Support for localized versions, which (at least in the previous century) supported expressions using localized values. Like Pravda for True and FaĆszywy (not too sure, but at least it did have the funny L) for False in Polish... Actually the English version would be able to read macros in any language, and convert on the fly. Other localized versions would not handle that though.
FAIL.
The VBE (Visual Basic Extensibility) object model is a lesser known and/or under-utilized feature. It lets you write VBA code to manipulate VBA code, modules and projects. I once wrote an Excel project that would assemble other Excel projects from a group of module files.
The object model also works from VBScript and HTAs. I wrote an HTA at one time to help me keep track of a large number of Word, Excel and Access projects. Many of the projects would use common code modules, and it was easy for modules to "grow" in one system and then need to be migrated to other systems. My HTA would allow me to export all modules in a project, compare them to versions in a common folder and merge updated routines (using BeyondCompare), then reimport the updated modules.
The VBE object model works slightly differently between Word, Excel and Access, and unfortunately doesn't work with Outlook at all, but still provides a great capability for managing code.
IsDate("13.50") returns True but IsDate("12.25.2010") returns False
This is because IsDate could be more precisely named IsDateTime. And because the period (.) is treated as a time separator and not a date separator. See here for a full explanation.
VBA supports bitwise operators for comparing the binary digits (bits) of two values. For example, the expression 4 And 7 evaluates the bit values of 4 (0100) and 7 (0111) and returns 4 (the bit that is on in both numbers.) Similarly the expression 4 Or 8 evaluates the bit values in 4 (0100) and 8 (1000) and returns 12 (1100), i.e. the bits where either one is true.
Unfortunately, the bitwise operators have the same names at the logical comparison operators: And, Eqv, Imp, Not, Or, and Xor. This can lead to ambiguities, and even contradictory results.
As an example, open the Immediate Window (Ctrl+G) and enter:
? (2 And 4)
This returns zero, since there are no bits in common between 2 (0010) and 4 (0100).
Deftype Statements
This feature exists presumably for backwards-compatibility. Or to write hopelessly obfuscated spaghetti code. Your pick.