JeanCremers 1 Posted October 2, 2021 Hi Folks, Is there a version of indy which can be installed on borland c++builder 6 that can use a newer protocol version, and the newer ssleay32.dll and libeay32.dll? I get a protocol error with the old dll and the newer from https://indy.fulgan.com/SSL/. refuse to load. Share this post Link to post
Remy Lebeau 1436 Posted October 4, 2021 (edited) The latest version of Indy 10 (https://github.com/IndySockets/Indy/) supports back to C++Builder 5, and can officially use OpenSSL DLLs up to 1.0.2u (for OpenSSL 1.1.x+, use this WIP code). TLS 1.2 has been available in OpenSSL since 1.0.1, and Indy 10 has supported TLS 1.2 for a long time. OpenSSL DLLs are available at https://github.com/IndySockets/OpenSSL-Binaries (https://indy.fulgan.com/SSL/ is retired). If you are getting errors from Indy's standard TIdSSLIOHandlerSocketOpenSSL component when it tries to load the DLLs, then you are likely using DLLs that are not compatible with your version of Indy. You can call Indy's WhichFailedToLoad() function in the IdSSLOpenSSLHeaders unit to find out why the DLLs are failing to load. Edited October 4, 2021 by Remy Lebeau 2 Share this post Link to post
JeanCremers 1 Posted October 6, 2021 Thanks Remy, WhichFailedToLoad reported ssleay32.dll, the newer one, but it also reports NULL when i try some other dll's. I looked at the WIP code but i don't feel like trying to use it, i'm afraid that i will break something. For now http still works, i'm using indy so my prog can download additional files / updates from my provider, this still works. Another prog i had refuses since awhile, i guess that website had some ssl update. So here's hoping http will keep working... ps what's up with all the q,r,s,t,u in the names? Share this post Link to post
Remy Lebeau 1436 Posted October 6, 2021 9 hours ago, JeanCremers said: WhichFailedToLoad reported ssleay32.dll, the newer one, but it also reports NULL when i try some other dll's. That means the DLL itself failed to load into memory, before Indy could even try to access any of its functions. Such a failure is most commonly because the DLL itself, or one of its dependent DLLs or imports, can't be found by the OS. 9 hours ago, JeanCremers said: ps what's up with all the q,r,s,t,u in the names? OpenSSL build numbers are represented as letters in the English alphabet. So, 1.0.2q is build 1.0.2.17, 1.0.2u is build 1.0.2.21, etc. Share this post Link to post
JeanCremers 1 Posted October 18, 2021 (edited) Remy I'm trying to make a dll in c++builder 10.3 to use that Indy version with my application made in c++builder 6. Is there a way to export OnWork(), OnWorkBegin(), OnWorkEnd()? How should those functions be declared, Something with TNotifyEvent? Thanks! #define EXPORT extern "C" __declspec(dllexport) EXPORT int getdata(char *url, char *data, ???onwork???) { TIdHTTP *http = new TIdHTTP; http->OnWork = onwork; // ?????????????????????????????? TIdSSLIOHandlerSocketOpenSSL *SSL; SSL = new TIdSSLIOHandlerSocketOpenSSL(NULL); IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0))); SSL->SSLOptions->Method = sslvTLSv1_2; http->IOHandler = SSL; http->ReadTimeout = 10000; String s = http->Get(url); memcpy(data, s.c_str(), s.Length()); } Btw i was getting an error about strcpy() not found, for now i'm using memcpy, probably to do with widechar, still haven't got into unicode that much because of my c++builder 6 code. Edited October 18, 2021 by JeanCremers Share this post Link to post
Remy Lebeau 1436 Posted October 18, 2021 (edited) On 10/18/2021 at 9:05 AM, JeanCremers said: Remy I'm trying to make a dll in c++builder 10.3 to use that Indy version with my application made in c++builder 6. Why not just install the latest Indy in C++Builder 6? Quote Is there a way to export OnWork(), OnWorkBegin(), OnWorkEnd()? No, not directly. Nor should you be trying to. What you should do instead is let the caller pass in its own function pointer(s), which your DLL then remembers, and can then call from inside its own OnWork event handler(s). For example, the following uses a little-known trick for assigning a standalone procedure as a VCL event handler, while also passing a user-defined parameter to it. There are other ways this could be implemented, but this is the most straight-forward way I can think of right now for your scenario: // HEADER #ifndef MY_DLL_H #define MY_DLL_H #ifdef __cplusplus extern "C" { #endif #ifdef BUILDING_DLL #define EXPORT __declspec(dllexport) #else #define EXPORT __declspec(dllimport) #endif enum OnWorkMode { WorkReading, WorkWriting }; typedef void (*OnWorkBeginFunc)(OnWorkMode AWorkMode, __int64 AWorkCountMax); typedef void (*OnWorkEndFunc)(OnWorkMode AWorkMode); typedef void (*OnWorkFunc)(OnWorkMode AWorkMode, __int64 AWorkCount); EXPORT char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork); EXPORT void freedata(char *data); #ifdef __cplusplus } #endif #endif // SOURCE #define BUILDING_DLL #include "mydll.h" static void __fastcall MyOnWorkBeginHandler(void *ASelf, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax) { OnWorkBeginFunc func = static_cast<OnWorkBeginFunc*>(ASelf); func(AWorkMode, AWorkCountMax); } static void __fastcall MyOnWorkEndHandler(void *ASelf, TObject *ASender, TWorkMode AWorkMode) { OnWorkEndFunc *func = static_cast<OnWorkEndFunc*>(ASelf); func(AWorkMode); } static void __fastcall MyOnWorkHandler(void *ASelf, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) { OnWorkFunc *func = static_cast<OnWorkFunc*>(ASelf); func(AWorkMode, AWorkCount); } template <typename EventType, typename FuncType, typename HandlerType> static EventType makeEventHandler(FuncType *func, HandlerType *handler) { TMethod m; m.Data = func; m.Code = handler; return reinterpret_cast<EventType&>(m); /* alternatively: EventType evt; TMethod &m = reinterpret_cast<TMethod&>(evt); m.Data = func; m.Code = handler; return evt; */ } static bool sslPathSet = false; char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork) { char *result = NULL; try { if (!sslPathSet) { sslPathSet = true; IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0))); } TIdHTTP *http = new TIdHTTP; try { if (onWorkBegin) http->OnWorkBegin = makeEventHandler<TWorkBeginEvent>(onWorkBegin, &MyOnWorkBeginHandler); if (onWorkEnd) http->OnWorkEnd = makeEventHandler<TWorkEndEvent>(onWorkEnd, &MyOnWorkEndHandler); if (onWork) http->OnWork = makeEventHandler<TWorkEvent>(onWork, &MyOnWorkHandler); TIdSSLIOHandlerSocketOpenSSL *SSL = new TIdSSLIOHandlerSocketOpenSSL(http); SSL->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2; http->IOHandler = SSL; http->ReadTimeout = 10000; UTF8String s = http->Get(url); result = new char[s.Length()+1]; memcpy(result, s.c_str(), s.Length()+1); } __finally { delete http; } } catch (...) { return NULL; } return result; } void freedata(char *data) { delete[] data; } And then you C++Builder 6 code can use it like this: #include "mydll.h" #pragma comment(lib, "mydll.lib") // or add the .lib file to your project void OnWorkBegin(OnWorkMode AWorkMode, __int64 AWorkCountMax) { //... } void OnWorkEnd(OnWorkMode AWorkMode) { //... } void OnWork(OnWorkMode AWorkMode, __int64 AWorkCount) { //... } char *data = getdata("url", &OnWorkBegin, &OnWorkEnd, &OnWork); if (data) { // ... freedata(data); } Quote Btw i was getting an error about strcpy() not found, for now i'm using memcpy, probably to do with widechar Yes, because System::String is an alias for System::UnicodeString in CB10.3, but is an alias for System::AnsiString in BCB6. Thus, you were indeed trying to pass a wchar_t* pointer to strcpy() rather than a char* pointer. Edited October 19, 2021 by Remy Lebeau 1 Share this post Link to post
JeanCremers 1 Posted October 19, 2021 Wow Remy, thanks a bunch, gonna work with it. The reason why i didn't want to use that WIP code is because i'm afraid to break things. Share this post Link to post
JeanCremers 1 Posted October 19, 2021 Hi Remy, Getting errors on the lines in the three functions, void(*) vs void(**): OnWorkBeginFunc func = static_cast<OnWorkBeginFunc*>(ASelf); [bcc32c Error] main.cpp(23): cannot initialize a variable of type 'OnWorkBeginFunc' (aka 'void (*)(OnWorkMode, long long)') with an rvalue of type 'OnWorkBeginFunc *' (aka 'void (**)(OnWorkMode, long long)') Share this post Link to post
Remy Lebeau 1436 Posted October 20, 2021 (edited) My bad, too many *s in the DLL's event handlers. Try this instead: static void __fastcall MyOnWorkBeginHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax) { OnWorkBeginFunc func = static_cast<OnWorkBeginFunc>(AData); func(AWorkMode, AWorkCountMax); } static void __fastcall MyOnWorkEndHandler(void *AData, TObject *ASender, TWorkMode AWorkMode) { OnWorkEndFunc func = static_cast<OnWorkEndFunc>(AData); func(AWorkMode); } static void __fastcall MyOnWorkHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) { OnWorkFunc func = static_cast<OnWorkFunc>(AData); func(AWorkMode, AWorkCount); } Edited October 20, 2021 by Remy Lebeau 1 Share this post Link to post
JeanCremers 1 Posted October 20, 2021 (edited) Gives these.. I've got it working nicely without the events though. [bcc32c Error] main.cpp(23): static_cast from 'void *' to 'OnWorkBeginFunc' (aka 'void (*)(OnWorkMode, long long)') is not allowed [bcc32c Error] main.cpp(24): cannot initialize a parameter of type 'OnWorkMode' with an lvalue of type 'Idcomponent::TWorkMode' Edited October 20, 2021 by JeanCremers Share this post Link to post
Remy Lebeau 1436 Posted October 20, 2021 <sigh> Try this: // SOURCE #define BUILDING_DLL #include "mydll.h" struct userFunctions { OnWorkBeginFunc onWorkBegin; OnWorkEndFunc onWorkEnd; OnWorkFunc onWork; }; static void __fastcall MyOnWorkBeginHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax) { userFunctions *funcs = static_cast<userFunctions*>(AData); funcs->onWorkBegin((OnWorkMode)AWorkMode, AWorkCountMax); } static void __fastcall MyOnWorkEndHandler(void *AData, TObject *ASender, TWorkMode AWorkMode) { userFunctions *funcs = static_cast<userFunctions*>(AData); funcs->onWorkEnd((OnWorkMode)AWorkMode); } static void __fastcall MyOnWorkHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) { userFunctions *funcs = static_cast<userFunctions*>(AData); funcs->onWork((OnWorkMode)AWorkMode, AWorkCount); } template <typename EventType, typename HandlerType> static EventType makeEventHandler(void *data, HandlerType *handler) { TMethod m; m.Data = data; m.Code = handler; return reinterpret_cast<EventType&>(m); /* alternatively: EventType evt; TMethod &m = reinterpret_cast<TMethod&>(evt); m.Data = data; m.Code = handler; return evt; */ } static bool sslPathSet = false; char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork) { char *result = NULL; try { if (!sslPathSet) { sslPathSet = true; IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0))); } TIdHTTP *http = new TIdHTTP; try { userFunctions funcs; if (onWorkBegin) { funcs.onWorkBegin = onWorkBegin; http->OnWorkBegin = makeEventHandler<TWorkBeginEvent>(&funcs, &MyOnWorkBeginHandler); } if (onWorkEnd) { funcs.onWorkEnd = onWorkEnd; http->OnWorkEnd = makeEventHandler<TWorkEndEvent>(&funcs, &MyOnWorkEndHandler); } if (onWork) { funcs.onWork = onWork; http->OnWork = makeEventHandler<TWorkEvent>(&funcs, &MyOnWorkHandler); } TIdSSLIOHandlerSocketOpenSSL *SSL = new TIdSSLIOHandlerSocketOpenSSL(http); SSL->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2; http->IOHandler = SSL; http->ReadTimeout = 10000; UTF8String s = http->Get(url); result = new char[s.Length()+1]; memcpy(result, s.c_str(), s.Length()+1); } __finally { delete http; } } catch (...) { return NULL; } return result; } void freedata(char *data) { delete[] data; } 1 Share this post Link to post
JeanCremers 1 Posted October 21, 2021 (edited) Hi Remy, I got it to compile with warnings m.Code = handler; [bcc32c Warning] ssl.cpp(50): implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension and.. http->OnWorkBegin = makeEventHandler<TWorkBeginEvent>(&funcs, &MyOnWorkBeginHandler); ssl.cpp(72): in instantiation of function template specialization 'makeEventHandler<void (__closure *)(System::TObject *, Idcomponent::TWorkMode) __attribute__((fastcall)), void (void *, System::TObject *, Idcomponent::TWorkMode) __attribute__((fastcall))>' requested here That last warning i also get on http->OnWorkEnd = makeEventHandler<TWorkEndEvent>(&funcs, &MyOnWorkEndHandler); But NOT on http->onWork In the test code OnWorkBegin and OnWork are never called. I tried debugging the dll to get an idea what was going on but run to cursor didn't work there, though i used the debug version. If you want we can call it a day? At least I can use ssl now.. Some funny thing, i could only get one function to export from the dll, if i tried to export only a small 'void mytest(void)' i got a linker error in the test app, though nirsoft's dll export vieuwer shows them allright. ps, could it have something to do with indy not living on a form? I tried to assign OnWork etc in the dll to a plain void __fastcall MyOnWork(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) in the dll but got an error about incompatible type. Edited October 21, 2021 by JeanCremers Share this post Link to post
Remy Lebeau 1436 Posted October 21, 2021 (edited) On 10/21/2021 at 3:04 AM, JeanCremers said: I got it to compile with warnings <sigh> Fine, then let's go the long way around, using a full wrapper class, forget TMethod (even though it will technically work at runtime, despite the compiler warnings): // SOURCE #define BUILDING_DLL #include "mydll.h" class eventHandlers { public: OnWorkBeginFunc onWorkBegin; OnWorkEndFunc onWorkEnd; OnWorkFunc onWork; void __fastcall OnWorkBeginHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax) { if (onWorkBegin) onWorkBegin((OnWorkMode)AWorkMode, AWorkCountMax); } void __fastcall OnWorkEndHandler(TObject *ASender, TWorkMode AWorkMode) { if (onWorkEnd) onWorkEnd((OnWorkMode)AWorkMode); } void __fastcall OnWorkHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) { if (onWork) onWork((OnWorkMode)AWorkMode, AWorkCount); } }; static bool sslPathSet = false; char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork) { char *result = NULL; try { if (!sslPathSet) { sslPathSet = true; IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0))); } TIdHTTP *http = new TIdHTTP; try { eventHandlers events; events.onWorkBegin = onWorkBegin; events.onWorkEnd = onWorkEnd; events.onWork = onWork; http->OnWorkBegin = &events.OnWorkBeginHandler; http->OnWorkEnd = &events.OnWorkEndHandler; http->OnWork = &events.OnWorkHandler; TIdSSLIOHandlerSocketOpenSSL *SSL = new TIdSSLIOHandlerSocketOpenSSL(http); SSL->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2; http->IOHandler = SSL; http->ReadTimeout = 10000; UTF8String s = http->Get(url); result = new char[s.Length()+1]; memcpy(result, s.c_str(), s.Length()+1); } __finally { delete http; } } catch (...) { return NULL; } return result; } void freedata(char *data) { delete[] data; } Quote In the test code OnWorkBegin and OnWork are never called. Odd, they should be. Especially since OnWorkEnd can't be called if OnWorkBegin is not called first. Quote ps, could it have something to do with indy not living on a form? No. Quote I tried to assign OnWork etc in the dll to a plain void __fastcall MyOnWork(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) in the dll but got an error about incompatible type. Correct, because the event is expecting a class method, not a standalone procedure. Using TMethod is the way to get around that. Edited October 22, 2021 by Remy Lebeau 1 Share this post Link to post
JeanCremers 1 Posted October 22, 2021 (edited) It's working great Remy. A progressbar on the testapp to show download progress! Your name already was in the credits and again you contributed to my program, many thanks again. Good old TeamB, i miss those times, Kent Reisdorph and all.. Does it still exist? ps, void __fastcall OnWorkHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) should be: void __fastcall OnWorkHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) Edited October 22, 2021 by JeanCremers Share this post Link to post
Remy Lebeau 1436 Posted October 22, 2021 7 hours ago, JeanCremers said: Good old TeamB, i miss those times, Kent Reisdorph and all.. Does it still exist? Not like it used to be. A few members are still lingering around the various forums. Several members have transitioned into MVPs, though some members (like me) haven't but are still recognized by a few Embarcadero employees to keep getting old TeamB perks (free versions, beta access, etc). 7 hours ago, JeanCremers said: void __fastcall OnWorkHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) should be: void __fastcall OnWorkHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) Thanks, fixed. 1 Share this post Link to post
JeanCremers 1 Posted October 22, 2021 (edited) Proud to say I have been an official borland betatester for a short while, around c++builder 1 that was, even got a thanks for my report. Till next time 🙂 Edited October 22, 2021 by JeanCremers Share this post Link to post