0

After consulting How to use webcam capture on a Microsoft Access form, I have a program where the user presses a button on an Excel form to open an Access form and take before & after photos using built-in webcam then save them to a predetermined folder. This works fine on several laptops including mine but when I try to run it on a tablet with front and back camera, it prompts me to choose between UNICAM Rear and UNICAM Front, which I presume means the code works fine and is connecting to the driver. However, the chosen camera doesn't connect; WM_CAP_DRIVER_CONNECT returns False and I get a black screen in the picture frame.

The tablet is an Acer One 10 running Win10 Home 32-bit and Access 365 Runtime. The I've tested the program using Access Runtime through Command Prompt on my laptop and it worked fine, I've checked that other apps are allowed to access the camera, nothing else is using the camera, tested 0 to 9 for WM_CAP_CONNECT parameters, changed LongPtr back to Long (which by the way still makes it work on win10 Pro 64-bit) and it still doesn't work.

I suspect it's an issue with the tablet and not the code since it's a company tablet and there are two cameras, perhaps I may be missing some permissions to connect to the camera via Access or the code doesn't work with two cameras, but I have no idea where to begin checking these.

I'm currently trying to find a laptop with two cameras to test the program on and in the meantime I'm totally lost and would appreciate suggestions for anything I could try to fix this problem, whether related to the code or not - though I would like to avoid running executables like CommandCam, seeing as I'm using company computers.

This is the part of my Excel code that affects opening Access:

Private Sub mainBtn_Click()
    Dim LCategoryID As Long
    Dim ShellCmd, LPath As String

    Dim wsMain, wsRec As Worksheet

    Set wsMain = Sheets("Main")
    Set wsRec = Sheets("Records")

    mainBtn.Enabled = False

    LPath = ThisWorkbook.Path + "\Database1.accdb"        

    If mainBtn.Caption <> "Record" Then
    If Dir(PathToAccess) <> "" And oApp Is Nothing Then
    ShellCmd = """" & PathToAccess & """ """ & LPath & """"
    VBA.Shell ShellCmd
    If oApp Is Nothing Then Set oApp = GetObject(LPath)
'    Set oApp = CreateObject("Access.Application")
    End If
    Application.Wait (Now + TimeValue("00:00:05"))

    On Error Resume Next
    oApp.OpenCurrentDatabase LPath
    oApp.Visible = False
    On Error GoTo 0
'passing a value through a sub on Access
    oApp.Run "getName", wsMain.Range("F5").Value
    End If
'before photo
    If mainBtn.Caption = "Before Photo" Then
    oApp.DoCmd.openform "Before Photo"
    mainBtn.Caption = "After Photo"
    mainBtn.Enabled = True

This is my code in Access:

Option Compare Database

Const WS_CHILD As Long = &H40000000
Const WS_VISIBLE As Long = &H10000000

Const WM_USER As Long = &H400
Const WM_CAP_START As Long = WM_USER

Const WM_CAP_DRIVER_CONNECT As Long = WM_CAP_START + 10
Const WM_CAP_DRIVER_DISCONNECT As Long = WM_CAP_START + 11
Const WM_CAP_SET_PREVIEW As Long = WM_CAP_START + 50
Const WM_CAP_SET_PREVIEWRATE As Long = WM_CAP_START + 52
Const WM_CAP_DLG_VIDEOFORMAT As Long = WM_CAP_START + 41
Const WM_CAP_FILE_SAVEDIB As Long = WM_CAP_START + 25

Private Declare PtrSafe Function capCreateCaptureWindow _
    Lib "avicap32.dll" Alias "capCreateCaptureWindowA" _
         (ByVal lpszWindowName As String, ByVal dwStyle As Long _
        , ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long _
        , ByVal nHeight As Long, ByVal hwndParent As LongPtr _
        , ByVal nID As Long) As Long

Private Declare PtrSafe Function SendMessage Lib "user32" _
    Alias "SendMessageA" (ByVal hWnd As LongPtr, ByVal wMsg As Long _
        , ByVal wParam As Long, ByRef lParam As Any) As Long

Private Declare Sub sapiSleep Lib "kernel32" _
        Alias "Sleep" _
        (ByVal dwMilliseconds As Long)

Dim hCap As LongPtr
Dim i As Integer
Private Sub cmd4_click()
' take picture
    Dim sFileName, sFileNameSub, dateNow, timeNow As String

    i = i + 1
    Call SendMessage(hCap, WM_CAP_SET_PREVIEW, CLng(False), 0&)

    dateNow = DateValue(Now)
    timeNow = TimeValue(Now)

    sFileName = CurrentProject.Path + "\dbimages\Before Change " + CStr(Year(dateNow)) + "." + CStr(Month(dateNow)) + "." + CStr(Day(dateNow)) + "  " + CStr(Hour(timeNow)) + "h" + CStr(Minute(timeNow)) + "m" + CStr(Second(timeNow)) + "s.jpg"
    Call SendMessage(hCap, WM_CAP_FILE_SAVEDIB, 0&, ByVal CStr(sFileName))
