Mesaje recente

Members
Stats
  • Total Posts: 17,786
  • Total Topics: 1,234
  • Online today: 289
  • Online ever: 318
  • (17 October 2024, 13:29)
Users Online
Users: 0
Guests: 286
Total: 286

Imbecilitate iostreams

Started by Dark, 07 August 2007, 19:35

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Dark

Incerc sa fac un plug-in pentru o aplicatie. Trebuie sa suport 3 versiuni ale aplicatiei respective, dintre care 2 au fost compilate cu VS 2003, iar ultima e compilata cu 2005. Plug-in-ul e facut cu 2005 si totul a fost idilic si frumos pina acum (mai putin haosul initial la care-ti supui userii din cauza redist-ului cel imbecil pentru CRT). Din pacate acum am nevoie sa folosesc o parte din SDK-ul aplicatiei care-mi da un ostream si ma pune sa deversez chestii in el.

Sint un adept ferm al teoriei ca porcarie mai mare decit iostreams se vede foarte rar; iata un argument practic adus pe lume prin imbecilitatea micromoale: ostream-ul ala care-mi vine mie e nascut de msvcp71.dll in alea 2 versiuni ale aplicatiei care-s facute cu 2003. Deoarece pluginul e compilat cu 2005, codul generat in plugin incearca sa foloseasca obiectul ala ca si cum ar fi fost produs de biblioteca din 2005, dar bibliotecile sint incompatibile. Rezultatul variaza in functie de ce incerci sa faci exact, de la "Microsoft C++ exception: TincompatibleData at memory location bla-bla" la "Access violation writing location bla-bla".

Morala ar fi ca iostream e o timpenie, dar stiam asta deja si din pacate in cazul de fata situatia nu e sub controlul meu. Eu vad doua solutii "posibile", dar as fi recunoscator daca mai are cineva si alte idei:

1) compilez plug-in-ul cu 2003 pentru alea 2 versiuni ale host-ului facute cu 2003 si cu 2005 pentru aia facuta cu 2005. Horror, as prefera sa nu recurg la asa ceva.

2) il conving pe 2005 sa foloseasca CRT-ul si STL-ul din 2003. A incercat cineva asa ceva vreodata? Se poate? Este obligatoriu sa te internezi la nebuni dupa ce incerci sau ai sanse sa scapi cu toate tiglele pe casa?

E bine ca pe unii ii preocupa aspectul splash screen-ului si alte astfel de chestii critice pentru programatori in loc sa se gindeasca la lucruri efemere cum ar fi compatibilitatea sau scosul in afara legii a ideilor tembele din limbaje consacrate.

PS: ca sa vedeti documentul din link-ul ala va trebuie IE7 si .niet 3.0, sau Vista. Este un document in formatul XPS, adica un fel de anti-PDF de la Microsoft care probabil va avea acelasi succes de care se bucura si MSN ca anti-Google. Pentru mai multe grozavii din "Orcas" unde puteti vedea in ce hal se desfasoara procesul de dezvoltare la MS, puteti merge aici. Va recomand cu caldura sa cititi cite lucruri bune vin in Orcas, cum ar fi "the most critical part of presenting a clearly new and updated experience for any product is updating the product's branding".

~Empathy~

Compilatoarele VS 2003 si VS 2005 sunt ABI incompatibile, deci nu o sa mearga *niciodata* cod generat de unul, legat dinamic de cod generat de celalalt. Sa exporti obiecte este de-a dreptul retardant, nu stiu ce a fost in mintea celor care au facut SDK-ul. Nu ai cum sa te legi dinamic de cod daca nu folosesti acelasi compilator.

Singura solutie care o vad este sa compilezi cu compilatoare diferite, sa le bagi in acelasi wrapper executabil care instaleaza ce trebuie.
We dance, and the music dies...

Dark

Merge sa exporti obiecte atita timp cit ambele compilatoare vad acelasi header pentru obiectul respectiv si nu te dedai la mari perversiuni. Am facut asta de o groaza de timp (plug-in-ul e in productie de mai bine de un an) si a mers, mai demult suportam si un host compilat cu VS 6.0, iar plug-in-ul era facut tot de 2005. SDK-ul e destul de bine facut, adica toate clasele pe care le exporta au in general un singur membru, care e un pointer la adevaratul obiect si toate metodele apeleaza obiectul ala. Ineficient, dar maxim de compatibil.

