HTTPS web addresses with Indy IdHTTP in C++Builder - c++builder

I am using the code below in C++Builder XE4 VCL 32bit. I am using the Indy components, version 10.6.0.497.
I have been using IdHTTP->Get() with HTTP addresses that have now changed to HTTPS. I believe I need to create a TIdSSLIOHandlerSocketOpenSSL component and add it to TIdHTTP as its IOHandler.
When I try to do this, the code below gives the error:
E2451 Undefined symbol 'TIdSSLIOHandlerSocketOpenSSL'
The error is on the code, std::auto_ptr<TIdSSLIOHandlerSocketOpenSSL>.
I am not sure why TIdSSLIOHandlerSocketOpenSSL is undefined, because I have Indy installed and can use TIdSSLIOHandlerSocketOpenSSL as a traditional component from the component palette.
Can anyone show me how I can set this code up to use HTTPS addresses?
std::auto_ptr<TIdSSLIOHandlerSocketOpenSSL> Local_IOHandler( new TIdSSLIOHandlerSocketOpenSSL( NULL ) );
//error: E2451 Undefined symbol 'TIdSSLIOHandlerSocketOpenSSL'
//error: E2299 Cannot generate template specialization from 'std::auto_ptr<_Ty>'
std::auto_ptr<TIdHTTP> Local_IdHTTP( new TIdHTTP( NULL ) );
Local_IdHTTP->Name="MyLocalHTTP";
Local_IdHTTP->HandleRedirects=true;
Local_IdHTTP->IOHandler=Local_IOHandler;
TStringStream *jsonToSend = new TStringStream;
UnicodeString GetURL = "https://chartapi.finance.yahoo.com/instrument/1.0/CLZ17.NYM/chartdata;type=quote;range=1d/csv/";
jsonToSend->Clear();
try
{
Local_IdHTTP->Get(GetURL, jsonToSend);
}
catch (const Exception &E)
{
ShowMessage( E.Message );
//error: IOHandler value is not valid
}

When I try to do this the code below gives the error E2451 Undefined symbol 'TIdSSLIOHandlerSocketOpenSSL'
Add #include <IdSSLOpenSSL.hpp> to your code.
I am not sure why 'TIdSSLIOHandlerSocketOpenSSL' is Undefined because I have Indy installed and can use 'TIdSSLIOHandlerSocketOpenSSL' as a traditional component from the compoenent pallet?
Dropping a component onto your Form at design-time auto-generates any necessary #include statements for you. TIdSSLIOHandlerSocketOpenSSL is no different.
That being said, once you get that fixed, you cannot assign a std::auto_ptr itself to the IOHandler. You need to use its get() method to get the object pointer:
Local_IdHTTP->IOHandler = Local_IOHandler.get();
And you should consider using std::auto_ptr for your TStringStream as well:
std::auto_ptr<TStringStream> json( new TStringStream );
Local_IdHTTP->Get(GetURL, json.get());
// use json as needed...
Though in this situation, I would suggest using the overloaded version of TIdHTTP::Get() that returns a String instead, there is no benefit to using a TStringStream:
String json = Local_IdHTTP->Get(GetURL);
// use json as needed...

Related

How to write raw binary data using Indy TCP Client in C++ Builder

Using Embarcadero C++ Builder 10.3.
I have a DynamicArray<uint8_t> myData object. I want to send/write its raw binary content (bytes) to a server using the TIdTcpClient component. I'm going about it like this:
TIdTcpClient tcpClient1;
// Bla Bla Bla
tcpClient1->IOHandler->Write(rawData);
Where rawData should be of type TIdBytes or TIdStream
So basically, it boils down to the following: How to convert myData object to a rawData type of either TIdBytes or TIdStream?
First off, TIdStream has not been part of Indy in a VERY VERY LONG time, which makes me wonder if you are using a very old version of Indy, not the one that shipped with C++Builder 10.3. Indy has supported the RTL's standard TStream class for a very long time.
That being said...
TIdBytes is an alias for System::DynamicArray<System::Byte>, where System::Byte is an alias for unsigned char, which is the same size and sign-ness as uint8_t (depending on compiler, uint8_t might even just be an alias for unsigned char).
So, the simplest solution, without having to make a separate copy of your data, is to simply type-cast it, eg:
tcpClient1->IOHandler->Write(reinterpret_cast<TIdBytes&>(myData));
This is technically undefined behavior, since DynamicArray<uint8_t> and DynamicArray<Byte> are unrelated types (unless uint8_t and Byte are both aliases for unsigned char), but it will work in your case since it is the same underlying code behind both arrays, and uint8_t and Byte have the same underlying memory layout.
Alternatively, the next simplest solution, without copying data or invoking undefined behavior, is to use Indy's TIdReadOnlyMemoryBufferStream class in IdGlobal.hpp, eg:
TIdReadOnlyMemoryBufferStream *ms = new TIdReadOnlyMemoryBufferStream(&myData[0], myData.Length);
try {
tcpClient1->IOHandler->Write(ms);
}
__finally {
delete ms;
}
Or:
{
auto ms = std::make_unique<TIdReadOnlyMemoryBufferStream>(&myData[0], myData.Length);
tcpClient1->IOHandler->Write(ms.get());
}
Otherwise, the final solution is to just copy the data into a TIdBytes, eg:
{
TIdBytes bytes;
bytes.Length = myData.Length;
memcpy(&bytes[0], &myData[0], myData.Length);
or:
std::copy(myData.begin(), myData.end(), bytes.begin());
tcpClient1->IOHandler->Write(bytes);
}

