Mesaje recente

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

Probleme si solutii

Started by Praetor, 10 August 2007, 10:47

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Praetor

In acest topic va rog sa puneti solutii la ce probleme mai dificile ati invins, enuntand problema si apoi solutia, indiferent daca ea a fost data pe acest forum in alt topic. Acest thread va contine doar situatia si rezolvarea, ca un mic cookbook.

Praetor

Original postat de Dark

Problema
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?

Solutie
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.