Home > front end >  How to paste clipboard screenshot of Access form to new Outlook email?
How to paste clipboard screenshot of Access form to new Outlook email?

Time:07-12

I've found a lot of resources for this for Excel, but have not been able to get any of them to work 100% with Access.

I tried this, but it ultimately didn't work because I'm not working with a string, I'm working with a bmp.

This post got me 90% of the way there, I am able to save the screenshot and see it in the clipboard, but I can't figure out how to proceed. I've tried other resources that build a new email from HTML, but I couldn't get that to work. I also tried building an email without HTML, and ultimately also could not get that to work. So then I tried to save the file locally and then add it to my email, but the code runs with no errors but doesn't save the file, so I hit a dead end there as well.

I'm mixing methods here, but I will post everything I have so it's complete:

In my Access database, I have a form. I click one button to take the screenshot of the form, and another button to send the email that I want to paste the screenshot into. I was able to create the email by using the query, but this doesn't work for me because the conditional formatting applied to the form is critical, and I lose that if it's just a plain table. Eventually this will all be automatic, the buttons are just for testing.

The form:

Access form

Form code:

    Option Compare Database
Option Explicit
Private Sub Command15_Click()
Screenshot.PrintScreen
End Sub
Public Sub Command4_Click()

Dim olapp As Object
Dim olItem As Variant
Dim strQry As String
Dim aHead(1 To 3) As String
Dim aRow(1 To 3) As String
Dim aBody() As String
Dim lCnt As Long
Dim objDoc As Word.Document
Dim objSel As Word.Selection
Dim db As Variant
Dim rec As Variant

Dim DataObj As MSForms.DataObject
Set DataObj = New MSForms.DataObject

Dim preamble As String



'Create the header row
aHead(1) = "AGC"
aHead(2) = "Battery Install Date"
aHead(3) = "Last EQ Charge"

preamble = "This email has been sent automatically because an AGC is due for an EQ charge. Please refer to the below table."

lCnt = 1
ReDim aBody(1 To lCnt)
aBody(lCnt) = "<HTML><body><table border='2'><tr><th>" & Join(aHead, "</th><th>") & "</th></tr>"

'Create each body row
strQry = "SELECT * From Query"
Set db = CurrentDb
Set rec = CurrentDb.OpenRecordset(strQry)

If Not (rec.BOF And rec.EOF) Then
    Do While Not rec.EOF
        lCnt = lCnt   1
        ReDim Preserve aBody(1 To lCnt)
        aRow(1) = rec("AGC")
        aRow(2) = rec("Battery Install Date")
        aRow(3) = rec("Last EQ Charge")
        aBody(lCnt) = "<tr><td>" & Join(aRow, "</td><td>") & "</td></tr>"
        rec.MoveNext
    Loop
End If

aBody(lCnt) = aBody(lCnt) & "</table></body></html>"

DataObj.GetFromClipboard
'strPaste = DataObj.GetText(1) 'Insert contents from clipboard to this variable so it can be added to email body

'create the email
Set olapp = CreateObject("Outlook.application")
Set olItem = olapp.CreateItem(0)


'olItem.Display
olItem.To = "[email protected]"
olItem.Subject = "AGC Battery Notification"
olItem.HTMLBody = Join(aBody, vbNewLine)  '"<p><font face=""Times New Roman"" size=""3"" color=""red""><b>" & preamble & "</b></font></p><p></p>"
olItem.Display

End Sub

Here is the other module I'm using:

    Option Compare Database
Option Explicit

Public Declare PtrSafe Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal _
  bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

Private Const VK_SNAPSHOT = &H2C

Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long

Private Declare PtrSafe Function GetClipboardData Lib "user32" (ByVal wFormat As Integer) As Long

Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long

Private Declare PtrSafe Function OleCreatePictureIndirect Lib "olepro32.dll" _
(PicDesc As uPicDesc, RefIID As GUID, ByVal fPictureOwnsHandle As Long, _
IPic As IPicture) As Long

