Click here to Skip to main content
15,399,959 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a function that uses ODBC to read access records.. via MFC OBDC... WORKS great!!!

I want; if a user selects a CListCtrl item, get the item, get the database item record, but when I get there it only show me the attachment name of Ok2.jpg

How to get the attachment file? or path to it? or something so I can load it into a CStatic Ctrl?

SO....

C++
 aDB.Open();
 
CString astr;
 if (aDB.GetRecordCount())
 {
     do
     {
         if (!aDB.m_Type.CollateNoCase(strTypes))
         {
             CString strName =aDB.m_Picture;
              /// NO PATH TO FILE????? or how to copy it? 
//All I GET HERE IS JUST "ok2.jpg" how to get the file ?
         }
         aDB.MoveNext();
     } while (!aDB.IsEOF());
 }
 aDB.Close();




I was trying this but I cannot get access to anything valid via the Record set.

What I have tried:

I was looking at this:
long lngSize = nSize = pRst.m_nFields->Item["Data"]->ActualSize;

But the Recordset does not accept these
pRst.m_nFields.xxx or pRst.m_nFields->

//Helper variable for retrieving image data
long lngOffSet = 0;
CDBVariant aVar;
long lngSize = nSize = pRst.m_nFields->Item["Data"]->ActualSize;  //**ERROR

const long ChunkSize = 50;
_variant_t varChunk;
UCHAR chData;
HRESULT hr;
long lBytesCopied = 0;
*lpData = new unsigned char[lngSize];

//Retrieveing data from vararray
while (lngOffSet < lngSize)
{
    try
    {
        //Get 50 size long chunk from database
        //varChunk = pField->GetChunk(ChunkSize);
        varChunk = pRst->Fields->Item["Data"]->GetChunk(ChunkSize);


        //putting chunk in to safe array
        for (long lIndex = 0; lIndex <= (ChunkSize - 1); lIndex++)
        {
            hr = SafeArrayGetElement(varChunk.parray, &lIndex, &chData);
            if (SUCCEEDED(hr))
            {
                ((UCHAR*)(*lpData))[lBytesCopied] = chData;
                lBytesCopied++;
            }
            else
                break;
        }
        lngOffSet += ChunkSize;
    }
    catch (_com_error &e)
    {
        CString sBuff = GetErrorDescription(e);
        AfxMessageBox(sBuff);
        return false;
    }
}
Posted
Updated 7-Apr-22 4:27am
v4
Comments
Shao Voon Wong 6-Apr-22 23:28pm
   
A database usually does not store images. They only store the image filenames. You go ask your manager or customer where the images are stored and for every image, combine the filename with that folder path to get the full path to retrieve it.
ninpo 7-Apr-22 10:23am
   
THis is a Microsoft Access database the attachment is internal to it, I just don't know how to get to it via C++/MFC
Richard MacCutchan 7-Apr-22 11:21am
   
You need to look at what the database is returning to you; that is not something you can change by code.
ninpo 7-Apr-22 14:20pm
   
It returns the text of the file it has in the attachment in this case returns ; Ok2.jpg

1 solution

You must use some more complex solution for it. When you want to show a picture you need some windows with complex code to load the picture. One problem is ofcourse getting the picture data, so you must provide some path where the code finds and loads the picture.
You also need to create some complex code with a background thread for doing it, because it may last a moment or fail which you may present some spinner, animation or load message.

tip: the function PostThreadMessage is sometimes useful when you message you main UI.
   
Comments
ninpo 8-Apr-22 10:17am
   
OK Yes I understand that, But, how to get the file or the address or extract it from Access??? I tried the VBA code in Access and that didn't work, it did not extract the files.

Some code or something is what I would like to see.


------------ VBA Code ----------------

Option Compare Database

Sub exportAttachments()

Dim strPath, fName, fldName, sName(3) As String
Dim rsPictures, rsDes As Variant
Dim rs As DAO.Recordset
Dim savedFile, i As Integer
savedFile = 0

strPath = Application.CurrentProject.Path

Set rs = CurrentDb.OpenRecordset("SELECT * FROM tblTools")

'Check to see if the recordset actually contains rows
If Not (rs.EOF And rs.BOF) Then
rs.MoveFirst 'Not required here, but still a good habit
Do Until rs.EOF = True
On Error Resume Next 'ignore errors

'Instantiate the child record set.
Set rsPictures = rs.Fields("Picture").Value
Set rsDes = rs.Fields("Name") 'use to name the picture later

'if no attachment available, go to next record
If Len(rsPictures.Fields("FileName")) = 0 Then
GoTo nextRS
End If
If rsPictures.RecordCount <> 0 Then
rsPictures.MoveLast
savedFile = rsPictures.RecordCount 'set savedFile = total no of attachments
End If
rsPictures.MoveFirst ' move to first attachment file

