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).
Related
I tried to follow up with the topic here:
How to connect to OPEN workbook in another instance of Excel
But I ran into a problem,
I am not able grab the new instance name or path.
However I know I have open Excel window in another instance (opened from a SAP system) and when I open VBA editor in that SAP generated Excel file and I type: ? Thisworkbook.Path in immediate window I get nothing, no path is given and thus this solutions does not get the instance path.
What can I do to make it work ?
My issue is that this: Set xlApp = GetObject("C:\Tmp\TestData2.xlsx") is not grabbing the workbook name (including This.workbook.name or activeworkbook.name)
Any idea how else I can make VBA code in instance 1 work with workbook in instance 2?
I only want to save it nothing more, I'm using Saveas option, or at least I try.
Have anyone had a similar issue?
Working with the Excel files downloaded from SAP is always problematic.
You can use the module below and add before the xls.Close SaveChanges:=False this line xls.SaveAs Filename:='Any name that you want after that just place a call in your code after downloading the Excel File with
Call Close_SAP_Excel("TestData2.xlsx")
And it should work fine.
Module:
#If VBA7 Then
Private Declare PtrSafe Function AccessibleObjectFromWindow Lib "oleacc" ( _
ByVal hwnd As LongPtr, ByVal dwId As Long, riid As Any, ppvObject As Object) As Long
Private Declare PtrSafe Function FindWindowExA Lib "user32" ( _
ByVal hwndParent As LongPtr, ByVal hwndChildAfter As LongPtr, _
ByVal lpszClass As String, ByVal lpszWindow As String) As LongPtr
#Else
Private Declare Function AccessibleObjectFromWindow Lib "oleacc" ( _
ByVal hwnd As Long, ByVal dwId As Long, riid As Any, ppvObject As Object) As Long
Private Declare Function FindWindowExA Lib "user32" ( _
ByVal hwndParent As Long, ByVal hwndChildAfter As Long, _
ByVal lpszClass As String, ByVal lpszWindow As String) As Long
#End If
Sub Close_SAP_Excel(ParamArray FileNames())
'Procedure to close files downloaded from SAP and at the same time close the Excel application instance that will be open with them.
Dim ExcelAppSAP As Variant
Dim ExcelFile As Variant
Dim FinishedLoop As Boolean, TimeoutReached As Boolean, FileClosed As Boolean
Dim ReTry As Long
Dim i As Long, x As Long
Set ExcelAppSAP = Nothing
ReTry = 100000 'Used as Timeout 100000 = ~10 seconds
i = 1
'The following loop is executed until excel file is closed.
'Inside of this, there is a For Loop for each Excel Instance and inside of that is another loop
'for each excel inside the instance. If name matches, it is closed.
Do While Not FinishedLoop
If i > ReTry Then
TimeoutReached = True
Exit Do
End If
For Each ExcelFile In GetExcelInstances() 'Function to Get Excel Open Instances
For Each xls In ExcelFile.Workbooks
For x = LBound(FileNames()) To UBound(FileNames())
If xls.Name = FileNames(x) Then
Set ExcelAppSAP = ExcelFile 'Set Instance opened by SAP to variable
'Here add actions if needed. Reference to workbook as xls e.g.: xls.Sheets(1).Range("A1").Copy
xls.Close SaveChanges:=False
FileClosed = True
End If
Next x
Next
Next
If FileClosed Then
FinishedLoop = True
End If
i = i + 1
Loop
ThisWorkbook.Activate
If Not TimeoutReached Then
If FileClosed Then
On Error Resume Next
If ExcelAppSAP.Workbooks.Count = 0 Then
ExcelAppSAP.Quit
End If
Else
MsgBox "Excel application instance from SAP was not closed correctly. Please close it manually or try again.", , "Error"
End If
Else
MsgBox "Max timeout reached", , "Error"
End If
End Sub
Public Function GetExcelInstances() As Collection
Dim guid&(0 To 3), acc As Object, hwnd, hwnd2, hwnd3
guid(0) = &H20400
guid(1) = &H0
guid(2) = &HC0
guid(3) = &H46000000
Set GetExcelInstances = New Collection
Do
hwnd = FindWindowExA(0, hwnd, "XLMAIN", vbNullString)
If hwnd = 0 Then Exit Do
hwnd2 = FindWindowExA(hwnd, 0, "XLDESK", vbNullString)
hwnd3 = FindWindowExA(hwnd2, 0, "EXCEL7", vbNullString)
If AccessibleObjectFromWindow(hwnd3, &HFFFFFFF0, guid(0), acc) = 0 Then
GetExcelInstances.Add acc.Application
End If
Loop
End Function
I had lots of issues dealing with that IE 11 download bar when downloading a file.
I checked different solutions but the only way to make it work the most reliably possible was to put two of them together.
Then I set the default internet download folder as my Desktop so that whenever I download a file with SendKeys I know where to find it with the code.
For the little story, my code is downloading the attached files for all the different incident cases. The number/type of attachments can vary and to oragnize it a little bit I decided to create a folder with the name of the incident case and store the attachments inside.
So here is my code, if you see a part which could be improved let me know :)
Option Explicit
Private objIE As SHDocVw.InternetExplorer
Private ContentFrame As HTMLIFrame
Public Declare Function SetCursorPos Lib "user32" (ByVal x As Long, ByVal y As Long) As Long
Public Declare Sub mouse_event Lib "user32" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)
Public Const MOUSEEVENTF_LEFTDOWN = &H2
Public Const MOUSEEVENTF_LEFTUP = &H4
Public Const MOUSEEVENTF_RIGHTDOWN As Long = &H8
Public Const MOUSEEVENTF_RIGHTUP As Long = &H10
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Sub LeftClick()
mouse_event MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0
Sleep 50
mouse_event MOUSEEVENTF_LEFTUP, 0, 0, 0, 0
End Sub
Sub DownloadAttachment()
'make sure Cell A4 isn't empty because it has to contain the incident case
If Sheets(1).Cells(4, 1) = "" Or Sheets(1).Cells(4, 1) = " " Then End
'make sure it's a valid case No. before going on
On Error GoTo Fin
If Len(Cells(4, 1)) <> 8 Or CLng(Sheets(1).Cells(4, 1)) = 0 Then
MsgBox "Please enter a valid Case No."
End
End If
Call GetDataFromIntranet(Sheets(1).Cells(4, 1)
'Delete content on cell A4
Fin:
Sheets(1).Cells(4, 1) = ""
End Sub
Function GetDataFromIntranet(CaseNo As Long)
Dim i As Integer
If ("attachmentDivId").Children(0).Children(1).Children.Length >= 1 Then Call CreateFolder(CaseNo) ' If there is at least 1 attachment then we'll create a folder which has the name of the incident case
For i = 0 To objIE.document.frames(1).frames(1).document.getElementById("attachmentDivId").Children(0).Children(1).Children.Length - 1 ' For each attachment...
RetourALaCaseDepart:
objIE.document.frames(1).frames(1).document.getElementById("attachmentDivId").Children(0).Children(1).Children(i).Click ' Click on it so that it gets activated (blue)
objIE.document.frames(1).frames(1).document.getElementsByName("download")(0).Click 'Click on Save as
'The following bit send keyboard keys to cope with the Internet Downaload window that appears down the page -> downloads the file and save it on the Desktop
Application.Wait Now + TimeSerial(0, 0, 10)
Application.SendKeys "%{S}"
Application.Wait Now + TimeSerial(0, 0, 10)
SendKeys "{F6}", True
SendKeys "{TAB}", True
SendKeys "{ENTER}", True
'Here we close the Desktop window which sometimes open because it can alter the SendKey codes which is very sensitive
Dim objShellWindows As New SHDocVw.ShellWindows
Dim win As Object
For Each win In objShellWindows
If win.LocationName = "Desktop" Then
win.Quit
End If
Next win
Application.Wait Now + TimeSerial(0, 0, 1)
If MakeSureDownloaded(objIE.document.frames(1).frames(1).document.getElementById("attachmentDivId").Children(0).Children(1).Children(i).Children(0).innerText, CaseNo) = False Then GoTo RetourALaCaseDepart ' We check if the attachment was successfully saved, if not we redo the saving process from "RetourALaCaseDepart
Next i
Exit Function
Fini:
MsgBox "No attachments found or attachment tab not found"
End Function
Function MakeSureDownloaded(FileName As String, CaseNo As Long) As Boolean
Dim FileSys As Object 'FileSystemObject
Dim objFile As Object 'File
Dim myFolder
Dim strFilename As String
Const myDir As String = "C:\Users\Seb\Desktop\"
'set up filesys objects
Set FileSys = CreateObject("Scripting.FileSystemObject") 'New FileSystemObject
Set myFolder = FileSys.GetFolder(myDir)
For Each objFile In myFolder.Files
If objFile.Name Like FileName & "*" Then ' If the file was saved then we will add it to the folder created earlier for that Case
strFilename = objFile.Name
MakeSureDownloaded = True
GoTo BienBien
End If
Next objFile
MakeSureDownloaded = False
Set FileSys = Nothing
Set myFolder = Nothing
Exit Function
BienBien:
Dim fso As Object
Set fso = VBA.CreateObject("Scripting.FileSystemObject")
Call fso.MoveFile("C:\Users\Seb\Desktop\" & strFilename, "Path...\Case_Attachments\" & CaseNo & "\" & strFilename)
Set FileSys = Nothing
Set myFolder = Nothing
End Function
Sub CreateFolder(CaseNo As Long)
Dim fsoFSO
Set fsoFSO = CreateObject("Scripting.FileSystemObject")
If fsoFSO.FolderExists("Path...\Case_Attachments\" & CaseNo) Then ' do nothing actually...
Else
fsoFSO.CreateFolder ("Path...\Case_Attachments\" & CaseNo)
End If
End Sub
I'm using VBA in access to open up a protected word template, fill in the data, and then re-protect it.... this way, if the database system goes down, the word template can still be used manually in its protected state.
I have just started using VBA and in this line:
If ActiveDocument.ProtectionType <> wdNoProtection Then
ActiveDocument.Unprotect Password:=""
End If
I'm concerned that whilst running the code in access, that if the user opens up another word document and makes it the focus, that it will occidentally get protected instead of the other. How do I keep active focus on the document I'm writing to... or do I need to reference my document somehow using WordApp.protect (or something similar that works)
Private Sub Command0_Click()
Dim WordApp As Word.Application
Dim strDatabasePath As String
Dim strTemplatePath As String
Dim strTemplate As String
Dim strJobTitle As String
Dim strFile As String
strFile1 = "testcoc.dotx"
strFile2 = "testcoc-private.dotx"
strDatabasePath = CurrentProject.Path & "\"
strTemplatePath = "\templates\"
strTemplate = strDatabasePath & strTemplatePath & strFile2
On Error Resume Next
Set WordApp = GetObject(, "Word.Application")
If Err.Number <> 0 Then
Set WordApp = CreateObject("Word.Application")
End If
On Error GoTo ErrHandler
WordApp.Visible = True
WordApp.WindowState = wdWindowStateMaximize
WordApp.Documents.Add Template:=strTemplate, NewTemplate:=True
'strJobTitle = DLookup("JobTitle", "Job", "JobNum = " & [JobType])
strJobTitle = DLookup("JobTitle", "Job", "JobNum = 'J0456'")
With WordApp.Selection
'Unprotect the file
If ActiveDocument.ProtectionType <> wdNoProtection Then
ActiveDocument.Unprotect Password:=""
End If
.Goto what:=wdGoToBookmark, Name:="bm_0_4"
.TypeText strJobTitle
End With
'Reprotect the document.
'If ActiveDocument.ProtectionType = wdNoProtection Then
'ActiveDocument.Protect _
'Type:=wdAllowOnlyFormFields, NoReset:=True, Password:=""
'End If
DoEvents
WordApp.Activate
Set WordApp = Nothing
Exit Sub
ErrHandler:
Set WordApp = Nothing
End Sub
Thank You
I haven't tried this but WordApp.Documents.Add Template:=strTemplate, NewTemplate:=True does return the new document. So I would do something like
Dim doc as Word.Document
Set doc = WordApp.Documents.Add(Template:=strTemplate, NewTemplate:=True)
and reference doc throughout my code instead of ActiveDocument. It seems like doing that should get help you avoid the particular situation you're concerned about.
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
I am new to programming in c# and visual basic. I am using visual studio 2010 and I am trying to retrieve some data from a mysql database. Installed using wamp.
I have already set connection to the database by going to 'Project', 'Application Properties', 'Settings'.
I have this error "'mAuto1' is not declared. It may be inaccessible due to its protection level" and I cant seem to solve it.
The code below is for a simple retrieve:
Public Class Form1
Private procAuto As CALCOM.Auto
Private Function Connect_To_Database() As Boolean
Dim mErrorNumQuery As Long
Dim mReturn As Boolean
procAuto = New CALCOM.Auto
procAuto.Connect(mErrorNumQuery)
If mErrorNumQuery = 0 Then
mReturn = True
Else
mReturn = False
End If
Connect_To_Database = mReturn
End Function
Private Function Get_Weight_By_TicketNumber(ByVal mTicketNumber As String, ByRef mAuto1 As Long, ByRef mAuto2 As Long, ByRef mTotalWeight As Long) As Boolean
Dim mErrorNumQuery As Long
Dim mXtr As New CALCOM.xTr
Dim mRec As ADODB.Recordset
Dim mReturn As Boolean
mRec = mXtr.GetList("Select Auto1,Auto2,TotalWeight From txticket Where TicketCode = '" & mTicketNumber & "'", , , mErrorNumQuery)
If mErrorNumQuery = 0 Then
mReturn = True
If mRec.RecordCount <> 0 Then
mRec.MoveFirst()
mRec.MoveFirst()
mAuto1 = mRec.Fields("Auto1").Value
mAuto2 = mRec.Fields("Auto2").Value
mTotalWeight = mRec.Fields("TotalWeight").Value
End If
Else
mReturn = False
End If
Get_Weight_By_TicketNumber = mReturn
End Function
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Label1.Text = mAuto1 <--------------------problem here
End Sub
End Class
This program was just a test to see if I can display results of a mysql query on a form label. I wanted to display results on the click of a button
How do I fix the error? Any help appreciated.
The mAuto1 variant is a local one at Get_Weight_By_TicketNumber function, you can't use it outside the function. If you want, declare a class-level variant and set it to the value of mAuto1.