ADO потокът не успява да запази BLOB, зареден от базата данни

ВЪВЕДЕНИЕ И СЪОТВЕТНА ИНФОРМАЦИЯ:

Написах демо приложение в опита си да науча как да използвам ADO потоци. Всичко работи добре, освен в един специален случай. Позволете ми да започна, като дам подходяща информация:

Използвам WinAPI за създаване на GUI и зареждам (и го записвам на диска)/вмъквам BLOB при натискане на бутон. Бутоните са в диалогов прозорец.

ПРОБЛЕМ:

Когато натисна бутона за вмъкване на BLOB в базата данни, виждам как се вмъква (държа MS Access отворен). Ако се опитам да заредя съществуващ BLOB от базата данни, нищо не се случва, но не се сигнализират грешки.

Въпреки това, ако извърша вмъкване, след което затворя и отворя отново диалогов прозорец, натискането на бутона за зареждане на BLOB и запазването му на диск работи добре.

Работя с Windows 8.1, използвайки Visual Studio 2013 на лаптоп с двуядрен процесор x64 (това може да е важно да се спомене). Тествах приложението си на дял C:\, където е операционната система, просто реших да спомена, в случай че е подходящо.

РЕДАКТИРАНЕ:

Изпробвах приложението си на дял D и работи. Това означава, че проблемът е някъде в разрешенията. Можете ли да ми помогнете с това, защото нямам представа как да започна да решавам това?

КРАЙ НА РЕДАКТИРАНЕ

SSCCE:

За да ви помогнем още повече, следвайте внимателно инструкциите, за да създадете много минимален примерен код, който възпроизвежда проблема:

1.) Създайте default Win32 project във Visual Studio.

2.) В stdafx.h добавете следното точно под #include <windows.h>:

#include <comutil.h>
#include <stdio.h>
#include <ole2.h>
#include <string>
#include <shlwapi.h>
#pragma comment( lib, "comsuppw.lib")
#pragma comment( lib, "shlwapi.lib")

3.) Добавете следното във вашия основен .cpp файл точно под #include директиви:

#import <C:\\Program Files\\Common Files\\System\\ado\\msado15.dll>  \
    rename( "EOF", "AdoNSEOF" )

4.) добавете следната функция над процедурата на диалоговия прозорец About:

BOOL OpenFile(HWND hDlg, LPWSTR szFileName, LPWSTR szFilter)
{
    // prepare OPENFILE dialog 
    OPENFILENAME ofn;

    memset(&ofn, 0, sizeof(ofn));
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = hDlg;
    ofn.lpstrFilter = szFilter;
    ofn.lpstrFile = szFileName;
    ofn.lpstrFile[0] = L'\0';
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST |
        OFN_DONTADDTORECENT | OFN_HIDEREADONLY |
        OFN_PATHMUSTEXIST;
    ofn.lpstrDefExt = L".pdf";

    if (GetOpenFileName(&ofn))
        return TRUE;

    return FALSE;
}

