Поток ADO не может сохранить BLOB, загруженный из базы данных

ВВЕДЕНИЕ И СООТВЕТСТВУЮЩАЯ ИНФОРМАЦИЯ:

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

Я использую WinAPI для создания графического интерфейса и загружаю (и сохраняю его на диске)/вставляю 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 buddy integer`
  • кнопки простые кнопки, ничего особенного
  • Управление редактированием простое, ничего особенного

8.) Создайте базу данных MS Access со следующими полями:

  • Имя таблицы: тест
  • Первое поле: ID autonumber, primary key
  • Второе поле: поле OLE Object
  • Третье поле: tip 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