DoFinally:
    Call SendMessage(hCap, WM_CAP_SET_PREVIEW, CLng(True), 0&)

    If i = 4 Then
    MsgBox "4 pictures taken. Exiting"
     DoCmd.Close
    Else
    MsgBox "Picture " + CStr(i) + " Taken"
    End If

End Sub
Private Sub Cmd3_Click()
Dim temp As Long
temp = SendMessage(hCap, WM_CAP_DRIVER_DISCONNECT, 0&, 0&)
End Sub
Private Sub cmd1_click()
'    Dim connectAttempts As Integer
    Dim i As Integer
    hCap = capCreateCaptureWindow("Take a Camera Shot", WS_CHILD Or WS_VISIBLE, 0, 0, PicWebCam.Width, PicWebCam.Height, PicWebCam.Form.hWnd, 0)
    If hCap <> 0 Then
        'Call SendMessage(hCap, WM_CAP_DRIVER_CONNECT, 0, 0)

'        For i = 0 To 9
        If CBool(SendMessage(hCap, WM_CAP_DRIVER_CONNECT, 0, 0)) = False Then
'            connectAttempts = connectAttempts + 1
            MsgBox "Failed to connect Camera"
        Else
'        Exit For
        End If
'        Next i
        Call SendMessage(hCap, WM_CAP_SET_PREVIEWRATE, 45, 0&)
        Call SendMessage(hCap, WM_CAP_SET_PREVIEW, CLng(True), 0&)
    End If
End Sub
Private Sub Cmd2_Click()
'back to excel
'Dim temp As Long
'temp = SendMessage(hCap, WM_CAP_DLG_VIDEOFORMAT, 0&, 0&)
    DoCmd.Close
End Sub
Private Sub Form_Close()
Dim temp As Long
temp = SendMessage(hCap, WM_CAP_DRIVER_DISCONNECT, 0&, 0&)
'DoCmd.ShowToolbar "Ribbon", acToolbarYes
End Sub
Private Sub Form_Load()
'DoCmd.ShowToolbar "Ribbon", acToolbarNo
i = 0
cmd1.Caption = "Start Cam"
cmd2.Caption = "Done"
cmd3.Caption = "dummy"
cmd4.Caption = "Tak&e Picture"
    DoCmd.RunCommand acCmdAppMinimize
    DoCmd.Maximize
    If stnName = "Head 1" Or stnName = "Head 2" Then
    Pic1.Picture = CurrentProject.Path + "\images\head_s.jpeg"
    ElseIf stnName = "Marriage Head" Or stnName = "Plus Clip Head" Then
    Pic1.Picture = CurrentProject.Path + "\images\marriage_s.jpeg"
    Else
    Pic1.Picture = CurrentProject.Path + "\images\6pair_s.jpeg"
    End If
    cmd1_click
    On Error Resume Next
    CurrentProject.Application.Visible = True
End Sub

Private Sub sSleep(lngMilliSec As Long)
    If lngMilliSec > 0 Then
        Call sapiSleep(lngMilliSec)
    End If
End Sub

EDIT: Camera app works fine on tablet but I get a black screen in picture box when trying to use it through Access.

EDIT2: Code for WM_CAP_GET_STATUS

Added the following line in main module:

Const WM_CAP_GET_STATUS = WM_CAP_START + 54

Added the following in another module:

Type CAPSTATUS
  uiImageWidth As Long
  uiImageHeight As Long
  fLiveWindow As Long
  fOverlayWindow As Long
  fScale As Long
  ptScroll As POINTAPI
  fUsingDefaultPalette As Long
  fAudioHardware As Long
  fCapFileExists As Long
  dwCurrentVideoFrame As Long
  dwCurrentVideoFramesDropped As Long
  dwCurrentWaveSamples As Long
  dwCurrentTimeElapsedMS As Long
  hPalCurrent As Long
  fCapturingNow As Long
  dwReturn As Long
  wNumVideoAllocated As Long
  wNumAudioAllocated As Long
End Type

New code for starting camera:

