MSAccess: FTP File Hanging Open? - ms-access

I am seeing a new oddity in an FTP module I've used for a few years.
Task:
Export an Access Table as XLS
FTP the XLS to my FTP site
Launch a Stored Procedure to insert the XLS into a SQL table
Delete FTP file
Issue: Recently the SP is returning a "0" (fail) when the function is run within quick succession (i.e., within 1-2 seconds). My DBA reports the error as "7303" which seems to suggest the SP cannot grab the XLS file as it is locked OPEN (?)
If I put an arbitrary WAIT of like 5 seconds after the FTP complete before the SP is run, seems to be OK.
Question: Is there any way to force the file to be unlocked/closed sooner?
Here's the FTP function (not mine):
Sub myFTP(Filename As String)
Dim INet As Long
Dim INetConn As Long
Dim hostFile As String
Dim localFile As String
Dim Password As String
Dim RetVal As Long
Dim serverName As String
Dim Success As Long
Dim Username As String
Dim strPath As String
Dim strMsg As String
Dim intReply As Integer
Const BINARY_TRANSFER = 2
serverName = "<my ftp address>"
Username = "<my username>"
Password = "<my password>"
strPath = Left(CurrentDb.NAME, InStrRev(CurrentDb.NAME, "\"))
localFile = strPath & Filename
hostFile = Mid(Filename, 1, Len(Filename) - 4) + "upload"
RetVal = False
INet = InternetOpen("MyFTP Control", 1&, vbNullString, vbNullString, 0&)
If INet > 0 Then
INetConn = InternetConnect(INet, serverName, 0&, Username, Password, 1&, 0&, 0&)
If INetConn > 0 Then
Success = FtpPutFile(INetConn, localFile, hostFile, BINARY_TRANSFER, 0&)
RetVal = InternetCloseHandle(INetConn)
End If
RetVal = InternetCloseHandle(INet)
End If
If Success <> 0 Then
'msgbox "FTP Upload Completed"
Else
MsgBox "FTP function failed"
End If
err_Handler_Exit:
Exit Sub
err_Handler:
errMessage Err.Number, Err.Description
Status "[FTP] - There was an FTP error - process stopped.", "Orange"
Resume err_Handler_Exit
End Sub

Related

Error found while opening excel file downloaded from a blob field in Oracle Database using Access VBA

I have following code which downloads data stored in a blob field in a linked Oracle table to a file. The blob data stores excel file (.xlsx) but when I try to open the downloaded file in excel, I get an error saying the file is corrupted and not in proper format. Here's the code -
Option Explicit
Const BlockSize = 32768
Public Function DownloadBlob()
Dim db As Database
Dim rst As Recordset
Dim NumBlocks As Integer, DestFile As Integer, i As Integer
Dim FileLength As Long, LeftOver As Long
Dim FileData, FilePath As String
Dim RetVal As Variant
Set db = CurrentDb
Set rst = db.OpenRecordset("Select Blob_Field FROM Table1;")
NumBlocks = FileLength / BlockSize
LeftOver = FileLength Mod BlockSize
DestFile = FreeFile()
FilePath = "C:\Desktop\test.xlsx"
Open FilePath For Output As DestFile
Close DestFile
Open FilePath For Binary As DestFile
FileData = rst.Fields(0).GetChunk(0, LeftOver)
Put DestFile, , FileData
For i = 1 To NumBlocks
FileData = rst.Fields(0).GetChunk((i - 1) * BlockSize + LeftOver, BlockSize)
Put DestFile, , FileData
Next i
Close DestFile
End Function
As #TimWilliams commented nested within the long thread, consider using ADO Stream object to extract the data from the recordset field value. Below demonstrates with ADO, a different DB-API, than what you were using with Access' default DB-API, DAO. But you may be able to still use a DAO recordset in the ADO stream object.
Public Function DownloadBlob()
' ENABLE REFERENCE: Microsoft ActiveX Data Objects X.X Library
Dim conn As New ADODB.Connection, rs AS New ADODB.Recordset
Dim stm As New ADODB.Stream
Dim strFile As String
strFile = "C:\Desktop\test.xlsx"
conn.Open "<Connection String To Oracle>"
rs.Open "SELECT Blob_Field FROM Table1", conn
With stm
.Open
.Type = adTypeBinary
.Write rs.Fields("Blob_Field").Value
.SaveToFile strFile
.Close
End With
rs.Close: conn.Close
ExitHandle:
Set stm = Nothing: Set rs = Nothing: Set conn = Nothing
ErrHandle:
Msgbox Err.Number & " - " & Err.Description, vbCritical
Resume ExitHandle
End Function
The beauty too of this DAO to ADO switch is this VBA code can work in any Office application including Excel as it absolves any MS Access object (i.e, linked table, DAO). And for that matter, even beyond VBA to any COM-connected library (Java's jacob, Python's win32com, R's RDCOMClient, PHP's COM).

Get contents of laccdb file through VBA

I want to be able to view the contents of my access database's laccdb file through VBA so I can use it to alert users (through a button) who else is in the database.
I specifically don't want to use a 3rd Party tool. I have tried using:
Set ts = fso.OpenTextFile(strFile, ForReading)
strContents = ts.ReadAll
This works fine if only 1 user is in the database. But for multiple users it gets confused by the presumably non-ASCII characters and goes into this kind of thing after one entry:
Does anyone have any suggestions? It's fine if I just open the file in Notepad++...
Code eventually used is as follows (I didn't need the title and have removed some code not being used):
Sub ShowUserRosterMultipleUsers()
Dim cn As New ADODB.Connection, rs As New ADODB.Recordset
cn.Provider = "Microsoft.ACE.OLEDB.12.0"
cn.Open "Data Source=" & CurrentDb.Name
Set rs = cn.OpenSchema(adSchemaProviderSpecific, , "{947bb102-5d43-11d1-bdbf-00c04fb92675}")
While Not rs.EOF
Debug.Print rs.Fields(0)
rs.MoveNext
Wend
End Sub
I found this which should help, it's not actually reading the ldb file, but it has the info that you need (Source: https://support.microsoft.com/en-us/kb/198755):
Sub ShowUserRosterMultipleUsers()
Dim cn As New ADODB.Connection
Dim cn2 As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim i, j As Long
cn.Provider = "Microsoft.Jet.OLEDB.4.0"
cn.Open "Data Source=c:\Northwind.mdb"
cn2.Open "Provider=Microsoft.Jet.OLEDB.4.0;" _
& "Data Source=c:\Northwind.mdb"
' The user roster is exposed as a provider-specific schema rowset
' in the Jet 4 OLE DB provider. You have to use a GUID to
' reference the schema, as provider-specific schemas are not
' listed in ADO's type library for schema rowsets
Set rs = cn.OpenSchema(adSchemaProviderSpecific, _
, "{947bb102-5d43-11d1-bdbf-00c04fb92675}")
'Output the list of all users in the current database.
Debug.Print rs.Fields(0).Name, "", rs.Fields(1).Name, _
"", rs.Fields(2).Name, rs.Fields(3).Name
While Not rs.EOF
Debug.Print rs.Fields(0), rs.Fields(1), _
rs.Fields(2), rs.Fields(3)
rs.MoveNext
Wend
End Sub
I put together some code to read through the lock file and output a message listing users currently using the system.
Trying to read the whole file in at once seems to result in VBA treating the string as Unicode in the same way notepad does so I read in character by character and filter out non printing characters.
Sub TestOpenLaccdb()
Dim stm As TextStream, fso As FileSystemObject, strLine As String, strChar As String, strArr() As String, nArr As Long, nArrMax As Long, nArrMin As Long
Dim strFilename As String, strMessage As String
strFilename = CurrentProject.FullName
strFilename = Left(strFilename, InStrRev(strFilename, ".")) & "laccdb"
Set fso = New FileSystemObject
Set stm = fso.OpenTextFile(strFilename, ForReading, False, TristateFalse) 'open the file as a textstream using the filesystem object (add ref to Microsoft Scripting Runtime)
While Not stm.AtEndOfStream 'Read through the file one character at a time
strChar = stm.Read(1)
If Asc(strChar) > 13 And Asc(strChar) < 127 Then 'Filter out the nulls and other non printing characters
strLine = strLine & strChar
End If
Wend
strMessage = "Users Logged In: " & vbCrLf
'Debug.Print strLine
strArr = Split(strLine, "Admin", , vbTextCompare) 'Because everyone logs in as admin user split using the string "Admin"
nArrMax = UBound(strArr)
nArrMin = LBound(strArr)
For nArr = nArrMin To nArrMax 'Loop through all machine numbers in lock file
strArr(nArr) = Trim(strArr(nArr)) 'Strip leading and trailing spaces
If Len(strArr(nArr)) > 1 Then 'skip blank value at end
'Because I log when a user opens the database with username and machine name I can look it up in the event log
strMessage = strMessage & DLast("EventDescription", "tblEventLog", "[EventDescription] like ""*" & strArr(nArr) & "*""") & vbCrLf
End If
Next
MsgBox strMessage 'let the user know who is logged in
stm.Close
Set stm = Nothing
Set fso = Nothing
End Sub

Changing a linked table file path to OS username in VBA?

I have linked tables in an Access Database. I want to share this database and the associated excel workbooks with other users. I want to program a one-time use macro that the user will use the first time they use the database to relink the linked tables to the new user's local folder.
For example:
The linked table is current pulling the file from:
C:\Users\jane.doe\Desktop\Database Imports\Premier Account List.xlsx
When the new user (let's say their name is John Smith) relinks the table, it needs to read:
C:\Users\john.smith\Desktop\Database Imports\Premier Account List.xlsx
I basically want to change the file path from my OS Username to new user's OS Username. I already have the code to pull the OS Username, but I'm not sure how to code changing the file path. Here is the code to pull the OS UserName:
Private Declare Function apiGetUserName Lib "advapi32.dll" Alias _
"GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long
Function fOSUserName() As String
' Returns the network login name
Dim lngLen As Long, lngX As Long
Dim strUserName As String
strUserName = String$(254, 0)
lngLen = 255
lngX = apiGetUserName(strUserName, lngLen)
If (lngX > 0) Then
fOSUserName = Left$(strUserName, lngLen - 1)
Else
fOSUserName = vbNullString
End If
End Function
I am fairly new to VBA/Access, so if you could be as specific as possible with your answer, that would be great. Thanks in advanced!
The TableDef object has a Connect property that you need to change. It's a Read/Write String. You just need some string manipulation to make it how you want. Note that if they're moving the database file to the same path, you can just pull CurrentProject.Path rather than futzing with username APIs.
Sub ChangeTableLink()
Dim sNewPath As String
Dim lDbaseStart As Long
Dim td As TableDef
Dim sFile As String
Dim db As DAO.Database
'This is what we look for in the Connect string
Const sDBASE As String = "DATABASE="
'Set a variable to CurrentDb and to the table
Set db = CurrentDb
Set td = db.TableDefs("Fuel Pricing")
'Whatever your new path is, set it here
sNewPath = CurrentProject.Path & "\"
'Find where the database piece starts
lDbaseStart = InStr(1, td.Connect, sDBASE)
'As long as you found it
If lDbaseStart > 0 Then
'Separate out the file name
sFile = Dir(Mid(td.Connect, lDbaseStart + Len(sDBASE), Len(td.Connect)))
'Rewrite Connect and refresh it
td.Connect = Left(td.Connect, lDbaseStart - 1) & sDBASE & sNewPath & sFile
td.RefreshLink
End If
End Sub

how to backup my sql database in vb.net

i want the user of my vb application to be able to backup and restore the database (MySQL) onto a storage medium. my problem is that i dont want to specify 'c:\ in the code because i want the application to be able to locate the dumb file whether it is created on drive c or not. below is the code i used but when i installed it on another machine, it had its windows and program files on D:. it turns out that i have to check the drive letter of every machine, change it in the code before i publish the application to allow backup which i dont want to do that. i want it do be universal. thus whether the dump file is on driver C, G or whatever. any help. below is the code i used.
Dim cmd As String
Private Sub cmdBackup_Click()
Screen.MousePointer = vbHourglass
DoEvents
cmd = Chr(34) & "C:\Program Files\MySQL\MySQL Server 5.1\bin\mysqldump" & Chr(34) & " -uroot -psecretpswd --routines --comments db_name > c:\MyBackup.sql"
Call execCommand(cmd)
Screen.MousePointer = vbDefault
MsgBox "done"
End Sub
There is a complied DLL called MySqlBackup.NET. Actually it is an alternative to MySqlDump.
Features
Export/Import Table's Structures & Rows
Export/Import Stored Procedures, Functions, Triggers, Events, Views
Custom Tables and Rows Export.
Able to apply encryption to the process.
Export BLOB and save as files.
Gather SQL Syntax errors during Import process.
Export/Import will report progress. Enable the usage of progress bar.
Able to execute in Synchronous or Asynchronous mode.
Export/Import To/From Zip File.
For more info, see the link below,
MySqlBackup.NET - MySQL Backup Solution for C#, VB.NET, ASP.NET
Edited: Code Examples Added
Backup a MySql Database
Dim con As String = "server=localhost;user=root;pwd=1234;database=test;"
Dim file As String = "C:\backup.sql"
Dim mb As New MySqlBackup(con)
mb.ExportInfo.FileName = file
mb.Export()
Restore a MySql Database
Dim con As String = "server=localhost;user=root;pwd=1234;database=test;"
Dim file As String = "C:\backup.sql"
Dim mb As New MySqlBackup(con)
mb.ImportInfo.FileName = file
mb.Import()
Usually this commands are built using parameters external to the application, not hard coding path to MySqlDump, Database Name and path to destination folder.
Your code should be changed to something like this
Private Sub cmdBackup_Click()
Screen.MousePointer = vbHourglass
DoEvents
Dim mySqlDumpCmd = ConfigurationManager.AppSettings("PathToMySqlDump")
Dim dbName = ConfigurationManager.AppSettings("DatabaseToBackup")
Dim destPath = ConfigurationManager.AppSettings("DestinationPath")
cmd = Chr(34) & mySqlDumpCmd & Chr(34) & " -uroot -psecretpswd --routines --comments " +
dbName & " > " & destPath
Call execCommand(cmd)
Screen.MousePointer = vbDefault
MsgBox "done"
End Sub
and your application.config file contains these values
<?xml version="1.0"?>
<configuration>
<configSections>
.......
<appSettings>
<add key="PathToMySqlDump" value="C:\Program Files\MySQL\MySQL Server 5.1\bin\mysqldump.exe"/>
<add key="DatabaseToBackup" value="db_name"/>
<add key="DestinationPath" value="C:\MyBackup.sql"/>
</appSettings>
.......
In this way you read the key information from the config file of your application. If the need arises you can easily change the information used by the command without touching anything in your application
Use this code. It works for me.
I had such a problem and then found this article
"http://www.experts-exchange.com/Programming/Languages/.NET/Q_27155602.html"
Example was in C#. I manually converted it into vb.net and add converting into 'utf8'.
Imports System.Text
Public Class Form1
Dim OutputStream As System.IO.StreamWriter
Sub OnDataReceived1(ByVal Sender As Object, ByVal e As System.Diagnostics.DataReceivedEventArgs)
If e.Data IsNot Nothing Then
Dim text As String = e.Data
Dim bytes As Byte() = Encoding.Default.GetBytes(text)
text = Encoding.UTF8.GetString(bytes)
OutputStream.WriteLine(text)
End If
End Sub
Sub CreateBackup()
Dim mysqldumpPath As String = "d:\mysqldump.exe"
Dim host As String = "localhost"
Dim user As String = "root"
Dim pswd As String = "Yourpwd"
Dim dbnm As String = "BaseName"
Dim cmd As String = String.Format("-h{0} -u{1} -p{2} {3}", host, user, pswd, dbnm)
Dim filePath As String = "d:\backup\fieName.sql"
OutputStream = New System.IO.StreamWriter(filePath, False, System.Text.Encoding.UTF8)
Dim startInfo As System.Diagnostics.ProcessStartInfo = New System.Diagnostics.ProcessStartInfo()
startInfo.FileName = mysqldumpPath
startInfo.Arguments = cmd
startInfo.RedirectStandardError = True
startInfo.RedirectStandardInput = False
startInfo.RedirectStandardOutput = True
startInfo.UseShellExecute = False
startInfo.CreateNoWindow = True
startInfo.ErrorDialog = False
Dim proc As System.Diagnostics.Process = New System.Diagnostics.Process()
proc.StartInfo = startInfo
AddHandler proc.OutputDataReceived, AddressOf OnDataReceived1
proc.Start()
proc.BeginOutputReadLine()
proc.WaitForExit()
OutputStream.Flush()
OutputStream.Close()
proc.Close()
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
CreateBackup()
End Sub
End Class

How do I utilize Access 2007 Linked Table Manager in C#

Scenario: I have a Front End and a Back End Access 2007 Database that are currently linked to each other through the Linked Table Manager Database Tool. The Back End DB is going to be moved to a location on a server. The server name will be different for each facility and there are about 40 or so now which will increase throughout the year.
What I need to try to accomplish is changing the linked tables programatically. I will need to build the linked string to something like:
\\something\facilitynum(gathered from Environment variable)\c$\somefolder\.
I have found that the column Database in MSysObjects contains the link string that would need to be changed. The question becomes, how do get permissions to change a System table or use some .dll that will allow me to change the link to the newly built string?
Everything that I have found so far always leads back to manually changing the link within the Access Database.
You can programmatically change the link from within Access (using VBA) like so (this uses a dsn file to contain the actual server information)
Private Sub UpdateDSN()
On Error GoTo ErrorHandler
Dim dbPath As String
Dim connStr As String
Dim Tdf As TableDef
dbPath = Application.CodeDb.Name
dbPath = Left(dbPath, InStr(dbPath, Dir(dbPath)) - 1)
For Each Tdf In CurrentDb.TableDefs
connStr = Tdf.Connect
If InStr(1, UCase(connStr), "ODBC") Then
connStr = "odbc; FILEDSN=" & dbPath & "db.dsn;"
Tdf.Connect = connStr
Tdf.RefreshLink
End If
Next
Dim fName As String
Dim fNumber As Integer
Dim InputStr As String
fNumber = FreeFile()
fName = dbPath & "db.dsn"
Dim serverName As String
Open fName For Input As fNumber
Do While Not EOF(fNumber)
Line Input #fNumber, InputStr
If InStr(1, UCase(InputStr), "SERVER=") > 0 Then
serverName = Right(InputStr, Len(InputStr) - _
(InStr(1, InputStr, "SERVER=") + 6))
End If
Loop
ErrorHandler:
On Error GoTo 0
DoCmd.OpenForm "Main"
cap = Forms!main.Caption
If InStr(1, cap, "(") > 1 Then
cap = Left(cap, InStr(1, cap, "("))
End If
Forms!main.Caption = "db" & " (" & serverName & ")"
End Sub