5.) Заменете диалоговата процедура About с това:

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    // connection string
    static wchar_t bstrConnect[MAX_PATH];
    switch (message)
    {
    case WM_INITDIALOG:
    {
        // create connection string by choosing database 
        wchar_t szFile[MAX_PATH];
        OpenFile(hDlg, szFile, L".accdb\0");
        swprintf_s(bstrConnect, MAX_PATH, 
            L"Provider=Microsoft.ACE.OLEDB.12.0; Data Source = %s;", szFile);
    }
         return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDC_BUTTON1)  // save BLOB to database
        {
            wchar_t szFileName[MAX_PATH] = L"";

            if (OpenFile(hDlg, szFileName, L"All files\0*.*"))
            {
                try
                {
                    HRESULT hr = CoInitialize(NULL);

                    ADODB::_ConnectionPtr pConn(L"ADODB.Connection");
                    ADODB::_RecordsetPtr pRS(L"ADODB.Recordset");

                    hr = pConn->Open(bstrConnect, 
                        L"", L"", ADODB::adConnectUnspecified);

                    // create new recordset 
                    pRS->Open( L"test", _variant_t((IDispatch*)pConn, true),
                        ADODB::adOpenKeyset, 
                        ADODB::adLockOptimistic, 
                        ADODB::adCmdTable);

                    // create stream object
                    ADODB::_StreamPtr pStream(L"ADODB.Stream");
                    // set stream type
                    pStream->Type = ADODB::adTypeBinary;
                    // missing parameter for Open 
                    _variant_t varOptional(DISP_E_PARAMNOTFOUND, VT_ERROR);  
                    // open stream
                    pStream->Open(varOptional, 
                        ADODB::adModeUnknown, 
                        ADODB::adOpenStreamUnspecified,
                        _bstr_t(L""), _bstr_t(L""));

                    // open selected file
                    hr = pStream->LoadFromFile(szFileName);

                    // error checking
                    if (FAILED(hr))
                        throw _com_error(hr);

                    // add new blank  record
                    pRS->AddNew();
                    // position stream to beginning
                    pStream->Position = 0;
                    // insert data to recordset

                    PathStripPath(szFileName);  // leave only filename

                    pRS->Fields->GetItem(L"tip")->Value = szFileName;
                    pRS->Fields->GetItem(L"field")->Value =
                        pStream->Read(ADODB::adReadAll);

                    // insert data
                    pRS->Update();

                    //cleanup
                    pRS->Close();
                    pConn->Close();
                    pStream->Close();
                    CoUninitialize();
                    MessageBeep(0);
                }
                catch (_com_error e)
                {
                    MessageBox(hDlg, (LPWSTR)e.Description(), L"", 0);
                }
            }
        }
        if (LOWORD(wParam) == IDC_BUTTON3)  // load BLOB and save it into disk
        {
            try
            {
                // napravi upit

                HRESULT hr = CoInitialize(NULL);

                ADODB::_ConnectionPtr pConn(L"ADODB.Connection");
                ADODB::_RecordsetPtr pRS(L"ADODB.Recordset");

                hr = pConn->Open(bstrConnect, L"", L"", ADODB::adConnectUnspecified);

                // create stream object
                ADODB::_StreamPtr pStream(L"ADODB.Stream");
                // set stream type
                pStream->Type = ADODB::adTypeBinary;
                // missing parameter for Open
                _variant_t varOptional(DISP_E_PARAMNOTFOUND, VT_ERROR); 
                // open stream 
                pStream->Open(varOptional, 
                    ADODB::adModeUnknown, 
                    ADODB::adOpenStreamUnspecified,
                    _bstr_t(L""), _bstr_t(L""));

                // primary key is taken from edit control
                wchar_t query[200] = L"";
                swprintf_s(query, 200, L"select tip, field from test where ID = %d;",
                    (int)GetDlgItemInt(hDlg, IDC_EDIT1, FALSE, FALSE));

                pRS->Open(query, _variant_t((IDispatch *)pConn, true),
                    ADODB::adOpenUnspecified, ADODB::adLockPessimistic,
                    ADODB::adCmdText);

                if (NULL == pRS)
                    MessageBox(hDlg, L"Empty recordset!", L"", 0);

                hr = pStream->Write(pRS->Fields->GetItem(L"field")->Value);

                if (FAILED(hr))
                    MessageBox(hDlg, L"stream write failed", L"", 0);

                // save to file
                // store this file on disk
                // int he same place our app is running
                wchar_t szFileName[MAX_PATH] = L"";

                swprintf_s(szFileName, MAX_PATH, L".\\%s",
                    pRS->Fields->GetItem(L"tip")->Value.bstrVal);

                hr = pStream->SaveToFile(szFileName, ADODB::adSaveCreateOverWrite);

                if (FAILED(hr))
                    MessageBox(hDlg, L"save to file failed", L"", 0);

                //cleanup
                pRS->Close();
                pConn->Close();
                pStream->Close();
                CoUninitialize();
                MessageBeep(0);
            }
            catch (_com_error e)
            {
                MessageBox(hDlg, (LPWSTR)e.Description(), L"", 0);
            }
        }
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

7.) Преработете диалоговия прозорец About в редактора rc, както е показано по-долу:

въведете описание на изображението тук

  • Контролът на завъртане има набор от стилове auto buddy, right align, andset приятелски цели числа
  • бутоните са прости бутони, нищо особено
  • контролът за редактиране е прост, нищо особено

8.) Създайте база данни на MS Access със следните полета:

  • Име на таблицата: тест
  • Първо поле: ID autonumber, primary key
  • Второ поле: поле OLE Object
  • Трето поле: съвет text това поле съхранява името на файла ( file.extension)

КАК ДА ИЗПОЛЗВАТЕ ТОВА ПРИЛОЖЕНИЕ:

Когато стартирате приложение, отворете About елемент от менюто, за да може да изскочи диалогов прозорец. Изберете база данни, която създадохме.

Щракнете върху левия бутон (от синия кръг в изпратеното преди това изображение). Изскача диалогов прозорец за отваряне на файл и можете да изберете произволен файл (пробвал съм с .pdf, .exe, .zip и jpeg). Файлът трябва да бъде вмъкнат в базата данни.

