Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[C++, WinAPI] Komunikacja z bazą danych MS Access

Ostatnio zmodyfikowano 2012-11-07 18:32
Autor Wiadomość
berkov
Temat założony przez niniejszego użytkownika
[C++, WinAPI] Komunikacja z bazą danych MS Access
» 2012-11-04 16:01:08
Witam,


Jestem strasznie uparty i lubie do wszystkiego dochodzic sam ale po 2 tyg przeszukiwania tysiaca postow na necie stwierdzilem ze najwyzszy czas sie poddac i poprosic o pomoc kogos innego.

Przeczytalem kurs programowania w WinApi (no prawie caly) i stwierdzilem ze najwyzszy czas przeniesc moj program z Excela (VBA) na form'ach do C++. Wiem, to troche smieszne jak ktos moze "programowac pod excelem" ale poczatkowo mial to byc malutki program ktory uzupelnia tabele w excelu z form (excel makra) na prosbe kumpla. Niestety co chwile mialem pytania czy nie moglbym "dolozyc malej rzeczy" i w ten oto sposob "niechcacy" stowrzylem program ksiegujacy, rozliczeniowy i archiwizujacy dzialajacy na kilkunastu form'ach pod Visual Basic, jako baza danych uzywam oczywiscie tabel excel bo taki temu od poczatku przyswiecal cel.

Niestety skonczyly mi sie mozliwosci VBA pod excelem i chcialbym zaprogramowac cos identycznego ale w C++ oraz na prawdziwej bazie dancyh (Access - by latwo eksportowac do excela).

doczytalem o ODBC i ADO... wybralem ADO ale wszystkei przyklady odnosza sie do sql a ja musze miec access.

dolaczylem #import "C:\Program Files\Common Files\System\ado\msado26.tlb" rename( "EOF", "ADOEOF" )
i ruszylem w boj!

so far so good, Visual Studio sie mnie nie przyczepilo i pozwolilo skompilowac wiec w WINAPI WinMain wrzucilem:
        
C/C++
HRESULT hr;
ADODB::_ConnectionPtr connection;
hr = connection.CreateInstance( __uuidof( ADODB::Connection ) );
ADODB::_RecordsetPtr recordset;
hr = recordset.CreateInstance( __uuidof( ADODB::Recordset ) );
smiga! zadne bledy, bajer!!

no to owieram polaczenie:
connection->CursorLocation = ADODB::adUseClient;
i????

i przy kompilacji dostaje blad!!!:
Unhandled exception at at 0xXXXXXXXX in XXXXX.exe Microsoft C++ exception:
_com_error at memory location 0xYYYYYYYY.

i nie działa, za ogolny blad zebym doszedl do tego co jest zle.

nie mowie juz ze dalsza czesc kodu sie oczywiscie nie wykonuje nawet:
hr = connection->Open( L"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\My Documents\\C++\\WinApiADO\\baza.accdb;", L"", L"", ADODB::adConnectUnspecified );


Pytanie:
czy ktos wie dlaczego? plakac mi sie juz chce... hehehe...


Pytanie 2:
czy ktos wie jak pozniej uzyc podobnie recordset (jak nizej) ale dla accessa a nie sql? cos takiego skopiowalem z netu ale widze ze to ewidentnie zapytanie z sql a ja musze pracowac na accessie:
 
C/C++
recordset->Open( "CREATE TABLE mytable (value NVARCHAR(255))",
connection.GetInterfacePtr(), ADODB::adOpenForwardOnly,
ADODB::adLockReadOnly, ADODB::adCmdText );


Bede strasznei wdzieczny za pomoc..  jak polacze sie z baza danych i bede  z niej potrafil czytac i pisac to z reszta juz sam sobie poradze..

z gory dzieki!

berkov
P-68453
DejaVu
» 2012-11-04 21:15:53
Wklej krótki kod, który da się skompilować. Obecnie powołujesz się na obiekt, który tak na dobrą sprawę nie wiemy czy jest zainicjalizowany.