Private Sub cmd1_click()
    Dim bool1, bool2, bool3 As Boolean
    Dim o As Integer
    Dim u As Integer
    Dim s As CAPSTATUS

    Open CurrentProject.Path + "\output.txt" For Output As #1

    i = 0 'global variable
    hCap = capCreateCaptureWindow("Take a Camera Shot", ws_child Or ws_visible, 0, 0, PicWebCam.Width, PicWebCam.Height, PicWebCam.Form.hwnd, 0)
    sSleep 5000
    If hCap <> 0 Then

        For i = 0 To 9
        Print #1, hCap
        bool1 = CBool(SendMessage(hCap, WM_CAP_DRIVER_CONNECT, i, 0))
        Print #1, bool1
        bool3 = SendMessage(hCap, WM_CAP_GET_STATUS, LenB(s), s)
        Print #1, bool3

            For u = 1 To 4
            If bool1 = True Then
            bool1 = SendMessage(hCap, WM_CAP_DRIVER_DISCONNECT, i, 0&)
            End If

            o = u * 7
            bool1 = CBool(SendMessage(hCap, WM_CAP_DRIVER_CONNECT, i, 0))
            Print #1, Tab(o); bool1
            bool3 = SendMessage(hCap, WM_CAP_GET_STATUS, LenB(s), s)
            Print #1, Tab(o); bool3
            Next u

        Call SendMessage(hCap, WM_CAP_SET_PREVIEWRATE, 45, 0&)
        Call SendMessage(hCap, WM_CAP_SET_PREVIEW, CLng(True), 0&)

        Next i
    End If
    Close #1
End Sub
  • Start by looking up the error you get: print `Err.LastDLLError` after `WM_CAP_DRIVERCONNECT` doesn't work and look it up in the [system error codes table](https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes). Also, never change back from `LongPtr` to `Long`, you're only introducing errors, and the only advantage is you can use it in Office 2007 and earlier, which are all unsupported. – Erik A Dec 18 '19 at 07:49
  • @ErikA Thanks, I'll do that and change to `LongPtr` again – user0038478193 Dec 18 '19 at 09:10
  • @ErikA I tested this part `hCap = capCreateCaptureWindow("Take a Camera Shot", WS_CHILD Or WS_VISIBLE, 0, 0, PicWebCam.Width, PicWebCam.Height, PicWebCam.Form.hWnd, 0)` sSleep `2000`Print #1, `hCap` If hCap <> 0 Then bool1 = `CBool(SendMessage(hCap, WM_CAP_DRIVER_CONNECT, 0, 0))` Print #1, `bool1` If bool1 = `False` Then Print #1, `Err.LastDllError` and my output was 329426, False, 0. Could I be getting 0 because it's Access Runtime or is there something else at work here? – user0038478193 Dec 19 '19 at 02:01
  • @ErikA you can't use `Err.LastDLLError` with `WM_CAP_DRIVER_CONNECT`, as avicap32 doesn't report error codes that way (or any way, for that matter). `Err.LastDLLError` is only meaningful for APIs that use `SetLastError()` to report error codes. If an API is not documented as supporting `GetLastError()` then you can't safely use it (or anything based on it) – Remy Lebeau Dec 19 '19 at 04:46
  • Which you choose, UNICAM Rear or UNICAM Front? Both them result in black screen? You can check Event Viewer to see if there is any related event and show error information. Refer to [here](https://answers.microsoft.com/en-us/windows/forum/all/random-black-screen-issue/4c0beeeb-a800-42bf-9d34-15a39e53c152) for how to check event. – Rita Han Dec 19 '19 at 06:43
  • @RitaHan-MSFT I want to use UNICAM Rear, I've tried both and both result in black screen. Will take a look at Event Viewer, thanks! – user0038478193 Dec 19 '19 at 09:04
  • @RitaHan-MSFT I've checked all the tabs in Event Viewer except the expanded Microsoft>Windows folders. There are no related events or error information at the time of occurrence, a black screen in the picture box probably wasn't major enough to warrant a log. – user0038478193 Dec 20 '19 at 02:49
  • This issue seems capture driver related. What you can do is trying to check the status of the capture window using [`WM_CAP_GET_STATUS`](https://learn.microsoft.com/en-us/windows/win32/multimedia/wm-cap-get-status) message. And have multiple attempts for each index of the capture driver and insert a delay between two attempt-connect to see if it is possible connecting successfully. – Rita Han Dec 20 '19 at 06:57
  • @RitaHan-MSFT I've now tried inserting 5 second sleep and 5 tries for each capture driver, what happens with index 0 is they prompt me to choose the camera each time and it fails, returning a false. Not sure if I'm using GET_STATUS properly - I'll put my code in an edit above. – user0038478193 Dec 24 '19 at 00:51
  • @user0038478193 `wParam` for `WM_CAP_GET_STATUS` is "Size, in bytes, of the structure referenced bys", in C++ it will be `sizeof(CAPSTATUS)` – Rita Han Dec 24 '19 at 07:12
  • @RitaHan-MSFT I've changed `wParam` to `LenB(s)` which should work for arguments that are not strings; got it to return true when I used it with my laptop but still fails on the tablet, it returns a false after each prompt of index 0. No prompts occur with the other indexes, they just return false. – user0038478193 Dec 26 '19 at 07:35

0 Answers0