За да прочетете BLOB и да го запишете на същото място, на което работи вашето приложение, въведете номера на записа, който искате да извлечете, и натиснете десния бутон (от червения кръг в изпратената преди това снимка). BLOB трябва да се зареди и след това да се запази на същото място, където е вашето приложение.

ЗАКЛЮЧИТЕЛНИ БЕЛЕЖКИ:

Това не е производствен код, моля, имайте предвид това. Това е само най-малкият, най-прост пример за код, който илюстрира проблема.

Отново, ако вмъкнете BLOB и след това се опитате да го заредите, нищо не се случва, но кодът се изпълнява без грешка.

Ако вмъкнете BLOB, след това затворете диалоговия прозорец, отворете диалоговия прозорец отново и след това опитайте да заредите BLOB от базата данни, всичко работи добре.

ВЪПРОС:

Как мога да пренапиша кода си, за да мога да вмъкна и след това да заредя BLOB, без да прибягвам до „решение №2“, описано по-рано.

РЕДАКТИРАНЕ:

Изпробвах приложението си на дял D и работи. Това означава, че проблемът е някъде в разрешенията. Можете ли да ми помогнете с това, защото нямам представа как да започна да решавам това?

КРАЙ НА РЕДАКТИРАНЕ

Благодаря ти.


person AlwaysLearningNewStuff    schedule 03.12.2014    source източник


Отговори (1)


Обикновено, когато реша проблема сам и няма отговори, просто изтривам въпроса. Въпреки това, публикуваният код може да бъде чудесен работещ пример за бъдещите поколения.