http://www.codeproject.com/Articles/6917/ADO-101-level-tutorial
http://www.codeguru.com/cpp/data/mfc_database/ado /article.php/c6729/Using-ADO-from-C.htm
P-68491
berkov
Temat założony przez niniejszego użytkownika
» 2012-11-05 07:31:41
:-)
no tak, kod o ktorym mowa pewnie bedzie troche potrzebny do analizy!
sorry :-)

wklejam zatem....

dodam tylko ze wszystkie te tutoriale juz przerobilem.. wszystkie ADO umieraja w tym samym momecie.
zaczynam miec podejrzenie ze to wina Windowsa 7 (64) pod ktorym kompiluje.
jak exe odpale na innym kompie to albo pisze ze aplikacja nie jest win32 (na jedno korowych) a jak puszcze na jeszcze innym dwukorowym  to znowu wyskakuje ze brakuje mu jakiejs biblioteki ... troche sie jeszcze bede musial nauczyc o kompatybilnosci kompilowania... ale first things first, narazie chce zeby czytal i pisal w MS Access....

Oto kod:

C/C++
#include <windows.h>
#include <windowsx.h>
#import "C:\Program Files\Common Files\System\ado\msado26.tlb" rename( "EOF", "ADOEOF" )

LPSTR klasa1 = "downolny unikatowy tekst";

MSG Komunikat;
HFONT hCzcionka =( HFONT ) GetStockObject( DEFAULT_GUI_FONT );

HWND hwnd;
HWND hKontrButton;
HWND hKontrCheckbox;
HWND hKontrTextbox;
HWND hKontrEdit;
HWND hKontrListbox;
HWND hKontrCombobox;
HWND hKontrCombobox2;
LPSTR HHH;


LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )

{
   
   
    HRESULT hr;
    ADODB::_ConnectionPtr connection;
    hr = connection.CreateInstance( __uuidof( ADODB::Connection ) );
    ADODB::_RecordsetPtr recordset;
    hr = recordset.CreateInstance( __uuidof( ADODB::Recordset ) );
   
    connection->CursorLocation = ADODB::adUseClient; // TUTAJ WYSKAKUJE BLAD
    hr = connection->Open( L"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\My Documents\\C++\\WinApiADO\\baza.accdb;", L"", L"", ADODB::adConnectUnspecified );
   
    recordset->Open( "CREATE TABLE mytable (value NVARCHAR(255))",
    connection.GetInterfacePtr(), ADODB::adOpenForwardOnly,
    ADODB::adLockReadOnly, ADODB::adCmdText );
   
   
    WNDCLASSEX wc1;
   
    wc1.cbSize = sizeof( WNDCLASSEX );
    wc1.style = CS_DBLCLKS;
    wc1.lpfnWndProc = WndProc;
    wc1.cbClsExtra = 0;
    wc1.cbWndExtra = 0;
    wc1.hInstance = hInstance;
    wc1.hIcon = LoadIcon( NULL, IDI_APPLICATION );
    wc1.hCursor = LoadCursor( NULL, IDC_ARROW );
    wc1.hbrBackground =( HBRUSH )( COLOR_WINDOW + 1 );
    wc1.lpszMenuName = NULL;
    wc1.lpszClassName = klasa1;
    wc1.hIconSm = LoadIcon( NULL, IDI_APPLICATION );
   
   
    RegisterClassEx( & wc1 );
   
   
   
   
    hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, klasa1, "Odrabane okno Łysego", WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, 500, 700, NULL, NULL, hInstance, NULL );
   
   
    hKontrButton = CreateWindowEx( 0, "BUTTON", "Odrabany Przycisk Łysego", WS_CHILD | WS_VISIBLE, 10, 20, 220, 30, hwnd,( HMENU ) 500, hInstance, NULL );
    SendMessage( hKontrButton, WM_SETFONT,( WPARAM ) hCzcionka, 0 );
    hKontrCheckbox = CreateWindowEx( 0, "BUTTON", "Odrabany check Łysego", WS_CHILD | WS_VISIBLE | BS_CHECKBOX, 10, 70, 220, 30, hwnd,( HMENU ) 501, hInstance, NULL );
    CheckDlgButton( hwnd, 501, BST_CHECKED );
    hKontrTextbox = CreateWindowEx( WS_EX_CLIENTEDGE, "EDIT", "Tu pisz do Łysego", WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 120, 220, 30, hwnd, NULL, hInstance, NULL );
    hKontrEdit = CreateWindowEx( WS_EX_CLIENTEDGE, "EDIT", "Tu pisz wiecej do Łysego", WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL, 10, 170, 220, 100, hwnd, NULL, hInstance, NULL );
    SetWindowText( hKontrEdit, "Tu pisz duzo wiecej do Łysego" );
   
    if( hwnd == NULL )
    {
        MessageBox( NULL, "Nie ma bata, okno nie pojdzie !", "...", MB_ICONEXCLAMATION );
        return 1;
    }
   
   
   
    ShowWindow( hwnd, nCmdShow );
    ShowWindow( hKontrButton, nCmdShow );
    ShowWindow( hKontrCheckbox, nCmdShow );
    ShowWindow( hKontrTextbox, nCmdShow );
    ShowWindow( hKontrEdit, nCmdShow );
    ShowWindow( hKontrListbox, nCmdShow );
    ShowWindow( hKontrCombobox2, nCmdShow );
   
   
   
   
    while( GetMessage( & Komunikat, NULL, 0, 0 ) )
    {
        TranslateMessage( & Komunikat );
        DispatchMessage( & Komunikat );
    }
    return 0;
}


LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
    case WM_CLOSE:
        DestroyWindow( hwnd );
        break;
       
    case WM_COMMAND:
       
        switch( wParam )
        {
           
        case 501:
            {
                if( IsDlgButtonChecked( hwnd, 501 ) == BST_CHECKED )
                     CheckDlgButton( hwnd, 501, BST_UNCHECKED );
                else
                     CheckDlgButton( hwnd, 501, BST_CHECKED );
               
            }
            break;
           
           
            default:
           
            ; }
       
        break;
       
    case WM_DESTROY:
        PostQuitMessage( 0 );
        break;
       
        default:
        return DefWindowProc( hwnd, msg, wParam, lParam );
    }
   
    return 0;
}
P-68524
xevuel
» 2012-11-05 07:52:30
W porównaniu do pierwszego linka, który podał @DejaVu:
  • Nie inicjalizujesz biblioteki COM
  • Nie sprawdzasz, czy wywołanie funkcji się powiodło

Tak więc: zamień
C/C++
HRESULT hr;
ADODB::_ConnectionPtr connection;
hr = connection.CreateInstance( __uuidof( ADODB::Connection ) );

na
C/C++
HRESULT hr = CoInitialize( NULL );
if( FAILED( hr ) )
{
    MessageBox( 0, "Błąd numer 1", 0, 0 );
}
ADODB::_ConnectionPtr connection;
hr = connection.CreateInstance( __uuidof( ADODB::Connection ) );
if( FAILED( hr ) )
{
    MessageBox( 0, "Błąd numer 2", 0, 0 );
}
i zobacz, czy czasem po odpaleniu programu nie pojawi Ci się jakieś okienko :)

Dodatkowo, blok
hr = connection->Open( L"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\My Documents\\C++\\WinApiADO\\baza.accdb;", L"", L"", ADODB::adConnectUnspecified );
umieść w blokach try...catch, zgodnie z tym akapitem:
Now, we are ready to open a database connection. This operation is placed within a C++ try/catch block. If this operation fails, then an exception is of type _com_error is thrown and immediately caught

tymczasowo niech wygląda tak:
C/C++
try
{
    hr = connection->Open( L"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\My Documents\\C++\\WinApiADO\\baza.accdb;", L"", L"", ADODB::adConnectUnspecified );
}
catch( _com_error & )
{
    MessageBox( 0, "Błąd numer 3", 0, 0 );
    CoUninitialize();
}
P-68525
berkov
Temat założony przez niniejszego użytkownika
» 2012-11-06 00:36:21
Hey ho!


Ok .. dodalem do twojego kodu 4ty blad dla pewnosci i puscilem kompilator obgryzajac paznokcie!!:
C/C++
HRESULT hr = CoInitialize( NULL );
if( FAILED( hr ) )
{
    MessageBox( 0, "Błąd numer 1", 0, 0 );
}



