MS Access application return value - ms-access

I need to make automatic application called from windows command line. This app start, make some DB operations and automatically exit.
Next I need to handle return values from Access. I tried everything what I find on Google but Access returns 0 every time. I need to return 1 or something else to tell cmd that there is error in database.
here is some code in VBA (access call Form when application starts):
Private Sub Form_Load()
Dim xSQL As String
Dim xRS As DAO.Recordset
xSQL = "select * from table1"
Set xRS = CurrentDb.OpenRecordset(xSQL, dbOpenDynaset)
xRS.MoveLast: xRS.MoveFirst
DoEvents
If xRS!field1 = "" Or IsNull(xRS!field1) Then
' HERE I WANT TO RETURN 1 OR ANY TRUE VALUE = ERROR
Application.Quit acQuitSaveAll
End If
' HERE I RETURN 0 = SUCCESS
Application.Quit acQuitSaveAll
End Sub
Some ideas how to do it?

I think your best solution can be setting an environment variable with the desired return value.
From your VBA code:
Private Declare Function SetEnvironmentVariable Lib "kernel32" _
Alias "SetEnvironmentVariableA" _
(ByVal lpName As String, _
ByVal lpValue As String) As Long
Private Sub Form_Load()
Dim xSQL As String
Dim xRS As DAO.Recordset
xSQL = "select * from table1"
Set xRS = CurrentDb.OpenRecordset(xSQL, dbOpenDynaset)
xRS.MoveLast: xRS.MoveFirst
DoEvents
If xRS!field1 = "" Or IsNull(xRS!field1) Then
SetEnvironmentVariable "ACCESSEXITCODE", "1"
Application.Quit acQuitSaveAll
End If
SetEnvironmentVariable "ACCESSEXITCODE", "0"
Application.Quit acQuitSaveAll
End Sub
From de command line:
IF %ACCESSEXITCODE%==1 GOTO HandleError

Related

How can I bring my current Access DB to foreground via VBA?