Проблемът беше в моята конструкция на пътя на файла. След като пренаписах моя код, както е показано по-долу, всичко работи. Ето пълната диалогова процедура:

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    // connection string
    static wchar_t bstrConnect[MAX_PATH];
    switch (message)
    {
    case WM_INITDIALOG:
    {
        wchar_t szFile[MAX_PATH];
        OpenFile(hDlg, szFile, L".accdb\0");
        swprintf_s(bstrConnect, MAX_PATH, 
            L"Provider=Microsoft.ACE.OLEDB.12.0; Data Source = %s;", szFile);
    }
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDC_BUTTON1:
        {
            wchar_t szFileName[MAX_PATH] = L"";

            if (OpenFile(hDlg, szFileName, L"All files\0*.*"))
            {
                try
                {
                    // disable write button, just in case, until we finish inserting
                    EnableWindow(GetDlgItem(hDlg, IDC_BUTTON3), FALSE);

                    HRESULT hr = CoInitialize(NULL);

                    ADODB::_ConnectionPtr pConn(L"ADODB.Connection");
                    ADODB::_RecordsetPtr pRS(L"ADODB.Recordset");

                    hr = pConn->Open(bstrConnect, L"", L"", 
                        ADODB::adConnectUnspecified);
                    // create new recordset 
                    pRS->Open(L"test", _variant_t((IDispatch*)pConn, true),
                        ADODB::adOpenKeyset, ADODB::adLockOptimistic, 
                        ADODB::adCmdTable);
                    // create stream object
                    ADODB::_StreamPtr pStream(L"ADODB.Stream");
                    // set stream type
                    pStream->Type = ADODB::adTypeBinary;
                    // missing parameter
                    _variant_t varOptional(DISP_E_PARAMNOTFOUND, VT_ERROR);  
                    // open stream
                    pStream->Open(varOptional, ADODB::adModeUnknown,
                        ADODB::adOpenStreamUnspecified, _bstr_t(L""), _bstr_t(L""));
                    // open selected file
                    hr = pStream->LoadFromFile(szFileName);
                    if(FAILED(hr))
                        throw _com_error(hr);
                    // add new blank  record
                    pRS->AddNew();
                    // position stream
                    pStream->Position = 0;
                    // insert data to recordset
                    PathStripPath(szFileName);  // leave only filename
                    pRS->Fields->GetItem(L"tip")->Value = szFileName; // store filename
                    pRS->Fields->GetItem(L"field")->Value =
                        pStream->Read(ADODB::adReadAll); // store BLOB
                    // insert data
                    pRS->Update();
                    //cleanup
                    pRS->Close();
                    pConn->Close();
                    pStream->Close();
                    CoUninitialize();
                    // now is safe to read BLOB from database so enable load button
                    EnableWindow(GetDlgItem(hDlg, IDC_BUTTON3), TRUE);
                    // make a sound just so we know we made it! :)
                    MessageBeep(0);
                }
                catch (_com_error e)
                {
                   MessageBox(hDlg, (LPWSTR)e.Description(), L"", 0);
                }
            }
        }
            break;
       case IDC_BUTTON3:
        {
            try
            {
                HRESULT hr = CoInitialize(NULL);

                if (FAILED(hr))
                    throw _com_error(hr);

                ADODB::_ConnectionPtr pConn(L"ADODB.Connection");
                ADODB::_RecordsetPtr pRS(L"ADODB.Recordset");

                hr = pConn->Open(bstrConnect, L"", L"", ADODB::adConnectUnspecified);

                if (FAILED(hr))
                {
                    MessageBox(hDlg, L"connection error", L"", 0);
                    throw _com_error(hr);
                }


                // create stream object
                ADODB::_StreamPtr pStream(L"ADODB.Stream");
                // set stream type
                pStream->Type = ADODB::adTypeBinary;
                // missing parameter
                _variant_t varOptional(DISP_E_PARAMNOTFOUND, VT_ERROR);  
                hr = pStream->Open(varOptional, ADODB::adModeUnknown,
                    ADODB::adOpenStreamUnspecified, _bstr_t(L""), _bstr_t(L""));

                if (FAILED(hr))
                {
                    MessageBox(hDlg, L"connection error", L"", 0);
                    throw _com_error(hr);
                }

                wchar_t query[200] = L"";
                BOOL ok = true;  // needed for error checking ( GetDlgInt -> see the docs )

                swprintf_s(query, 200, L"select tip, field from test where ID = %d;",
                    (int)GetDlgItemInt(hDlg, IDC_EDIT1, &ok, FALSE));

                if (!ok)
                    MessageBox(hDlg, L"dlgitemint failed", L"", 0);

                hr = pRS->Open(query, _variant_t((IDispatch *)pConn, true),
                    ADODB::adOpenUnspecified, ADODB::adLockPessimistic,
                    ADODB::adCmdText);

                if (FAILED(hr))
                {
                    MessageBox(hDlg, L"connection error", L"", 0);
                    throw _com_error(hr);
                }

                if (NULL == pRS)
                {
                    MessageBox(hDlg, L"NULL pRS!", L"", 0);
                    throw _com_error(hr);
                }

                if (pRS->BOF && pRS->AdoNSEOF)
                    MessageBox(hDlg, L"empty rs", L"", 0);

                hr = pStream->Write(pRS->Fields->GetItem(L"field")->Value);

                if (!pStream->GetSize())
                    MessageBox(hDlg, L"stream write failed", L"", 0);

                if (FAILED(hr))
                {
                    MessageBox(hDlg, L"stream write failed", L"", 0);
                    throw _com_error(hr);
                }

                // save to file
                wchar_t szFileName[MAX_PATH] = L"";

                // this returns directory where our exe is running
                GetModuleFileName(NULL, szFileName, MAX_PATH);
                // now we remove name and extension of the exe file
                PathRemoveFileSpec(szFileName);
                // add backslash at the end
                PathAddBackslash(szFileName);
                // now we "glue" the name of our BLOB ( we stored it into DB )
                wcscat_s(szFileName, MAX_PATH,
                   pRS->Fields->GetItem(L"tip")->Value.bstrVal);

                if (!wcslen(szFileName))
                    MessageBox(hDlg, L"load field name failed", L"", 0);

                hr = pStream->SaveToFile(szFileName, ADODB::adSaveCreateOverWrite);

                if (FAILED(hr))
                {
                    MessageBox(hDlg, L"save to file failed", L"", 0);
                    throw _com_error(hr);
                }

                //cleanup
                pRS->Close();
                pConn->Close();
                pStream->Close();
                CoUninitialize();
                // beep so we know all went well
                MessageBeep(0);
            }
            catch (_com_error e)
            {
                MessageBox(hDlg, (LPWSTR)e.Description(), L"", 0);
            }
        }
            break;
        case IDOK:
        case IDCANCEL:
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }    
            break;
        default:
            break;
        }
    }
    return (INT_PTR)FALSE;
}

Това беше само тестов код, но все пак важната част за мен работи (вмъкване или зареждане + запазване на BLOB). Надяваме се, че ще бъде полезно за бъдещите читатели.

В случай, че други членове имат предложения за подобряване на частта от кода, която чете/пише BLOB, моля, оставете коментар. Градивната критика винаги е добре дошла.

Пренаписах кода, така че почти всеки файл да може да се съхранява. Тествах успешно GIF, PDF, DOCX, XLSX, JPEG, BMP, EXE и TXT. Също така стартирах приложението си на USB и то също работи добре.

person AlwaysLearningNewStuff    schedule 03.12.2014