ADODB::_ConnectionPtr connection;
hr = connection.CreateInstance( __uuidof( ADODB::Connection ) );
if( FAILED( hr ) )
{
    MessageBox( 0, "Błąd numer 2", 0, 0 );
}




ADODB::_RecordsetPtr recordset;
hr = recordset.CreateInstance( __uuidof( ADODB::Recordset ) );
if( FAILED( hr ) )
{
    MessageBox( 0, "Błąd numer 3", 0, 0 );
}





connection->CursorLocation = ADODB::adUseClient; // TUTAJ wyskakiwal BLAD

try
{
    hr = connection->Open( "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\\My Documents\\C++\\xxx\\baza.accdb;", "", "", ADODB::adConnectUnspecified );
}
catch( _com_error & )
{
    MessageBox( 0, "Błąd numer 4", 0, 0 );
    CoUninitialize();
}

wczesniej tez probowalem z tego artykulu ale pamietam ze nic mi chyba nie wyskoczylo tylko strasznie dlugo uruchamial program a jak juz uruchomil to na koncu i tak dostalem taki sam blad jaki podalem w pierwszym poscie (stad nota bene zostaly mi hr wszedzie w moim orginalnym kodzie).. w kazdym badz razie po zainicjowaniu COM i skopilowaniu ... ZADZIALALO!!!!!!!
Zaden "blad numer .."!!!!!! i zobaczylem swoje okienko!!!


W koncu widac swiatelko w tunelu!!!!


Ddzis w pracy szukalem po sieci wiecej o obsludze MS Accessa przez ADO ale wszystkie (a przynajmniej znaczna wiekszosc) posty wydaja sie odnosic do sql a nie ms access.  Dostalismy sie juz do pliku ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\\xx\xx.xx") ... i teraz pytanie!!! jak dostac sie do konkretnej tabelki z accessa i jak np przeczytac i zmodyfikowac konkretna komorke?


jesli to wiesz to bede wdzieczny, jesli nie i tak jestem starsznie wdzieczny za ten kod i jutro rusze w poszukiwaniu obslugi accessa!!!!

tutaj cos znanazlem ale przyznaje ze srednio to rozumiem (moze to przez pozna pore i kolejne piwo):
http://msdn.microsoft.com/en-us/library/ff965871.aspx#DataProgrammingWithAccess2010_ADOExample

chyba najpierw bede musial przerobic ten kod na winapi i jeszcze raz popatrzec na to ze swiezym umyslem.

generalnie chodzi o to aby np. z hKontrTextbox wpisywal cos do tabeli np "Dane" -> rzedu np 10 (wedlug klucza), w kolumnie np "Dostawca" i na odwrot aby do np hKontrEdit wypisal rzad 9  z tabelki "Dane".


dziekuje i pozdrawiam!
berkov

p.s.

uzylismy tutaj "COM", staralem sie o tym poczytac bo nie mam zielonego pojecia co to jest (a nie lubie uzywac czegos czego nie rozumiem, taka moja obsesja) ale przy drugim tutorialu sie poddalem i stwierdzilem ze jeszcze duzo mleko uplynie zanim bede w stanie to polapac! no nic..

P-68598
xevuel
» 2012-11-06 07:42:48
Hmm, powiem tak... Nie bawiłem się nigdy Accessem.

jak dostac sie do konkretnej tabelki z accessa i jak np przeczytac i zmodyfikowac konkretna komorke?
Zgodnie z tym poniżej:

In order to use MS Access or any other SQL database you need to learn the SQL language
Za: http://www.daniweb.com/software-development/cpp/threads /67312/connecting-c-to-ms-access

Musisz znać SQL. Podpowiem, że te dwa zapytania, których potrzebujesz, to SELECT (do odczytania) oraz UPDATE (do zmodyfikowania).

tutaj cos znanazlem ale przyznaje ze srednio to rozumiem (moze to przez pozna pore i kolejne piwo):
http://msdn.microsoft.com/en-us/library/ff965871.aspx#DataProgrammingWithAccess2010_ADOExample
Nigdy nie używałem tej biblioteki... Ale to trudne nie jest, zapytanie to pobierze konkretne dane wszystkich spółek znajdujących się w bazie. Jakie dane? Company oraz First Name, czyli pewnie identyfikator oraz pełna nazwa firmy.

