Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

void* to tagVARIANT (base64 in XML)

180 views
Skip to first unread message

Johnny

unread,
May 30, 2003, 2:07:49 PM5/30/03
to

Hi,


I am trying to save an image as a base64 encoded binary in an
XML file.I believe I have the solution but I am stuck at
casting a void* to tagVariant. I am using MSXML 4.0. Here is a sample of the code I am trying

TMemoryStream* pstream = new TMemoryStream();

//create xml objects as needed
Msxml2_tlb::IXMLDOMDocumentPtr pDomDoc;

Msxml2_tlb::IXMLDOMElementPtr pRootNode;

Msxml2_tlb::IXMLDOMNodePtr pChildNode;

short IsSuccessfulLoad;

Image1->Picture->Bitmap->SaveToStream(pstream);


//Load xml file
tagVARIANT v(Variant(WideString("c:\\SomeFileForImage.xml")));

pDomDoc.CreateInstance(CLSID_DOMDocument);

pDomDoc->load(v,&IsSuccessfulLoad);


//create root node

pRootNode = pDomDoc->createElement(WideString("ROOT"));

pDomDoc->_set_documentElement(pRootNode);

pRootNode = pDomDoc->documentElement;


//create blob element and set as base64
pChildNode = pDomDoc->createElement(WideString("BLOB"));
pChildNode->dataType = WideString("bin.base64");


//THIS IS THE FUNCTION TO SET THE DATA BUT IT EXPECTS Variant
//and I have a void* MemoryStream->memory

pChildNode->set_nodeTypedValue();

pChildNode = pRootNode->appendChild(pChildNode);

pDomDoc->save(v);

delete pstream;


John

Remy Lebeau (TeamB)

unread,
May 30, 2003, 2:24:17 PM5/30/03
to

"Johnny" <ms_C...@yahoo.com> wrote in message
news:3ed79df5$1...@newsgroups.borland.com...

> I believe I have the solution but I am stuck at casting a
> void* to tagVariant. I am using MSXML 4.0.

It is not a matter of simply casting the memory stream to a variant. You
are missing a very important step in between. Your TMemoryStream contains
just the raw binary image data. You need to pass that binary data through
the base64 encoding algorithm first (which you have to implement yourself or
else find a third-party implementation) in order to produce a normal string
value that you can then assign to the XML node you create. You can't just
assign the binary data as-is to the XML node. The dataType attribute is
just a string value like any other. The XML node does not parse the binary
data for you automatically. It is your own responsibility to handle that
yourself. The same goes for reading the data back later - you have to read
the XML node value as a string first, and then pass it through the base64
decoding algorithm in order to get the original raw binary data back, which
you can then store in a stream for use with the TImage's LoadFromStream()
method.


Gambit


johnny

