BorlandTalk.com Forum Index BorlandTalk.com
Borland discussion newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Variant of char Array to TMemoryStream

 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (VCL Components Usage)
View previous topic :: View next topic  
Author Message
Maurice Anderson
Guest





PostPosted: Sun May 13, 2007 7:55 am    Post subject: Variant of char Array to TMemoryStream Reply with quote



Hello,

In a database if have a jpg stored as binary information in a table. The
size of the original pic is 51,797 bytes.

Through some ADO code I connect to the database and retrieve the data into a
Variant known as vData. The data is stored as an array of character bytes
in vData. Here is the code I use to retrieve the bytes into a buffer
(thanks Remy!):

unsigned char* bytes;

if( (vData.IsArray()) && (vData.Type() & varByte) )
{
int lBound = vData.ArrayLowBound();
int hBound = vData.ArrayHighBound();
if( hBound >= lBound )
{
bytes = ( unsigned char*)vData.ArrayLock();
int numBytes = ((hBound - lBound) + 1);

MemoryStream1->Write(bytes, (numBytes / 4));

vData.ArrayUnlock();
}
}

Image1->Picture->Bitmap->LoadFromStream(MemoryStream1);

Strange thing is numBytes, the bytes in vData, is 207188, which is 4x the
original size of 51,797 bytes.

My question is how do I retrieve the bytes from the variant to the original
51,797 bytes?

Thanks
Back to top
Clayton Arends
Guest





PostPosted: Mon May 14, 2007 8:10 am    Post subject: Re: Variant of char Array to TMemoryStream Reply with quote



How does this record get populated? If it gets populated by your code can
you show that code? I suspect that the binary data in the field truly is
207,188 bytes.

- Clayton
Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Mon May 14, 2007 10:31 pm    Post subject: Re: Variant of char Array to TMemoryStream Reply with quote



"Maurice Anderson" <mauriceanderson (AT) hotmail (DOT) com> wrote in message
news:46467e20$1 (AT) newsgroups (DOT) borland.com...

Quote:
Through some ADO code I connect to the database and retrieve
the data into a Variant known as vData.

You did not show that code. And why are you retreiving the bytes as a
Variant array instead of a TStream in the first place? Assuming you
are using Borland's ADO components, and the DB field is a blob field,
then you can use the TDataSet::CreateBlobStream() method to access the
raw field data directly.

Quote:
if( (vData.IsArray()) && (vData.Type() & varByte) )

Did you verify that Type() is not returning varByRef as well?

Quote:
int lBound = vData.ArrayLowBound();
int hBound = vData.ArrayHighBound();

What are lBound and hBound being set to exactly? What does
vData.ArrayDimCount() return?

Quote:
MemoryStream1->Write(bytes, (numBytes / 4));

You are not setting the Position back to 0 before loading the stream.

Quote:
Image1->Picture->Bitmap->LoadFromStream(MemoryStream1);

If the DB field is storing a JPG, then that statement will fail every
time. You can't load a JPG image into a TBitmap. You would have to
instantiate a separate TJPEGImage object, load the data into that, and
then Assign() it to the TPicture. For example:

#include <jpeg.hpp>

TJPEGImage *jpg = new TJPEGImage;
try
{
MemoryStream1->Position = 0;
jpg->LoadFromStream(MemoryStream1);
Image1->Picture->Assign(jpg);
}
__finally
{
delete jpg;
}

Quote:
Strange thing is numBytes, the bytes in vData, is 207188, which is
4x the
original size of 51,797 bytes.

Then either the image really is that large to begin with, or else the
array is not holding the image as 1-byte characters.


Gambit
Back to top
Maurice Anderson
Guest





PostPosted: Tue May 15, 2007 8:11 am    Post subject: Re: Variant of char Array to TMemoryStream Reply with quote

Here is some of my code that loaded the picture into the database in the 1st
place. Note: I successfully stored the pic into the registry. I thins
code, I am retrieving from the registry and putting it into the DB.

My Ado components all work fine.

My Registry component works as well as I am able to write to and display the
picture directly from the registry.

BYTE *buffer = NULL;

TMemoryStream *DataOut = new TMemoryStream;

/** Registry Setup. Retrieve into a buffer **/

String KeyPath = "HKEY_LOCAL_MACHINE\\Software\\0Maurice";
RegIniFilePlus1->CloseKey();
RegIniFilePlus1->OpenKey(KeyPath, 0, false);
buffer = RegIniFilePlus1->ReadREG_BINARY("Dom");

/** I write the pic to a Stream for testing purposes **/

DataOut->Write(buffer, 51797);

/** Ado Setup and Connection **/

if(!AdoConnection1->Connected){
AdoConnection1->Connect();
}