C/C++
for( long nIndex = 0; nIndex < pFields->GetCount(); nIndex++ )
{
    cout << " | " << _bstr_t( pFields->GetItem( nIndex )->GetName() );
}
cout << endl;
Co to robi? Jako że wykona się tylko raz, podejrzewam, że wypisuje to nazwy kolumn w tej tabeli, czyli Company oraz First Name.

C/C++
while( !pRS->AdoNSEOF )
{
    for( long nIndex = 0; nIndex < pFields->GetCount(); nIndex++ )
    {
        cout << " | " << _bstr_t( pFields->GetItem( nIndex )->GetValue() );
    }
    cout << endl;
    pRS->MoveNext();
    rowCount++;
}
Najprawdopodobniej wypisuje wszystkie wartości, jakie zostały pobrane z bazy.

Powracając: jak odczytać wartość konkretnej komórki? Musisz odpowiednio spreparować zapytanie SQL, tak, aby wybierało tylko tą jedną (Możesz też bawić się z analizą wszystkich komórek wewnątrz aplikacji, ale to trochę bez sensu IMO).
Potem będziesz musiał odczytać tą wartość, w podobny sposób, jak wyżej. Na końcu już tylko zostaje wyświetlenie tej wartości, u Ciebie pewnie za pomocą » WinAPISetWindowText.

Może się przydać: http://www.codeproject.com/Articles/4829/Guide-to-BSTR-and-C-String-Conversions

Aha, czym jest COM? Myślę że tu: http://msdn.microsoft.com/en-us/library/windows/desktop/ee663262%28v=vs.85%29.aspx jest to wyjaśnione :)

Daj znać, czy Ci się udało, powodzenia ;)
P-68603
berkov
Temat założony przez niniejszego użytkownika
» 2012-11-06 08:22:50
Chyba sie udalo, spac nie moglem hehe, spokoju mi nie dalo!

dalsza czesc kodu:

C/C++
_bstr_t query = "SELECT Dane.[Imie], Dane.[Miasto] FROM Dane ORDER BY Dane.[Miasto] ASC;";

try
{
    hr = recordset->Open( query,
    _variant_t(( IDispatch * ) connection, true ),
    ADODB::adOpenUnspecified,
    ADODB::adLockUnspecified,
    ADODB::adCmdText );
   
}
catch( _com_error & )
{
    MessageBox( 0, "Błąd numer 5", 0, 0 );
    CoUninitialize();
}


ADODB::Fields * pFields = NULL;
try
{
    hr = recordset->get_Fields( & pFields );
}
catch( _com_error & )
{
    MessageBox( 0, "Błąd numer 6", 0, 0 );
    CoUninitialize();
}



teraz juz wiem chyba jak przechodzic po indexie (np index 4):
pFields->GetItem(4)->GetValue() (jesli dobrze zrozumialem)
nie za bardzo natomiast wiem jak:
1. wypisac teraz do LPCSTR komorke z konkretnej kolumny (np "Miasto")
2. jak ogolnie dzialaja te "ms access" statement, wygladaja podobnie do sql troche ale to nie to samo, nie wiem skad wziasc jakies zrodla opisujace te statement, czyli jak zmienic, dodac, wypisac rekord itd...
3. co to to cholerne COM (w przyjaznym do zrozumienia jezyku), zebym chociaz wiedzial mniej wiecej z czego korzystam.

Swoja droga to niesamowicie pokrecony sposob zeby dostac jakas wartosc z bazy danych, czlowiek musi kilka stron kodu napisac zeby cos dostac!!!! heheh



P-68604
DejaVu
» 2012-11-06 09:27:45
@up: ciesz się, że nie musisz pisać bibliotek COM-owych - to dopiero jest masakra :) W każdym razie - skoro jest to baza danych to wszystkie operacje powinno dać się wykonać za pomocą zapytań, tj. zarówno utworzenie tabel, dodawanie i aktualizacja rekordów oraz pobieranie danych.
P-68607
« 1 » 2
  Strona 1 z 2 Następna strona