'WARNING: all of my attachments are picture with JPG extension.
'loop through all attachments
For i = 1 To savedFile 'rename all files and save
If Not rsPictures.EOF Then
fName = strPath & "\\Attachments\\" & rsDes & i & ".JPG"
rsPictures.Fields("FileData").SaveToFile fName
sName(i) = fName 'keep path in an array for later use
rsPictures.MoveNext
End If
Next i

'insert image name and path into database an edit
' rs.Edit

' If Len(sName(1)) <> 0 Then
' rs!PicPath1 = CStr(sName(1)) 'path
' rs!PicDes1 = Left(Dir(sName(1)), InStr(1, Dir(sName(1)), ".") - 1) 'file name without extension
' End If
' If Len(sName(2)) <> 0 Then
' rs!PicPath2 = CStr(sName(2))
' rs!PicDes2 = Left(Dir(sName(2)), InStr(1, Dir(sName(2)), ".") - 1)
' End If
' If Len(sName(3)) <> 0 Then
' rs!PicPath3 = CStr(sName(3))
' rs!PicDes3 = Left(Dir(sName(3)), InStr(1, Dir(sName(3)), ".") - 1)
' End If

' rs.Update 'update record
nextRS:
rsPictures.Close 'close attachment
savedFile = 0 'reset for next
fName = 0 'reset

'Move to the next record.
rs.MoveNext
Loop

Else
MsgBox "There are no records in the recordset."
End If

MsgBox "Attachments were exported!"

rs.Close 'Close the db recordsets
Set rs = Nothing 'Clean up

End Sub
ninpo 8-Apr-22 10:20am
   
Also if I do succeed in doing from a vba standpoint, is it possible to call that module from C++/MFC Application?
KarstenK 8-Apr-22 10:43am
   
a) you can try to use that code to write a (specially named) picture file which you can load in the C++ code. Create some small exe which you can execute from C++.

But anyway: if it is VBA you wont have good chances, but with VB.net would be better. But I think it is better and faster to rewrite that code in C++
ninpo 8-Apr-22 10:58am
   
Excellent, how to do that?

Because it "appears" that you cannot get C++/MFC to export attachments in a Microsoft database.
ninpo 8-Apr-22 11:13am
   
What about Exporting the VBA function as a API?
So if I make the VBA exportAttachment (long index)


Then in MFC,C++
Import the module exportAttachment (long index)

And use it?


void CDlg::OnBnClickedButtonGetAttachments()
{
long nRef=1;
long uReturnVal=-1;

long (*lpfnDllFunc1) (long ) = NULL;
// HINSTANCE hDLL; // Handle to DLL

HMODULE hDLL;
hDLL=LoadLibrary(_T("Access Database? or ?.dll"));

// hDLL=AfxLoadLibrary(_T("ToolLib.dll"));
if(hDLL != NULL)
{

lpfnDllFunc1 = (long (*)(long))GetProcAddress(hDLL,"VBA_ExportAttachment");


if (!lpfnDllFunc1)//
{
// handle the error
FreeLibrary(hDLL);
// AfxFreeLibrary(hDLL);
AfxMessageBox(_T("error loading function"));
}
else
{
// call the function ( just want the dlg to pop up)
uReturnVal = lpfnDllFunc1(nRef,);
if(uReturnVal==0)
AfxMessageBox(_T(" failed"));
}
}
FreeLibrary(hDLL);
// AfxFreeLibrary(hDLL);

}
ninpo 8-Apr-22 12:32pm
   
Apparently it is possible here, but i cannot get this to work in MFC?

https://www.codeproject.com/Articles/24969/An-MFC-picture-control-to-dynamically-show-picture?msg=5870920#xx5870920xx

The comment he has this:

bool CDataDlg::GetImageDataFromDB(unsigned char** lpData, long& nSize, _RecordsetPtr& pRst)
{
//Helper variable for retrieving image data
long lngOffSet = 0;
long lngSize = nSize = pRst->Fields->Item["Data"]->ActualSize;

const long ChunkSize = 50;
_variant_t varChunk;
UCHAR chData;
HRESULT hr;
long lBytesCopied = 0;
*lpData = new unsigned char[lngSize];

//Retrieveing data from vararray
while (lngOffSet < lngSize)
{
try
{
//Get 50 size long chunk from database
//varChunk = pField->GetChunk(ChunkSize);
varChunk = pRst->Fields->Item["Data"]->GetChunk(ChunkSize);


//putting chunk in to safe array
for (long lIndex = 0; lIndex <= (ChunkSize - 1); lIndex++)
{
hr = SafeArrayGetElement(varChunk.parray, &lIndex, &chData);
if (SUCCEEDED(hr))
{
((UCHAR*)(*lpData))[lBytesCopied] = chData;
lBytesCopied++;
}
else
break;
}
lngOffSet += ChunkSize;
}
catch (_com_error &e)
{
CString sBuff = GetDocument()->GetErrorDescription(e);
AfxMessageBox(sBuff);
return false;
}
}

return true;
}

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
Top Experts
Last 24hrsThis month


CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900