I have seen this code online:
Dim TheOS As String
TheOS = Application.Operatingsystem
But I don't really understand how to implement that code to display what the OS is so I can test it
You can use the built-in compilation argument Mac:
#If Mac Then
' Code for the Mac
#Else
' Code for Windows
#End If
This function will return 1 if the operating system is Windows, else 2.
Function OS() As Long
Dim OSname As String
OSname = Application.OperatingSystem
If InStr(1, OSname, "windows", vbTextCompare) Then
' do one thing
OS = 1
Else
' do another
OS = 2
End If
End Function
You can examine the OSname string further, to determine which version of Windows or MAC. You can use the above code directly or indirectly, meaning, you may take different action within the function or use the result it returns to take different action.
Let me first say that I saw the other two threads that mentioned this issue here and here, but they didn't help me solve my problem.
I've been testing a program for several weeks in the on-prem Excel 2016 environment (32-bit) with no problems. My company is making the move to Office 365 soon, so I decided to test it over there as well. On that system, I'm getting a run-time error on the line Functions.Connection = objConnection
Option Explicit
Public Functions As SAPFunctionsOCX.SAPFunctions
Private LogonControl As SAPLogonCtrl.SAPLogonControl
Private objConnection As SAPLogonCtrl.Connection
Public Func As SAPFunctionsOCX.Function
Public Commit As SAPFunctionsOCX.Function
Public TableFactory As SAPTableFactory
Public silentLogon As Boolean
Public tblReadTableOptions, tblReadTableFields, tblReadTableData As SAPTableFactoryCtrl.Table
Sub ExtractProjectData()
If objConnection Is Nothing Then LogonToSAP
InitiateSAPVariables
Set Func = Functions.Add("BBP_RFC_READ_TABLE")
Set tblReadTableOptions = Func.Tables("OPTIONS")
Set tblReadTableFields = Func.Tables("FIELDS")
Set tblReadTableData = Func.Tables("DATA")
'extract/transform data from SAP tables
End Sub
Function InitiateSAPVariables()
Set Functions = Nothing
Set TableFactory = Nothing
Set Func = Nothing
Set Functions = CreateObject("SAP.Functions")
Set TableFactory = CreateObject("SAP.TableFactory.1")
Functions.Connection = objConnection 'run-time error here in Office 365 but not in on-prem
End Function
Function LogonToSAP()
Dim establishConnection As Boolean
silentLogon = false
Set LogonControl = CreateObject("SAP.LogonControl.1")
Set objConnection = LogonControl.NewConnection
objConnection.Client = "###"
objConnection.Language = "EN"
objConnection.SystemNumber = "##"
objConnection.User = ""
objConnection.Password = ""
objConnection.HostName = "###############"
objConnection.System = "###"
objConnection.ApplicationServer = "###.###.#.##"
establishConnection = objConnection.Logon(0, silentLogon)
End Function
A quick check of objConnection tells me that logon was successful...so I know that part is working on 365. For some reason though, it doesn't like assigning the Connection property of the Functions SAPFunctionsOCX.SAPFunctions object in the 365 environment (please feel free to correct my verbiage on that...I know it's not quite right).
Note that I'm not seeing any reference issues nor am I getting any compile errors in either environment. The first sign of trouble is on execution of Functions.Connection = objConnection
There's one more twist here and that is that I have another older VBA program that logs into SAP and runs remote function calls that doesn't use SAPFunctionsOCX.SAPFunctions, but rather declares variable R3 as Public R3 As Object and then sets R3 later in the logon code as Set R3 = CreateObject("SAP.Functions")...it does not use OCX. In other words, the old routine uses late binding. When the Functions object (R3 in this case) is set this way, I am able to run RFCs in both on prem and Office 365 environments.
Function LogonProdSAP(Optional SuppressLoginScreen As Boolean)
Application.ScreenUpdating = False
'**********************************************
'Create Server object and Setup the connection for DEV
'**********************************************
Set R3 = CreateObject("SAP.Functions")
If SuppressLoginScreen Then
R3.Connection.System = "###"
R3.Connection.HostName = "###################"
R3.Connection.SystemNumber = "##"
R3.Connection.Client = "###"
R3.Connection.User = "##########"
R3.Connection.Password = "#########"
R3.Connection.Language = "EN"
' Call Logger("LogonProdSAP> " & GetUserName)
End If
LogonProdSAP = R3.Connection.logon(0, SuppressLoginScreen)
If LogonProdSAP <> True Then MsgBox ("Logon error"): Exit Function
End Function
I could just go back to doing it this way, but I'd rather not have to reconfigure all of the code I just set up. In addition, I prefer binding early so Intellitype works to show all properties/methods available to that object. I'm sure there are other benefits as well.
What do I have to do to get the early-binding technique to work on Office 365?
It's due to the fact that your Office is in 64 bits version, and SAP GUI for Windows up to version 7.60 is in 32 bits (next SAP GUI version 7.70 should be in 64 bits, so it should work again).
You have a workaround to make VBA work with SAP GUI 32-bits DLL, by using DLL Surrogate, i.e. by editing the Windows Registry of all incompatible SAP GUI DLL. The original solution was proposed here at SAP Community.
To simplify the task, you may create my .REG file, execute it to update automatically the Windows Registry, and your VBA macro should then work.
I duplicate here my .REG file:
; ====================================================================================
; SAP Logon Unicode Control %ProgramFiles(x86)%\SAP\FrontEnd\SAPgui\wdtlogU.ocx {0AAF5A11-8C04-4385-A925-0B62F6632BEC}
; ====================================================================================
[HKEY_CLASSES_ROOT\WOW6432Node\AppID\{0AAF5A11-8C04-4385-A925-0B62F6632BEC}]
"DllSurrogate"=""
[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{0AAF5A11-8C04-4385-A925-0B62F6632BEC}]
"AppID"="{0AAF5A11-8C04-4385-A925-0B62F6632BEC}"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{0AAF5A11-8C04-4385-A925-0B62F6632BEC}]
; ====================================================================================
; SAP Remote Function Call Unicode Control %ProgramFiles(x86)%\SAP\FrontEnd\SAPgui\wdtfuncu.ocx {0AF427E7-03B9-4673-8F21-F33A683BCE28}
; ====================================================================================
[HKEY_CLASSES_ROOT\WOW6432Node\AppID\{0AF427E7-03B9-4673-8F21-F33A683BCE28}]
"DllSurrogate"=""
[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{0AF427E7-03B9-4673-8F21-F33A683BCE28}]
"AppID"="{0AF427E7-03B9-4673-8F21-F33A683BCE28}"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{0AF427E7-03B9-4673-8F21-F33A683BCE28}]
; ====================================================================================
; SAP Logon Control (not Unicode) %ProgramFiles(x86)%\SAP\FrontEnd\SAPgui\wdtlog.ocx {B24944D6-1501-11CF-8981-0000E8A49FA0}
; ====================================================================================
[HKEY_CLASSES_ROOT\WOW6432Node\AppID\{B24944D6-1501-11CF-8981-0000E8A49FA0}]
"DllSurrogate"=""
[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{B24944D6-1501-11CF-8981-0000E8A49FA0}]
"AppID"="{B24944D6-1501-11CF-8981-0000E8A49FA0}"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{B24944D6-1501-11CF-8981-0000E8A49FA0}]
; ====================================================================================
; SAP Remote Function Call Control (not Unicode) %ProgramFiles(x86)%\SAP\FrontEnd\SAPgui\wdtfuncs.ocx {5B076C03-2F26-11CF-9AE5-0800096E19F4}
; ====================================================================================
[HKEY_CLASSES_ROOT\WOW6432Node\AppID\{5B076C03-2F26-11CF-9AE5-0800096E19F4}]
"DllSurrogate"=""
[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{5B076C03-2F26-11CF-9AE5-0800096E19F4}]
"AppID"="{5B076C03-2F26-11CF-9AE5-0800096E19F4}"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{5B076C03-2F26-11CF-9AE5-0800096E19F4}]
I have a program that is supposed to read data from an SQL database and report back to Excel. It works as expected on a 32-bit machine, but since I moved over to a 64-bit work environment, the program has failed to run. Here is a sample of my code (the first error returned):
Private Sub SearchBox_Change()
ResultBox.Clear
Call CompileQuery
'If the query is empty
If SearchBox.Value = "" Then
NumShowingLabel = "Showing 0 of 0 Results"
ResultBox.Clear
GoTo noSearch
End If
'Open a new query with varQuery
With varRecordset
.ActiveConnection = varConnection
.Open varQuery
End With
'Set NumShowingLabel
If varRecordset.RecordCount > varMaxResults Then
NumShowingLabel = "Showing 60 of " & varRecordset.RecordCount & " Results"
Else
NumShowingLabel = "Showing " & varRecordset.RecordCount & " of " & varRecordset.RecordCount & " Results"
End If
'As long as there is a record, move to the first one
If Not varRecordset.RecordCount = 0 Then varRecordset.MoveFirst
'Add each record to ResultBox
If varRecordset.RecordCount > varMaxResults Then
For varTempInt = 1 To varMaxResults
ResultBox.AddItem varRecordset.Fields("FileName").Value
varRecordset.MoveNext
Next
Else
For varTempInt = 1 To varRecordset.RecordCount
ResultBox.AddItem varRecordset.Fields("FileName").Value
varRecordset.MoveNext
Next
End If
'Release varRecordSet
varRecordset.Close
noSearch:
End Sub
When run, Excel returns an error "Type Mismatch" and highlights .RecordCount of For varTempInt = 1 To varRecordset.RecordCount (the last for loop in the sample). I have installed the hotfix recommended by the Windows Support Article 983246, at least to the best of my understanding. I installed it to the C: directory and restarted my machine, but it still does not work.
Edit 1: Just wanted to clarify that I was previously using ADO 2.5 NOT ADO 6.1
TL;DR: How can I fix a RecordSet.RecordCount "Type Mismatch" error on a 64-bit machine running Excel 2010?
this issue is actually caused by a bug in earlier excels. there is a hotfix out there. HotFix
I develop some macros on office 16, but when I do UAT on previous versions, it fails, a quick easy solution for this is simply to cast the RecordCount
rst = SomeRecordset
dim rstCount as Long
rstCount = CLng(rst.RecordCount)
Thank you guys for your quick replies, however, I somehow managed to get the idea of using ADO 6.1 instead of ADO 2.5. It appears that using a more up-to-date version of ActiveX Database Objects did the trick, duh.
For future reference, if you are going to upgrade to ADO 6.0, the ConnectionString value will be the same EXCEPT you must use User ID=<USR>; Password=<PSWD> instead of USR=<USR>;PWD=<PWD>
I change it from
as Long
to
as LongLong
Then my VBA starts to work. No hotfix needed...
I haven't had this exact problem, but I've found that the recordcount property on an ADODB recordset is hit or miss. Your best bet is to rewrite the loops like:
recordset.movefirst
While Not recordset.eof
<your stuff with your record>
recordset.movenext
Loop
Also, to test that there are records in your recordset you can use:
If recordset.BOF and recordset.EOF THEN
<Something is wrong there are no records>
End If
My guess is that the ADODB recordcount property is probably crap with the 64 bit version of whatever ODBC driver you are using as it is in nearly every ODBC driver.
We had this error due to the same type of comparison and used the same sort of answer as from dfresh22 and Jonson Tsai. Thanks folks!
The only difference for us was that the error was occurring on 64-bit Office and we still have several users on 32-bit Office so it was easier to convert down to the smaller 32-bit variable instead of up to the larger 64-bit variable. Since the variable we were comparing against RecordCount will always be less than 100 (and definitely always < 32K) I was able to just Convert from Long to Integer in the 64-bit version and 32-bit code would just convert from Integer to Integer:
IF intNumRecs > CInt(DBreports.RecordCount) THEN...
Just check the version of Excel. ADO works well 32 bit and teething issues with 64 bit.
another in my beginnerish series of questions about VBA.
I am in the process of writing an Excel add-in in VBA, and the add-in uses a local configuration file.
This file needs to contain a password for a remote service.
Obviously, it is less than ideal to store this password as plaintext. But I am looking for an algorithm that can encode/decode text so it at least doesn't look like plaintext in the configuration file.
I came across a reference to Windows DPAPI but I'm not sure whether this is an appropriate solution for Excel VBA. I also am not sure how I can use this API from within VBA, as I've only found references to using it with .NET. Visual Studio is unavailable to this project.
So the 2-part question is this:
1) If it is possible to use DPAPI from within VBA, can I have an example of its use?
2) If it is not possible to use DPAPI in VBA, do you have any suggestions for how to store text in some encoded fashion that is reversible?
The solution must work in Excel 2003 and later, if it matters.
Thank you once again.
The solution must work in Excel 2003 and later, if it matters.
For Excel VBA, I suggest using the CAPICOM Library.
Download the file from here. Once it is installed, follow these instructions for registering the Dll.
32 bit OS
Copy the file Capicom.dll from the C:\Program Files\Microsoft CAPICOM 2.1.0.2 SDK\Lib to C:\Windows\System32
Next on Start Menu | Run , type this
Regsvr32 C:\Windows\System32\Capicom.dll
64 bit OS
Copy the file Capicom.dll from the C:\Program Files (x86)\Microsoft CAPICOM 2.1.0.2 SDK\Lib\X86 to C:\Windows\SysWOW64
Next on Start Menu | Run , type this
Regsvr32 C:\Windows\SysWOW64\Capicom.dll
Now we are set to use it in our VBA Project
Paste this code in a module
Option Explicit
Sub Sample()
Dim TextToEncrypt As String, EncryptedText As String
Dim TextToDeCrypt As String, DeCryptedText As String
Dim KeyToEncrypt As String
TextToEncrypt = "Hello World"
KeyToEncrypt = "JoshMagicWord"
EncryptedText = EncryptString(TextToEncrypt, KeyToEncrypt)
DeCryptedText = DecryptString(EncryptedText, KeyToEncrypt)
Debug.Print "The string " & TextToEncrypt & " after encryption looks like this"
Debug.Print "-----------------------------------------------------------------"
Debug.Print EncryptedText
Debug.Print "-----------------------------------------------------------------"
Debug.Print "The above string after decrypting looks like this"
Debug.Print "-----------------------------------------------------------------"
Debug.Print DeCryptedText
End Sub
Public Function EncryptString(strText As String, ky As String) As String
Dim Cap As Object
Dim cryptIt
Set Cap = CreateObject("CAPICOM.EncryptedData")
Cap.Algorithm = 3
Cap.SetSecret ky
Cap.Content = strText
EncryptString = Cap.Encrypt
End Function
Public Function DecryptString(strText As String, ky As String) As String
Dim Cap As Object
Dim cryptIt
Set Cap = CreateObject("CAPICOM.EncryptedData")
Cap.Algorithm = 3
Cap.SetSecret ky
Cap.Decrypt strText
DecryptString = Cap.Content
End Function
The function EncryptString encrypts the string and the function DecryptString decrypts the string. See snapshot of results when you run the above Sub Sample
Is there a way to have multiline strings in VB.NET like Python
a = """
multi
line
string
"""
or PHP?
$a = <<<END
multi
line
string
END;
Of course something that is not
"multi" & _
"line
You can use XML Literals to achieve a similar effect:
Imports System.XML
Imports System.XML.Linq
Imports System.Core
Dim s As String = <a>Hello
World</a>.Value
Remember that if you have special characters, you should use a CDATA block:
Dim s As String = <![CDATA[Hello
World & Space]]>.Value
2015 UPDATE:
Multi-line string literals were introduced in Visual Basic 14 (in Visual Studio 2015). The above example can be now written as:
Dim s As String = "Hello
World & Space"
MSDN article isn't updated yet (as of 2015-08-01), so check some answers below for details.
Details are added to the Roslyn New-Language-Features-in-VB-14 Github repository.
VB.Net has no such feature and it will not be coming in Visual Studio 2010. The feature that jirwin is refering is called implicit line continuation. It has to do with removing the _ from a multi-line statement or expression. This does remove the need to terminate a multiline string with _ but there is still no mult-line string literal in VB.
Example for multiline string
Visual Studio 2008
Dim x = "line1" & vbCrlf & _
"line2"
Visual Studio 2010
Dim x = "line1" & vbCrlf &
"line2"
I used this variant:
Dim query As String = <![CDATA[
SELECT
a.QuestionID
FROM
CR_Answers a
INNER JOIN
CR_Class c ON c.ClassID = a.ClassID
INNER JOIN
CR_Questions q ON q.QuestionID = a.QuestionID
WHERE
a.CourseID = 1
AND
c.ActionPlan = 1
AND q.Q_Year = '11/12'
AND q.Q_Term <= (SELECT CurrentTerm FROM CR_Current_Term)
]]>.Value()
it allows < > in the string
Multi-line strings are available since the Visual Studio 2015.
Dim sql As String = "
SELECT ID, Description
FROM inventory
ORDER BY DateAdded
"
You can combine them with string interpolation to maximize usefullness:
Dim primaryKey As String = "ID"
Dim inventoryTable As String = "inventory"
Dim sql As String = $"
SELECT {primaryKey}, Description
FROM {inventoryTable}
ORDER BY DateAdded
"
Note that interpolated strings begin with $ and you need to take care of ", { and } contained inside – convert them into "", {{ or }} respectively.
Here you can see actual syntax highlighting of interpolated parts of the above code example:
If you wonder if their recognition by the Visual Studio editor also works with refactoring (e.g. mass-renaming the variables), then you are right, code refactoring works with these. Not mentioning that they also support IntelliSense, reference counting or code analysis.
Multiline string literals are introduced in Visual Basic 14.0 - https://roslyn.codeplex.com/discussions/571884
You can use then in the VS2015 Preview, out now - http://www.visualstudio.com/en-us/downloads/visual-studio-2015-downloads-vs (note that you can still use VS2015 even when targeting an older version of the .NET framework)
Dim multiline = "multi
line
string"
VB strings are basically now the same as C# verbatim strings - they don't support backslash escape sequences like \n, and they do allow newlines within the string, and you escape the quote symbol with double-quotes ""
this was a really helpful article for me, but nobody mentioned how to concatenate in case you want to send some variables, which is what you need to do 99% of the time.
... <%= variable %> ...
Here's how you do it:
<SQL>
SELECT * FROM MyTable WHERE FirstName='<%= EnteredName %>'
</SQL>.Value
Well, since you seem to be up on your python, may I suggest that you copy your text into python, like:
s="""this is gonna
last quite a
few lines"""
then do a:
for i in s.split('\n'):
print 'mySB.AppendLine("%s")' % i
# mySB.AppendLine("this is gonna")
# mySB.AppendLine("last quite a")
# mySB.AppendLine("few lines")
or
print ' & _ \n'.join(map(lambda s: '"%s"' % s, s.split('\n')))
# "this is gonna" & _
# "last quite a" & _
# "few lines"
then at least you can copy that out and put it in your VB code. Bonus points if you bind a hotkey
(fastest to get with:Autohotkey) to do this for for whatever is in your paste buffer. The same idea works well for a SQL formatter.
Multi-line string literals in vb.net using the XElement class.
Imports System.Xml.Linq
Public Sub Test()
dim sOderBy as string = ""
dim xe as XElement = <SQL>
SELECT * FROM <%= sTableName %>
<ORDER_BY> ORDER BY <%= sOrderBy %></ORDER_BY>
</SQL>
'** conditionally remove a section
if sOrderBy.Length = 0 then xe.<ORDER BY>.Remove
'** convert XElement value to a string
dim sSQL as String = xe.Value
End Sub
To me that is the most annoying thing about VB as a language.
Seriously, i once wrote the string in a file and wrote code something along the lines of:
Dim s as String = file_get_contents("filename.txt")
just so i could test the query directly on SQL server if i need to.
My current method is to use a stored procedure on the SQL Server and just call that so i can pass in parameters to the query, etc
I figured out how to use both <![CDATA[ along with <%= for variables, which allows you to code without worry.
You basically have to terminate the CDATA tags before the VB variable and then re-add it after so the CDATA does not capture the VB code. You need to wrap the entire code block in a tag because you will you have multiple CDATA blocks.
Dim script As String = <code><![CDATA[
<script type="text/javascript">
var URL = ']]><%= domain %><![CDATA[/mypage.html';
</script>]]>
</code>.value
You could (should?) put the string in a resource-file (e.g. "My Project"/Resources) and then get it with
Dim a = My.Resources.Whatever_you_chose
Disclaimer: I love python. It's multi-line strings are only one reason.
But I also do VB.Net, so here's my short-cut for more readable long strings.
Dim lines As String() = {
"Line 1",
"Line 2",
"Line 3"
}
Dim s As String = Join(lines, vbCrLf)
you can use XML for this like
dim vrstr as string = <s>
some words
some words
some
words
</s>
in Visual studio 2010 (VB NET)i try the following and works fine
Dim HtmlSample As String = <anything>what ever you want to type here with multiline strings</anything>
dim Test1 as string =<a>onother multiline example</a>
Available in Visual Basic 14 as part of Visual Studio 2015
https://msdn.microsoft.com/en-us/magazine/dn890368.aspx
But not yet supported by R#. The good news is they will be supported soon! Please vote on Youtrack to notify JetBrains you need them also.
If you need an XML literal in VB.Net with an line code variable, this is how you would do it:
<Tag><%= New XCData(T.Property) %></Tag>
Since this is a readability issue, I have used the following code:
MySql = ""
MySql = MySql & "SELECT myTable.id"
MySql = MySql & " FROM myTable"
MySql = MySql & " WHERE myTable.id_equipment = " & lblId.Text
You can also use System.Text.StringBuilder class in this way:
Dim sValue As New System.Text.StringBuilder
sValue.AppendLine("1st Line")
sValue.AppendLine("2nd Line")
sValue.AppendLine("3rd Line")
Then you get the multiline string using:
sValue.ToString()
Use vbCrLf or vbNewLine. It works with MessageBoxes and many other controls I tested.
Dim str As String
str = "First line" & vbCrLf & "Second line"
MsgBox(str)
str = "First line" & vbNewLine & "Second line"
MsgBox(str)
It will show two identical MessageBoxes with 2 lines.
No, VB.NET does not yet have such a feature. It will be available in the next iteration of VB (visual basic 10) however (link)
if it's like C# (I don't have VB.Net installed) you can prefix a string with #
foo = #"Multiline
String"
this is also useful for things like #"C:\Windows\System32\" - it essentially turns off escaping and turns on multiline.