Problema e ca VC2003 care a compilat aplicatia a vazut alta definitie a obiectelor ostream si istream decit vede VC2005 care compileaza plug-in-ul, dar ambele cred ca vorbesc despre acelasi obiect. Pachetul de instalare oricum contine 3 versiuni ale plug-in-ului, pentru ca SDK-urile sint incompatibile. Retardarea a survenit la ei cind s-au indragostit de iostream si l-au bagat in SDK, dar eu n-am folosit partea aia din API pina acum, cind imi trebuie pentru un nou feature. Problema mea e ca nu vreau sa tin 2 IDE-uri si 2 .vcproj-uri pentru proiect si sa schimb IDE-ul cind vreau sa fac debug la o alta configuratie. O sa incerc miine sa-l conving pe 2005 sa foloseasca CRT-ul din 2003, va relatez dupa aia despre ce s-a petrecut (daca nu imi iau cimpii si ma apuc de apicultura). Din nefericire e a doua oara cind trebuie sa trec printr-o experienta d-asta, anul trecut a trebuit sa-l conving pe gcc 3.4 sa linkeze cu STL-ul din 3.3 (dar presimt ca aici va fi mult mai nasol).

~Empathy~

A mers dar asta e o exceptie nu o regula. In principiu compilatoarele microsoft nu sufera de incompatibilitati ABI asa de mari cum sufera GCC-ul, dar o problema de fond tot exista.
We dance, and the music dies...

Dark

Pe scurt, se poate compila un program cu 2005 cu CRT-ul de 2003, dar nu prea merg exceptiile.

Pe lung, se copiaza crt-ul de 2003 undeva si se fac urmatoarele setari:
- ignore standard include path (/X)
- adaugat la include paths: "$(VCINSTALLDIR)\PlatformSDK\Include";"$(ProjectDir)\vc2003crt\include" (va folosi PSDK-ul care vine cu 2005, doar cu CRT-ul sintem in razboi)
- ignore all default libraries (/NODEFAULTLIB)
- additional dependencies: "$(ProjectDir)\vc2003crt\lib\MSVCRT.lib" "$(ProjectDir)\vc2003crt\lib\MSVCPRT.LIB"

Se observa unresolved symbol ___CxxFrameHandler3. Eroarea persista chiar daca-i spui compilatorului ca nu vrei exceptii. Cu un pic de debugging intr-un proiect normal de 2005 descoperim prototipul si adaugam urmatorul cod pe undeva prin proiect:


extern "C"
{
struct EHExceptionRecord;
struct EHRegistrationNode;

int __CxxFrameHandler3(EHExceptionRecord* pExcept, EHRegistrationNode* pRN, void* pContext, void* pDC)
{
return 0;
}
};


Merge, dar cind arunci o exceptie se termina programul brusc, nu ajunge in catch() si asa mai departe. Daca umbli la valoarea returnata poti sa-l convingi sa spuna "This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information." dar tot n-o sa poti sa prinzi exceptia.

Dam cu debugger-ul in functia aia din CRT-ul de 2005 si descoperim ca nu face decit sa cheme __InternalCxxFrameHandler() cu parametrii pe care i-a primit plus valoarea din eax in momentul in care a fost apelata functia. In eax se afla un pointer spre o structura _s_FuncInfo care nu-i pe nicaieri prin headere, dar e in PDB-ul de la CRT. Ne conformam:

struct EHExceptionRecord;
struct EHRegistrationNode;
struct _CONTEXT;
struct _s_FuncInfo;

extern int __InternalCxxFrameHandler(EHExceptionRecord* pExcept, EHRegistrationNode* pRN, _CONTEXT* pContext, void* pDC, const _s_FuncInfo * pFuncInfo, int CatchDepth, EHRegistrationNode* pMarkerRN, unsigned char recursive);

