Loading and Saving Annotations Using Databases (C++ 5.0 and later)
Note: This topic is for Document/Medical only.
Take the following steps to start a project and to add some code that demonstrates adding annotation objects to or deleting annotation objects from the BLOB field of a database file.
You need to create a database file (DB1.MDB) with the fields listed below, before running this tutorial.
Field Name |
Type |
Image |
OleObject |
Size |
Number |
1. |
Start a new project as follows: |
|
|
Run Microsoft Visual C++ 5.0, select the File >New menu option, and do the following: |
|
|
a. |
Click the Projects tab. |
|
b. |
Select MFC AppWizard (exe) as the project type |
|
c. |
In the Project name text box, specify AnnBlob. |
|
d. |
In the Location text box, specify the path of the project. |
|
e. |
Click the OK button. |
2. |
In the Step 1 dialog box, do the following: |
|
|
a. |
Select Dialog based. |
|
b. |
Click the Next button. |
3. |
In the Step 2 of 4 dialog box, do the following: |
|
|
a. |
Ensure that About Box is selected. |
|
b. |
Ensure that 3D Controls is selected. |
|
c. |
Click the Next button. |
4. |
In the Step 3 of 4 dialog box, do the following: |
|
|
a. |
For comments, ensure that Yes, Please is selected. |
|
b. |
For how to use the MFC library, select Use MFC in a Shared DLL. |
|
c. |
Click the Next button. |
5. |
In the Step 4 of 4 dialog box, just click Finish. |
|
6. |
Read New Project Information, and click OK. (The AppWizard creates the project files and opens the project.) |
|
7. |
Add #include statements to your program so you can access the LEAD Class Library constants and classes: |
|
|
a. |
In the Project Workspace, click the FileView tab. |
|
b. |
Double-click the AnnBlob files folder to open it. |
|
c. |
Double-click the Header Files folder to open it. |
|
d. |
Double-click the StdAfx.h file to edit it. |
|
Add the following lines to the end of the file (keep in mind, you may have to change the path to where the header files reside): |
#include ""..\..\..\..\include\ClassLib\ltWrappr.h
8. |
Add #import statements to your program so you can access the Microsoft ActiveX Data Object 2.6 Library: |
|
|
a. |
In the Project Workspace, click the FileView tab. |
|
b. |
Double-click the AnnBlob files folder to open it. |
|
c. |
Double-click the Header Files folder to open it. |
|
d. |
Double-click the StdAfx.h file to edit it. |
|
Add the following lines to the end of the file (keep in mind, you may have to change the path to where the .tlb files reside): |
#import <C:\\Windows\\system32\\msado26.tlb> rename("EOF", "ADOEOF")
using namespace ADODB
9. |
Add a Static Text control to the main window as follows: |
|
|
a. |
In the Project Workspace, click the ResourceView tab. |
|
b. |
Double-click the AnnBlob resources folder to open it. |
|
c. |
Double-click the Dialog folder to open it. |
|
d. |
Double-click IDD_ANNBLOB_DIALOG to design the form. |
|
e. |
Select the TODO... text control; then press the Delete key to delete it. |
|
f. |
Click the Static Text control icon, then size and position the control as you want it to appear at run time. |
10. |
Add six command buttons to your form and name them as follows: |
|
|
ID |
Caption |
|
IDC_CONNECT |
&Connect To Database |
|
IDC_DISCONNECT |
&Disconnect From Data Base |
|
IDC_ADDRECORD |
&Add Record |
|
IDC_DELETERECORD |
D&elete Record |
|
IDC_MOVENEXT |
Move &Next |
|
IDC_MOVEPREVIOUS |
Move &Previous |
11. |
Press Ctrl-F4 to close all windows back to the Project Workspace. |
12. |
Do the following to add m_btnAddRecord to the CAnnBlobDlg class and link the variable to the CButton control using dynamic data exchange: |
|
|
a. |
Press Ctrl-W. (The MFC ClassWizard dialog box appears.) |
|
b. |
Click the Member Variables tab. |
|
c. |
In the Class Name box, select CAnnBlobDlg. |
|
d. |
In the Control IDs list, select IDC_ADDRECORD. |
|
e. |
Click the Add Variable... button. |
|
f. |
Specify m_btnAddRecord as the variable name, and Control as the category. |
|
g. |
Click OK to close the dialog box, and click OK to close the MFC ClassWizard. |
13. |
Repeat step 12 for the other buttons, using the following variable names: |
|
|
ID |
Member Variable Name |
|
IDC_MOVEPREVIOUS |
m_btnMovePrevious |
|
IDC_MOVENEXT |
m_btnMoveNext |
|
IDC_DISCONNECT |
m_btnDisconnect |
|
IDC_DELETERECORD |
m_btnDeleteRecord |
|
IDC_CONNECT |
m_btnConnect |
14. |
Add the following Member Variables to the CAnnBlobDlg class: |
|
|
_ConnectionPtr |
m_pConnection; |
|
_RecordsetPtr |
m_pRecordset; |
|
LAnnotationWindow |
m_LAnnoWnd |
15. |
Go to the OnInitDialog() function as follows: |
|
|
a. |
In the Project Workspace, click the ClassView tab. |
|
b. |
Double-click the AnnBlob classes folder to open it. |
|
c. |
Expand the CAnnBlobDlg class. |
|
d. |
Double-click the OnInitDialog() function to edit it. |
16. |
Edit the OnInitDialog() function to add the following code after the line that says //TODO: Add extra initialization here: |
LBase::LoadLibraries(LT_KRN);
LBase::LoadLibraries(LT_DIS);
LBase::LoadLibraries(LT_FIL);
LBase::LoadLibraries(LT_ANN);
LSettings::UnlockSupport(L_SUPPORT_DOCUMENT, L_KEY_DOCUMENT);
RECT rc;
GetDlgItem(IDC_STATIC)->GetWindowRect(&rc);
ScreenToClient(&rc);
HWND hWnd = m_LAnnoWnd.CreateWnd(this->GetSafeHwnd(),0,
WS_VISIBLE|L_BS_CENTER|
L_BS_PROCESSKEYBOARD,
rc.left ,rc.top ,rc.right ,rc.bottom );
if (!m_LAnnoWnd.GetToolBar().IsCreated())
m_LAnnoWnd.GetToolBar().Create(hWnd, NULL, ANNTOOLALIGN_RIGHT | ANNTOOLALIGN_TOP, TRUE, NULL, NULL);
m_LAnnoWnd.EnableAutoScroll(TRUE);
m_LAnnoWnd.SetFileName (TEXT("C:\\parrots.jpg"));
int nRet = m_LAnnoWnd.Load (0,ORDER_BGR, 1);
HRESULT hr;
CoInitialize(NULL);
hr = m_pConnection.CreateInstance(__uuidof( Connection) );
m_pRecordset.CreateInstance(__uuidof(Recordset));
m_btnMoveNext.EnableWindow(FALSE);
m_btnMovePrevious.EnableWindow(FALSE);
m_btnAddRecord.EnableWindow(FALSE);
m_btnDeleteRecord.EnableWindow(FALSE);
17. |
Press Ctrl-W to go to the MFC Class Wizard; then do the following: |
|
|
a. |
Click the Message Maps tab. |
|
b. |
In the Class Name combo box, select CAnnBlobDlg. |
|
c. |
In the Object IDs list box, select IDC_CONNECT. |
|
d. |
In the Messages list box, select BN_CLICKED. |
|
e. |
Click the Add function button. Choose OK for the default function name (OnConnect). |
|
f. |
Click the Edit Code button and enter the following code: (you will have to change to file name to an existing database file name on your computer) |
HRESULT hr;
_bstr_t bstrQuery("SELECT * FROM Images");
_variant_t vNull;
vNull.vt = VT_ERROR ;
vNull.scode = DISP_E_PARAMNOTFOUND ;
if (m_pConnection->State == adStateClosed)
{
hr = m_pConnection->Open( _bstr_t(L"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=DB1.mdb;"),
_bstr_t(L""), _bstr_t(L""), adModeUnknown ) ;
}
if (SUCCEEDED(hr))
{
m_pRecordset->PutRefActiveConnection(m_pConnection);
hr = m_pRecordset->Open(_variant_t(bstrQuery), vNull, adOpenKeyset, adLockOptimistic, adCmdText );
if (SUCCEEDED(hr))
{
if(!m_pRecordset->GetBOF())
{
m_pRecordset->MoveFirst();
}
if (!m_pRecordset->GetADOEOF())
{
m_btnMoveNext.EnableWindow(TRUE);
m_btnMovePrevious.EnableWindow(TRUE);
m_btnDeleteRecord.EnableWindow(TRUE);
}
m_btnAddRecord.EnableWindow(TRUE);
}
}
18. |
Press Ctrl-W to go to the MFC Class Wizard; then do the following: |
|
|
a. |
Click the Message Maps tab. |
|
b. |
In the Class Name combo box, select CAnnBlobDlg. |
|
c. |
In the Object IDs list box, select IDC_DISCONNECT. |
|
d. |
In the Messages list box, select BN_CLICKED. |
|
e. |
Click the Add function button. Choose OK for the default function name (OnDisconnect). |
|
f. |
Click the Edit Code button and enter the following code: |
if (m_pConnection->State == adStateOpen)
{
m_pConnection->Close();
}
if (m_pRecordset->State == adStateOpen )
{
m_pRecordset->Close();
}
m_btnMoveNext.EnableWindow(FALSE);
m_btnMovePrevious.EnableWindow(FALSE);
m_btnAddRecord.EnableWindow(FALSE);
m_btnDeleteRecord.EnableWindow(FALSE);
19. |
Press Ctrl-W to go to the MFC Class Wizard; then do the following: |
|
|
a. |
Click the Message Maps tab. |
|
b. |
In the Class Name combo box, select CAnnBlobDlg. |
|
c. |
In the Object IDs list box, select IDC_ADDRECORD. |
|
d. |
In the Messages list box, select BN_CLICKED. |
|
e. |
Click the Add function button. Choose OK for the default function name (OnAddRecord). |
|
f. |
Click the Edit Code button and enter the following code: |
SAFEARRAYBOUND sabFldData[1];
VARIANT vData;
long ix[1];
L_UINT32 nSize ,i;
int nRet;
if (m_pConnection->State == adStateClosed )
{
MessageBox (TEXT("Connection closed"));
return;
}
m_pRecordset->AddNew();
HGLOBAL hMem = 0;
nRet = m_LAnnoWnd.GetContainerObject().SaveMemory (ANNFMT_NATIVE, false,&hMem,&nSize, NULL);
sabFldData[0].cElements = nSize ;
sabFldData[0].lLbound = 0;
SAFEARRAY * psaFieldData = SafeArrayCreate(VT_I1, 1, sabFldData);
BYTE * pData = (BYTE *)GlobalLock(hMem);
for (i = 0; i < nSize; ++i)
{
ix[0] = i;
long data = pData[i];
HRESULT hr = SafeArrayPutElement(psaFieldData, ix, &data);
}
VariantInit(&vData);
V_VT(&vData) = VT_ARRAY | VT_UI1;
vData.parray = psaFieldData;
m_pRecordset->Fields->Item[_variant_t( (long) 1)]->Value = _variant_t(vData);
m_pRecordset->Fields->Item[_variant_t( (long) 2)]->Value = _variant_t( (long) nSize);
m_pRecordset->Update();
if(m_pRecordset->RecordCount >= 0)
{
m_btnMoveNext.EnableWindow(TRUE);
m_btnMovePrevious.EnableWindow(TRUE);
m_btnDeleteRecord.EnableWindow(TRUE);
}
VariantClear(&vData);
GlobalUnlock(hMem);
GlobalFree(hMem);
20. |
Press Ctrl-W to go to the MFC Class Wizard; then do the following: |
|
|
a. |
Click the Message Maps tab. |
|
b. |
In the Class Name combo box, select CAnnBlobDlg. |
|
c. |
In the Object IDs list box, select IDC_DELETERECORD. |
|
d. |
In the Messages list box, select BN_CLICKED. |
|
e. |
Click the Add function button. Choose OK for the default function name (OnDeleteRecord). |
|
f. |
Click the Edit Code button and enter the following code: |
if (m_pConnection->State == adStateClosed )
{
MessageBox (TEXT("Connection closed"));
return;
}
if (!m_pRecordset->GetBOF() || !m_pRecordset->GetADOEOF())
{
m_pRecordset->MoveLast();
m_pRecordset->Delete(adAffectCurrent);
m_pRecordset->Update();
if(m_pRecordset->RecordCount <= 0)
{
m_btnMoveNext.EnableWindow(FALSE);
m_btnMovePrevious.EnableWindow(FALSE);
m_btnDeleteRecord.EnableWindow(FALSE);
}
}
21. |
Press Ctrl-W to go to the MFC Class Wizard; then do the following: |
|
|
a. |
Click the Message Maps tab. |
|
b. |
In the Class Name combo box, select CAnnBlobDlg. |
|
c. |
In the Object IDs list box, select IDC_MOVENEXT. |
|
d. |
In the Messages list box, select BN_CLICKED. |
|
e. |
Click the Add function button. Choose OK for the default function name (OnMoveNext). |
|
f. |
Click the Edit Code button and enter the following code: |
int nSize;
int nRet;
unsigned char *pData = NULL;
if (m_pConnection->State == adStateClosed )
{
MessageBox (TEXT("Connection closed"));
return;
}
if (!m_pRecordset->GetADOEOF())
{
m_pRecordset->MoveNext();
}
if (m_pRecordset->GetADOEOF())
{
MessageBox(TEXT("Unable to get the Annotation from db"));
m_pRecordset->MovePrevious();
}
else
{
variant_t varSize;
varSize = m_pRecordset->Fields->Item[2L]->Value;
nSize = varSize.intVal;
if(nSize)
{
_variant_t vData;
vData = m_pRecordset->Fields->Item[1L]->Value;
pData =(BYTE *) malloc(nSize);
memset(pData,0,nSize);
if(vData.vt == (VT_ARRAY | VT_UI1))
{
for(int i = 0 ; i < nSize ; ++i)
{
long data = 0;
SafeArrayGetElement(vData.parray , (long*) &i , &data);
pData[i] = (BYTE)data;
}
}
nRet = m_LAnnoWnd.GetContainerObject().LoadMemory(pData, nSize);
if(pData)
{
free(pData);
pData = NULL;
}
}
}
22. |
Press Ctrl-W to go to the MFC Class Wizard; then do the following: |
|
|
a. |
Click the Message Maps tab. |
|
b. |
In the Class Name combo box, select CAnnBlobDlg. |
|
c. |
In the Object IDs list box, select IDC_MOVEPREVIOUS. |
|
d. |
In the Messages list box, select BN_CLICKED. |
|
e. |
Click the Add function button. Choose OK for the default function name (OnMovePrevious). |
|
f. |
Click the Edit Code button and enter the following code: |
int nSize;
int nRet;
BYTE * pData = NULL;
if (m_pConnection->State == adStateClosed )
{
MessageBox (TEXT("Connection closed"));
return;
}
if (!m_pRecordset->GetBOF())
{
m_pRecordset->MovePrevious();
}
if (m_pRecordset->GetBOF())
{
MessageBox(TEXT("Unable to get the Annotation from db"));
m_pRecordset->MoveNext();
}
else
{
_variant_t varSize;
varSize = m_pRecordset->Fields->Item[2L]->Value;
nSize = varSize.intVal;
if(nSize)
{
_variant_t vData;
vData = m_pRecordset->Fields->Item[1L]->Value;
pData =(BYTE *) malloc(nSize);
memset(pData,0,nSize);
if(vData.vt == (VT_ARRAY | VT_UI1))
{
for(int i = 0 ; i < nSize ; ++i)
{
long data = 0;
SafeArrayGetElement(vData.parray , (long*) &i , &data);
pData[i] = (BYTE)data;
}
}
nRet = m_LAnnoWnd.GetContainerObject().LoadMemory((L_UCHAR L_FAR *)pData, nSize);
if(pData)
{
free(pData);
pData = NULL;
}
}
}
23. |
Press Ctrl-W to go to the MFC Class Wizard; then do the following: |
|
|
a. |
Click the Message Maps tab. |
|
b. |
In the Class Name combo box, select CAnnBlobDlg. |
|
c. |
In the Messages list box, select WM_CLOSE. |
|
d. |
Click the Add function button. Choose OK for the default function name (OnClose). |
|
e. |
Click the Edit Code button and enter the following code: |
if (m_LAnnoWnd.IsAllocated())
{
m_LAnnoWnd.Free();
}
LBase::UnloadLibraries(LT_KRN);
LBase::UnloadLibraries(LT_DIS);
LBase::UnloadLibraries(LT_FIL);
LBase::UnloadLibraries(LT_ANN);
CoInitialize(NULL);
24. |
On the main menu, select Build > Build AnnBlob.exe to build the project. |
25. |
On the main menu, select Build > Execute AnnBlob.exe to run the project. |