if(AdoConnection1->Connected){

ADOCommand1->Connection = AdoConnection1;
}
else{
Memo1->Lines->Add("AdoConnection1 Not Connected);
return;
}

/** Convert to Binary **/

char * output = new char[2*DataOut->Size];
BinToHex( buffer, output, DataOut->Size );
String ALine = output;

/** Write to database **/

ADOCommand1->CommandText = "Insert into pics (pic) values ('" + ALine +
"'); ";

Quote:
And why are you retreiving the bytes as a Variant array instead of a
TStream in the first place?

I am a junior level programmer and couldnt think of a better way to pull it
off. :)

Quote:
Assuming you are using Borland's ADO components,

I wrote my own components that connect to a datasource using ADO api.

Quote:
Did you verify that Type() is not returning varByRef as well?

Yes - it checks out as 8209[x2011].

Quote:
What are lBound and hBound being set to exactly?

vData contains an array of char bytes, they set the bounds of the array.

What does vData.ArrayDimCount() return?

1

Quote:
#include <jpeg.hpp

TJPEGImage *jpg = new TJPEGImage;
try
{
MemoryStream1->Position = 0;
jpg->LoadFromStream(MemoryStream1);
Image1->Picture->Assign(jpg);
}
__finally
{
delete jpg;
}

Sidebar question: What is the rule for when to use "try" or not to use?

Quote:
original size of 51,797 bytes.
Then either the image really is that large to begin with, or else the
array is not holding the image as 1-byte characters.

I think you may be on to something here. I dont think that its a
co-incidence that 207188 is exactly 4 times 51797, which is the original
size of the jpg {according to windows}.

Maybe the original file was stored in Hex or something intead of raw binary.
For example, when I convert Hex character 'D' to binary it comes to '1101'.
And in terms of characters, 'D' is exactly 4 times smaller than '1101'. So
maybe somehow it got converted. I guess I would need a way to
"down-convert" '1101' back to 'D' and so on.
Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Tue May 15, 2007 2:33 pm    Post subject: Re: Variant of char Array to TMemoryStream Reply with quote

"Maurice Anderson" <mauriceanderson (AT) hotmail (DOT) com> wrote in message
news:46493dba$1 (AT) newsgroups (DOT) borland.com...

Quote:
Here is some of my code that loaded the picture into the
database in the 1st place.

No wonder your retreival code is all messed up. You are not storing
the actual image data to begin with. You are converting the binary
data into a hex string and then storing that instead of the original
data as-is.

Quote:
Note: I successfully stored the pic into the registry.

That is a very bad idea. Images are much too large to store in the
Registry efficiently, and Microsoft strongly discourages storing
anything above 2K into the Registry. Your image is just over 50K -
that's more than 25x the recommended limit!

