Tuesday, June 17, 2014

Tutorial compilare Openssl si curl cu ajutorul MinGW32 si msys




   Acest tutorial este adresat in mare parte incepatorilor care doresc sa compileze aceste librarii fara batai de cap.
Pentru a putea efectua acesti pasi aveti nevoie de urmatoarele:
   - Mingw32 si msys, compilator si mediu de dezvoltare cli ce pot fi descarcate de aici.
   - Perl ce poate fi descarcat de aici.
   - Libraria Openssl ce poate fi descarcata de aici, descarcati ultima versiune a arhivei(de obicei cea marcata cu rosu). Pentru a o dezarhiva sugerez sa folositi 7zip deoarece winrar se pare ca da rateuri la arhive tar.gz
   - Libraria libcurl ce poate fi descarcata de aici.

Pentru acesst tutorial vom face cateva presupuneri:
   - Ati instalat compilatorul si msys la adresa C:/MinGW/ respectiv C:/MinGW/msys/ (daca instalati compilatorul la alta adresa, asigurati-va ca adresa respectiva nu contine spatii si alte semne
   - Folderul in care se gaseste compilatorul(executabilul mingw32-g++/mingw32-gcc/mingw32-make) este C:/MinGW/bin
   - Folderul in care se gasesc headerele este C:/MinGW/include
   - Folderul in care se gasesc librariile statice(nume_librarie.a) este C:/MinGW/lib

   Incepem cu libraria OpenSSL(la data creeri acestui tutorial aceasta era la versiunea 1.0.1h)
   Dupa ce dezarhivam libraria ar trebui sa ramanem cu un folder numit openssl-1.0.1h. Copiem acest folder in folderul C:/Mingw/msys/home/Nume_userul_vostru/. Pornim msys(C:/MinGW/msys/1.0/msys.bat).
Schimbam directorul de lucru din cel in care porneste msys in /home/openssl-1.0.1h. Aici executam comanda urmatoare:
 
./config --prefix=/mingw --openssldir=/openssl shared

--prefix=/mingw seteaza ca si compilator utilizat mingw
--openssldir=/openssl seteaza folderul in care fisierele rezultate in urma compilarii se vor salva. Acest folder se gasi in mod normal la aici: C:/MinGW/msys/1.0/openssl.
shared seteaza daca libraria va fi compilata sub forma de dll, sau sub forma de librarie statica(*.a)

Dupa ce procesul de configurare se inchieie rulam pe rand comenzile "make" si "make install".
Dupa ce termina si a 2-a comanda(make install) va trebui sa copiati din folderul C:/MinGw/msys/1.0/mingw cele 3 foldere, bin, include, lib in folderul C:/MinGW/msys/1.0/openssl. Acest pas va fi necesar pentru a putea compila libraria libcurl. Pentru a putea folosi ,mai usor, libraria aceasta in programele noastre este recomandat sa copiati cel putin folderele bin, include si lib din folderul c:/MinGW/msys/1.0/openssl in folderul C:/MinGW/ suprascriind folderele bin, include si lib din acest folder.

   Compilarea librariei libcurl(la data creeri acestui tutorial aceasta era la versiunea 7.30.0)
   Dupa ce dezarhivam libraria, ar trebui sa avem un folder numit curl-7.37.0. Copiem acest folder in folderul C:/MinGw/msys/1.0/home/Nume_userul_vostru. Mutam directorul de lucru din /home/openssl-1.0.1h in /home/curl-7.37.0.
Executam comanda
./configure --with-ssl=/openssl/

--with-ssl va seta ca libraria sa se compileze cu includerea librariei openssl si ca aceasta librarie se gaseste la adresa /openssl// . Dupa ce configurarea se incheie executam pe rand comenzile "make" si "make install"
Dupa ce "make install" termina de executat, daca nu au existat erori, vom gasi fisierele noastre in folderul C:/MinGW/msys/1.0/local. De aici copiem folderele bin, include, lib si share in folderul C:/MinGW/ suprascriind folderele deja existente.

In acest moment putem folosii librariile acesta pentru programele de care avem nevoie.
Pentru cei ce doresc cateva alternative, acestea exista.
Pentru Openssl avem urmatoarele:
PolarSSL si un tutorial despre cum sa il compilati aveti tot pe pagina lor aici. Aceasta implementare este disponibila atat sub liceenta open source cat si sub licenta platita.
Network Security Service(NSS) un proiect ce apartine fundatiei Mozila, ce include pe langa SSL/TSL mai multe metode de criptare a datelor utilizate in transferul de date.Aici aveti cateva informatii despre compilarea si utilizarea acestei librarii.
O alta alternativa open source(in mare parte, foloseste licenta LGPL 2.0) este GNUTLS. Aici aveti un tutorial ce ar trebui sa va ajute la compilarea acestei librarii.

Pentru LibCurl avem urmatoarele alternative:
WinInet este o alternativa ce functioneaza strict pe windows, pentru cei ce nu au o problema cu asta.Pentru aceasta "librarie"/Api aveti nevoie de minim Windows XP sau Windows Server 2003.
LibWWW este o alta alternativa, de data aceasta open source.

Daca aveti intrebari nu ezitati sa le puneti.
Sper sa va ajute.

Surse de inspiratie:
Stack Overflow
BowmanSolutions

Saturday, June 14, 2014

Tutorial introducere in threading in WinApi(C++)





Introducere in threaduri WinApi(C++)

   Ce sunt threadurile?
   Threadurile sunt fire de executie separate care exista in interiorul unei aplicatii.
Firele de executie multiple, permit unei aplicatii sa faca(sau sa dea senzatia ca fac) mai multe lucruri in acelasi
timp.
Acest efect este atins prin permiterea fiecarui fir de executie o anumita perioada de lucru in procesor.

   In majoritatea limbajelor de programare moderne(care in general incorporeaza concepte de programare
orientata pe obiect), threadurile sunt portretizate sub forma de obiecte, threadurile respective avand
caracteristici, si metode de utilizare. In contrast, threadurile din WinApi sunt mult mai bazice. In esenta un
thread in WinAPi este doar o simpla functie, ce se executa separat de threadul principal. Acest lucru, pe
langa ca le face un pic mai complicat de folosit le face insa si mai versatile, permitand programatorului sa
creeze pe baza facilitatilor respective, alte reprezentari ale threadurilor.

Cam atat cu introducerea, deci hai sa mergem mai departe cu un pic de cod.
#include <iostream>
#include <windows.h>

using namespace std;

DWORD WINAPI trdFnk(LPVOID lpParam)
{
    for(int i = 0; i< 100;i++)
    {
        cout << i << endl;
    }
}

int main()
{
    int data = 0;
    DWORD identificatorTRD = 0;
    HANDLE trdHdl = CreateThread(NULL, 0, trdFnk, &data, 0, &identificatorTRD);
    if(trdHdl == NULL)
 {
  cout <<"Eroare la creere thread, iesim." << endl;
  return 0;
 }
 WaitForSingleObject(trdHdl, INFINITE);
    return 0;
}
Ce face codul asta?
Creaza un thread in care se face o enumerare dupa care iese. Creerea unui tred e formata din 2 pasi.
   1. O functie(ce va fii threadul nostru) care are definitia urmatoare: DWORD WINAPI
nume_functie(LPVOID lpParam).  Este necesar ca functia noastra thread sa respecte aceasta definitie pentru
 a fi recunoscuta de catre al 2-lea pas si anume:
   2. Functia CreateThread, aceasta are urmatoarea definitie:
      HANDLE WINAPI CreateThread( _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
         _In_ SIZE_T dwStackSize,
         _In_ LPTHREAD_START_ROUTINE lpStartAddress,
         _In_opt_ LPVOID lpParameter,
         _In_ DWORD dwCreationFlags,
         _Out_opt_ LPDWORD lpThreadId );
   Functia returneaza un obiect de tip HANDLE. Acesta nu este altceva decat un pointer
void(typedef PVOID HANDLE). Un pointer void poate fii folosit pentru a indica catre orice alt tip de
obiect.
      Argumente:
         -_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes: - LPSECURITY_ATTRIBUTES este
o structura cu 3 membri. Structura este folosita pentru a seta diferite atribute de securitate ale unei
aplicatii sau fir de executie(thread). Structura e definita cam asa:
            typedef struct _SECURITY_ATTRIBUTES {
            DWORD nLength;
            LPVOID lpSecurityDescriptor;
            BOOL bInheritHandle; } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
            -DWORD nLength contine marimea acestei structuri.
            -De aici membrul important este LPVOID lpSecurityDescriptor, care este o alta structura nedocumentata.
            -BOOL bInheriteHandle seteaza daca alte procese /threaduri create din threadul curent sa ii mosteneasca atributele de securitate.
         -_In_ SIZE_T dwStackSize: - stocheaza marimea initiala a stivei firului de executie(marimea in memorie pe care firul de executie o ocupa), daca acest parametru este 0, atunci firul de executie va avea aceeasi marime ca si procesul/firul de executie parinte. Marimea stivei firului de executie se va modifica pe parcursul rularii firului, aceasta marime putand sa scada sau sa creasca.
         -_In_ LPTHREAD_START_ROUTINE lpStartAddress - este de fapt numele functiei care reprezinta
firul nostru de executie.
         -_In_opt_ LPVOID lpParameter: - este un alt pointer void ce poate indica catre orice tip de obiect, aici puteti sa puneti, fie de unul singur sau daca este necesa trimiterea mai multor variabile, acestea se pot pune
intr-un struct.
         -_In_ DWORD dwCreationFlags: - reprezinta unul din flagurile/starile in care threadul este
creeat - 0 inseamna ca threadul ruleaza imediat ce a fost creat. - CREATE_SUSPENDED inseamna ca threadul este creeat intr-o stare suspendata, acesta incepe sa ruleze doar dupa ce functia ResumeThread a fost chemata - STACK_SIZE_PARAM_IS_A_RESERVATION, daca acest parametru este specificat,
atunci dwStackSize va reprezenta marimea initiala a stive, daca nu este specificat va reprezenta marimea
memoriei rezervate pentru firul respectiv de executie. Memoria rezervata este cantitatea de memorie la care
sistemul de operare garanteaza ca firul de executie va avea acces.
         -_Out_opt_ LPDWORD lpThreadId: - reprezinta id-ul firului de executie, cu acest id firul de executie
poate fi identificat in interiorul aplicatiei. Acest id este utilizat pentru functii de genul OpenThread, functie
folosita pentru a deschide si accesa obiectul unui thread. Acest id este creat in functia CreateThread si stocat in acest parametru.
Pentru a creea un thread suspendat,atunci cand nu dorim sa porneasca imediat, vom folosi parametrul
CREATE_SUSPENDED Ceva de genul acesta:

#include <iostream>
#include <windows.h>

using namespace std;

DWORD WINAPI trdFnk(LPVOID lpParam)
{
    for(int i = 0; i< 100;i++)
    {
        cout << i << endl;
    }
}

int main()
{
    int data = 0;
    DWORD identificatorTRD = 0;
    HANDLE trdHdl = CreateThread(NULL, 0, trdFnk, &data, CREATE_SUSPENDED, &identificatorTRD);
 if(trdHdl == NULL)
 {
  cout <<"Eroare la creere thread, iesim." << endl;
  return 0;
 }
 ResumeThread(trdHdl);
    WaitForSingleObject(trdHdl, INFINITE);
    return 0;
}



   Ok, sa zicem ca trebuie sa suspendam un thread care ruleaza. Pentru asta va trebui sa apelam urmatoarea
functie: DWORD WINAPI SuspendThread( _In_ HANDLE hThread ); - argumentul necesar este
handle-ul threadului. Acesta este obtinut prin chemarea functiei CreateThread despre care am vorbit putin
mai sus. Bine-inteles, dupa ce am suspendat un thread o sa avem nevoie as il si repornim, corect? Pentru
asta folosim functia DWORD WINAPI ResumeThread( _In_ HANDLE hThread ); - la fel ca si
SuspendThread, ResumeThread primeste ca argument handle-ul threadului. Haide sa vedem un mic exemplu:

#include <iostream>
#include <windows.h>

using namespace std;

DWORD WINAPI trdFnk(LPVOID lpParam)
{
    for(int i = 0; i< 100;i++)
    {
        cout << i << endl;
        Sleep(100); // Am adaugat un mic timer ca threadul sa nu termine treaba asa repede :)
    }
}

int main()
{
    int data = 0;
    DWORD identificatorTRD = 0;
    HANDLE trdHdl = CreateThread(NULL, 0, trdFnk, &data, 0, &identificatorTRD);
 if(trdHdl == NULL)
 {
  cout <<"Eroare la creere thread, iesim." << endl;
  return 0;
 }
 cout << "Suspendam threadul... ";
 Sleep(5000); // asteptam 5 secunde dupa care suspendam threadul.
 SuspendThread(trdHdl);
 cout << "Threadul a fost suspendat, asteptam inca 5 secunde dupa care il repornim.";
 Sleep(5000);
 ResumeThread(trdHdl);
    WaitForSingleObject(trdHdl, INFINITE);
    return 0;
}

   Atunci cand lucram cu threadurile, se poate intampla, daca codul nu este scris corect, sa avem situatii in care
2 threaduri incearca sa acceseze aceeasi variabila sau aceleasi date. Desigur asta e problema cea mai
comuna, pot aparea multe altele. Acest lucru tine de sincronizarea datelor si threadurilor. Winapi pune la
dispozitie 5 obiecte care ajuta la contrulul si efectuarea sincronizarii. Acestea sunt mutex, event,
semaphore si waitableTimer si critical_section.
   Sa le luam pe rand sa vorbim un pic despre ele.
   Mutex:
      - Mutexi sunt folositi pentru a proteja variabile de la a fi accesate si modificate de catre 2 sau mai multe
threaduri/procese in acelasi timp. Ei fac asta prin permiterea unui proces/thread sa preia proprietatea asupra
acelui mutex. Un alt thread care incearca sa modifice variabila respectiva va trebui sa verifice si sa astepte
pana mutex-ul respectiv nu mai este sub proprietatea altui thread/proces pentru a putea sa efectueze
actiunea dorita. Un mutex se creeaza cu ajutorul functiei CreateMutex care are urmatoarea definitie:
      HANDLE WINAPI CreateMutex( _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
         _In_ BOOL bInitialOwner,
         _In_opt_ LPCTSTR lpName );
      Parametrii sunt urmatorii:
         - _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes este o structura cu 3 membri.
Structura este folosita pentru a seta diferite atribute de securitate ale unei aplicatii sau fir de
executie(thread) Structura e definita cam asa:
         typedef struct _SECURITY_ATTRIBUTES {
            DWORD nLength;
            LPVOID lpSecurityDescriptor;
            BOOL bInheritHandle; } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
            - DWORD nLength contine marimea acestei structuri.
            - De aici membrul important este LPVOID lpSecurityDescriptor, care este o alta structura
nedocumentata.
            - BOOL bInheriteHandle seteaza daca alte procese /threaduri create din threadul curent sa ii mosteneasca atributele de securitate.
         - _In_ BOOL bInitialOwner va specifica daca threadul/procesul care creeaza acest mutex este si
detinatorul lui initial, in felul acesta "inchizand" mutex-ul, caz in care oricare alt thread ce va dori sa il
foloseasca va trebui sa astepte pana cand threadul/procesul creator il va elibera.
         -_In_opt_ LPCTSTR lpName - este numele atribuit mutex-ului. Acest nume va fi folosit cu functia
OpenMutex prin care un thread poate sa preia proprietatea asupra unui thread care nu este detinut de
nimeni. Daca functia este executata cu succes atunci aceasta returneaza un handle catre obiectul dorit.
Daca obiectul exista deja(pe baza numelui furnizat ultimului argument) atunci este returnat un handle catre
acest obiect deja existent. In acest caz se poate apela functia GetLastError care va returna
ERROR_ALREADY_EXISTS. In cazul in care functia esueaza, valoarea returnata va fi NULL.
      Mai jos avem un mic exemplu ce foloseste mutex-i pentru a proteja o variabila. In acest exemplu creem
2 threaduri, un mutex si un contor(ctr). Threadurile preiau proprietatea asupra mutex-ului pe rand si
incrementeaza variabila dupa care elibereaza mutex-ul.

#include <iostream>
#include <windows.h>

using namespace std;

int ctr = 0; // counterul ce va fi incrementat pe rand de cele 2 threaduri
HANDLE mutex; // mutex-ul care va proteja variabila respectiva.

DWORD WINAPI trdLoop(LPVOID lpParam)
{
    DWORD dwWaitResult; // va stoca rezultatul actiuni de asteptare pentru obtinerea proprietatii threadului
    do
    {
        dwWaitResult = WaitForSingleObject(mutex, INFINITE);
        switch(dwWaitResult)
        {
        case WAIT_OBJECT_0:
            {
                    cout << GetCurrentThreadId() << " " << ctr++ << endl;
                    if(ReleaseMutex(mutex) == 0)
     {
      cout << "Eroare la eliberare mutex, eroare: " << GetLastError() << endl;
     }
                    break;
            }
        default:
            {
                cout << "Eroare nespecificata in threadul " << GetCurrentThreadId() << " " << GetLastError() << endl;
            }
        }

        //cout << i << endl;
        Sleep(100); // Dormim un pic pentru a nu printa prea repede datele si pentru a da timp si celuilalt thread sa execute, altfel threadurile vor executa
  // incrementarea aproape la intamplare, in functie de cum primesc timp de procesare pe procesor
    }while(ctr < 100);
}

int main()
{
    DWORD identificatorTRD1 = 0;
    DWORD identificatorTRD2 = 0;
    // creem intai mutex-ul deoarece daca creem intai threadurile vor porni direct
    // si vor incerca sa acceseze un mutex care nu exista
    mutex = CreateMutex(NULL, FALSE, "grMutex"); 
 if(mutex == NULL)
    {
        cout << "Eroare la creere mutex, iesim." << endl;
        return 0;
    }

    HANDLE trdHdl1 = CreateThread(NULL, 0, trdLoop, NULL, 0, &identificatorTRD1);
 if(trdHdl1 == NULL)
 {
  cout <<"Eroare la creere thread 1, iesim." << endl;
  return 0;
 }
 HANDLE trdHdl2 = CreateThread(NULL, 0, trdLoop, NULL, 0, &identificatorTRD2);
 if(trdHdl2 == NULL)
    {
        cout << "Eroare la creere thread 2, iesim." << endl;
        return 0;
    }

    WaitForSingleObject(trdHdl1, INFINITE); // asteptam ca threadul1 sa termine
    WaitForSingleObject(trdHdl2, INFINITE); // asteptam ca threadul2 sa termine
 // inchidem handle-urile
 CloseHandle(trdHdl1);
 CloseHandle(trdHdl2);
 CloseHandle(mutex);

    return 0;
}

   CriticalSection(sectiune critica):
      - Este asemanator unui mutex, doar ca o sectiune critica poate fi folosita doar in interiorul unui singur
proces, pe cand un mutex, poate fi folosit si la comunicarea intre procese. Procesul de utilizare a unei
sectiuni critice este urmatorul:
         - definirea unei structuri de tipul CRITICA_SECTION, in multe cazuri aceasta strucuta va fi globala, dar nu este obligatoriu este important ca threadurile ce se vor folosi de ea, sa o poata accesa, intr-un fel sau altul.
         - initializarea acestei structuri cu ajutorul functiei InitializeCriticalSection
         - un tread va incerca sa preia controlul asupra unei sectiuni critice cu ajutorul functiei
EnterCriticalSection, care va astepta in cazul in care sectiunea critica este detinuta de alt proces/thread,
sau TryEnterCriticalSection care va returna imediat indiferent daca va putea sau nu sa preia proprietatea
acelei sectiuni critice
         - parasirea sectiuni critice cu functia LeaveCriticalSection, aceasta functie trebuie folosita de fiecare data cand folositi una din functiile de intrare.
      Haide sa vedem un exemplu: Folosim un exemplu aproape identic cu cel de la Mutex doar ca in cazul acesta folosim o sectiune critica pentru a proteja variabila ctr.

#include <iostream>
#include <windows.h>

using namespace std;

int ctr = 0; // counterul ce va fi incrementat pe rand de cele 2 threaduri
CRITICAL_SECTION crit_sect; // sectiunea critica ce va progeja  variabila ctr;
/**
DWORD WINAPI trdFnk(LPVOID lpParam)
{
    for(int i = 0; i< 100;i++)
    {
        cout << i << endl;
        Sleep(100); // Am adaugat un mic timer ca threadul sa nu termine treaba asa repede :)
    }
    cout << "Exiting thread\n";
    ExitThread((DWORD) 0);
}
*/
DWORD WINAPI trdLoop(LPVOID lpParam)
{
    DWORD dwWaitResult; // va stoca rezultatul actiuni de asteptare pentru obtinerea proprietatii threadului
    do
    {
        EnterCriticalSection(&crit_sect); // intram in sectiunea critica
        {
            cout << GetCurrentThreadId() << " " << ctr++ << endl; // efectuam operatia pe care o dorim
        }
        LeaveCriticalSection(&crit_sect); // iesim din sectiunea critical
        //cout << i << endl;
        Sleep(10); // dam timp si celuilalt thread sa preia controlul aspura sectini critice si sa faca incrementarea
        // in caz contrar doar primul thread va face incrementarile.
    }while(ctr < 100);
}

int main()
{
    DWORD identificatorTRD1 = 0;
    DWORD identificatorTRD2 = 0;
    // creem intai seciunea critica deoarece daca creem intai threadurile vor porni direct
    // si vor incerca sa acceseze o sectiune critiune critica inexistenta
    InitializeCriticalSection(&crit_sect); // initializam sectiunea critica
    HANDLE trdHdl1 = CreateThread(NULL, 0, trdLoop, NULL, 0, &identificatorTRD1);
 if(trdHdl1 == NULL)
 {
  cout <<"Eroare la creere thread 1, iesim." << endl;
  return 0;
 }
 HANDLE trdHdl2 = CreateThread(NULL, 0, trdLoop, NULL, 0, &identificatorTRD2);
 if(trdHdl2 == NULL)
    {
        cout << "Eroare la creere thread 2, iesim." << endl;
        return 0;
    }

    WaitForSingleObject(trdHdl1, INFINITE); // asteptam ca threadul1 sa termine
    WaitForSingleObject(trdHdl2, INFINITE); // asteptam ca threadul2 sa termine
 // inchidem handle-urile
 CloseHandle(trdHdl1);
 CloseHandle(trdHdl2);

    return 0;
}
   Semaphores:
      - Semafoarele sunt obiecte folosite pentru a limita numarul de threaduri care pot avea acces la o resursa
in acelasi timp Sa zicem ca avem o baza de date si vrem sa limitam numarul de conexiuni la acea baza de
date. In cazul acesta vom folosi un semaphore Creerea unui semaphore se face cu ajutorul urmatoarei functii:
      HANDLE WINAPI CreateSemaphore( _In_opt_ LPSECURITY_ATTRIBUTES                                          lpSemaphoreAttributes,
         _In_ LONG lInitialCount,
         _In_ LONG lMaximumCount,
         _In_opt_ LPCTSTR lpName );
      Daca functia este executata cu succes atunci aceasta returneaza un handle catre obiectul dorit. Daca
obiectul exista deja(pe baza numelui furnizat ultimului argument) atunci este returnat un handle catre acest
obiect deja existent. In acest caz se poate apela functia GetLastError care va returna
ERROR_ALREADY_EXISTS. In cazul in care functia esueaza, valoarea returnata va fi NULL.
      Parametrii asteptati de aceasta functie sunt urmatorii:
         - _In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes la fel ca si in celelalte cazuri de mai sus acest parametru spune ce permisiuni are acest semaphore
         - _In_ LONG lInitialCount - numarul initial de threaduri/procese care pot sa acceseze
aceasta resursa. Atunci cand un thread/proces preia proprietatea asupra acestui semaphore aceasta valoare
este decrementata, iar cand threadul respectiv elibereaza semaphore-ul, cu ajutorul functiei
ReleaseSemaphore, aceasta valoare este incrementata. Valoarea aceasta nu poate depasii valoarea
lMaximumCount
         - _In_ LONG lMaximumCount - numarul maxim de threaduri/procese care pot accesa aceasta resursa          - _In_opt_ LPCTSTR lpName - numele semaphore-ului. Mai jos aveti un exemplu in care mai multe
threaduri incrementeaza o variabila, totusi semaforul nu permite decat la 4 threaduri sa faca incrementarea in
acelasi timp.
      In acest exemplu ne folosim de sectiunea critica pentru a ne asigura ca nu sunt 2 threaduri care incearca sa scrie aceeasi variabila in acelasi timp.

#include <iostream>
#include <windows.h>

using namespace std;

int ctr = 0; // counterul ce va fi incrementat pe rand de cele 2 threaduri
CRITICAL_SECTION crit_sect; // Sectiune critica ce va proteja variabila ctr de la a fi scrisa de mai multe threaduri odata.
HANDLE semafor; // ce se va asigura ca doar 4 threaduri lucreaza in acelasi timp
int MAX_SEM = 4; // numarul maxim de threaduri ce pot accesa variabila
int MAX_THREADS = 10; // numarul maxim de threaduri

DWORD WINAPI trdLoop(LPVOID lpParam)
{
    DWORD dwWaitResult; // va stoca rezultatul actiuni de asteptare pentru obtinerea proprietatii threadului
    for(int i = 0; i < MAX_THREADS; i++)
    {
        dwWaitResult = WaitForSingleObject(semafor, INFINITE); // asteptam ca semaforul sa semnalizeze ca avem permisiunea sa accesam zona respectiva
        switch(dwWaitResult)
        {
        case WAIT_OBJECT_0:
            {
                EnterCriticalSection(&crit_sect); // intram in sectiunea critica
                {
                    cout << GetCurrentThreadId() << " " << ctr++ << endl; // efectuam operatia pe care o dorim
                }
                LeaveCriticalSection(&crit_sect); // iesim din sectiunea critical
                break;
            }
        default:
            {
                cout << "Caz necunoscut, eroare: " << GetLastError() << endl;
            }

        }
        ReleaseSemaphore(semafor, 1, NULL);

        //cout << i << endl;
        Sleep(10); // dam timp si celuilalt thread sa preia controlul aspura sectini critice si sa faca incrementarea
        // in caz contrar doar primul thread va face incrementarile.
    }
}

int main()
{
    DWORD identificatoriThreaduri[MAX_THREADS];
    semafor = CreateSemaphore(NULL, MAX_SEM, MAX_SEM, NULL);
    HANDLE thrds[MAX_THREADS];
    if(semafor == NULL)
    {
        cout << "Eroare la creere semafor, iesim." << endl;
        return 0;
    }
    // creem intai mutex-ul deoarece daca creem intai threadurile vor porni direct
    // si vor incerca sa acceseze un mutex care nu exista
    InitializeCriticalSection(&crit_sect);
    for(int i = 0; i < MAX_THREADS;i++) // creem cele 10 threaduri
    {
        thrds[i] = CreateThread(NULL, 0, trdLoop, NULL, 0, &identificatoriThreaduri[i]);
        if(thrds[i] == NULL)
        {
            cout << "Eroare la creere thread numarul " << i << " iesim. " << endl;
            return 0;
        }
    }
    WaitForMultipleObjects(MAX_THREADS, thrds, TRUE, INFINITE); // asteptam ca threadurile sa termine.

    for(int i = 0; i < MAX_THREADS;i++)
    {
        CloseHandle(thrds[i]);
    }

    return 0;
}
   Waitable time
      -Este un obiect folosit in mare parte atunci cand avem nevoie sa verificam sau sa facem ceva la un
anumit interval de timp Sa zicem ca o data la 5 secunde trebuie sa verificam ceva si sa facem ceva dupa,
in cazul acesta s-ar putea folosi un waitable timer.
      El este creat cu functia urmatoare:
         HANDLE WINAPI CreateWaitableTimer( _In_opt_ LPSECURITY_ATTRIBUTES                                     lpTimerAttributes,
            _In_ BOOL bManualReset,
            _In_opt_ LPCTSTR lpTimerName );
         Daca functia este executata cu succes atunci aceasta returneaza un handle catre obiectul dorit. Daca
obiectul exista deja(pe baza numelui furnizat ultimului argument) atunci este returnat un handle catre acest
obiect deja existent. In acest caz se poate apela functia GetLastError care va returna
ERROR_ALREADY_EXISTS. In cazul in care functia esueaza, valoarea returnata va fi NULL.
      Parametri:
         - _In_opt_ LPSECURITY_ATTRIBUTES lpTimerAttributes descriptorul de securitate comun celor 5 obiecte folosite in sincronizare
         - _In_ BOOL bManualReset specifica daca este un timer ce trebui resetat manual sau se va reseta
singur. Daca valoarea este adevarata atunci timerul va trebui resetat de catre programator dupa efectuarea
unui WaitOnSingleObject/WaitOnMultipleObjects. Daca acest parametru este false atunci timer-ul se va
reseta automat atunci cand are loc cu succes o asteptare pentru una din functiile de asteptare.
         - _In_opt_ LPCTSTR lpTimerName numele timer-ului, acesta poate fi NULL Trebuie avut grija cu
aceste obiecte insa, in conditiile in care timer-ul este verificat de prea multe ori, poatet folosi o parte mare din
timpul procesorului, acest lucru ne mai permitand celorlalte threaduri sa execute operatiile lor. Din aceasta
cauza este de preferat, acolo unde se poate sa se foloseasca evenimente, despre care vom discuta un pic
mai jos.
      Mai jos aveti un exemplu de folosire al waitable timers:
#include <iostream>
#include <windows.h>

using namespace std;

int ctr = 0; // counterul ce va fi incrementat pe rand de cele 2 threaduri
HANDLE timer; // ce se va asigura ca doar 4 threaduri lucreaza in acelasi timp

DWORD WINAPI trdLoop(LPVOID lpParam)
{
    DWORD dwWaitResult; // va stoca rezultatul actiuni de asteptare pentru obtinerea proprietatii threadului
    LARGE_INTEGER timpAsteptare;
    timpAsteptare.QuadPart = -1000000LL; // 100 milisecunde (1/100) secunde in pasi de 100 nanosecunde
    do
    {
        dwWaitResult = WaitForSingleObject(timer, INFINITE);
        if(dwWaitResult != WAIT_OBJECT_0)
        {
            cout << "Eroare la asteptare: " << GetLastError() << endl;
        }
        cout << "Thread " << GetCurrentThreadId() << " " << ctr++ << endl;
        if(!SetWaitableTimer(timer, &timpAsteptare, 0, NULL, NULL, 0))
        {
            cout << "Eroare la setare timer, iesim. " << endl;
            return 1;
        }
    }while (ctr < 10);
}

int main()
{
    timer = CreateWaitableTimer(NULL, TRUE, NULL);// creem timer cu resetare manuala
    LARGE_INTEGER timpAsteptare;
    HANDLE thrd;
    DWORD identificatorThread;
    timpAsteptare.QuadPart = -1000000LL;
    if(timer == NULL)
    {
        cout << "Eroare la creere waitable timer, iesim. " << endl;
        return 0;
    }
    if(!SetWaitableTimer(timer, &timpAsteptare, 0, NULL, NULL, 0)) // il setam la 100 milisecunde
    {
        cout << "Eroare la setare timer, iesim. " << endl;
        return 0;
    }
    thrd = CreateThread(NULL, 0, trdLoop, NULL, 0, &identificatorThread); // creem si rula, thread
    if(thrd == NULL)
    {
        cout << "Eroare la creere thread, iesim. " << endl;
        return 0;
    }

    WaitForSingleObject(thrd, INFINITE); //asteptam ca threadul sa termine

    CloseHandle(thrd); // inchidem threadul
    CloseHandle(timer); // inchidem timerul


    return 0;
}
 
   Evenimente
      -Comparativ cu celelalte obiecte de sincronizare nu sunt folosite pentru a proteja variabile comune sau
pentru a face o actiune la un anumit interval de timp. Evenimentele sunt folosite pentru a semnaliza intre
threaduri intalnirea anumitor conditii, incheierea unei anumite actiuni, smd. Procesul de utilizare al
evenimentelor este urmatorul:
         - Creere eveniment cu anumite stari(manual reset/auto reset, stare initiala semnalizata sau nesemnalizata
         - Setarea evenimentului(in general de catre cel care l-a si creat) - In alt threadu/proces sau in acelasi
thread asteptam ca threadul sa devina semnalizat cu functia WaitForSingleObject, sau daca avem de
asteptat dupa mai multe evenimente cu WaitForMultipleObjects
         - Daca evenimentul este creeat cu manual reset in resetam cu ajutorul functiei SetEvent, sau daca
dorim sa il resetam inainte ca acesta sa fie prelucrat cu functia WaitForSingleObject /
WaitForMultipleObjects folosim functia ResetEvent.
      Creerea unui event se face cu ajutorul urmatoarei functii:
         HANDLE WINAPI CreateEvent( _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
            _In_ BOOL bManualReset,
            _In_ BOOL bInitialState,
            _In_opt_ LPCTSTR lpName );
         Parametri:
            - _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes - descriptorul a tributelor de securitate             - _In_ BOOL bManualReset specifica daca evenimentul se reseteaza manual(cu functiile specificate
mai sus) sau se reseteaza singur la starea de nesemnalizare
            - _In_ BOOL bInitialState specifica daca evenimentul este creeat in stare semnalizata sau
nesemnalizata. Un eveniment, sau orice alt obiect cu care am lucrat pana acum, care este in stare semnalizata
va fi prins de functiile de genul WaitForSingle/MultipleObject. Atunci cand un astfel de obiect este
nesemnalizat atunci functiile de asteptare ruleaza pana cand obiectul este semnalizat
            - _In_opt_ LPCTSTR lpName numele evenimentului. In acest caz este de preferat sa numiti
evenimentele, pentru a fi mai usor de utilizat.
         Daca functia este executata cu succes atunci aceasta returneaza un handle catre obiectul dorit.
Daca obiectul exista deja(pe baza numelui furnizat ultimului argument) atunci este returnat un handle catre
acest obiect deja existent. In acest caz se poate apela functia GetLastError care va returna
ERROR_ALREADY_EXISTS. In cazul in care functia esueaza, valoarea returnata va fi NULL.
         Mai jos avem un mic exemplu. Codul creeaza 2 threaduri, primul thread ruleaza incrementand
variabila ctr, al 2-lea thread asteapta in acest timp ca primul thread sa termine. Cand threadul termina
semnalizeaza acest lucru cu functia SetEvent. Dupa asta al 2-lea thread face si el incrementarile pe care le
are de facut.

#include <iostream>
#include <windows.h>

using namespace std;

int ctr = 0; // counterul ce va fi incrementat pe rand de cele 2 threaduri
HANDLE event; // evenimentul nostru

DWORD WINAPI trdLoop1(LPVOID lpParam) // primul thread
{
    for(int i = 0; i < 10; i++)
    {
        cout << "Thread " << GetCurrentThreadId() << " " << ctr++ << endl;
        Sleep(100);
    }
    if(!SetEvent(event)) // Setam evenimentul ca semnalizat, in acest caz al 2-lea thread primeste confirmarea ca am terminat si poate incepe sa ruleze
    {
        cout << "Eroare la setare eveniment, iesim." << endl;
        return 0;
    }
}

DWORD WINAPI trdLoop2(LPVOID lpParam) // al 2-lea thread
{
    DWORD dwWaitResult = WaitForSingleObject(event, INFINITE);
    if(dwWaitResult == WAIT_OBJECT_0)
    {
        for(int i = 0; i < 10; i++)
        {
            cout << "Thread " << GetCurrentThreadId() << " " << ctr++ << endl;
            Sleep(100);
        }
    }
}

int main()
{
    DWORD identificatorTrd1;
    DWORD identificatorTrd2;
    event = CreateEvent(NULL, TRUE, FALSE, "TRD1Finish"); // creem evenimentul si ne asiguram ca e corect creat
    if(event == NULL)
    {
        cout << "Eroare la creere eveniment, iesim. " << endl;
        return 0;
    }
    HANDLE trd1;
    HANDLE trd2;
    trd1 = CreateThread(NULL, 0, trdLoop1, NULL, 0, &identificatorTrd1); // creem thread 1
    if(trd1 == NULL)
    {
        cout << "Eroare la creere thread1, iesim. " << endl;
        return 0;
    }
    trd2 = CreateThread(NULL, 0, trdLoop2, NULL, 0, &identificatorTrd2); // creem thread 2
    if(trd2 == NULL)
    {
        cout << "Eroare la creere thread2, iesim. " << endl;
        return 0;
    }

    WaitForSingleObject(trd1, INFINITE);
    WaitForSingleObject(trd2, INFINITE);

    CloseHandle(trd1);
    CloseHandle(trd2);
    CloseHandle(event);


    return 0;
}


   Pana acum am vorbit despre creerea, resumarea si inchiderea unui thread, si despre elementele de
sincronizare a datelor si threadurilor. Totusi in toate cele 3 cazuri am o functie pe care nu am explicat-o.

   Aceasta este WaitForSingleObject(trdHdl, INFINITE); De fapt functia aceasta ce face? Asteapta dupa un semnal de la unul din posibilele handle-uri pe care le accepta, si anume : thread, mutex, event, semaphore si sectiune critica.
      Pentru threade-uri semnalul asteptat este de incheiere al executiei
      Pentru mutex asteapta ca mutex-ul sa semnalizeze ca nu mai este "inchis"/"detinut" de catre un alt proces/thread
      Pentru semaphores asteapta pana cand exista un loc liber in lista cu obiecte ce detin proprietatea asupra semaforului.
      Pentru sectiuni critice actioneaza la fel ca si in cazul mutexilor
      Pentru evenimente asteapta ca avenimentul respectiv sa devina semnalizat.
   
      Functia este cam asa:
         DWORD WINAPI WaitForSingleObject(
            _In_ HANDLE hHandle,
            _In_ DWORD dwMilliseconds
            );
         - _In_ HANDLE hHandle este dupa cum am spus mai sus, handle-ul threadului pentru care dorim sa
asteptam sa termine.
         - _In_ DWORD dwMilliseconds este perioada de timp pe care programul sa o astepte. Acest al 2-lea argument poate primi 3 valori:
            - valoarea zero caz in care pur si simplu verifica daca threadul a semnalizat ca si-a incheiat procesul, dupa care functia iese
            - o valoare nonzero pozitiva, in milisecunde dupa cum numele sugereaza
            - valoarea INFINITE caz in care functia va astepta pana cand threadul va semnaliza iesirea(dupa cum am facut noi acum)
      Functia asta poate returna 4 valori:
         - WAIT_ABANDONED este returnat cand in thread termina executia inainte sa elibereze un mutex pe care il detine
         - WAIT_OBJECT_0 este returnat atunci cand threadul asteptat si-a semnalizat incheierea executiei
         - WAIT_TIMEOUT este returnat atunci cand parametrului dwMilliseconds ii este furnizata o valoare diferita de 0 si de INFINITE, iar aceasta perioada trece
         - WAIT_FAILED indica faptul ca a aparut o eroare in asteptarea threadului respectiv. Pentru a vedea ce eroare am primit se poate apela functia GetLastError. Atentie, aceasta functie trebuie apelata imediat
dupa executia functiei aceasta deoarece orice alta functie din WinApi ce poate intalni o eroare va inlocui eroarea dorita de noi.

   Exista o functie sora a acesteia, care asteapta un anumit set de threaduri sa termine executia
      Functia arata cam asa:
         DWORD WINAPI WaitForMultipleObjects(
            _In_ DWORD nCount,
            _In_ const HANDLE *lpHandles,
            _In_ BOOL bWaitAll,
            _In_ DWORD dwMilliseconds
            );
         - _In_ DWORD nCount reprezinta numarul de handle-uri pentru care functia trebuie sa astepte. Acest trebuie sa aiba valoarea mai mare decat 0, si in mod normal ar trebui sa fie egal cu numarul de elemente din lpHandles
         - _In_ const HANDLE *lpHandles este un pointer catre o matrice ce contine handle-urile catre threadurile noastre. Aveti grija, in aceasta matrice nu trebuie sa existe handle-uri care sa indice catre acelasi
thread. De asemenea aveti grija sa nu inchideti threadurile cu CloseHandle inainte ca acestea sa termine,
deoarece functia nu va mai functiona corect. In acest caz este vorba de "comportament nedefinit".
         - _In_ BOOL bWaitAll specifica daca functia sa astepte dupa toate threadurile sau doar dupa 1 din ele. Daca parametrul este TRUE atunci functia va astepta dupa absolut toate threadurile din matrice. Daca parametrul este FALSE atunci functia va incheia executa in momentul in care primul thread semnalizeaza ca
si-a incheiat executia.
         - _In_ DWORD dwMilliseconds reprezinta perioada pe care functia o va astepta, la fel ca si la functia WaitForSingleObject si aceasta va accepta aceleasi valori:
            - valoarea zero caz in care pur si simplu verifica daca threadul a semnalizat ca si-a incheiat procesul, dupa care functia iese
            - o valoare nonzero pozitiva, in milisecunde dupa cum numele sugereaza
            - valoarea INFINITE caz in care functia va astepta pana cand threadul va semnaliza iesirea(dupa cum am facut noi acum)
      Functia aceasta poate returna urmatoarele valori:
         - WAIT_OBJECT_0 daca parametrul bWaitAll este TRUE. Asta inseamna ca toate threadurile au semnalizat ca si-au incheiat executia.
         - WAIT_OBJECT_0 + (nCount - 1) daca parametrul bWaitAll este false. In cazul asta valoarea returnata va fi suma dintre WAIT_OBJECT_0 si indexul la care se afla threadul ce a semnalizat incheierea
executiei. Evident, pentru a afla care este indexul threadului respectiv in matricea cu threaduri este suficient sa scadem din numarul returnat valoarea WAIT_OBJEC_0
         - WAIT_ABANDONED_0 daca bWaitAll este TRUE indica faptul ca cel putin unul din handle-uri
este un mutex abandonat(vezi aceeasi valoare de return la WaitForSingleObject) si ca celelalte threaduri au
semnalizat incheierea executiei
         - WAIT_ABANDONED_0 daca bWaitAll este FALSE indica indexul unui mutex care a fost abandonat(procesul/threadul care l-a creeat si-a terminat executia sau a fost inchis) dar care totusi este in stare semnalizata. In acest caz, proprietatea asupra threadului va fi trecuta catre threadul/procesul care a
executat functia de asteptare iar mutex-ul va fi trecut in stare nesemnalizata.
         - WAIT_TIMEOUT este returnat atunci cand perioada stabilita prin parametrul dwMillisecond este un numar finit iar conditia pusa de parametrul bWaitAll nu este satisfacuta(pentru bWaitAll adevarat, nu toate obiectele asteptate au fost semnalizate, daca bWaitAll este fals, nici
unul din obiectele asteptate nu a semnalizat.
         - WAIT_FAILED indica faptul ca a existat o problema in executarea functiei. In acest caz se poate obtine textul erorii cu ajutorul functiei GetLastError.


   Cam aici se incheie primul tutorial privind threadurile din WinApi in C++. Vor mai urma cateva tutoriale cu notiuni pe aceasta tema in care vom discuta si alte subiecte
Ca si un fel de tema de acasa incercati sa faceti si voi cate o mini aplicatie in care sa folositi cele invatate in acest tutorial.
Ca si cateva idei, puteti sa faceti un joculet in care sa folositi un waitable timer pentru a permite utilizatorului doar un anumit timp pentru efectuarea unei actiuni,
sau ati putea sa scriei un programel care pur si simplu copiaza un fisier dintr-o parte in alta(intr-un alt thread) iar la final threadul ce face copierea sa semnalizeze incheierea printr-un event.
Sper ca v-a placut tutorialul si ca va v-a ajuta. Daca aveti intrebari nu eziatati sa le puneti.

Tuesday, July 3, 2012

[C++]Functii odbc operare baza date access 2003-2007




Salut, va prezint un alt proiect mai vechi, de aceasta data legat de operarea cu bazele de date access - asta aveam nevoie la timpul respectiv. Sunt doar 4 functii simple una pentru "select" din sql, alta pentru "insert", una pentru recuperarea erori/erorilor si una pentru a face curat dupa celelalte 3 functii. Functiile sunt foarte simple, dar pentru proiectul la care am avut nevoie la momentul respectiv si-au facut treaba cu brio. Pentru a le utiliza aveti nevoie sa legati (link) libraria odbc32. Functiile se folosesc de api-ul ODBC, si pot fi modificate pentru a putea rula cu orice tip de baza de date la care va puteti conecta cu ODBC. Pentru a le utiliza nu trebuie decat sa adaugati cele 2 fisiere proiectului vostru.

functii_odbc.h
#ifndef FUNCTII_ODBC_H_INCLUDED
#define FUNCTII_ODBC_H_INCLUDED

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
#include <windows.h>
#include <sql.h>
#include <sqlext.h>


int select(std::string dns, int numarCampuri, std::string querry, std::vector<std::vector< std::string> >& array, std::string& erroare);
void getErrors(SQLSMALLINT type,SQLHANDLE handle, std::string& error);
void freeDbResources(HSTMT& hStmt, HENV& hEnv, HDBC& hDbc);
int insert(std::string dns, std::string num_Tabel, std::vector<std::string> Campuri, std::vector<std::string> Valori, std::string& error);

#endif // FUNCTII_ODBC_H_INCLUDED


functii_odbc.cpp
#include "functii_odbc.h"
/**
 * Lista argumente:
 * string dns = adresa fisierului acces mdb/accdb pe hard-disk
 * numarCampuri = numarul de campuri care urmeaza a fi selectate prin querry-ul respectiv
 * vector<vector<string> >& array = este un vector de vectori de stringuri, trimis prin referinta care va contine datele extrase
 * in felul urmator: array[0][0] va fi prima coloana returnata pe primul rand
 * array[0][1] va fi a 2-a coloana
 * array[0][2] va fi a 3-a coloana smd
 * iar array[x][0], x va desemna randul de date extrase
 * string& eroare = va contine mesajele de eroare, in situatia in care apare o eroare in executia functiei sau a querry-ului atunci aceasta va fi salvata in acest string
 * Functia are doar 2 valori de return 1 = succes in executarea querry-ului, sau -1 eroare
 * Functia face doar cateva verificari de baza in privinta corectitudinii querry-ului
 * De asta trebuie sa se asigure utilizatorul inainte
 * Functia va verifica doar daca exista un posibil insert in querry caz in care iese.
 */
using namespace std;
int select(string dns, int numarCampuri, string querry, vector<vector<string> >& array, string& erroare)
{
    if(querry.find("SELECT") == string::npos || querry.find("INSERT") != string::npos)
    {
        erroare = "Nu ai introdus o comanda de select;";
    }
    else
    {
        SQLCHAR valoareCustomers[numarCampuri][128];
        HSTMT querryStmt;
        string tempNume;
        int retCode[numarCampuri];
        HENV hEnv;
        HDBC hDbc;
        RETCODE rc;
        int len = querry.size();
        char szConnStrOut[255];
        int iConnStrLength2Ptr;
        char szDSN[256] = "Driver={Microsoft Access Driver (*.mdb)};DSN='';DBQ=";
        unsigned char* statementQuerry = (unsigned char*)querry.c_str();
        //vStrArray.clear(); // golim vectorul primit ca container de output (este datoria utilizatorului sa se asigure ca nu are nimic important in vector inainte de a fi trimis acestei functii
        strcat(szDSN, dns.c_str());
        rc = SQLAllocEnv(&hEnv);
        if(!SQL_SUCCEEDED(rc))
        {
            erroare = "Nu am putut aloca Envorimentul.";
            cout << "Nu am putut aloca envorimentul.";
            freeDbResources(querryStmt, hEnv, hDbc);
            return -1;
        }
        rc = SQLAllocConnect(hEnv, &hDbc);
        if(!SQL_SUCCEEDED(rc))
        {
            erroare = "Nu am putut aloca conexiunea.";
            cout << "Nu am putut aloca conexiunea.";
            freeDbResources(querryStmt, hEnv, hDbc);
            return -1;
        }

        rc = SQLDriverConnect(hDbc, NULL, (SQLCHAR*)szDSN,
                            SQL_NTS, (SQLCHAR*)szConnStrOut,
                            255, (SQLSMALLINT*)&iConnStrLength2Ptr, SQL_DRIVER_NOPROMPT);
        if(!SQL_SUCCEEDED(rc))
        {
            getErrors(SQL_HANDLE_DBC , hDbc, erroare);
            freeDbResources(querryStmt, hEnv, hDbc);
            return -1;
        }
        rc = SQLAllocStmt(hDbc, &querryStmt);
        if(!SQL_SUCCEEDED(rc))
        {
            erroare = "Nu am putut aloca statementul.";
            cout << "Nu am putut aloca statementul.";
            freeDbResources(querryStmt, hEnv, hDbc);
            return -1;
        }
        rc = SQLPrepare(querryStmt, statementQuerry, SQL_NTS);
        if(!SQL_SUCCEEDED(rc))
        {
            erroare = "Nu am putut pregatii statementul.";
            cout << "Nu am putut pregatii statementul.";
            freeDbResources(querryStmt, hEnv, hDbc);
            return -1;
        }
        for(int i = 0; i < numarCampuri; i++)
        {
           rc = SQLBindCol(querryStmt, (i + 1), SQL_C_CHAR, valoareCustomers[i], 128, (SQLINTEGER*)&retCode[i]);
           if(!SQL_SUCCEEDED(rc))
            {
                erroare = "Nu am putut bindui coloana.";
                cout << "Nu am putut bindui coloana.";
                freeDbResources(querryStmt, hEnv, hDbc);
                return -1;
            }
        }
        cout << endl << endl << statementQuerry << endl;
        rc = SQLExecute(querryStmt);
        if(SQL_ERROR == rc || SQL_SUCCESS_WITH_INFO == rc)
        {
            cout << "Eroare in executare statement, sau SQL_SUCCESS_WITH_INFO";
            erroare = "Eroare in executare statement, sau SQL_SUCCESS_WITH_INFO";
            getErrors(SQL_HANDLE_STMT, querryStmt, erroare);
            return -1;
        }
        else if(SQL_NEED_DATA == rc)
        {
            cout << "SQL_NEED_DATA.";
            erroare = "SQL_NEED_DATA.";
            getErrors(SQL_HANDLE_STMT, querryStmt, erroare);
            return -1;
        }
        else if(SQL_STILL_EXECUTING == rc)
        {
            cout << "SQL_STILL_EXECUTING.";
            erroare = "SQL_STILL_EXECUTING.";
            getErrors(SQL_HANDLE_STMT, querryStmt, erroare);
            return -1;
        }
        else if(SQL_NO_DATA == rc)
        {
            cout << "SQL_NO_DATA.";
            erroare = "SQL_NO_DATA.";
            getErrors(SQL_HANDLE_STMT, querryStmt, erroare);
            return -1;
        }
        else if(SQL_INVALID_HANDLE == rc)
        {
            cout << "SQL_INVALID_HANDLE.";
            erroare = "SQL_INVALID_HANDLE";
            getErrors(SQL_HANDLE_STMT, querryStmt, erroare);
            return -1;
        }
        rc = SQLFetch(querryStmt);
        if(SQL_SUCCESS == rc)
        {
            while(SQL_SUCCEEDED(rc))
            {
                vector<string> vString;
                string temp;
                for(int i = 0; i < numarCampuri; i++)
                {
                    temp = (char*)valoareCustomers[i];
                    vString.push_back(temp);
                }
                array.push_back(vString);
                rc = SQLFetch(querryStmt);
            }
            freeDbResources(querryStmt, hEnv, hDbc);
            return 1;
        }
        else if(SQL_SUCCESS_WITH_INFO == rc || SQL_ERROR == rc)
        {
            cout << "Nu am putut executa SQLFetch.\n";
            cout << "Nu am putut executa SQLFetch.\n";
            getErrors(SQL_HANDLE_STMT, querryStmt, erroare);
            freeDbResources(querryStmt, hEnv, hDbc);
            return -1;
        }
        else if(SQL_STILL_EXECUTING == rc)
        {
            cout << "Inca se executa statementul.\n";
            erroare = "Inca se executa statementul.\n";
            freeDbResources(querryStmt, hEnv, hDbc);
            return -1;
        }
        else if(SQL_NO_DATA == rc)
        {
            cout << "Nu exista date de returnat.\n";
            erroare = "Nu exista date de returnat.\n";
            freeDbResources(querryStmt, hEnv, hDbc);
            return -1;
        }
    }
    return -1;
}



/**
 * Functia preia prin referinta handle-urile pentru conexiune, mediu si statement si le elibereaza
 */
void freeDbResources(HSTMT& hStmt, HENV& hEnv, HDBC& hDbc)
{
    SQLFreeStmt(hStmt, SQL_UNBIND);
    SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
    SQLDisconnect(hDbc);
    SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
}


/**
 * Functia preia datele necesare pentru a putea prelua o eroare legata de un handle pentru conexiune, statement sau mediu
 * SQLSMALLINT type poate fi: QL_HANDLE_DBC, SQL_HANDLE_DBC_INFO_TOKEN, SQL_HANDLE_DESC, SQL_HANDLE_ENV, SQL_HANDLE_STMT
 * SQLHANDLE handle va fi de fapt handle-ul pentru care aven nevoie sa preluam eroarea
 * string& eroare = va tine textul erori
 * Functia preia doar erorile care pot fi preluate prin SQLGetDiagRec.
 * Mai multe informatii despre SQLGetDiagRec aici: http://msdn.microsoft.com/en-us/library/windows/desktop/ms716256(v=vs.85).aspx
 */
void getErrors(SQLSMALLINT type,SQLHANDLE handle, string& eroare)
{
    SQLINTEGER i = 1;
    SQLINTEGER native; // codul nativ al erori ( depinde de sistemul de operare si de driver
    SQLCHAR state[7]; // starea driverului mai multe informatii despre state : http://msdn.microsoft.com/en-us/library/windows/desktop/ms716412(v=vs.85).aspx
    SQLCHAR text[256]; // textul erori
    SQLSMALLINT len; // marimea textului erori
    SQLRETURN ret; // stocheaza raspunsul functiei SQLGetDiagRec
    ret = SQLGetDiagRec(type, handle, i, (SQLCHAR*)state, &native, (SQLCHAR*)text, sizeof(text), &len );
    if(SQL_SUCCESS == ret || SQL_SUCCESS_WITH_INFO == ret)
    {
        stringstream buffer(stringstream::in | stringstream::out);
        string temp;
        eroare = "Eroare:\n";
        eroare.append("State: ");
        cout << "State: " << state << endl;
        buffer << state;
        buffer >> temp;
        eroare.append(temp);
        buffer.flush();
        string temp3((char*)text);
        eroare.append("\nTextul eroarei: ");
        eroare.append(temp3);
        buffer.flush();
        cout << "Textul erori: " << text << endl;
    }
    else if(SQL_ERROR == ret)
    {
        eroare = "eroare Raporting: SQLGetDiagRec returneaza o eroare.\n";
    }
    else if(SQL_INVALID_HANDLE == ret)
    {
        eroare = "eroare Raporting: Handleul furnizat pentru preluarea erori a fost incorect.\n";
    }
    else if(SQL_NO_DATA == ret)
    {
        eroare = "eroare Raporting: Nu au fost date de returnat.\n";
    }
}

/**
 * Lista argumente:
 * string dns = adresa pe disk a fisierului access
 * string num_Tabel = numele tabelului in care se face inserarea
 * vector<string> Campuri = Un vector ce contine campurile in care se va face inserarea
 * vector<string> Valori = Un vector cu valorile ce vor fi introduse in fiecare camp
 * string& eroare va tine textul oricarei posibile erori
 * Pentru cei doi vectori marimea lor trebuie sa fie identica.
 * Exemplu Campuri va contine stringurile "Nume", "Prenume" "telefon"
 * vectorul Valori va contine "ION", "VASILE", "02345241590"
 * Functia are doar 2 valori de return 1 = succes in inserarea datelor, sau -1 eroare
 */
int insert(string dns, string num_Tabel, vector<string> Campuri, vector<string> Valori, string& eroare)
{
    //Incepem pregatirea statmentului
    string comanda = "INSERT INTO ";
    string comanda2 = ") VALUES ('";
    string comanda3 = ");";
    string campuri_tabel;
    string campuri_valori;
    //verificam daca numarul de campuri si de valori e identic
    if(Campuri.size() != Valori.size())
    {
        eroare = "Numarul de campuri si de valori nu este identic. Pentru ca inserarea sa se poata efectua este necesar ca cele 2 sa fie identice.\n";
        return -1;
    }
    // pregatim lista cu campurile in care se introduc datele
    for(unsigned int i = 0; i < Campuri.size(); i++)
    {
        if(i == (Campuri.size() - 1))
        {
            campuri_tabel.append(Campuri[i]);
            //campuri_tabel.append(")");
        }
        else
        {
            campuri_tabel.append(Campuri[i]);
            campuri_tabel.append(", ");
        }
    }
    // pregatim lista cu valorile de introdus
    for(unsigned int i = 0; i < Valori.size(); i++)
    {
        if(i == (Valori.size() - 1))
        {
            campuri_valori.append(Valori[i]);
            campuri_valori.append("'");
        }
        else
        {
            campuri_valori.append(Valori[i]);
            campuri_valori.append("', '");
        }
    }
    string sql_statement;
    sql_statement.append(comanda);
    sql_statement.append(num_Tabel);
    sql_statement.append("(");
    sql_statement.append(campuri_tabel);
    sql_statement.append(comanda2);
    sql_statement.append(campuri_valori);
    sql_statement.append(");");
    // am terminat pregatirea statementului

    // pregatim conexiunea la bd
    // inceput definitii variabile necesare conexiunii la db
    HENV hEnv;
    HDBC hDbc;
    HSTMT hStmt;
    RETCODE rc;
    char szConnStrOut[255];
    int iConnStrLength2Ptr;
    char szDSN[256] = "Driver={Microsoft Access Driver (*.mdb)};DSN='';DBQ=";
    // incheiere definitii
    // Initiem conexiunea
    strcat(szDSN, dns.c_str());
    rc = SQLAllocEnv(&hEnv);
    if(!SQL_SUCCEEDED(rc))
    {
        eroare = "Nu am putut aloca Envorimentul.";
        cout << "Nu am putut aloca envorimentul.";
        return -1;
    }
    rc = SQLAllocConnect(hEnv, &hDbc);
    if(!SQL_SUCCEEDED(rc))
    {
        eroare = "Nu am putut aloca conexiunea.";
        cout << "Nu am putut aloca conexiunea.";
        return -1;
    }

    rc = SQLDriverConnect(hDbc, NULL, (SQLCHAR*)szDSN,
                           SQL_NTS, (SQLCHAR*)szConnStrOut,
                           255, (SQLSMALLINT*)&iConnStrLength2Ptr, SQL_DRIVER_NOPROMPT);
    if(!SQL_SUCCEEDED(rc))
    {
        getErrors(SQL_HANDLE_DBC , hDbc, eroare);
        freeDbResources(hStmt, hEnv, hDbc);
        return -1;
    }
    rc = SQLAllocStmt(hDbc, &hStmt);
    rc = SQLPrepare(hStmt, (SQLCHAR*)sql_statement.c_str(), SQL_NTS);
    rc = SQLExecute(hStmt);
    if(!SQL_SUCCEEDED(rc))
    {

        eroare = "Inserarea nu a putut fi efectuata.";
        cout << "Inserarea nu a putut fi efectuata.";
        getErrors(SQL_HANDLE_STMT, hStmt, eroare);
        freeDbResources(hStmt, hEnv, hDbc);
        return -1;
    }
    else if(SQL_SUCCEEDED(rc))
    {
        freeDbResources(hStmt, hEnv, hDbc);
        return 1;
    }
    return -1;
}
Sper sa va fie utile.

[C++]Generator de lista cuvinte(passwords)




Va prezint un proiect ceva mai vechi de al meu, acesta a fost scris in principiu pentru a genera toate combinatiile, ordonat, ale unei liste de caractere. Generarea cuvintelor se face incepand cu un cuvant format din prima litera a sirului * lungimea dorita a cuvantului si se va termina cu un cuvant format din ultima litera din sir * lingimea cuvantului cerut.De exemplu pentru un sir de caracter a-z si lungimea cuvantului de 5 caractere, functia va incepe cu "aaaaa" si va termina cu "zzzzz". Generarea se face printr-o functie recursiva, intr-un thread separat pentru a nu bloca interfata aplicatiei. Sistemul nu este nici cel mai eficient si nici cel mai rapid, totusi poate fi folosit ca punct de plecare. Proiectul a fost scris utilizant framework-ul wxWidgets, inclusiv modelul de threading al acestuia. Puteti compila cele 4 fisiere cpp/h adaugandu-le unui proiect menit sa compileze o aplicatie wxWidgets. In mod normal ar trebui sa poata fi compilata chiar si pe linux cu un minim de modificari, totusi nu am testat la momentul respectiv. Proiectul a fost postat initial aici, unde puteti si adresa intrebari daca aveti. Link-ul direct pentru a descarca proiectul se gaseste aici.

Friday, June 22, 2012

[c++]Crypted ip to ip chat




[RO]
Va prezint un program de chat securizat. Traficul dintre cele 2 persoane care vorbesc este criptat cu ajutorul algoritmului RSA, care este un algoritm te criptare cu cheie publica/privata.Libraria utilizata este crypto++ iar pentru interfata grafica am utilizat libraria wxWidgets versiunea 2.8.12. Mai jos voi adauga un link de download a proiectului, acesta a fost facut cu ajutorul ide-ului Code Blocks. Proiectul poate fi compilat cu orice alt ide prin adaugarea celor 4 fisiere cpp/h unui proiect gol creat de voi, atat timp cat proiectul este setat corect pentru a putea compila un proiect wxWidgets. Puteti utiliza sample-urile care vin o data cu libraria wxWidgets ca punct de start. Pentru crypto++ sugerez sa folositi libraria statica. Proiectul in romana

[EN]
I present you a secure chat. The trafic between the 2 people talking is encrypted with the help of the RSA algorithm, which is a public/private key encryption algorithm. The library used is crypto++, and for the graphic interface i used the wxWidgets library version 2.8.12. Below i will ad a download link for the project, which was created with the help of Code Blocks ide. The project can be compiled with any other ide by adding the 4 cpp/h files to an empty project you create, as long as the project is correctly set up to compile wxWidgets application. You can use the samples that come along the wxWidgets librari as a starting point. For crypto++ i sugest you use the static library. English project

Tuesday, September 14, 2010

[C++]Conversie numere scrise in cifre arabe si viceversa




Salut, mai jos va voi prezenta u bucata de cod scrisa in C++, care poate fi folosita in diferite aplicatii. Codul de mai jos v-a prelua de la utilizator fie o cifra intre 0 si 9 fie corespondentul lor scris adica : zero, unu, doi, si v-a afisa valoarea convertita, adica daca utilizatorul va introduce cifra 5 programul v-a afisa "cinci" . Dupa va voi explica ce face si codul in sine.
#include <iostream>
#include <vector>
#include <conio.h>
#include <string>
#include <stdlib.h>
 
using namespace std;
 
 
int main()
{    
   char din_nou = ' ';
   const int marime_vector = 10;
   int numar_de_control = 0; //folosit pentru controlul buclelor(loop's)
   string temp; // stocheaza imput-ul utilizatorului
   vector<string> numere(marime_vector);
   numere[0] = "zero";
   numere[1] = "unu";
   numere[2] = "doi";
   numere[3] = "trei";
   numere[4] = "four";
   numere[5] = "cinci";
   numere[6] = "sase";
   numere[7] = "sapte";
   numere[8] = "opt";
   numere[9] = "noua";
   do
   {
      numar_de_control = 0; // resetam numarul de control
      cout << "Acest program v-a prelula de la utilizator un input,\n";
      cout << "precum '7' sau 'sapte' si v-a printa pe ecran corespondentul lui.\n";
      getline(cin,temp); // preluam imput de la utilizator
 
      if(temp.size() == 1) // utilizatorul introduce un singur caracter
      {
         if((int(temp[0]) >= 48 && int(temp[0] <= 57)) || (temp[0] >= 0 && temp[0] <= 9)) // check if it's an number
         {
            for(int i = 0; i < 10; i++) // cautam numarul in vector
            {
               if(atoi(temp.c_str()) == i)
               {
                  cout << "Ai introdus " << temp << " adica " << numere[i] << "." << endl;
 
               }
 
            }
         }
         else
         {
            cout << "Nu ai introdus o cifra.\n";
         }
      }
      if(temp.size() > 1) // utilizatorul introduce mai mult de un caracter
      {
         for(unsigned int i = 0; i < temp.size(); i++) // verificam daca exista caractere scrise cu caps lock
         {
            if(temp[i] >= 65 && temp[i] <= 90)
            {
               cout << "Ai introdus caractere cu caps lock, acestea sunt interzise, vrei sa incerci din nou?\n";
               i = temp.size();
               numar_de_control = 1; // trecem numar_de_control la 1 daca gasim caractere cu caps lock
               din_nou = getch();
               break;
            }
         }
         if(numar_de_control != 0)
         {
            continue; // resetam bucla daca este gasit
         }
         else
         {
            for(int i = 0; i < marime_vector; i++) // cautam cifrele scrise pe litere in interiorul vectorului
            {
               if(temp == numere[i])
               {
                  cout << "Ai introdus " << temp << " adica " << i << ".\n";
                  i = marime_vector;
               }
               else if(i == (marime_vector - 1)) // bucla ajunge la ultimul element si nu a gasit nici un cuvant cunoscut
               {
                  if(temp != numere[marime_vector -1])
                  {
                     cout << "Cuvantul introdus nu este o cifra scrisa cu litere.\n";
                  }
               }
 
            }
         }
 
 
      }
 
      cout << "Vrei sa mai incerci o data ?\n";
      din_nou = getch();
      system("CLS"); // e un obicei prost dar pentru utilizatori de windows momentan nu este o problema
   }while(din_nou == 'y' || din_nou == 'Y');
 
 
 
   return 0;
}

Bun acum sa luam fiecare bucata de cod in parte si sa o analizam.
char din_nou = ' ';
   const int marime_vector = 10;
   int numar_de_control = 0; //folosit pentru controlul buclelor(loop's)
   string temp; // stocheaza imput-ul utilizatorului
   vector<string> numere(marime_vector);
   numere[0] = "zero";
   numere[1] = "unu";
   numere[2] = "doi";
   numere[3] = "trei";
   numere[4] = "four";
   numere[5] = "cinci";
   numere[6] = "sase";
   numere[7] = "sapte";
   numere[8] = "opt";
   numere[9] = "noua";
Ce avem de fapt aici? Avem declarate variabilele pe care le vom folosii.

char din_nou = ' '; 
Declaram variabila "din_nou", de tip char, caruia ii asignam un caracter spatiu, este intotdeauna bine sa initializam variabilele dupa declararea lor pentru a nu avea probleme pe parcursul rularii programului.Aceasta v-a pastra un caracter de control, la sfarsitul programului utilizatorul v-a fi intrebat daca doreste sa incerce din nou, daca v-a apasa "y" sau "Y" atunci programul o va lua de la inceput, daca v-a apasa alta tasta programul se v-a inchide.



const int marime_vector = 10;
Aici creem o valoare constanta de tip int, ea nu poate fi modificata pe parcursul rularii programului. Este preferata utilizarea valorilor constante asupra "constantelor magice". Constantele magice sunt valori atribuite unor variabile , valori care insa nu se vor modifica pe parcursul rularii programului.
Sa va dau un exemplu:

vector<string> numere[10]
Valoarea 10 este o "constanta magica" daca pe parcursul rularii unui program aceasta nu se v-a modifica.


string temp = " ";
Aici creem si initializam o variabila de tip string, ce v-a contine ceea ce utilizatorul v-a introduce atunci cand este rugat.


vector<string> numere(marime_vector);
numere[0] = "zero";
numere[1] = "unu";
numere[2] = "doi";
numere[3] = "trei";
numere[4] = "four";
numere[5] = "cinci";
numere[6] = "sase";
numere[7] = "sapte";
numere[8] = "opt";
numere[9] = "noua";
Aici creem un vector de tip string, cu o marime fixa, aceasta este marime_vector = 10. Aveti un pic mai sus explicatia pentru "marime_vector". Dupa ce creem vectorul il initializam, adica ii adaugam o valoare initiala.

Pentru a nu fi necesara repornirea programului pentru fiecare incercare o sa folosim o constructie de tipul

do
{
   Cod de executat aici
}while(conditii de reexecutare)
prin folosirea "do" ne asiguram ca programul este executat cel putin o data.

while(conditii de executare)
{
 cod de executat
}
O constructie de genul "while" v-a verifica intai conditiile si abea apoi v-a rula codul, ceea ce poate duce la neexecutarea codului daca nu sunt indeplinite conditiile nici macar o data.


numar_de_control = 0; 
cout << "Acest program v-a prelula de la utilizator un input,\n";
cout << "precum '7' sau 'sapte' si v-a printa pe ecran corespondentul lui\n";
getline(cin,temp); 
Aici avem partea de intampinare si solicitarea primului input al userului. Setam variabila "numar_de_control" la 0 aici deoarece avem nevoie ca de fiecare data cand programul trece prin bucla sa putem controla executia codului cu aceasta variabila, daca nu am avea variabila aici la urmatoarea trecere prin bucla o sa avem eroiri.
folosim linia urmatoare pentru a prelua absolut tot ceea ce utilizatorul a introdus.

getline(cin,temp);
Folosim "getline" in loc de "cin" deoarece la folosirea "cin" daca utilizatorul va introduce un spatiu in input, in variabila "temp" nu ar ajunge decat ceea ce s-a introdus pana la spatiu.

In continuare intram in partea de control a executiei programului.

if(temp.size() == 1) // user enters 1 character
{
   if((int(temp[0]) &gt;= 48 &amp;&amp; int(temp[0] &lt;= 57)) || (temp[0] &gt;= 0 &amp;&amp; temp[0] &lt;= 9)) // check if it's an number
   {
      for(int i = 0; i &lt; 10; i++) // cautam numarul in vector
      {
         if(atoi(temp.c_str()) == i)
         {
            cout &lt;&lt; "Ai introdus " &lt;&lt; temp &lt;&lt; " adica " &lt;&lt; numere[i] &lt;&lt; "." &lt;&lt; endl;

         }
      }
   }
   else
   {
      cout &lt;&lt; "Nu ai introdus o cifra.\n";
   }
}
Intai verificam daca utilizatorul a introdus un caracter.
Daca s-a introdus un singur caracter v-om presupune ca utilizatorul a dorit introducerea unei cifre si v-om verifica daca intr-adevar s-a introdus o cifra si nu altceva.
verificarea marimi sirului de caractere se v-a face prin urmatorul cod

if(temp.size() == 1)
"size()" este o functie membru a obiectului "string". Ea ne v-a returna o valoare de tip "int" pe care noi o comparam cu valoarea dorita, in cazul de fata "1".
In continuare verificam daca s-a introdus un numar

if((int(temp[0]) >= 48 && int(temp[0] <= 57)) || (temp[0] >= 0 && temp[0] <= 9))
Valorile "48", "57", sunt luate din tabelul ASCI astfel verificam daca valoarea "int" a variabilei introduse este intre "48 si "57" adica intre 0 si 9, apoi verificam separat si daca ceea ce s-a introdus este de tip "int" si comparam direct cu valorile "0" - "9". Verificarea se face de 2 ori deoarece este posibil ca utilizatorul sa introduca o litera, asta fiind testat in prima parte a verificari, iar daca utilizatorul introduce o cifra, verificam in a 2-a parte a codului.


for(int i = 0; i < marime_vector; i++) // cautam numarul in vector
{
   if(atoi(temp.c_str()) == i)
   {
      cout << "Ai introdus " << temp << " adica " << numere[i] << "." << endl;

    }
}
Aici folosim o construcite de tip for.Aceasta preaia un element de control, un contorizator, sub forma unei variabile, pe baza celei variabile se verifica niste conditii, iar daca acele conditii sunt indeplinite se v-a face o actiune(cea dupa i < marime_vector in cazul nostru si anume i++), dupa care se v-a executa codul dintre acolade. Acest tip de constructie este util atunci cand cunoastem numarul de executii necesar, in cazul nostru 10(marime_vector)
Functia atoi preia un "c style string" adica un sir de caractere utilizat in limbajul de programare "C" si mostenit de "C++", sirul de caractere este tinut intr-un sir(array) de tip char. Functia returneaza o valoare de tip int pe care noi apoi o comparam cu valoarea "i".Totusi noi nu avem un "c style string" ci un string normal. Aici intervine functia c_str. Aceasta preaia un string c++ si reda un "c style string". Mai multe referinte pentru atoi si c_str aveti in linkurile puse pe numele lor. De ce comparam cu "i"? Pai tineti minte vectorul nostru "numere"? Putem afisa oricare din elemente dupa numarul lui, numerotarea incepe de la 0, primul element are 0, al 2-lea are 1, si asa mai departe. In momentul in care cifra introdusa de utilizator este egala cu "i" atunci stim care element din vectorul "numere" sa afisam.
Dupa ce gasim care este cifra noastra prin

if(atoi(temp.c_str()) == i)
afisam cifra si corespondentul ei prin

cout << "Ai introdus " << temp << " adica " << numere[i] << "." << endl;
Intotdeauna cand folosim "if" este bine sa avem si un "else", "else" este o masura de siguranta, daca nu sunt indeplinite conditiile de la "if" codul de la "else" v-a fi executat.
Aici afisam utilizatorului ca nu a introdus o cifra prin:
else
{
   cout << "Nu ai introdus o cifra.\n";
}
A doua parte a programului e ceva mai complicata fiind nevoie de mai multe verificari pentru a ne asigura ca totul este in regula.
if(temp.size() > 1) // utilizatorul introduce mai mult de un caracter
{
   for(unsigned int i = 0; i < temp.size(); i++) // verificam daca exista caractere scrise cu caps lock
   {
      if(temp[i] >= 65 && temp[i] <= 90)
      {
         cout << "Ai introdus caractere cu caps lock, acestea sunt interzise, vrei sa incerci din nou?\n";
         i = temp.size();
         numar_de_control = 1; // trecem numar_de_control la 1 daca gasim caractere cu caps lock
         din_nou = getch();
         break;
      }
   }
   if(numar_de_control != 0)
   {
      continue; // resetam bucla daca este gasit
   }
   else
   {
      for(int i = 0; i < marime_vector; i++) // cautam cifrele scrise pe litere in interiorul vectorului
      {
         if(temp == numere[i])
         {
            cout << "Ai introdus " << temp << " adica " << i << ".\n";
            i = marime_vector;
         }
         else if(i == (marime_vector - 1)) // loop reaces last item in vector and the number is not found
         {
            if(temp != numere[marime_vector -1])
            {
               cout << "Cuvantul introdus nu este o cifra scrisa cu litere.\n";
            }
         }
      }
   }
}
Mai departe.
for(unsigned int i = 0; i < temp.size(); i++) // verificam daca exista caractere scrise cu caps lock
{
   if(temp[i] >= 65 && temp[i] <= 90)
   {
      cout << "Ai introdus caractere cu caps lock, acestea sunt interzise, vrei sa incerci din nou(y sau Y pentru da, orice alta tasta pentru nu)?\n";
      i = temp.size();
      numar_de_control = 1; // trecem numar_de_control la 1 daca gasim caractere cu caps lock
      din_nou = getch();
      break;
   }
}
Folosim din nou o constructie "for" pentru a trece prin fiecare caracter din string-ul introdus de catre utilizator de data asta pentru a verifica daca utilizatorul a introdus caractere scrise cu caps lock on. Folosim 65 si 90 deoarece in tabellul ASCII 65 este prima litera cu caps lock si anume "A" si 90 este "Z". Daca este gasit un caracter cu caps lock atunci v-om intreba utilizatorul daca doreste sa incerce din nou, si v-a fi nevoit sa introduca "y" sau "Y" pentru a incerca din nou sau orice alta tasta daca nu mai doreste. Imediat dupa aceasta parte avem :
if(numar_de_control != 0)
{
   continue; // resetam bucla daca este gasit
}
Aici intra in joc "numar_de control". Daca acesta este diferit de "0" bucla o va lua de la capat datorita comenzii "continue". Daca nu am gasit nici o majuscula atunci executam a 2-a parte din cod si anume:
else
{
   for(int i = 0; i < marime_vector; i++) // cautam cifrele scrise pe litere in interiorul vectorului
   {
      if(temp == numere[i])
      {
         cout << "Ai introdus " << temp << " adica " << i << ".\n";
         i = marime_vector;
      }
      else if(i == (marime_vector - 1)) // loop reaces last item in vector and the number is not found
      {
         if(temp != numere[marime_vector -1])
         {
            cout << "Cuvantul introdus nu este o cifra scrisa cu litere.\n";
         }
      }
   }
}
Dupa cum spuneam mai sus ne folosim de "if" si "else", intai am avut error checking, acum cautam de fapt cifra introdusa pe litere si afisam ceea ce gasim. Folosim din nou un o bucla "for" pentru a trece prin fiecare element al vectorului "numere" si comparam cu ceea ce a introdus utilizatorul.
for(int i = 0; i < marime_vector; i++) // cautam cifrele scrise pe litere in interiorul vectorului
{
   if(temp == numere[i])
   {
      cout << "Ai introdus " << temp << " adica " << i << ".\n";
      i = marime_vector;
   }
   else if(i == (marime_vector - 1)) // loop reaces last item in vector and the number is not found
   {
      if(temp != numere[marime_vector -1])
      {
         cout << "Cuvantul introdus nu este o cifra scrisa cu litere.\n";
      }
   }
}
Mai devreme am spus despre "if" si "else" totusi sunt cazuri unde dorim sa verificam mai multe chestii, c++ nu are o constructie speciala pentru asta dar putem combina if si else in felul urmator
if(conditie)
{
   cod de executat;
}
else if(conditie)
{
   cod de executat;
}
else if(conditie)
{
   cod de executat;
}
.
.
.
.
else
{
   cod de executat;
}
Aici verificam daca prima conditie este indeplinita, daca nu mergem la a 2-a, la a 3-a, daca nu v-a fi indeplinita nici una din conditii atunci se va executa conditia "else". In cazul codului nostru totusi avem o mica exceptie, avem "if", "else if" dar nu mai avem "else", asta pentru ca in cazul de fata conditia de siguranta este executata in "else if" pentru ca si in cazul conditiei de siguranta trebuie sa verificam niste conditii, si anume daca am ajuns la capatul vectorului "numere". Daca utilizatorul nu a introdus un cuvant cunoscut, o cifra scrisa pe litere atunci calculatorul v-a afisa acest lucru prin:
if(temp != numere[marime_vector -1])
{
   cout << "Cuvantul introdus nu este o cifra scrisa cu litere.\n";
}
In incheiere utilizatorul este intrebat daca doreste sa ruleze programul inca o data prin:
cout << "Vrei sa mai incerci o data ?\n";
din_nou = getch();
system("CLS"); // e un obicei prost dar pentru utilizatori de windows momentan nu este o problema
Afisam utilizatorului intrebarea prin "cout" dupa care folosim functia "getch()". Aceasta functie de fapt preia un caracter introdus care nu este afisat pe ecran si returneaza valoarea int a acesteia, valoarea int este cea din tabelul ASCII. In acest moment aceasta functie este oarecum invechita si este sugerat sa folosititi functia "_getch()" efectul este acelasi pentru abmele, doar ca a 2-a este o varianta mai noua a functiei respective. Dupa ce utilizatorul da raspunsul facem curat pe ecran folosint functia "system()", aceasta preia anumiti parametri speciali, cum ar fi "CLS" dar functia aceasta primeste parametri in functie de sistemul de operare, spre exemplu pe un sistem linux, codul "system("CLS"); v-a da o eroare. Cateva referinte despre "system()" aveti aici Dupa ce am facut si curat pe ecran verificam ceea ce a introdus utilizatorul cand a fost intrebat daca doreste sa mai ruleze programul inca o data prin:
}while(din_nou == 'y' || din_nou == 'Y');
dupa care avem comanda de terminare cu succes si anume "return 0;" Si cu asta am terminat acest program. Daca aveti intrebari postatile la comentarii si va voi raspunde cat de repede pot.