unread,
May 30, 2003, 2:44:28 PM5/30/03
to
You are right. I reread the asp example I was using
(http://www.perfectxml.com/articles/xml/binary.asp)
it was reading binary which was already base64 encoded data from an access database.

I am using BCBC6 which has TIdBase64Encoder. Would this encoder suffice? TIdBase64Encoder->CodeString() expects an AnsiString. I will try the following then

char *buf = (char*) pMemoryStream->Memory;
AnsiString Text = buf;
Encoder->CodeString(Text);

and see what happens

thanks

Remy Lebeau (TeamB)

unread,
May 30, 2003, 4:05:22 PM5/30/03
to

"johnny" <ms_c...@yahoo.com> wrote in message
news:3ed7a68c$2...@newsgroups.borland.com...

> I am using BCBC6 which has TIdBase64Encoder.

BCB6 comes with Indy 8, which is an outdated version. I would suggest
upgrading to Indy 9 from http://www.nevrona.com/indy if you are going to use
that component.

> Would this encoder suffice?

Perhaps. But I know a lot of people have problems using that component
correctly.

You could try implementing the base64 algorithm manually. It is a
relatively simple algorithm to implement. At the bottom of this message is
a snippet of code I wrote for my own custom XML base64 handling. With it,
you could simply call Base64EncodeStream() to convert your TMemoryStream
into an encoded AnsiString for passing to the XML node, and then later call
Base64DecodeStringToStream() to convert the XML value back to a stream for
passing to TImage's LoadFromStream() method.

> TIdBase64Encoder->CodeString() expects an
> AnsiString. I will try the following then

That is not correct. You are treating the Memory as a char*, but binary
data frequently has null characters in it, so your code will break on the
first null it encounters. Thus you won't be encoding the full data
correctly because the full data will not be assigned to the AnsiString.

Upgrading to Indy 9 will allow you to pass a TStream instead of an
AnsiString, so that you do not lose any data.


Gambit


const char decode_table[256] = {
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
};

char encode_table[64] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

bool Base64Encode(TStream *Src, TStream *Dest, bool LineBreaks, int
Columns)
{
BYTE inbuf[3], outbuf[4];
int pos = 0, linelen = 0, total = 0, size;
AnsiString out;
char *p;

// the base64 RFC dictates that lines cannot be longer than
// 76 characters. Cutting off at 74 (data + CRLF) to be safe
if( Columns < 4 )
Columns = 4;
else if( Columns > 72 )
Columns = 72;

if( !Src || !Dest || (LineBreaks && ((Columns % 4) != 0)) )
{
// invalid parameters
return false;
}

try
{
size = (Src->Size - Src->Position);
if( size <= 0 )
{
// invalid parameters
return false;
}

try
{
// we know that the data will grow by 4/3 adjusted to 3
boundary
int allocsize = (((size*4)/3)+2);

if( LineBreaks )
allocsize += (((allocsize/Columns)+1)*2);

out.SetLength(allocsize);
p = out.c_str();
}
catch (const Exception&)
{
// out of memory
return false;
}

while( pos < size )
{
memset(inbuf, 0, 3);
int read = Src->Read(inbuf, 3);
if( read <= 0 )
{
// encode failed
return false;
}

pos += read;

outbuf[0] = encode_table[(inbuf[0] >> 2) & 0x3F];
outbuf[1] = encode_table[((inbuf[0] << 4) | (inbuf[1] >> 4))
& 0x3F];
outbuf[2] = encode_table[((inbuf[1] << 2) | (inbuf[2] >> 6))
& 0x3F];
outbuf[3] = encode_table[inbuf[2] & 0x3F];

if( read < 3 )
{
outbuf[3] = '=';
if( read == 1 )
outbuf[2] = '=';
}

if( LineBreaks )
{
if( (linelen > 0) && ((linelen % Columns) == 0) )
{
memcpy(p, "\r\n", 2);
p += 2;

linelen = 0;
total += 2;
}
}

memcpy(p, outbuf, 4);
p += 4;
linelen += 4;
total += 4;
}

if( total > 0 )
{
if( (total % 4) != 0 )
{
memcpy(p, "\r\n", 2);
total += 2;
}

Dest->WriteBuffer(out.c_str(), total);
}
}
catch( const Exception&)
{
// encode failed
return false;
}

// all ok
return true;
}

AnsiString Base64EncodeStream(TStream *Src, bool LineBreaks, int
Columns)
{
TStringStream *Strm = NULL;
AnsiString Result;

try
{
Strm = new TStringStream("");
try
{
if( Encode(Src, Strm, LineBreaks, Columns) )
Result = Strm->DataString;
}
__finally {
delete Strm;
}
}
catch (const Exception&)
{
// out of memory
return "";
}

return Result;
}


bool Base64Decode(TStream *Src, TStream *Dest)
{
BYTE inbuf[4], outbuf[3];
int pos = 0, len = 0, size, idx;
AnsiString out;
char *p, c;

if( !Src || !Dest )
{
// invalid parameters
return false;
}

try
{
size = (Src->Size - Src->Position);
if( size <= 0 )
{
// invalid parameters
return false;
}

if( (size % 4) != 0 )
{
// invalid size
return false;
}

try
{
out.SetLength(((size*3)/4)+2);
p = out.c_str();
}
catch (const Exception&)
{
// out of memory
return false;
}

while( pos < size )
{
memset(inbuf, 0, 4);
idx = 0;

// loop through the data ignoring line breaks and making
sure
// the data is valid before decoding
while( (pos < size) && (idx < 4) )
{
Src->ReadBuffer(&c, 1);
++pos;

if( (c == '\r') || (c == '\n') )
continue;

if( (decode_table[c] == -1) && (c != '=') )
{
// invalid data
return false;
}

// current character is ok, move on to the next one
inbuf[idx++] = c;
}

// was a full buffer read?
if( idx < 4 )
{
// was any data read at all?
if( idx == 0 )
break;

// invalid data
return false;
}

outbuf[0] = ((decode_table[inbuf[0]] << 2) |
(decode_table[inbuf[1]] >> 4));
outbuf[1] = (((decode_table[inbuf[1]] << 4) & 0xF0) |
(decode_table[inbuf[2]] >> 2));
outbuf[2] = (((decode_table[inbuf[2]] << 6) & 0xC0) |
decode_table[inbuf[3]]);

int num = 3;
if( inbuf[3] == '=' )
--num;
if( inbuf[2] == '=' )
--num;

memcpy(p, outbuf, num);
p += num;
len += num;
}

if( len > 0 )
Dest->WriteBuffer(out.c_str(), len);
}
catch (const Exception&)
{
// decode failed
return false;
}

// all is ok
return true;
}

bool Base64DecodeStringToStream(const AnsiString &Src, TStream *Dest)
{
TStringStream *Strm = NULL;
bool Success = false;

try
{
Strm = new TStringStream(Src);
try {
Success = Decode(Strm, Dest);
}
__finally {
delete Strm;
}
}
catch (const Exception&)
{
// out of memory
return false;
}

return Success;
}


Gambit


johnny

unread,
Jun 2, 2003, 12:47:40 PM6/2/03
to

Hi,

Absolutely correct about casting from Memorystream to
AnsiString, it had null characters. I had done something
similar in the past, but the Memory stream only had strings.

The code you posted works beautifully. Tried encoding decoding and it works great. Thank you, its was greatly appreciated!!!

The only issue that remains is in saving the data to the xml file(or reading it). Data is missing and therefore decoding fails (Correct amount = 2868372 bytes. Amount saved (or read)
2829609);

I am using set_text to save the base64 string


TMemoryStream* pSourceStream = new TMemoryStream();

//create xml objects as needed
Msxml2_tlb::IXMLDOMDocumentPtr pDomDoc;
Msxml2_tlb::IXMLDOMElementPtr pRootNode;
Msxml2_tlb::IXMLDOMNodePtr pChildNode;
short IsSuccessfulLoad;

Image1->Picture->Bitmap->SaveToStream(pSourceStream);

//Load xml file
tagVARIANT v(Variant(WideString("c:\\SomeFileForImage.xml")));
pDomDoc.CreateInstance(CLSID_DOMDocument);
pDomDoc->load(v,&IsSuccessfulLoad);

//create root node
pRootNode = pDomDoc->createElement(WideString("ROOT"));
pDomDoc->_set_documentElement(pRootNode);
pRootNode = pDomDoc->documentElement;

//create blob element and set as base64
pChildNode = pDomDoc->createElement(WideString("BLOB"));

//reset position so parsing begins from start of stream
pSourceStream->Position = 0;
// Base64EncodeStream <=== Remy Lebeau(TeamB)
AnsiString strBinaryImage = Base64EncodeStream(pSourceStream,true,76);

pChildNode->set_text(WideString(strBinaryImage));

pChildNode = pRootNode->appendChild(pChildNode);

pDomDoc->save(v);
delete pSourceStream;

I tried using :
pChildNode->set_nodeValue(TVariant(strBinaryImage));
but no data was saved at all.

I read the XML node using :

AnsiString strBinary = pChildNode->get_text();

I believe the error occurs on reading the XML file, because if I strip all XML tags from the file, it is the correct size.

Any suggestions or ideas what the problem may be?


"Remy Lebeau \(TeamB\)" <gamb...@yahoo.com> wrote:
>

0 new messages