I am having some issues adding my signature into the Adobe PDF. For some reason, I can't add my digital ID into the PDF using the additional code.
I keep on getting a Run-Time Error '13': Type Mismatch
Any suggestions?
I got the macro working so that it adds in the Signature field using the following code -
Sub Prepare_PDF()
On Error GoTo Err_Handler
Dim pdfPDDoc As New AcroPDDoc, oJS As Object, oFields, oAttachment As Object
Dim strFName As String
Dim strSignature As String
Dim strSignFName As String
Dim oParam As Parameter
strFName = "70145173 - 0100771347.pdf"
'------- Add signature fields to PDF file----------
If pdfPDDoc.Open(strFName) Then
Set oJS = pdfPDDoc.GetJSObject
Set oFields = oJS.AddField("SignatureField1", "signature", 0, Array(200, 620, 450, 670))
'------- Save PDF file------------------
strFName = Left(strFName, Len(strFName) - 4) & "-signed.pdf"
pdfPDDoc.Save 1, strFName
End If
Exit_Proc:
Exit Sub
Err_Handler:
MsgBox "In test" & vbCrLf & Err.Number & "--" & Err.Description
Resume Exit_Proc
End Sub
Now when I add this code in to get my digital ID added, it error's out.
Sub Prepare_PDF()
On Error GoTo Err_Handler
Dim pdfPDDoc As New AcroPDDoc, oJS As Object, oFields, oAttachment As Object
Dim strFName As String
Dim strSignature As String
Dim oPpklite As Object
Dim strSignFName As String
Dim oParam As Parameter
strFName = "A:\PDF File\Cost Transfer - 70145173 - 0100771347.pdf"
strSignature = "C:\Users\Desktop\FirstName.pfx"
'------- Add signature fields to PDF file----------
If pdfPDDoc.Open(strFName) Then
Set oJS = pdfPDDoc.GetJSObject
Set oFields = oJS.AddField("SignatureField1", "signature", 0, Array(200, 620, 450, 670))
Set oSign = oJS.GetField("SignatureField1")
Set oPpklite = oJS.security.getHandler("Adobe.PPKLite", True)
oPpklite.login "{'Password', '" & strSignature & "'}"
oSign.signatureSign oPpklite
oPpklite.logout
'------- Save PDF file------------------
strFName = Left(strFName, Len(strFName) - 4) & "-signed.pdf"
pdfPDDoc.Save 1, strFName
End If
Exit_Proc:
Exit Sub
Err_Handler:
MsgBox "In test" & vbCrLf & Err.Number & "--" & Err.Description
Resume Exit_Proc
End Sub
This is what works for me: i do the signing in two steps. (i did it in Access, but it should be almost the same for Excel)
Option Compare Database
Option Explicit
'the position of the signing box on the PDF
Const Y = 524
Const X = 14
Const W = 106
Const H = 59
Public Function PreparePDF(InputPDF As String) As Boolean
On Error GoTo Err_Handler
Dim pdfPDDoc As Acrobat.AcroPDDoc
Dim OutputPdf As String: OutputPdf = "C:\fact\Temp.pdf"
'it needs to be an array
Dim sg_arr() As Variant: sg_arr = Array(X, Y + H, X + W, Y)
Dim oJS As Object
Dim oPpklite As Object
Dim SignField As Object
Dim HadError As Boolean: HadError = False
Set pdfPDDoc = New AcroPDDoc
If pdfPDDoc.Open(InputPDF) Then
Set oJS = pdfPDDoc.GetJSObject
Set oPpklite = oJS.security.getHandler("Adobe.PPKLite")
'add the signature field to the PDF, then save and close. Otherwise,
'Acrobat will give the following message:
'"GeneralError: Operation failed. Field.signatureSign:0: This operation cannot be done before the document has finished downloading. Please try again later."
Set SignField = oJS.AddField("Sgn", "signature", 0, sg_arr)
pdfPDDoc.Save 1, OutputPdf
End If
Exit_Proc:
If Not oPpklite Is Nothing Then oPpklite.logout
Set oJS = Nothing
Set oPpklite = Nothing
Set pdfPDDoc = Nothing
If Not HadError Then
If SignPDF(InputPDF) Then
MsgBox "Successfully signed " & InputPDF & "!", vbOKOnly + vbInformation
Dim Fso As New FileSystemObject
Fso.DeleteFile "C:\fact\Temp.pdf", True
Set Fso = Nothing
PreparePDF = True
Else
PreparePDF = False
End If
Else
PreparePDF = False
End If
Exit Function
Err_Handler:
HadError = True
Debug.Print "In SignPDF" & vbCrLf & Err.Number & "--" & Err.Description
Resume Exit_Proc
End Function
Public Function SignPDF(OutputPdf As String) As Boolean '(inputpdf As String, outputpdf As String)
On Error GoTo Err_Handler
Dim InputPDF As String: InputPDF = "C:\fact\temp.pdf"
Dim strSignFName As String: strSignFName = "C:\fact\PFA.pfx"
Dim pass As String: pass = "******"
Dim pdfApp As Acrobat.AcroApp
Dim pdfPDDoc As Acrobat.AcroPDDoc
Dim oJS As Object
Dim oSign As Object
Dim oPpklite As Object
Dim ResultLogin As Boolean
Dim ResultSign As Boolean
Dim arr() As String
Set pdfApp = New AcroApp
Set pdfPDDoc = New AcroPDDoc
If pdfPDDoc.Open(InputPDF) Then
Set oJS = pdfPDDoc.GetJSObject
Set oPpklite = oJS.security.getHandler("Adobe.PPKLite")
ResultLogin = oPpklite.login(pass, "C:\fact\PFA.pfx")
oPpklite.setPasswordTimeout pass, 200
Set oSign = oJS.GetField("Sgn")
arr = VBA.Split("", ",")
ResultSign = oSign.signatureSign(oPpklite, arr, OutputPdf)
End If
SignPDF = True
Exit_Proc:
If Not oPpklite Is Nothing Then oPpklite.logout
If Not pdfApp Is Nothing Then
pdfApp.CloseAllDocs
pdfApp.Hide
pdfApp.Exit
End If
Set oJS = Nothing
Set oSign = Nothing
Set oPpklite = Nothing
Set pdfPDDoc = Nothing
Set pdfApp = Nothing
Exit Function
Err_Handler:
SignPDF = False
Debug.Print "In SignPDF" & vbCrLf & Err.Number & "--" & Err.Description
Resume Exit_Proc
End Function
And to call it:
PreparePDF "pdf_file_to_be_signed.pdf"
Obviously you need to reference acrobat.tlb
Related
I was hoping to get some help. I have the following code that works on replacing text from a word documents with a certain word in excel. For example I have ClientName in a cell and the Cell next to it has John, so each time the word ClientName is found it is replaced with John and so on. Here is the code that works for word documents. Can it be altered to work for .pptx too?
Sub AutoContract()
Dim cell As Range
Dim rng As Range
Dim wdApp As New Word.Application
Dim wdDoc As Word.Document
Dim wdDoc2 As Word.Document
Dim FilePath As String
Dim FilePath2 As String
Dim ending As String
Dim rngPara As Range
Dim Prompt As String
Dim Filesave As String
Dim FileSave2 As String
On Error GoTo ErrorHandler
Set wdApp = Nothing
FilePath = ThisWorkbook.Path
FilePath2 = Left(FilePath, InStr(FilePath, "\Calculations") - 1)
Filename = "Filename.docx"
StrDoc = FilePath2 & "\Inputs" & "\" & Filename
Set wdDoc2 = wdApp.Documents.Open(StrDoc)
Set rngPara = Range("A1:Z1058").Find("Variable Parameters")
If rngPara Is Nothing Then
MsgBox "Variable Parameters column was not found."
GoTo ErrorHandler
End If
Set rng = Range(rngPara, rngPara.End(xlDown))
wdApp.Visible = True
For Each cell In rng
If cell.Value = "" Then Exit For
With wdDoc2.Content.Find
.ClearFormatting
.Replacement.ClearFormatting
.MatchWildcards = False
.Wrap = wdFindContinue
.Text = cell.Value
.Replacement.Text = cell.Offset(0, 1)
.Execute Replace:=wdReplaceAll
End With
Next
SaveAsName = Left(FilePath, InStr(FilePath, "\Calculations") - 1) & "\Outputs\" & Range("EmployName").Value & " " & Range("TodayDate").Value & " Contract" & ".docx"
wdDoc2.SaveAs2 SaveAsName
ErrorExit:
Set wdApp = Nothing
Exit Sub
ErrorHandler:
If Err.Number = 5174 Then
MsgBox "Please check the file name you specified is correct."
Resume ErrorExit
Else
MsgBox "Error No: " & Err.Number & "; There is a problem"
If Not wdApp Is Nothing Then
wdApp.Quit False
End If
Resume ErrorExit
End If
End Sub
I have written some code to run when the Workbook is opened and it works flawlessly on my personal computer. However after sending the file to my laptop I get the "application-defined or object-defined error" message. I genuinely do not understand why this has happened or how I can solve this.
Private Sub Workbook_Open()
'loads the combobox when book opened
MenuGenerator.miscComboBox.List = Application.WorksheetFunction.Transpose(ThisWorkbook.Names("MenuMiscellaneous").RefersToRange)
MenuGenerator.soupCombobox.List = Application.WorksheetFunction.Transpose(ThisWorkbook.Names("MenuSoups").RefersToRange)
MenuGenerator.saladComboBox.List = Application.WorksheetFunction.Transpose(ThisWorkbook.Names("MenuSalads").RefersToRange)
MenuGenerator.meatComboBox.List = Application.WorksheetFunction.Transpose(ThisWorkbook.Names("MenuMeat").RefersToRange)
MenuGenerator.fishComboBox.List = Application.WorksheetFunction.Transpose(ThisWorkbook.Names("MenuFish").RefersToRange)
MenuGenerator.starchComboBox.List = Application.WorksheetFunction.Transpose(ThisWorkbook.Names("MenuStarch").RefersToRange)
MenuGenerator.veggieComboBox.List = Application.WorksheetFunction.Transpose(ThisWorkbook.Names("MenuVegetable").RefersToRange)
MenuGenerator.dessertComboBox.List = Application.WorksheetFunction.Transpose(ThisWorkbook.Names("MenuDessert").RefersToRange)
End Sub
The code is set to load named ranges I created into the comboboxes.
Add a diagnostic message box to the code.
Option Explicit
Sub Workbook_Open()
Dim wb As Workbook, ws As Worksheet, i As Integer
Dim fn, arObj, arMenu, rng As Range, obj
Dim msg As String
Set fn = Application.WorksheetFunction
Set wb = ThisWorkbook
Set ws = wb.Sheets("MenuGenerator")
arObj = Array("misc", "soup", "salad", "meat", "fish", "starch", "veggie", "dessert")
arMenu = Array("Miscellaneous", "Soups", "Salads", "Meat", _
"Fish", "Starch", "Vegetable", "Dessert")
On Error Resume Next
For i = 0 To UBound(arObj)
Set rng = Nothing
Set obj = Nothing
msg = ""
Set rng = wb.Names("Menu" & arMenu(i)).RefersToRange
If rng Is Nothing Then
msg = msg & vbLf & "Error with name range 'Menu" & arMenu(i) & "'"
End If
Set obj = ws.OLEObjects(arObj(i) & "ComboBox")
If obj Is Nothing Then
msg = msg & vbLf & "Error with '" & arObj(i) & "ComboBox" & "'"
End If
If msg = "" Then
obj.Object.List = fn.Transpose(rng)
Else
MsgBox msg, vbExclamation
End If
Next
On Error GoTo 0
End Sub
I am trying to run a PowerPoint Macro through Excel VBA, I used to be able to run a macro on a powerpoint file with ease but I am having trouble passing a parameter in excel.
Sub Test()
Dim arr(1 To 1), macname As String, objPP As Object, PPTFilePath As String, ObjPPFile As Object,
PPtFileName As String
PPTFileName ="Report.pptm"
PPTFilePath ThisWorkbook.Path & PPTFileName
Set objPP = CreateObject("PowerPoint.Application")
objPP.Visible = True
Set objPPFile = objPP.Presentations.Open(PPTFilePath)
Application.EnableEvents = False
arr(1) = ThisWorkbook.Path
macname = "'" & PPTFileName & "'!Module3.UpdateSpecificLinks"
objPP.Run macname, arr
objPPFile.Save
waiting (3)
Application.EnableEvents = True
End Sub
I get an error on objPP.Run macname, arr , it is: Run-time error '-2147188160 (80048240)': Application.Run :Invalid request. Sub or Function not defined.
How do I properly Pass a parameter to the powerpoint macro: Sub UpdateSpecificLinks(LNK as String)
If your SubUpdateSpecificLinks is e.g. in a private module, the call to it will fail; it must be public.
I think this is the problem, though:
macname = "'" & PPTFileName & "'!Module3.UpdateSpecificLinks"
Try this instead:
macname = PPTFileName & "!Module3.UpdateSpecificLinks"
A couple of examples, calling from a PPTM file to another (closed) PPTM file:
Here are the calling macros:
Sub TestWithString()
Dim sFileName As String
Dim oPres As Presentation
sFileName = "C:\temp\runme.pptm"
Set oPres = Presentations.Open(sFileName, , , False)
Application.Run "C:\temp\RunMe.pptm!RunMe", "This is the passed parameter"
oPres.Close
End Sub
Sub TestWithArray()
Dim sFileName As String
Dim oPres As Presentation
Dim aStrings(1 To 3) As String
sFileName = "C:\temp\runme.pptm"
Set oPres = Presentations.Open(sFileName, , , False)
aStrings(1) = "String 1"
aStrings(2) = "String 2"
aStrings(3) = "String 3"
Application.Run "C:\temp\RunMe.pptm!HowAboutAnArray", aStrings
oPres.Close
End Sub
And here are the macros they call:
Sub RunMe(sMsg As String)
MsgBox "You said " & sMsg
End Sub
Sub HowAboutAnArray(vParm As Variant)
Dim x As Long
For x = 1 To ubound(vParm)
MsgBox vParm(x)
Next
End Sub
I am working on a "mail bot", where I will receive a filled template, and populate and save an Excel file with that information.
I can fill the first file and quit the Excel file.
When a second mail arrives, I get
'1004 - application-defined or object-defined error'
Why am I getting the error on the second and beyond ones?
I am running the code when a new mail arrives
Option Explicit
Private WithEvents inboxItems As Outlook.Items
Private Sub Application_Startup()
Dim outlookApp As Outlook.Application
Dim objectNS As Outlook.NameSpace
Set outlookApp = Outlook.Application
Set objectNS = outlookApp.GetNamespace("MAPI")
Set inboxItems = objectNS.GetDefaultFolder(olFolderInbox).Items
End Sub
The main sub
Private Sub inboxItems_ItemAdd(ByVal Item As Object)
On Error GoTo ErrorHandler
Dim Msg As Outlook.MailItem
Dim MessageInfo
Dim Result
Dim splitter() As String
Dim splitter2() As String
Dim str As Variant
Dim LoopCali As Integer
Dim xlApp As Object
Dim sourceWB As Workbook
Dim sourceWS As Worksheet
Dim strFile As String
If TypeName(Item) = "MailItem" Then
If InStr(Item.Subject, "BOT") > 0 Then
splitter = Split(Item.Body, vbCrLf)
splitter2 = Split(splitter(40), "-")
Set xlApp = CreateObject("Excel.Application")
strFile = "C:\Users\e1257539\Desktop\SMOBOT\SMO_TOOL_BOT.xlsm"
With xlApp
.Visible = TRUE
.EnableEvents = FALSE
End With
Set sourceWB = Workbooks.Open(strFile, , False, , , , , , , True)
sourceWB.Activate
With xlApp
.Worksheets("HEADER").Range("D6").Value2 = splitter(22)
.Worksheets("HEADER").Range("D8").Value2 = splitter(12)
.Worksheets("HEADER").Range("F4").Value2 = "AINT"
.Worksheets("HEADER").Range("F3").Value2 = "EXW"
.Worksheets("HEADER").Range("C2").Value2 = Worksheets("QuoteSTG").Range("A" + CStr(Worksheets("QuoteSTG").Range("B1").Value2)).Value2
.Worksheets("QuoteSTG").Range("A" + CStr(Worksheets("QuoteSTG").Range("B1").Value2)).Value2 = ""
End With
If splitter(2) = "Calibração" Then
Result = MsgBox(splitter(2), vbOKOnly, i)
LoopCali = splitter(26)
End If
If splitter(2) = "Trainamento" Then
End If
End If
MessageInfo = "" & _
"Sender : " & Item.SenderEmailAddress & vbCrLf & _
"Sent : " & Item.SentOn & vbCrLf & _
"Received : " & Item.ReceivedTime & vbCrLf & _
"Subject : " & Item.Subject & vbCrLf & _
"Size : " & Item.Size & vbCrLf & _
"Message Body : " & vbCrLf & Item.Body
End If
xlApp.Quit
Set xlApp = Nothing
Set sourceWB = Nothing
Set sourceWS = Nothing
ErrorHandler:
MsgBox Err.Number & " - " & Err.Description
xlApp.Quit
Set xlApp = Nothing
Set sourceWB = Nothing
Set sourceWS = Nothing
'Resume ExitNewItem
End Sub
As checked on the link sent by the user: Niton
Excel application not closing from Outlook VBA function
The main issue was that the excel file wasn't closing.
After some changes this was the final result:
Private Sub inboxItems_ItemAdd(ByVal Item As Object)
On Error GoTo ErrorHandler
Dim Msg As Outlook.MailItem
Dim MessageInfo
Dim Result
Dim splitter() As String
Dim splitter2() As String
Dim str As Variant
Dim LoopCali As Integer
Dim i As Integer
Dim xlApp As Object
Dim sourceWB As Object
Dim Header, QuoteSTG, AINT As Object
Dim strFile As String
Dim file_name As String
'
i = 0
'
If TypeName(Item) = "MailItem" Then
If InStr(Item.Subject, "BOT") > 0 Then
splitter = Split(Item.Body, vbCrLf)
splitter2 = Split(splitter(40), "-")
Result = MsgBox(splitter2(0), vbOKOnly, i)
Result = MsgBox(splitter2(1), vbOKOnly, i)
'
Set xlApp = CreateObject("Excel.Application")
strFile = "C:\Users\e1257539\Desktop\SMOBOT\SMO_TOOL_BOT.xlsm"
With xlApp
.Visible = True
.EnableEvents = False
End With
Set sourceWB = Workbooks.Open(strFile)
sourceWB.Activate
Set Header = sourceWB.Sheets(4) 'header
Set QuoteSTG = sourceWB.Sheets(13) 'quotestg
Set AINT = sourceWB.Sheets(7) 'aint
If splitter(2) = "Calibração" Then
LoopCali = splitter(26)
file_name = QuoteSTG.Range("A" + CStr(QuoteSTG.Range("B1").Value2)).Value2
QuoteSTG.Range("A" + CStr(QuoteSTG.Range("B1").Value2)).Value2 = ""
sourceWB.Save
Header.Range("D6").Value2 = splitter(22)
Header.Range("D8").Value2 = splitter(12)
Header.Range("F4").Value2 = "AINT"
Header.Range("F3").Value2 = "EXW"
Header.Range("C2").Value2 = file_name
End If
If splitter(2) = "Treinamento" Then
End If
End If
End If
MkDir "C:\Users\e1257539\Desktop\SMOBOT\" + file_name
sourceWB.SaveAs FileName:="C:\Users\e1257539\Desktop\SMOBOT\" + file_name + "\" + file_name
sourceWB.Close
xlApp.Quit
Set xlApp = Nothing
Set sourceWB = Nothing
Set AINT = Nothing
Set QuoteSTG = Nothing
Set Header = Nothing
ErrorHandler:
MsgBox Err.Number & " - " & Err.Description
End Sub
In Excel VBA, if a variable is Excel.Range, and the range it refers to is deleted, it loses its reference. Any attempt to access the variable results in Runtime Error 424: object required.
Dim rng As Range
Set rng = Sheet1Range("A1")
Sheet1.Rows(1).Delete 'Range has been deleted.
Debug.Print rng.Address() 'Any access attempt now raises runtime error 424.
Is there a way to test for this state of "lost reference" without an error handler..?
Testing Nothing, Vartype(), and Typename() were all not useful because the variable is still a Range. I literally read through all of Excel.Application in the Object browser, but found nothing. Perhaps there's something I'm overlooking..? Such as one of those strange vestigial functions from prehistoric versions of Excel, like ExecuteExcel4Macro()..?
I've searched Google for the answer to this question, but didn't find anything helpful.
EDIT:
Some have asked why I'm trying to avoid an error handler. This is my normal programming philosophy for a couple reasons:
I do recognize that sometimes an error handler is the quickest way, or the only way. But it's not the most elegant way. It just seems, well...crude to me. It's like the difference between white-washing a picket fence and painting a portrait of my cat. =-)
The other reason I avoid error handlers is education. Many times when searching for an alternative, I'll discover properties, procedures, objects, or even entire libraries that I never knew before. And in doing so I find more armor with which to bulletproof my code.
Here's an approach that should be able to workaround the issue, although it isn't a great solution for checking if it was removed by itself. I think error handling is probably your best approach.
Sub Example()
Dim foo1 As Range
Dim foo2 As Range
Dim foo3 As Range
Dim numberOfCells As Long
Set foo1 = Sheet1.Range("A1")
Set foo2 = foo1.Offset(1, 0) 'Get the next row, ensure this cell exists after row deletion!
Set foo3 = Union(foo1, foo2)
numberOfCells = foo3.Cells.Count
Debug.Print "There are " & numberOfCells & " cells before deletion"
Sheet1.Rows(1).Delete
Debug.Print "There are now " & foo3.Cells.Count & " cells"
If foo3.Cells.Count <> numberOfCells Then
Debug.Print "One of the cells was deleted!"
Else
Debug.Print "All cells still exist"
End If
End Sub
Also, here is a more function oriented approach which may be a slightly better approach to add to your codebase. Again, not ideal, but it should not require an error handler.
Private getRange As Range
Sub Example()
Dim foo As Range
Dim cellCount As Long
Set foo = Sheet1.Range("A1")
cellCount = GetCellCountInUnion(foo)
Sheet1.Rows(1).Delete
If Not cellCount = getRange.Cells.Count Then
Debug.Print "The cell was removed!"
Else
Debug.Print "The cell still exists!"
End If
End Sub
Private Function GetCellCountInUnion(MyRange As Range) As Long
Set getRange = Union(MyRange, MyRange.Parent.Range("A50000")) ‘second cell in union is just a cell that should exist
GetCellCountInUnion = getRange.Cells.Count
End Function
Just in case someone needs a solution for this problem and doesn't mind using the error handler.
Option Explicit
Public Sub Example()
Dim rng1 As Range, rng2 As Range
Set rng1 = Range("A1")
Set rng2 = Range("A2")
ActiveSheet.Rows(1).Delete ' rng1 will loose its reference
Debug.Print "rng1 has reference? : " & RangeHasReference(rng1)
Debug.Print "rng2 has reference? : " & RangeHasReference(rng2)
End Sub
Private Function RangeHasReference(rng As Range) As Boolean
Dim Creator As Long
On Error Resume Next
Creator = rng.Creator ' try access some property
RangeHasReference = (Err.Number <> 424)
End Function
An example using a range name:
Dim ws As Worksheet, rng As Range, nm As Name
Set ws = ActiveSheet
Set rng = ws.Range("A2")
Names.Add Name:="testName", RefersTo:=rng
Set nm = Application.Names("testName")
ws.Rows(2).Delete 'Range has been deleted.
If InStr(1, nm.RefersTo, "#REF!") > 0 Then
'If InStr(1, Names("testName").RefersTo, "#REF!") > 0 Then
Debug.Print "lost reference"
Else
Debug.Print rng.Address()
End If
nm.Delete
'Names.Add Name:="testName", RefersTo:=""
Below an example of a sheet module to synchronize from an excel listobject to a database table (ms access).
UPDATE Jul 05, 20': some testing with the code below seems to shows a lost of info about the counter of selected rows/columns in the "names" editor window panel (top left, next to formula editor) in cases of multiple cell selections.
Private IdAr As Variant, myCount As Integer
Private Sub Worksheet_Activate()
Names.Add Name:="myName", RefersTo:=Selection, Visible:=False
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Rows.Count = Me.Rows.Count Then Exit Sub
On Error GoTo ExceptionHandling
Names.Add Name:="myName", RefersTo:=Target, Visible:=False
If Not Application.Intersect(Target, Me.ListObjects("Table2").DataBodyRange) Is Nothing Then
Dim tblRow As Long, y As Integer, i As Integer
tblRow = Target.Row - Me.ListObjects("Table2").HeaderRowRange.Row
y = Target.Rows.Count
If y > 1 Then
ReDim IdAr(0 To y - 1)
For i = 0 To y - 1
IdAr(i) = Me.ListObjects("Table2").ListColumns("ID").DataBodyRange(tblRow + i)
Next i
Else
'If Application.CutCopyMode = False Then
IdAr = Me.ListObjects("Table2").ListColumns("ID").DataBodyRange(tblRow).Value
'End If
End If
End If
CleanUp:
On Error Resume Next
Exit Sub
ExceptionHandling:
MsgBox "Error: " & Err.Description
Resume CleanUp
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo ExceptionHandling
Application.EnableEvents = False
If Not Application.Intersect(Target, Me.ListObjects("Table2").DataBodyRange) Is Nothing Then
Dim myCell As Range
For Each myCell In Target
If Not Application.Intersect(myCell, Me.ListObjects("Table2").ListColumns("ID").DataBodyRange) Is Nothing Then
If InStr(1, Names("myName").RefersTo, "#") > 0 Then
Debug.Print "Lost reference"
Delete_record
myCount = myCount + 1
Cancelado = True
Else
If myCell.Text = vbNullString Then
Debug.Print "Selecting listObject row and clear contents"
Delete_record
myCount = myCount + 1
Cancelado = True
End If
End If
Else
If Cancelado = False Then
If Not Application.Intersect(myCell, Me.Range("Table2[[FIELD1]:[FIELD3]]")) Is Nothing Then Update_record myCell
End If
End If
Next myCell
End If
CleanUp:
On Error Resume Next
myCount = 0
Application.EnableEvents = True
Exit Sub
ExceptionHandling:
MsgBox "Error: " & Err.Description
Resume CleanUp
End Sub
Sub Update_record(myCell As Range)
On Error GoTo ExceptionHandling
Dim tblRow As Long, IdTbl As Long, sField As String, sSQL As String
sField = Me.ListObjects("Table2").HeaderRowRange(myCell.Column)
tblRow = myCell.Row - Me.ListObjects("Table2").HeaderRowRange.Row
IdTbl = Me.ListObjects("Table2").ListColumns("ID").DataBodyRange(tblRow).Value
'Dim cnStr As String
'cnStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & sPath & ";Jet OLEDB:Database Password=123"
'Dim cn As ADODB.Connection
'Set cn = New ADODB.Connection
'cn.CursorLocation = adUseServer
'cn.Open cnStr
If IdTbl > 0 Then
sSQL = "UPDATE MYTABLE SET " & sField & " = '" & myCell.Value & "' WHERE ID = " & Me.ListObjects("Table2").ListColumns("ID").DataBodyRange(tblRow).Value
MsgBox sSQL
'Dim cmd As ADODB.Command
'Set cmd = New ADODB.Command
'Set cmd.ActiveConnection = cn
'cmd.CommandText = sSQL
'cmd.Execute , , adCmdText + adExecuteNoRecords
''cn.Execute sSQL, RecsAffected 'alternative to Command
''Debug.Print RecsAffected
Else
sSQL = "SELECT ID, " & sField & " FROM MYTABLE"
MsgBox sSQL
'Dim rst As ADODB.Recordset
'Set rst = New ADODB.Recordset
'rst.Open sSQL, cn, adOpenForwardOnly, adLockOptimistic, adCmdText
'cn.BeginTrans
'rst.AddNew
'rst(sField).Value = myCell.Value
'rst.Update
'IdTbl = rst(0).Value
'MsgBox "New Auto-increment value is: " & IdTbl
'tbl.ListColumns("ID").DataBodyRange(Fila) = IdTbl
'rst.Close
'cn.CommitTrans
End If
CleanUp:
On Error Resume Next
cn.Close
Exit Sub
ExceptionHandling:
MsgBox "Error: " & Err.Description & vbLf & Err.Number
Resume CleanUp
Resume 'for debugging
End Sub
Sub Delete_record()
Dim sSQL As String
If IsArray(IdAr) Then
sSQL = "DELETE FROM MYTABLE WHERE ID = " & IdAr(myCount)
MsgBox sSQL
Else
sSQL = "DELETE FROM MYTABLE WHERE ID = " & IdAr
MsgBox sSQL
End If
End Sub
UPDATE Aug 02 '20 Finally i'm using the code below for detecting deleted rows and upward synchronizing from an excel ListObject table to a database table:
Private IdAr As Variant, tbRows As Integer, myCount As Integer, Cancelado As Boolean
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Rows.Count = Me.Rows.Count Then Exit Sub
On Error GoTo ExceptionHandling
If Not Application.Intersect(Target, Me.ListObjects("Table1").DataBodyRange) Is Nothing Then
Dim tblRow As Long, y As Integer, i As Integer
tblRow = Target.Row - Me.ListObjects("Table1").HeaderRowRange.Row
y = Target.Rows.Count
If y > 1 Then
ReDim IdAr(0 To y - 1)
For i = 0 To y - 1
IdAr(i) = Me.ListObjects("Table1").ListColumns("ID").DataBodyRange(tblRow + i)
Next i
Else
'If Application.CutCopyMode = False Then
IdAr = Me.ListObjects("Table1").ListColumns("ID").DataBodyRange(tblRow).Value
'End If
End If
tbRows = Me.ListObjects("Table1").ListRows.Count
End If
CleanUp:
On Error Resume Next
Exit Sub
ExceptionHandling:
MsgBox "Error: " & Err.Description
Resume CleanUp
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo ExceptionHandling
Application.EnableEvents = False
If Not Application.Intersect(Target, Me.ListObjects("Table1").DataBodyRange) Is Nothing Then
Cancelado = False
Dim myCell As Range
For Each myCell In Target
If Not Application.Intersect(myCell, Me.ListObjects("Table1").ListColumns("ID").DataBodyRange) Is Nothing Then
If Me.ListObjects("Table1").ListRows.Count > tbRows Then
Cancelado = True
Else
If Me.ListObjects("Table1").ListRows.Count = tbRows Then
If myCell.Text = vbNullString Then
Debug.Print "Selected ListObject Row and Cleared Contents"
Cancelado = True
Delete_record
myCount = myCount + 1
End If
Else
Cancelado = True
Debug.Print "ListObject Row Deleted"
Delete_record
myCount = myCount + 1
End If
End If
Else
If Cancelado = False Then
If Not Application.Intersect(myCell, Me.Range("Table1[[FIELD1]:[FIELD3]]")) Is Nothing Then Update_record myCell
End If
End If
Next myCell
End If
CleanUp:
On Error Resume Next
myCount = 0
Application.EnableEvents = True
Exit Sub
ExceptionHandling:
MsgBox "Error: " & Err.Description & vbLf & Err.Number
Resume CleanUp
Resume 'for debugging
End Sub
Sub Update_record(myCell As Range)
On Error GoTo ExceptionHandling
Dim tblRow As Long, IdTbl As Long, sField As String, sSQL As String
sField = Me.ListObjects("Table1").HeaderRowRange(myCell.Column)
tblRow = myCell.Row - Me.ListObjects("Table1").HeaderRowRange.Row
IdTbl = Me.ListObjects("Table1").ListColumns("ID").DataBodyRange(tblRow).Value
'Dim cnStr As String
'cnStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & sPath & ";Jet OLEDB:Database Password=123"
'Dim cn As ADODB.Connection
'Set cn = New ADODB.Connection
'cn.CursorLocation = adUseServer
'cn.Open cnStr
If IdTbl > 0 Then
sSQL = "UPDATE MYTABLE SET " & sField & " = '" & myCell.Value & "' WHERE ID = " & Me.ListObjects("Table1").ListColumns("ID").DataBodyRange(tblRow).Value
MsgBox sSQL
'Dim cmd As ADODB.Command
'Set cmd = New ADODB.Command
'Set cmd.ActiveConnection = cn
'cmd.CommandText = sSQL
'cmd.Execute , , adCmdText + adExecuteNoRecords
''cn.Execute sSQL, RecsAffected 'alternative to Command
''Debug.Print RecsAffected
Else
sSQL = "SELECT ID, " & sField & " FROM MYTABLE"
MsgBox sSQL
'Dim rst As ADODB.Recordset
'Set rst = New ADODB.Recordset
'rst.Open sSQL, cn, adOpenForwardOnly, adLockOptimistic, adCmdText
'cn.BeginTrans
'rst.AddNew
'rst(sField).Value = myCell.Value
'rst.Update
'IdTbl = rst(0).Value
'MsgBox "New Auto-increment value is: " & IdTbl
'Me.ListObjects("Table1").ListColumns("ID").DataBodyRange(tblRow) = IdTbl
'rst.Close
'cn.CommitTrans
End If
CleanUp:
On Error Resume Next
If Not cn Is Nothing Then
If cn.State = adStateOpen Then cn.Close
End If
'DriveMapDel
'https://codereview.stackexchange.com/questions/143895/making-repeated-adodb-queries-from-excel-sql-server
'... get rid of the redundant assignments to Nothing; the objects are going out of scope at End Sub, they're being destroyed anyway.
'Set rst = Nothing
'Set cmd = Nothing
'Set cn = Nothing
Exit Sub
ExceptionHandling:
MsgBox "Error: " & Err.Description & vbLf & Err.Number
Resume CleanUp
Resume 'for debugging
End Sub
Sub Delete_record()
Dim sSQL As String
If IsArray(IdAr) Then
sSQL = "DELETE FROM MYTABLE WHERE ID = " & IdAr(myCount)
MsgBox sSQL
Else
sSQL = "DELETE FROM MYTABLE WHERE ID = " & IdAr
MsgBox sSQL
End If
End Sub