Bitte um Hilfe bei CRecordset



  • Hi zusammen,

    ich bin neu hier und seit langer Zeit nicht mehr als Entwickler tätig 😞

    Möchte nun jedoch mit einem schon vorhandenen kleinen Programm auch eine
    Anbindung an einen MS-SQL-Server hinbekommen. Meine Mittel sind zZt leider
    etwas veraltet (Win XP, VC6). Ich hoffe, Ihr könnt mir trotzdem helfen. 🙄

    Ich möchte den DB-Connect gerne ueber eine extern definierte ODBC-Datenquelle
    herstellen, so dass im Source mögl. nur noch der Name der Datenquelle
    auftaucht und sich mind DB-Benutzer und Passwort von aussen durch ein
    Config-File definieren lassen oder besser noch in der Definition der
    Datenquelle selbst einmal vom Installer hinterlegt wird. Beim Anlegen der
    Datenquelle ergab der Test eine Erfolgsmeldung. Momentan bekomme ich immer ein
    Login-Dialog, wo ich das Passwort eingeben muss, der natürlich weg soll. Habe
    das Passwort aber auch schon im Source versucht anzugeben. Ohne Erfolg.

    Ich habe schon ein paar Tege damit verbracht, mir durch Google zu behelfen.
    Habe hier die ersten (für mich) brauchbaren Hinweise gefunden. Leider sind auch
    diese manchmal etwas Lückenhaft. In den Artikeln www.c-plusplus.net/forum/164310-full
    und www.c-plusplus.net/forum/39098 ist das Grundgerüst ansich schon recht gut
    aufgearbeitet, jedoch bleibt folgendes 'zusammengebasteltes' am dSet->Open()
    (Zeile 66) hängen (ich habe einige der auskommentierten Stellen stehengelassen,
    um zu dokumentieren, was ich schon versucht habe):

    #include "stdafx.h"
    #include "afxdb.h"
    
    using namespace std;
    
    class CDemoSet : public CRecordset
    {
       // Feld-/Parameterdaten
       //{{AFX_FIELD(CHideSet, CRecordset)
       long Number;
       byte State;
       long UID;
       //}}AFX_FIELD
    
       public : CDemoSet::CDemoSet(CDatabase* pdb) : CRecordset(pdb)
       {
          Number = 0;
          State  = 0;
          UID    = 0;
       }
    
       void CDemoSet::DoFieldExchange(CFieldExchange* pFX)
       {
          pFX->SetFieldType ( CFieldExchange::outputColumn );
          RFX_Long ( pFX, _T("[dbo].[Number]"), Number );
          RFX_Byte ( pFX, _T("[dbo].[State]"), State );
          RFX_Long ( pFX, _T("[dbo].[UID]"), UID );
       }
    
       CString CDemoSet::GetDefaultConnect()
       {
           return _T("DSN=TEST;UID=....;PWD=....;DATABASE=....");
       }
       CString CDemoSet::GetDefaultSQL()
       {
           return _T("select [Number],[State],[CardUIDInUse] from [dbo].[Locker] where len([Number])>0");
       }
    
       long CDemoSet::GetNumber() { return Number; }
       byte CDemoSet::GetState() { return State; }
       long CDemoSet::GetUID() { return UID; }
    };
    
    int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
    {
    	// MFC initialisieren, Ausgabe und Fehlermeldung bei Fehlern
    	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    	{
    		// ZU ERLEDIGEN: Fehlercode gemäß Ihren Anforderungen ändern
    		cerr << _T("Fatal Error: MFC initialization failed") << endl;
    		exit ( 1 );
    	}
    
       CDatabase db;
       //db.Open ( _T("DSN=TEST;UID=....;PWD=....;DATABASE=....") );
       db.Open ( _T("TEST") );
       if ( db.IsOpen() )
       {
          printf ( "DB is open\n" );
    
          CDemoSet *dSet = new CDemoSet(&db);
          printf ( "dSet created\n" );
          //dSet->m_strFilter.Format ( _T("len(Number)>0") );
          //dSet->m_strSort = "Number";
          BOOL isOpen = dSet->Open (); // CRecordset::snapshot, _T("select [Number],[State],[CardUIDInUse] from [dbo].[Locker] where len([Number])>0"), CRecordset::readOnly );
          printf ( "dSet IsOpen        : %d\n", (int)isOpen );
          printf ( "SQL                : %s\n", (const char *)dSet->GetSQL() );
          printf ( "GetDefaultSQL()    : %s\n", (const char *)dSet->GetDefaultSQL() );
          printf ( "GetDefaultConnect(): %s\n", (const char *)dSet->GetDefaultConnect() );
          if ( !dSet->IsBOF() )
          {
             printf ( "daten da\n" );
             int row = 0;
             while ( !dSet->IsEOF() )
             {
                long number = dSet->GetNumber();
                byte state  = dSet->GetState ();
                long uid = dSet->GetUID ();
                row = row + 1;
                printf ( "ROW %d NUMBER:%ld - STATE:%d - UID:%ld\n", row, number, (int)state, uid );
                dSet->MoveNext ();
             }
          }
          dSet->Close ();
          db.Close ();
       }
       else
       {
          printf ( "can't open DB\n" );
       }
    
       printf ( "exit odbc test\n" );
       return 1;
    }
    

    Bitte noch zwei Fragen:

    - Was mir noch unklar ist, sind die Member DoFieldExchange(),
    GetDefaultConnect() und GetDefaultSQL(). Wird erstere immer implizit durch
    MoveNext() oder so aufgerufen oder muss ich das selber machen? Ebenso die
    beiden letzten. In meinem Kontext werden die offensichtlich nicht aufgerufen.
    Wann geschieht das bzw. wie kann ich von diesen profitieren?

    - Gibt es in den Klassen CRecordset bzw. CDatabase irgendetwas an Fehler-Codes
    oder Meldungen, die ich abpruefen kann oder wie bekomme ich genauere Hinweise
    auf aufgetretene Fehler?

    Falls ich hier einen Bock geschossen habe und ihn nicht sehe, seid bitte gnädig 🙄

    lg


  • Mod

    1. Du hast den Source Code der MFC und DU kannst Debuggen. Das hilft beim Verständnis ungemein.
    2. Ja DoFieldExcange wir dimmer aufgerufen durch die Move Befehle. Mit denen werden die Daten aus Deiner Copy in den Datensatz geschaufelt und umgekehrt. Das ist ähnlich DoDataExchange bei Dialogen.
    3. GetDefaultConnect wird immer in Open benutzt (Du hast den Sourcecode)
    4. GetDefaultSQL wird auch in Open verwendet, wenn Du kein SQL Statement angibst. (Du hast den Sourcecode)



  • Danke fuer den naheliegenden Hinweis! Zunächst habe ich einen try/catch-
    Block drumgelegt und die Meldung ausgegeben:

    "Vor dem Aufrufen von SQLFetchScroll/SQLExtendedFetch waren keine Spalten
    gebunden"

    Hier ist offensichtlich irgend etwas nicht initialisiert. Habe zunächst den
    nOpenType von CRecordset::Open von snapshot auf forwardOnly gesetzt und
    Open() kam zurück. Meine Getter lieferten jedoch nur Nullen. Die Anzahl der
    ausgegebenen Records mit gut 5500 ist korrekt, was mir sagt, dass das Open()
    prinzipiell funktionierte. Da durch einfache Textausgabe in DoFieldExchange(),
    die nicht in stdout erschien, klar ist, dass DoFieldExchange() nicht durch
    MoveNext() aufgerufen wird, liegt vermutlich wie auch bei der o.g. Fehler-
    meldung irgend eine Verbindung oder Initialisierung nicht vor.

    Leider bin ich auch mit dem Debugger bislang nicht weitergekommen. Habt Ihr
    noch einen Tip?


  • Mod

    Wieso kommst Du nicht weiter?

    CRecordset::MoveNext() ruft
    CRecordset::Move(long nRows, WORD wFetchType) auf, das wiederum ruft
    CRecordset::Fixups() auf, was wiederum
    CRecordset::DoFieldExchange()

    Wo hakt es nun?
    Debuggen...

    Obige Zusammenhänge habe ich in 2 Minuten herausgefunden... Sourcen hast Du!



  • Na, in DBCORE.CPP fand ich auch die Stelle

    if (m_nFields > 0 && !IsEOF() && !IsBOF() &&
    		!(m_dwOptions & useMultiRowFetch))
    	{
    		Fixups();
    	}
    

    nur hab ich nicht herausgefunden, warum m_nFields = 0 ist. Ich hab' versucht
    eine Stelle zu finden, wo schreibend auf m_nFields zugegriffen wird und sie
    bislang nicht gefunden. Wohl in der Klasse CFieldExchange, nur scheint dieses
    m_nFields nichts mit dem von CRecordset zu tun zu haben? Ich dachte, dass in
    Open() ein Hinweis auf das Parsen des mitgegebenen SQL-Statements zu finden
    sein muesste (habe auch immer wieder versucht das SQL-Statement dem Open()
    mitzugeben - oben auskommentiert) . Bislang leider ohne Erfolg.



  • Sagt, habt Ihr nicht eine Idee?


  • Mod

    30 Sekunden in den Source Code gesehen.

    Im Dataexchange Mechanismus wird das verwendet:

    BOOL CFieldExchange::IsFieldType(UINT* pnField)
    {
    	ENSURE_ARG(pnField != NULL);
    	if (m_nFieldType == outputColumn)
    	{
    		*pnField = ++m_nFields;
    		// Recordset's m_nFields must match number of Fields!
    		ASSERT(m_nFields <= m_prs->m_nFields);
    	}
    

Anmelden zum Antworten