// Daca nu dam off la runtime checks o sa faca varza eax-ul cind pune padding-ul ala pentru buffer overlow in debug. E mai sigur cu __declspec(naked) si scris totul manual da' merge si asa.
#pragma runtime_checks("", off)
int __CxxFrameHandler3(EHExceptionRecord* pExcept, EHRegistrationNode* pRN, void* pContext, void* pDC)
{
const _s_FuncInfo* pFuncInfo;
__asm mov pFuncInfo, eax;
__InternalCxxFrameHandler(pExcept, pRN, (_CONTEXT*)pContext, pDC, pFuncInfo, 0, 0, 0);
}
#pragma runtime_checks("", restore)


Acum avem o noua problema, __InternalCxxFrameHandler() nu exista. In CRT-ul de 2003 se gaseste doar in variantele statice, dar in 2005 se gaseste si in DLL-uri. E o functie mare care pare sa faca multe lucruri deci n-am chef s-o descifrez, asa ca am linkat cu CRT-ul static doar sa vad daca merge. Din pacate primul membru al structurii _s_FuncInfo e un version number. Functia din CRT-ul de 2003 il compara cu 0x19930520 (*). VC2005 il seteaza la 0x19930522, deci functia avorteaza. Am vrut sa modific numarul inainte de a chema __InternalCxxFrameHandler() dar nu merge pentru ca structura e intr-o pagina read-only (probabil vine de la kernel). In schimb, am modificat lib-ul sa compare cu numarul mai nou. Surpriza: merge. Imi intra in catch, obiectul exceptie e corect si continua dupa aia fara probleme.

Concluzia: merg exceptiile dar doar cind linkezi static cu CRT-ul. In loc de bibliotecile mentionate mai sus trebuie folosite: "$(ProjectDir)\vc2003crt\lib\libcmt.lib" "$(ProjectDir)\vc2003crt\lib\libcpmt.lib". Byte-ul care trebuie schimbat ca sa mearga __InternalCxxFrameHandler() e in libcmt.lib la offset-ul 0x60c3a. Valoarea initiala e 0x20, trebuie pus 0x22. Codul cu care am testat:


#include <iostream>
#include <map>
#include <vector>
#include <string>

extern "C"
{
struct EHExceptionRecord;
struct EHRegistrationNode;
struct _CONTEXT;
struct _s_FuncInfo;

extern int __InternalCxxFrameHandler(EHExceptionRecord* pExcept, EHRegistrationNode* pRN, _CONTEXT* pContext, void* pDC, const _s_FuncInfo * pFuncInfo, int CatchDepth, EHRegistrationNode* pMarkerRN, unsigned char recursive);

#pragma runtime_checks("", off)
int __CxxFrameHandler3(EHExceptionRecord* pExcept, EHRegistrationNode* pRN, void* pContext, void* pDC)
{
const _s_FuncInfo* pFuncInfo;
__asm mov pFuncInfo, eax;
__InternalCxxFrameHandler(pExcept, pRN, (_CONTEXT*)pContext, pDC, pFuncInfo, 0, 0, 0);
}
#pragma runtime_checks("", restore)
};

int main()
{
std::cout << "iostream suge" << std::endl;
std::vector<std::map<std::string, std::string> > crap;

crap.resize(2);
crap[0]["asd"] = "fgh";
crap[0]["rjifreg"] = "etrw";
crap[0]["asd"] = "rigrge";
crap[1]["asd"] = "eirjgerjger";
crap[1]["reegrE"] = "regfregre";

std::map<std::string, std::string>::iterator it = crap[1].find("asd");
if(it != crap[1].end())
{
std::cout << "Este" << std::endl;
crap[1].erase(it);
}
else
std::cout << "Nu este" << std::endl;

crap.resize(10);
crap[0].clear();

try
{
throw std::exception("test mesaj");
}
catch(std::exception& e)
{
std::cout << e.what() << std::endl;
}

std::cout << "rekoegkreger" << std::endl;

return 0;
}


E foarte probabil sa nu mearga in altceva decit programul meu de test. In release da niste unresolved externals din iostream da' m-am plictisit si nu mai stau sa vad ce-i si cu aia.

Gata, v-am plictisit destul.

----------
(*) sper ca se intelege ca initial au vrut sa foloseasca un time stamp ca version number - 20 mai 1993 - dar au ajuns sa-l incrementeze ca pe orice alt numar. La mine e 22 pentru ca am VC 2005 SP1, probabil fara SP e 21.