'\\ Declare a UDT to store a GUID for the IPicture OLE Interface
Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type

'\\ Declare a UDT to store the bitmap information
Private Type uPicDesc
    Size As Long
    type As Long
    hPic As Long
    hPal As Long
End Type

Private Const CF_BITMAP = 2
Private Const PICTYPE_BITMAP = 1

Sub PrintScreen()
    keybd_event VK_SNAPSHOT, 1, 0, 0
End Sub
Public Sub MyPrintScreen(FilePathName As String)

    Call PrintScreen

    Dim IID_IDispatch As GUID
    Dim uPicinfo As uPicDesc
    Dim IPic As IPicture
    Dim hPtr As Long
    
        Dim DataObj As MSForms.DataObject
    Set DataObj = New MSForms.DataObject

    OpenClipboard 0
    hPtr = GetClipboardData(CF_BITMAP)
    CloseClipboard

    '\\ Create the interface GUID for the picture
    With IID_IDispatch
        .Data1 = &H7BF80980
        .Data2 = &HBF32
        .Data3 = &H101A
        .Data4(0) = &H8B
        .Data4(1) = &HBB
        .Data4(2) = &H0
        .Data4(3) = &HAA
        .Data4(4) = &H0
        .Data4(5) = &H30
        .Data4(6) = &HC
        .Data4(7) = &HAB
    End With

    '\\ Fill uPicInfo with necessary parts.
    With uPicinfo
        .Size = Len(uPicinfo) '\\ Length of structure.
        .type = PICTYPE_BITMAP '\\ Type of Picture
        .hPic = hPtr '\\ Handle to image.
        .hPal = 0 '\\ Handle to palette (if bitmap).
    End With

   '\\ Create the Range Picture Object
   OleCreatePictureIndirect uPicinfo, IID_IDispatch, True, IPic

    '\\ Save Picture Object
    stdole.SavePicture IPic, FilePathName
    
        Dim oPic
On Error Resume Next
Set oPic = Clipboard.GetData
On Error GoTo 0
If oPic Is Nothing Then
  MsgBox "No image"
Else
  SavePicture oPic, "C:\pic.jpg"
End If
    

End Sub

This runs with no errors. I can take a screenshot from the form and see that it is in the clipboard. It creates the new email, but doesn't paste, and doesn't save to my machine anywhere. But when I manually do CTRL V/Paste, it pasted into the email just fine but I can't get VBA to do that on initial creation. SavePicture oPic runs without errors but doesn't actually do anything. There is no "no image" message that pops up. I've tried defining a FilePathName, but that also just does nothing.

Picture of screenshot in the clipboard

Currently this code produces an email like this which removes my formatting. If I delete the table it pulls in and hit paste, it brings in my screenshot:

Desired result

Here are all of the references I am using

I'm super stuck here, I feel like it's so close to working but I can't figure it out. Any help with this is appreciated and thanks in advance.

CodePudding user response:

At long last, I have gotten it to work. It wasn't just a single thing that worked, it was several minor things so I will post the new code that does the job. Keep in mind that this code is based on an Access form with one button to take the screenshot, and one button to send the email. It is also not perfect; it sometimes takes a screenshot of a random part of the screen, so I have to click on my form and make sure it's active before trying again. I also sometimes get memory errors and the screenshot shows up in the path, but it's broken. However, when it does work it works fine, and I'm sure all of these issues can be solved so I am going to mark this post solved. Here is my working code and I will note the changes at the end. This is the code behind the form itself:

        Option Compare Database
    Option Explicit

    Private Sub Command15_Click()
    
    Screenshot.MyPrintScreen ("C:\Temp\test.jpg")
    
