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. |