Quote:
if(!AdoConnection1->Connected){

You are using a TADOConnection component, which provides access to
TCustomADODataSet objects, which in turn derive from TDataSet. Like I
said earlier, you should be using the TDataSet::CreateBlobStream()
method to access binary blob field data directly.

Quote:
ADOCommand1->CommandText = "Insert into pics (pic) values ('" +
ALine +
"'); ";

I would strongly suggest NOT working with binary data that way! Add a
new record to the DB, then retreive the TField for the "pic" field and
load the binary data directly into it with a blob stream. No command
text needed, and much more efficient and straight forward.

Quote:
And why are you retreiving the bytes as a Variant array instead of
a
TStream in the first place?

I am a junior level programmer and couldnt think of a better way to
pull it
off. Smile

To pull what off exactly?

Quote:
I wrote my own components that connect to a datasource using ADO
api.


That is not what the code you showed says you are doing.

Quote:
What are lBound and hBound being set to exactly?

vData contains an array of char bytes, they set the bounds of the
array.


That is not what I asked you. I wanted you to show the actual numbers
being returned. You still haven't shown the code that is actually
creating and filling in the array.

Quote:
I think you may be on to something here. I dont think that its a
co-incidence that 207188 is exactly 4 times 51797, which is
the original size of the jpg {according to windows}.

That would definately indicate that you are not storing the image data
properly to begin with.

Quote:
Maybe the original file was stored in Hex or something intead
of raw binary.

Did you try actually inspecting the bytes that are loaded into your
array and TMemoryStream yet?

Quote:
I guess I would need a way to "down-convert" '1101' back
to 'D' and so on.

No. You need to fix your code to store your original image data in
its original binary format to the DB in the first place with no
conversions being performed at all.


Gambit
Back to top
Maurice Anderson
Guest





PostPosted: Tue May 15, 2007 9:09 pm    Post subject: Re: Variant of char Array to TMemoryStream Reply with quote

Quote:
Note: I successfully stored the pic into the registry.

That is a very bad idea. Images are much too large to store in the
Registry efficiently, and Microsoft strongly discourages storing anything
above 2K into the Registry.

I was doing it for test purposes only. Good tip. Thanks.

Quote:
if(!AdoConnection1->Connected){

You are using a TADOConnection component, which provides access to
TCustomADODataSet objects,

I wrote my own Ado components using the same names as AdoExpress. Please
see a snippet below

Quote:
Like I said earlier, you should be using the TDataSet::CreateBlobStream()

Again, I created my own AdoRecordSet, etc. So there is no
CreateBlobStream() method.

However, I want to create my own CreateBlobStream(). So what would be a
good way to send a blobStream to a database without mangling the stream of
data the way I did? If you can show me some code that would put the data
into a storage area that I can send to the database that would be great.
Then afterwards, I can code the component to actually apply the changes to
the database. Probably something like:

SomeWayOfHoldingADataStream *pVal;
ADOField * FpField;
FpField->set_Value( &pVal ) ;

Quote:
I am a junior level programmer and couldnt think of a better way to pull
it off. :)

To pull what off exactly?

You asked me why I was retreiving the bytes as a Variant array instead of a
TStream in the first place. I use a Variant because I didnt know of a
better storage medium than a Variant. Maybe I can store it in a BYTE buffer
or something but then I would have to worry about telling the developer the
size of the data and so on.

Quote:
vData contains an array of char bytes, they set the bounds of the array.

That is not what I asked you. I wanted you to show the actual numbers
being returned. You still haven't shown the code that is actually
creating and filling in the array.

Here is a sampling of my Code. I created my Own AdoRecordset and so on. I
have reduced the code to a summary to for simplier viewing:

/*******************************************************/
ADORecordset * rs;

hr = CoCreateInstance(
CLSID_CADORecordset,
NULL,
CLSCTX_INPROC_SERVER,
IID_IADORecordset,
(LPVOID *)&rs);

//.. My Next() Method
if(pFields)
{
pFields->Release();
}
rs->MoveNext();
hr = rs->get_Fields(&pFields);
FAdoFields->pFields = pFields; <--- FAdoFields is a derived class of
TOwnedCollection

//..In my TAdoCollectionItem component

FData Variant;

VARIANT pVal ;
WideString pName2;
VARIANT Type;

FpField->get_Name( &pName2 ) ; // memory leak
FpField->get_Value( &pVal ) ; // memory leak
FpField->get_Type(&dt);

this->FData = pVal;
this->FFieldName = pName2;
this->FType = dt;

Variant __fastcall TAdoCollectionItem::GetAsVariant()
{
return FData;
}

//..Form1 cpp

vData = ADODataSet1->AdoFields->FieldByName("Pic")->AsVariant;

<-- Again, ADODataSet1 is not AdoExpress. It is my own component built
around MicroSoft's ADORecordset.
/*******************************************************

Quote:
Did you try actually inspecting the bytes that are loaded into your
array and TMemoryStream yet?

No I didnt. Could you show me how though? And what would be the purpose?
Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Wed May 16, 2007 12:19 am    Post subject: Re: Variant of char Array to TMemoryStream Reply with quote

"Maurice Anderson" <mauriceanderson (AT) hotmail (DOT) com> wrote in message
news:4649db2a$1 (AT) newsgroups (DOT) borland.com...

Quote:
I wrote my own Ado components using the same names as AdoExpress.

You should not have done that. Always name new components uniquely to
avoid conflicts.

What is wrong with using the standard ADO components?

Quote:
However, I want to create my own CreateBlobStream().

Then I suggest you look at Borland's ADO source code to see how the
TCustomADODataSet::CreateBlobStream() method and the TADOBlobStream
class are implemented.

Quote:
FpField->get_Name( &pName2 ) ; // memory leak

That is not a memory leak. The WideString will take ownership of the
returned BSTR and will free it when itself goes out of scope.

Quote:
FpField->get_Value( &pVal ) ; // memory leak

That is a leak because you are not calling VariantClear(). Passing a
VARIANT to a Variant creates a copy of the VARIANT data, so you have
to clear the original VARIANT when you are done with it.

Quote:
Again, ADODataSet1 is not AdoExpress. It is my own component built
around MicroSoft's ADORecordset.

So is Borland's, so why aren't you using Borlands components?

Quote:
Did you try actually inspecting the bytes that are loaded into
your
array and TMemoryStream yet?

No I didnt. Could you show me how though?

Just use the debug insteptor for that.

Quote:
And what would be the purpose?

You know what the original bytes were before you stored them, so you
should be verifying that the bytes are actually what you expect them
to be when you load them back later.


Gambit
Back to top
Display posts from previous:   
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (VCL Components Usage) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.