Encoding utf-8 with TIdHTTP->Put()

I want to PUT json data to a REST service with TIdHTTP.
It works, as long as I don't have scandinavian letter in the json-data (ÅÄÖ). Then the server rejects the message. I can send the same data OK with Postman, so it is not a server issue.
My code:
String JsonData = "{...}";
TStringStream *JsonStream = new TStringStream(JsonData);
IdHTTP1->Request->CustomHeaders->AddValue("user", AUser);
IdHTTP1->Request->CustomHeaders->AddValue("password", APassword);
IdHTTP1->Request->ContentType = "application/json";
IdHTTP1->Request->CharSet = "utf-8";
IdHTTP1->Put("https://restserver", JsonStream);
delete JsonStream;
I've found examples in Delphi, where you create the TStringStream with an encoding flag:
AStream := TStringStream.Create(SomeData, TEncoding.UTF8);
But I can not see how an eqvuivalent works in c++.
This is an multi device application written with C++Builder v10.3
The TEncoding class is declared in the <System.SysUtils.hpp> header, and has a static UTF8 property (an example of its use is in Embarcadero's DocWiki). In your case, the construction of the TStringStream should look like this:
TStringStream *JsonStream = new TStringStream(JsonData, TEncoding::UTF8, false);

type TMessage is not a defined class with virtual function C++ builder sample

Im trying to use embarcadero sample on useing android camera and geting an error:
"type TMessage is not a defined class with virtual function" on lines:
void __fastcall TForm1::DoMessageListener(const TObject *Sender, TMessage const *M) {
TMessageDidFinishTakingImageFromLibrary const *v = dynamic_cast<TMessageDidFinishTakingImageFromLibrary const *>(M);
if (v) {
Image1->Bitmap->Assign(v->Value);
}
}
In Delphi, TMessage works fine, but in C++Builder you must use TMessageBase instead:
void __fastcall TForm1::DoMessageListener(const TObject *Sender, TMessageBase const *M)
This is clearly stated in the documentation:
Sending and Receiving Messages Using the RTL:
The RTL only defines one type of message, TMessage. It is actually a template that you can use to create messages for specific types of values; for example: TMessage<int> or TMessage<UnicodeString>. You can also subclass TMessage to define your own message types or, if you are using FireMonkey, you can reuse the message types defined by the framework.
Note: For C++ projects use TMessageBase instead of TMessage.
System.Messaging.TMessage
TMessage represents the base class used for message purposes. It can be inherited in order to send custom messages.
Warning: For C++ projects, use TMessageBase instead.
System.Messaging.TMessageBase
Alias to System.Messaging.TMessage.
Use System.Messaging.TMessageBase for C++ projects instead of System.Messaging.TMessage.
This use of TMessageBase is also demonstrated in the documentation's System.Messaging (C++) example.

How do I construct this Delphi class in C++Builder?

What is the C++ equivalent of this code
ImageEnView1.IEBitmap.VirtualBitmapProvider := TIESlippyMap.Create();
I get a compile error
[bcc32 Error] Unit1.cpp(12907): E2285 Could not find a match for 'TIESlippyMap::TIESlippyMap()'
on my code
ImageEnview1->IEBitmap->VirtualBitmapProvider = new TIESlippyMap();
ImageEnView1->IEBitmap->VirtualBitmapProvider = new TIESlippyMap();
Update: You are trying to call this constructor:
constructor Create(provider:TIESlippyMapProvider = iesmpMapQuest; const cachePath:string = '');
The compiler error you are getting means that the C++ compiler cannot find a constructor that has no parameters, or at least a constructor with parameters that all have default values assigned to them. Depending on which C++Builder version you are using, it is likely that the Delphi compiler included with it is not emitting the default parameter values when generating the C++ .hpp file for the class. Older Delphi compiler versions did not do that correctly, but newer versions do. In which case, it sounds like you are using an affected version, so you will have to fill in those parameter values explicitly:
ImageEnView1->IEBitmap->VirtualBitmapProvider = new TIESlippyMap(iesmpMapQuest, "");
Or else edit the .hpp file to include the default values correctly:
class TIESlippyMap : public ...
{
...
public:
__fastcall TIESlippyMap(TIESlippyMapProvider provider = iesmpMapQuest, const String cachePath = "");
...
};

Error converting variant into double [ Delphi XE + IBObjects 4.9.12 ]

My configuration:
Delphi XE
Firebird 2.1
IBObjects 4.9.12
Windows 7 64bits
I get an exception when I try to set a value to a IBOQuery parameter ("Could not convert variant of type (UnicodeString) into type (Double)").
The exception is raised from TIB_Column.SetAsVariant procedure in IB_Components.pas (line 42795). To create this situation, just try to pass a string to a date parameter:
myQuery.paramByName('mydate').AsString := DateToStr(IncDay(Now,5));
During last 25 days I'm trying to solve this situation, but in IBO support list I've got no answers.
Someone have an idea?
IBObjects's architecture is converting(at a moment of execution) all parameters, fields, etc to String or Variants. If your 'mydate' parameter is 'DateTime'(numeric) type then you must fill it up with a corespondent type value. Is not logic to fill an 'numeric' type parameter with a string...
try this
myQuery.paramByName('mydate').AsDateTime:= Now+5; //is the same as David's answer.
or
myQuery.paramByName('mydate').AsFloat:=Now+5; //or IncDay(Now,5)
Best regards,
Radu

Resources