End Sub
            
   
Public Sub Command4_Click()

    Dim oApp As Outlook.Application
    Dim oEmail As MailItem
    Dim colAttach As Outlook.Attachments
    Dim oAttach As Outlook.Attachment

    'create new Outlook MailItem
    Set oApp = CreateObject("Outlook.Application")
    Set oEmail = oApp.CreateItem(olMailItem)

    'add graphic as attachment to Outlook message
    'change path to graphic as needed
    Set colAttach = oEmail.Attachments
    Set oAttach = colAttach.Add("C:\temp\test.jpg")
    oEmail.Close olSave

    'change the src property to 'cid:your picture filename'
    'it will be changed to the correct cid when its sent.
    oEmail.HTMLBody = "<BODY><IMG src=""cid:test.jpg"" </BODY>"
    oEmail.Save
    oEmail.To = "[email protected]"
    oEmail.Subject = "test"
    oEmail.Display
    'oEmail.Send

    Set oEmail = Nothing
    Set colAttach = Nothing
    Set oAttach = Nothing
    Set oApp = Nothing

End Sub

And the code for the screenshot module:

    Option Compare Database
Option Explicit


Public Declare PtrSafe Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal _
  bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

Private Const VK_SNAPSHOT = &H2C

Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long

Private Declare PtrSafe Function GetClipboardData Lib "user32" (ByVal wFormat As Integer) As Long

Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long

Private Declare PtrSafe Function OleCreatePictureIndirect Lib "oleaut32.dll" (PicDesc As uPicDesc, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long

'\\ Declare a UDT to store a GUID for the IPicture OLE Interface
Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type

'\\ Declare a UDT to store the bitmap information
Private Type uPicDesc
    Size As Long
    type As Long
    hPic As Long
    hPal As Long
End Type

Private Const CF_BITMAP = 2
Private Const PICTYPE_BITMAP = 1

Sub PrintScreen()
    keybd_event VK_SNAPSHOT, 1, 0, 0
End Sub
Public Sub MyPrintScreen(FilePathName As String)

    Call PrintScreen

    Dim IID_IDispatch As GUID
    Dim uPicinfo As uPicDesc
    Dim IPic As IPicture
    Dim hPtr As Long
    
        Dim DataObj As MSForms.DataObject
    Set DataObj = New MSForms.DataObject

    OpenClipboard 0
    hPtr = GetClipboardData(CF_BITMAP)
    CloseClipboard

    '\\ Create the interface GUID for the picture
    With IID_IDispatch
        .Data1 = &H7BF80980
        .Data2 = &HBF32
        .Data3 = &H101A
        .Data4(0) = &H8B
        .Data4(1) = &HBB
        .Data4(2) = &H0
        .Data4(3) = &HAA
        .Data4(4) = &H0
        .Data4(5) = &H30
        .Data4(6) = &HC
        .Data4(7) = &HAB
    End With

    '\\ Fill uPicInfo with necessary parts.
    With uPicinfo
        .Size = Len(uPicinfo) '\\ Length of structure.
        .type = PICTYPE_BITMAP '\\ Type of Picture
        .hPic = hPtr '\\ Handle to image.
        .hPal = 0 '\\ Handle to palette (if bitmap).
    End With

   '\\ Create the Range Picture Object
   OleCreatePictureIndirect uPicinfo, IID_IDispatch, True, IPic

    '\\ Save Picture Object
    stdole.SavePicture IPic, FilePathName


Set DataObj = Nothing


End Sub

Out of the blue I had an error for a missing olepro32.dll. Several scans later telling me there was nothing wrong, I ended up having to make it oleaut32.dll and that was a step in the right direction. Also note that you have to do some extra stuff in order to get the img HTML embedding to work, I ended up redoing that entire section and replacing it with code found from [this][8] other post.

Next I had to delete this part:

            Dim oPic
On Error Resume Next
Set oPic = Clipboard.GetData
On Error GoTo 0
If oPic Is Nothing Then
  MsgBox "No image"
Else
  SavePicture oPic, "C:\pic.jpg"
End If

That stackoverflow I linked originally says it was needed to save, but it was already being done by the rest of the procedure and it was causing errors. I just deleted it entirely. I began setting objects to none to help with the memory problems.

  • Related