Object with special character "/" produces Automation Error in GetObject - vba

We have an old Microsoft Access font end that serves as the GUI to our user database. I was never much of a VBA person so as I go through fixing bugs I'm learning as I go.
Our Access DB has a number of commands to sync info to Active Directory. One such command is to add a user to a group. However, whenever the group contains a / the group is never added.
The debug produces this as:
Run-time error -2147463168 (80005000)': Automation Error".
Printing out the targetgroup shows the DN as I expect it. Trying to escape the / before the GetObject doesn't help and causes its own auth error.
Here's the top part of the function -
Function AddGroup(TargetGroup, strUserID, Optional strOptReqBy)
Dim objDL
Set objUser = GetObject("LDAP://" & GetDName(CStr(strUserID)))
Set objDL = GetObject("LDAP://" & TargetGroup)
On Error Resume Next
objDL.Add (objUser.ADsPath)
objDL.SetInfo
On Error GoTo 0
This works fine if the group does not contain a /.
Debug points to Set objDL = GetObject("LDAP://" & TargetGroup)
Looking for some input on why this is happening. Thanks!

In an LDAP path, the / is a separator. Not only is the // used near the beginning, but you can also specify the server you want to connect to, followed by a /, then the DN of the object, like this:
LDAP://example.com/DC=example,DC=com
That's necessary if the computer you're running this from isn't not joined to the same (or trusted) domain than the domain you're connecting to.
So that means that if the DN of the object you want to bind to has a /, it will think that everything before the / is a server to connect to and it explodes.
So you just need to escape it, which, as you've already learned, is done with a \:
LDAP://OU=This\/That,DC=example,DC=com
So yeah, a simple replace will do:
Set objUser = GetObject("LDAP://" & Replace(GetDName(CStr(strUserID)), "/", "\/")
Don't feel bad. Even Microsoft has this bug in their code.

Related

MS Access: "Cannot open any more databases."

The Access error, "Cannot open any more databases.", has been discussed several times on StackOverflow[1, 2, 3, 4]. There's also an interesting discussion of the error on Bytes.com, in which Allen Brown and David Fenton weigh in on what can cause the error and argue about whether the limit behind it is about connections or table handles. None of the causes or solutions in those places apply to my situation, as far as I can tell.
I've got a complex database that was working well, but then started throwing this error. I've boiled down the VBA code and data structure to the following bare bones:
Option Explicit
Public Function TestFunction(ID_test_1 As Integer, nID_test_2 As Integer) As Variant
Dim oCurDb_Ftn As DAO.Database, oTestData As DAO.Recordset, sSQL As String, vTestCode As Variant
Set oCurDb_Ftn = CurrentDb()
sSQL = "SELECT * FROM t_test_2 WHERE ID_test_2 = " & nID_test_2 & ";"
Set oTestData = oCurDb_Ftn.OpenRecordset(sSQL)
vTestCode = oTestData![bValue]
oTestData.Close
Set oTestData = Nothing
Set oCurDb_Ftn = Nothing
TestFunction = vTestCode
End Function ' TestFunction
The numbers in the tables are arbitrary. The fields ID_test_n are primary keys. The code above is the entire contents of TestModule. The query TestQuery is:
SELECT t_test_1.ID_test_1, TestFunction(ID_test_1,3) AS [Test code]
FROM t_test_1;
When the query is opened, it at first appears to be okay. But if I scroll down through it, it throws the "cannot open any more" error:
I've discovered one way that I can get rid of the error. If I remove the first parameter of the function TestFunction(), so that it's definition becomes:
Public Function TestFunction(nID_test_2 As Integer) As Variant
...
and the call to it in the query becomes just:
TestFunction(3)
then I can scroll down and up through the query sheet multiple times without error. Those changes are possible because in the bare-bones code of the function, there is no reference to ID_test_1. But in the actual database, that parameter is passed for a reason and omitting it is not an option. Still, it is mysterious to me that whatever is causing the error does not happen if that parameter is not passed.
Can anyone see what's going on here, why I'm getting that error, and how to fix it without excluding parameters from the function?
Environment: Windows 10 Pro 64-bit, Access 2019.
This may be due to a bug in an Office update, about Jan 26, 2022. See Reddit post
Try system restore to roll back the update; or...
Go into Access, File Options, Trust center and add the local front end directory and then check the box to then add the backend data location as a trusted location.

Dot inside VBA string of an Access application gets interpreted as a number sign upon execution

I'm working on a small Access database application that, among other things, exports a csv file when requested by the user.
The csv file should be created by the following code:
DoCmd.TransferText acExportDelim, "CSVexport", "tblExport", sLoc & Format(Date, "ddMMyyyy") & "_" & category & "_Export.csv", True
After fixing an issue with the data source part of the export (changed it from a query to a table), the _Export.csv part suddenly gets interpreted by Access as _Export#csv.
The error I'm getting:
Run-Time error '3011': The Microsoft Access database engine could not find the object '03022020_AluKippers_Export#csv'. Make sure the object exists and that you spell its name and path correctly . If '03022020_AluKippers_Export#csv' is not a local object, check your network connection or contact the server administrator.
I have no idea what's causing this.
Update: When changing the file extension to .txt, I get the same error. When using anything else after the dot, I get the cannot update error. I suspect the error may have something to do with the saved specification CSVexport...
Found the problem. The export specification I created, used the dot as the decimal symbol. After changing the decimal symbol back to , and the field delimiter from , to ;, the code in my question worked as expected.

Visio: DOS Sharing violation (Error 1532)

So I'm really confused right now. Out of the blue my code gets me the error "DOS Sharing violation".
It's weird because, it says that is trying to save my document, but I just want to open it.
This is my Code:
Public Sub ReadActivity()
Dim vsoDocument As Visio.Document
Dim vsoPage As Visio.Page
Set vsoDocument = Documents.Open("C:\Users\Philip\Dropbox\Test\Aktivität0.vsdx")
Set vsoPage = vsoDocument.Pages(1)
SvgExport (ActiveDocument.path & "\files_and_images\" & Left(ActiveDocument.name, (InStrRev(ActiveDocument.name, ".", -1, vbTextCompare) - 1)) & ".svg")
CreateCodeActivity
vsoDocument.Close
End Sub
So as you might see the code is simple nothing special is going on.
Before calling the method I'm using this for encoding my textfile: VBA : save a file with UTF-8 without BOM
And two things are very weird. First of all, I used this method two days in a row for coding the method "CreateCodeActivity" and I didn't have any problems. And second, if I call the method let's say three times, on the third time everything works perfectly...
Where might be the problem?
Thank you #Shmukko for the tip, it is really the windows defender that gives me the error.
For Windows 10 the solution is: Go to Settings and select Update & security -> Windows Defender. Select Exclude a file extension and enter the file type for Visio.
That's it.

Access not exiting

Recently, my Access .mdb database started intermittently not allowing Access (both Access 2003 and 2007) to quit. If I quit (whether by pressing the X button or from the menu, then it closes the database and appears to exit Access, as well, but then it suddenly reappears (without any database open). The only way for me to exit at that point is from the task manager.
There are two significant changes that I did recently that might be related. 1) I started using the WinSCP .Net assembly to access an ftp server, which I had to install and register for COM from the instructions here. 2) I started using ODBC, first as a linked table, and then from VBA ADO code (see this). I doubt that this second change is causing this problem because I've had the problem both when I was using the linked tables and when with ADO.
This doesn't happen every time I open the database, and I haven't noticed a pattern. What could be causing this strange problem?
Edit - I found the root of the problem. By breaking my ftp download code at various points and seeing whether it will exit, I narrowed it down to the following:
Dim PDFFolders As Recordset
Set PDFFolders = CurrentDb.OpenRecordset("PDFFolders")
Dim syncOptions As TransferOptions
Set syncOptions = New TransferOptions
syncOptions.filemask = "*/*.pdf"
On Error Resume Next 'In case it doesn't exist
Do While Not PDFFolders.EOF
sess.SynchronizeDirectories SynchronizationMode_Local, info!RTFFolder, _
info!BasePDFFolder & "/" & PDFFolders!Name, False, , , _
syncOptions
PDFFolders.MoveNext
Loop
PDFFolders.Close
Set syncOptions = Nothing
Set PDFFolders = Nothing
On Error GoTo 0
If it runs the sess.SynchronizeDirectories statement, then access won't exit, otherwise it does. Looks to me like a bug win WinSCP.
I can do other things like downloading files, creating directories, etc. with no problem, but when it gets to this statement, it doesn't exit access afterwards.
Sticky instances of Access usually result from hanging object references.
If Access hangs the way you described, I would suspect a nasty circular reference.
To investigate on circular references, you basically have two options:
Inspect your code on circular dependencies - That might become tedious and not so easy. But if you know your code base deeply, you might have suspects where to look first.
Add logging to your code - In case you cannot spot the problem via inspection alone, you can consider adding consequent logging of object creation/deletion (through Class_Initialize/Class_Terminate). For larger code bases using classes heavily, this is a good investment to start with.
If your problem is with classes where you cannot control the code (as is your case), you might consider using that external classes only through wrapper classes where you can log creation/deletion. Of course in tricky cases, termination of the wrapper class does not mean termination of the wrapped class.
BTW, I strongly recommend to make sure to set every object reference explicitly to Nothing ASAP:
Set MyObj = GetMyObject()
' Proceed with coding here later
' First write the following line
Set MyObj = Nothing
Special thoughts have to be given in the case of local error handling to make sure to set the reference to Nothing in either case.
A good way to ensure this is using a With-block instead of an explicit variable (if the usage pattern allows to):
With GetMyObject()
' Use the object's members here
End With
With this pattern you save declaring the local variable and can be sure that the object reference does not survive the current method.
I still think it's a bug in WinSCP, but I found a workaround. I noticed that it only happened if I took the information from a table in the database, and not if I put in a hard-coded string. So I just added & vbNullString, which concatenates a blank string, which changes the data type from a Field to a String, and now it doesn't happen anymore.

protecting software to run only on one computer in vb.net

I have developed a small application and now i want to protect it.
I want to run it only on my own computer and i have developed it for myself.
How can i do that?
A. Don't publish it.
B. Hard-code your computer name in the code, and make the first thing the program does to be verifying that System.Environment.MachineName matches it.
You could always check the processor ID or motherboard serial number.
Private Function SystemSerialNumber() As String
' Get the Windows Management Instrumentation object.
Dim wmi As Object = GetObject("WinMgmts:")
' Get the "base boards" (mother boards).
Dim serial_numbers As String = ""
Dim mother_boards As Object = _
wmi.InstancesOf("Win32_BaseBoard")
For Each board As Object In mother_boards
serial_numbers &= ", " & board.SerialNumber
Next board
If serial_numbers.Length > 0 Then serial_numbers = _
serial_numbers.Substring(2)
Return serial_numbers
End Function
Private Function CpuId() As String
Dim computer As String = "."
Dim wmi As Object = GetObject("winmgmts:" & _
"{impersonationLevel=impersonate}!\\" & _
computer & "\root\cimv2")
Dim processors As Object = wmi.ExecQuery("Select * from " & _
"Win32_Processor")
Dim cpu_ids As String = ""
For Each cpu As Object In processors
cpu_ids = cpu_ids & ", " & cpu.ProcessorId
Next cpu
If cpu_ids.Length > 0 Then cpu_ids = _
cpu_ids.Substring(2)
Return cpu_ids
End Function
Was taken from where: http://www.vb-helper.com/howto_net_get_cpu_serial_number_id.html
Here's a question by Jim to convert this for Option Strict.
It really depends on who is the "enemy".
If you wish to protect it from your greedy, non-cracker, friends, then you can simply have the application run only if a certain password is found in the registry (using a cryptographically secure hash function), or use the MachineName as Jay suggested.
But if you're thinking of protecting it from serious "enemies", do notice: It has been mathematically proven that as long as the hardware is insecure, any software running on it is inherently insecure. That means that every piece of software is crackable, any protection mechanism is bypassable (even secured-hardware devices such as Alladin's Finjan USB product key, since the rest of the hardware is insecure).
Since most (if not all) of today's hardware is insecure, you simply cannot get 100% security in a software.
In between, there are lots of security solutions for licensing and copy-protection. It all comes down to who is the enemy and what is the threat.
No matter how hard you try, if someone really want to run it on another computer, they will.
All need to do is reverse engineer your protection to
remove it
play with it
Another option might be to have your program ask the USER a question that has a derived answer. Here's a brain dead example....
Your Program: "What time is it now?"
You Enter: (TheYear + 10 - theDay + 11) Mod 13
In this way its actually ONLY YOU that can run the program instead of it being MACHINE dependent.
I have made things like this in VB DOS.
I either made a non-deletable file that is key to a specific machine with a code inside, and/or read the .pwl files and have several checks, that are only on your machine. The non-editable file is made with extended character sets like char 233 so when a person tries to look at it, it will open a blank copy (edit) (write.ex), so data cannot be read and it cannot be edited moved or deleted.
It needs to be certain characters; I am not sure if every charter between 128 and 255 will work it, some extended characters work to do this some will not, also it can be defeated, but it will keep some people out,
But it can be read or checked in a program environment. Nothing is totally secure, this is some of the things I mess with.
Note: the file will be very hard to delete, maybe make a test directory to test this.
I hope this is OK I am not very good at conveying info to people; I have programmed since 1982.
Another idea ... I wrote a program that cannot be run directly, it is only ran by an external file, so you could add in a password entry section to it and encrypt password so it cannot be read very easily ,I made an executable version of a vb program to test. it writes in to slack space a character so if the program sees that value it will not run, BUT the runner program has a different character, and it changes it to that character ,and the program is designed to only let in if the character is the proper one ,made only by the runner , then when it enters it changes it back so it is not left open , I have made this sorta thing, and it does work, there is always a way to defeat any protection , the goal is to slow them down or discourage them from running or using your program if you do not want them to.I may include examples at a later date.