I'm trying to get my access db to come to the foreground based on the couple of lines, below. I'm not sure why it isn't working though, or if there are better methods to do this.
From what I've read, this should work:
Access.Visible = False
Access.Visible = True
But doesn't actually bring the database to the front.
Edit for more info:
Private Sub Form_Open(Cancel As Integer)
getStrUserName = Environ("username")
dbName = "myDB.accdb" ' database name
versionChckDB = "versionCheckDB.accdb" ' version check db name
strServer = "C:\My\Path\to\Server" ' server location string
strDesktop = "C:\My\Path\to\Local" ' desktop location string
strVersionCheck = "C:\My\Path\to\Version" ' version check location
Static acc As Access.Application
Dim db As DAO.Database
Dim strDbName As String
If FileLocked(strDesktop & "\" & versionChckDB) Then
Dim objAccess As Access.Application
Set objAccess = GetObject(strDesktop & "\" & versionChckDB)
objAccess.Application.Quit acQuitSaveAll
Set objAccess = Nothing
DoCmd.OpenForm "frmMainMenu"
DoCmd.RunCommand acCmdAppMaximize
Access.Visible = False
Access.Visible = True
GoTo exitSub
Else
strDbName = strDesktop & "\" & versionChckDB
Set acc = New Access.Application
acc.Visible = True
Set db = acc.DBEngine.OpenDatabase(strDbName, False, False)
acc.OpenCurrentDatabase strDbName
End If
'db.Close
exitSub:
Call SetForegroundWindow(Application.hWndAccessApp) ' bringing access DB to foreground
End Sub
Usually one uses an API function for that.
From http://www.access-programmers.co.uk/forums/showthread.php?t=132129 :
Public Declare Function SetForegroundWindow Lib "user32" (ByVal hWnd As Long) As Long
and then
Call SetForegroundWindow(Application.hWndAccessApp)
Edit
If you want to get the newly opened Access application window to the front, you need its hWnd:
Call SetForegroundWindow(acc.hWndAccessApp)
Edit 2
This works for me. Notepad is briefly in the foreground, then the Access window.
Module:
Public Declare Function SetForegroundWindow Lib "user32" (ByVal hWnd As Long) As Long
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Public Sub TestAccessToForeground()
Sleep 300
Shell "notepad.exe", vbNormalFocus
Sleep 300
Call SetForegroundWindow(Application.hWndAccessApp)
End Sub
Form:
Private Sub Form_Open(Cancel As Integer)
Call TestAccessToForeground
End Sub

Having issues migrating data with attachments in Access

All,
I have an MS Access database that has some file attachments that I need to programmatically copy to another MS Access table (both tables are linked tables to a SharePoint 2007 list). I have the following code.
Private Sub AddAttachments(rsSource As Recordset, rsDest As Recordset)
Dim rs2Source As Recordset2
Dim rs2Dest As Recordset2
Set rs2Source = rsSource.Fields!Attachments.Value
Set rs2Dest = rsDest.Fields("Attachments").Value
rs2Source.MoveFirst
If Not (rs2Source.BOF And rs2Source.EOF) Then
While Not rs2Source.EOF
rs2Dest.AddNew
rs2Dest!FileData = rs2Source!FileData
rs2Dest.Update
rs2Source.MoveNext
Wend
End If
Set rs2Source = Nothing
Set rs2Dest = Nothing
End Sub
My issue is that when it gets to rs2Dest!FileData = rs2Source!FileData, it keeps giving me an Invalid Argument error. So, if what I am trying to do is possible, how can I adjust my code to read the attachment data from one list and import it into the other list (both linked as linked-tables in an instance of MS Access).
Thanks in advance.
All,
Here is the clunky solution I came up with in case it helps someone else.
First, I needed to access the URLmon library's URLDownloadToFileA function.
Private Declare Function URLDownloadToFileA Lib "urlmon" (ByVal pCaller As Long, ByVal szURL As String, ByVal szfilename As String, ByVal dwreserved As Long, ByVal ipfnCB As Long) As Long
Then, I would use this library to download the file to my disk, upload from my disk, and delete the temporarily stored file as follows:
Private Function DownloadFile(URL As String, LocalFilename As String) As Boolean
DownloadFile = (URLDownloadToFileA(0, URL, LocalFilename, 0, 0) = 0)
End Function
Private Function GetRight(strText As String, FindText As String) As String
Dim i As Long
For i = Len(strText) - Len(FindText) + 1 To 1 Step -1
If Mid(strText, i, Len(FindText)) = FindText Then
GetRight = Mid(strText, i + 1, Len(strText))
Exit For
End If
Next i
End Function
Private Sub AddAttachments(rsSource As Recordset, rsDest As Recordset)
Dim rs2Source As Recordset2
Dim rs2Dest As Recordset2
Set rs2Source = rsSource.Fields!Attachments.Value
Set rs2Dest = rsDest.Fields("Attachments").Value
Dim strDownload As String
Dim strTemp As String
strTemp = Environ$("TEMP")
If Not (rs2Source.BOF And rs2Source.EOF) Then
rs2Source.MoveFirst
If Not (rs2Source.BOF And rs2Source.EOF) Then
While Not rs2Source.EOF
rs2Dest.AddNew
'rs2Dest.Update
'rs2Dest.MoveLast
'rs2Dest.Edit
strDownload = strTemp & "\" & GetRight(rs2Source!FileURL, "/")
Debug.Print DownloadFile(rs2Source!FileURL, strDownload)
rs2Dest.Fields("FileData").LoadFromFile strDownload
rs2Dest.Update
rs2Source.MoveNext
Kill strDownload 'delete the temporarily stored file
Wend
End If
End If
Set rs2Source = Nothing
Set rs2Dest = Nothing
End Sub
I'm sure there's an easier way, but this seem to work for my purposes (albeit in a clunky fashion that is only fitting for the likes of VBA).

Passing Functions through Sub Procedure

I am trying to call a function when running a sub proecudere, however, I keep getting an error message saying "Argument not optional", can someone help?
Code as follows:
Public Sub ret()
Dim FSO As New Scripting.FileSystemObject
Const cstrFolderF = "\\tblSCFLAGCHECKER.txt"
If FSO.FileExists(cstrFolderF) Then
DoCmd.RunSQL "DELETE * FROM [tblSCFLAG_CHECKER]"
DoCmd.TransferText acImportDelim, "tblSCFLAG_CHECKER", "tblSCFLAG_CHECKER", cstrFolderF, True
changefieldnames
Else
'SCAnswer = MsgBox("SC Flags does not exist, do you wish to continue?", vbYesNo Or vbQuestion Or vbDefaultButton2)
'If SCAnswer = vbNo Then Exit Sub
End If
End Sub
Private Sub changefieldnames()
Dim db As Database
Dim tdf As TableDef
Dim n As Object
Set db = CurrentDb
Set tdf = db.TableDefs("tblSCFLAG_CHECKER")
For Each n In tdf.Fields
If n.Name = "?Person ID" Then n.Name = "Person ID"
Next n
Set tdf = Nothing
Set db = Nothing
End Sub
Your changefieldnames function requires two arguments but you give none in the call after
DoCmd.TransferText acImportDelim, "tblSCFLAG_CHECKER", "tblSCFLAG_CHECKER", cstrFolderF, True
changefieldnames
As a remark: you should try to debug your code instead of just posting an error without even stating where exactly the error occurs.

WScript Command - Run Minimized? (MSAccess/VBA)

I am performing a quick PING against the user-selected server IP to confirm it is reachable.
The following code does exactly what I need, except I would like to avoid the quick flash of the Command Shell window.
What do I need to modify to minimize that pesky CMD window?
SystemReachable (myIP)
If InStr(myStatus, "Reply") > 0 Then
' IP is Confirmed Reachable
Else
' IP is Not Reachable
End If
''''''''''''''''''''''
Function SystemReachable(ByVal strIP As String)
Dim oShell, oExec As Variant
Dim strText, strCmd As String
strText = ""
strCmd = "ping -n 1 -w 1000 " & strIP
Set oShell = CreateObject("WScript.Shell")
Set oExec = oShell.Exec(strCmd)
Do While Not oExec.StdOut.AtEndOfStream
strText = oExec.StdOut.ReadLine()
If InStr(strText, "Reply") > 0 Then
myStatus = strText
Exit Do
Else
myStatus = ""
End If
Loop
End Function
This question may be a little old but I figure that this answer may still be able to help.
(Tested with Excel VBA, have not been able to test with Access)
The WshShell.Exec Method enables the use of .StdIn, .StdOut, and .StdErr functions to write to and read from the consol window.
The WshShell.Run Method does not allow this functionality so for some purposes using Exec is required.
While it's true that there is no built in function to start the Exec method minimized or hidden you can use API's to quickly find the Exec window hwnd and minize/hide it.
My below script takes the ProcessID from the Exec object to find the window's Hwnd. With the Hwnd you can then set the window's show state.
From my testing with Excel 2007 VBA, in most cases I never even see the window... In some cases it might be visible for a few milliseconds but would only appear a quick flicker or blink... Note: I had better results using SW_MINIMIZE than I did with SW_HIDE, but you can play around with it.
I added the TestRoutine Sub to show an example of how to use the 'HideWindow' function.
The 'HideWindow' function uses the 'GetHwndFromProcess' function to get the window hwnd from the ProcessID.
Place the below into a Module...
Option Explicit
' ShowWindow() Commands
Public Const SW_HIDE = 0
Public Const SW_MINIMIZE = 6
'GetWindow Constants
Public Const GW_CHILD = 5
Public Const GW_HWNDFIRST = 0
Public Const GW_HWNDLAST = 1
Public Const GW_HWNDNEXT = 2
Public Const GW_HWNDPREV = 3
Public Const GW_OWNER = 4
' API Functions
Public Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
Public Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Public Declare Function GetDesktopWindow Lib "user32" () As Long
Public Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Sub TestRoutine()
Dim objShell As Object
Dim oExec As Object
Dim strResults As String
Set objShell = CreateObject("WScript.Shell")
Set oExec = objShell.Exec("CMD /K")
Call HideWindow(oExec.ProcessID)
With oExec
.StdIn.WriteLine "Ping 127.0.0.1"
.StdIn.WriteLine "ipconfig /all"
.StdIn.WriteLine "exit"
Do Until .StdOut.AtEndOfStream
strResults = strResults & vbCrLf & .StdOut.ReadLine
DoEvents
Loop
End With
Set oExec = Nothing
Debug.Print strResults
End Sub
Function HideWindow(iProcessID)
Dim lngWinHwnd As Long
Do
lngWinHwnd = GetHwndFromProcess(CLng(iProcessID))
DoEvents
Loop While lngWinHwnd = 0
HideWindow = ShowWindow(lngWinHwnd, SW_MINIMIZE)
End Function
Function GetHwndFromProcess(p_lngProcessId As Long) As Long
Dim lngDesktop As Long
Dim lngChild As Long
Dim lngChildProcessID As Long
On Error Resume Next
lngDesktop = GetDesktopWindow()
lngChild = GetWindow(lngDesktop, GW_CHILD)
Do While lngChild <> 0
Call GetWindowThreadProcessId(lngChild, lngChildProcessID)
If lngChildProcessID = p_lngProcessId Then
GetHwndFromProcess = lngChild
Exit Do
End If
lngChild = GetWindow(lngChild, GW_HWNDNEXT)
Loop
On Error GoTo 0
End Function
ShowWindow function:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633548%28v=vs.85%29.aspx
GetWindow function:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633515%28v=vs.85%29.aspx
GetDesktopWindow function:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633504%28v=vs.85%29.aspx
GetWindowThreadProcessId function:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633522%28v=vs.85%29.aspx
If you need more information on how the API's work, a quick google search will provide you with a ton of information.
I hope that this can help... Thank You.
Found a very workable and silent approach:
Dim strCommand as string
Dim strPing As String
strCommand = "%ComSpec% /C %SystemRoot%\system32\ping.exe -n 1 -w 500 " & myIP & " | " & "%SystemRoot%\system32\find.exe /i " & Chr(34) & "TTL=" & Chr(34)
strPing = fShellRun(strCommand)
If strPing = "" Then
MsgBox "Not Connected"
Else
MsgBox "Connected!"
End If
'''''''''''''''''''''''''''
Function fShellRun(sCommandStringToExecute)
' This function will accept a string as a DOS command to execute.
' It will then execute the command in a shell, and capture the output into a file.
' That file is then read in and its contents are returned as the value the function returns.
' "myIP" is a user-selected global variable
Dim oShellObject, oFileSystemObject, sShellRndTmpFile
Dim oShellOutputFileToRead, iErr
Set oShellObject = CreateObject("Wscript.Shell")
Set oFileSystemObject = CreateObject("Scripting.FileSystemObject")
sShellRndTmpFile = oShellObject.ExpandEnvironmentStrings("%temp%") & oFileSystemObject.GetTempName
On Error Resume Next
oShellObject.Run sCommandStringToExecute & " > " & sShellRndTmpFile, 0, True
iErr = Err.Number
On Error GoTo 0
If iErr <> 0 Then
fShellRun = ""
Exit Function
End If
On Error GoTo err_skip
fShellRun = oFileSystemObject.OpenTextFile(sShellRndTmpFile, 1).ReadAll
oFileSystemObject.DeleteFile sShellRndTmpFile, True
Exit Function
err_skip:
fShellRun = ""
oFileSystemObject.DeleteFile sShellRndTmpFile, True
End Function
the run method of wscript already contains argumewnts to run minimized. So without all that effort shown above simply use
old code
oShellObject.Run sCommandStringToExecute & " > " & sShellRndTmpFile, 0, True
new code
oShellObject.Run sCommandStringToExecute & " > " & sShellRndTmpFile, 7, True
see Microsoft help for using the run method in wscript.
regards
Ytracks

VBScript to interrogate an Access database

I want to extract all the fields associated to my tables in my access database, to get an inventory of all the data objects. This has to populate a form I've created. I've copied an extract of code to determine whether an object in the database is a query or a table and I would like to alter this, if possible.
Any help will be appreciated
Option Compare Database
Option Explicit
Private Sub AddInventory(strContainer As String)
Dim con As DAO.Container
Dim db As DAO.Database
Dim doc As DAO.Document
Dim rst As DAO.Recordset
Dim intI As Integer
Dim strType As String
Dim varRetval As Variant
On Error GoTo HandleErr
' You could easily modify this, using the
' OpenDatabase() function, to work on any database,
' not just the current one.
varRetval = SysCmd(acSysCmdSetStatus, _
"Retrieving " & strContainer & " container information...")
Set db = CurrentDb
Set con = db.Containers(strContainer)
Set rst = db.OpenRecordset("zstblInventory")
For Each doc In con.Documents
If Not IsTemp(doc.Name) Then
' Handle the special queries case.
' Tables and queries are lumped together
' in the Tables container.
If strContainer = "Tables" Then
If IsTable(doc.Name) Then
strType = "Tables"
Else
strType = "Queries"
End If
Else
strType = strContainer
End If
rst.AddNew
rst("Container") = strType
rst("Owner") = doc.Owner
rst("Name") = doc.Name
rst("DateCreated") = doc.DateCreated
rst("LastUpdated") = doc.LastUpdated
rst.Update
End If
Next doc
ExitHere:
If Not rst Is Nothing Then
rst.Close
Set rst = Nothing
End If
Exit Sub
HandleErr:
MsgBox Err.Number & ": " & Err.Description, , _
"AddInventory"
Resume ExitHere
End Sub
Private Sub RebuildInventory()
On Error GoTo HandleErr
DoCmd.Hourglass True
Me.lstInventory.RowSource = ""
Call CreateInventory
Me.lstInventory.RowSource = "SELECT ID, Container, Name, " & _
"Format([DateCreated],'mm/dd/yy (h:nn am/pm)') AS [Creation Date], " & _
"Format([lastUpdated],'mm/dd/yy (h:nn am/pm)') AS [Last Updated], " & _
"Owner FROM zstblInventory ORDER BY Container, Name;"
ExitHere:
DoCmd.Hourglass False
Exit Sub
HandleErr:
Resume ExitHere
End Sub
Private Sub CreateInventory()
If (CreateTable()) Then
' These routines use the status line,
' so clear it once everyone's done.
Call AddInventory("Tables")
Call AddInventory("Forms")
Call AddInventory("Reports")
Call AddInventory("Scripts")
Call AddInventory("Modules")
Call AddInventory("Relationships")
' Clear out the status bar.
Call SysCmd(acSysCmdClearStatus)
Else
MsgBox "Unable to create zstblInventory."
End If
End Sub
Private Function CreateTable() As Boolean
' Return True on success, False otherwise
Dim qdf As DAO.QueryDef
Dim db As DAO.Database
Dim strSQL As String
On Error GoTo HandleErr
Set db = CurrentDb()
db.Execute "DROP TABLE zstblInventory"
' Create zstblInventory
strSQL = "CREATE TABLE zstblInventory (Name Text (255), " & _
"Container Text (50), DateCreated DateTime, " & _
"LastUpdated DateTime, Owner Text (50), " & _
"ID AutoIncrement Constraint PrimaryKey PRIMARY KEY)"
db.Execute strSQL
' If you got here, you succeeded!
db.TableDefs.Refresh
CreateTable = True
ExitHere:
Exit Function
HandleErr:
Select Case Err
Case 3376, 3011 ' Table or Object not found
Resume Next
Case Else
CreateTable = False
End Select
Resume ExitHere
End Function
Private Function IsTable(ByVal strName As String)
Dim tdf As DAO.TableDef
Dim db As DAO.Database
On Error Resume Next
' Normally, in a function like this,
' you would need to refresh the tabledefs
' collection for each call to the function.
' Since this slows down the function
' by a very large measure, this time,
' just Refresh the collection the first
' time, before you call this function.
Set db = CurrentDb()
' See CreateTable().
'db.Tabledefs.Refresh
Set tdf = db.TableDefs(strName)
IsTable = (Err.Number = 0)
Err.Clear
End Function
Private Function IsTemp(ByVal strName As String)
IsTemp = Left(strName, 7) = "~TMPCLP"
End Function
Private Sub cmdCreateInventory_Click()
Call RebuildInventory
End Sub
Private Sub Detail0_Click()
End Sub
Private Sub Form_Open(Cancel As Integer)
Call RebuildInventory
End Sub
Check out the source code in this answer. You should be able to modify it to do what you need. Unless, as Remou pointed out in his comment, you are working with a pre-2000 version of Access.