A: 

If you simply pass a WCHAR -- the length information is lost. The BSTR is malformed and this is probably causing you all the grief. You need to use SysAllocString to use it across components. See MSDN -- the Remarks section. Try:

BSTR imagen = SysAllocString(L"imagen1.jpg");
dirkgently
well I've already done that ... in some of my other tries, and I get a similar result. I've tried again with SysAllocString and no change. However, i'm gonna leave it so. I think the mistake is when trying to take out the value from the serialized object, otherwise I don't understand.
markitus82
A: 

OK, this answer depends on you doing something odd these days, but might be applicable. A long, long time ago I had to pass a VB string under VB6 and VC++6(pro) from a VB app. to a VC++ app. The length came through OK but I often received one character on the other side.

The problem was that the receiving app. was not compiled for unicode, but rather as an ANSI project. The COM layer code that unpacked it on the far side of transfer did an interesting trick that I only found documented in an obscure corner of the MSDN in a book excerpt: it created an ABSTR.

An ABSTR isn't actually a type. There is no way to declare one. It's actually a reformatting of a BSTR's underlying storage so that you can pretend it's an ASCII char* in C++. This is done by first making sure the BSTR points to the first character after its header (typical anyway for such structures in C++, IIRC) and then shuffling the actual string data so that it contains all of the first bytes followed by all of the second bytes. Another name for this is "pure evil".

Two very bad things can happen this way: if this conversion is done, and you treat the result as if it's still a wide character string, you get gibberish. If it's not done and you treat the results as an ASCII character array, you usually get a single character, if the original contained only ASCII-range characters, since in the wide representation every other byte is a zero and the high bytes come second.

I can't quite tell from your description if this is what happened to you. But I recommend stopping the thing in a debugger and looking at all of the string data under that received value to see if it's been reshuffled in some unexpected way. If it has been shuffled, ask yourself why and look at the way you built the project.

The fact of an almost undocumented format wedged into an existing type as an alternative memory layout that's really hard to find, even by the how-many-string-formats-can-we-make-up standards of MS development, just about made me scream. It was almost as bad as trying to identify "GetModuleFileName" for the first time as the function to use to get the current executable's path.

Thomas Kammeyer
A: 

Your object needs to create a copy of the string in both the getter and the setter:

STDMETHODIMP CAnalisis::getImgName(BSTR* imgName)
{
    *imgName = SysAllocString(img.imgName);

    return S_OK;
}

STDMETHODIMP CAnalisis::setImgName(BSTR imgName)
{
    SysFreeString(img.imgName);
    img.imgName=SysAllocString(imgName);
    return S_OK;
}

of course, you need to free the string in the destructor, check for NULL ptrs etc.

peterchen
A: 

Alternatively you can use CComBSTR instead of BSTR. CComBSTR is a smarter than BSTR, it takes care of allocating and deallocating the memory.

Vinay
+1  A: 

The problem is that the serialization of Image class treats it as a contiguous block of memory. Since BSTR is really a pointer only the pointer value is serialized and the BSTR payload is lost.

Instead you should write all fields except BSTRs as binary and process BSTRs separately. For example, you can write BSTR length as integer first, then its payload. When reading you will read length first, call SysAllocStringLen() to allocate a buffer, then read the payload.

Leave serialization of simple fields as it is (the IPersistStreamInit::Save() ):

pStm->Write(&(img.color), (ULONG)sizeof(float), &cb);

For BSTRs do this:

int length = SysStringLen( img.uname );
pStm->Write(&length, (ULONG)sizeof(int), &cb);
if( length > 0 ) {
   pStm->Write( img.uname, (ULONG)(length * sizeof(WCHAR) ), &cb);
}

Similar for reading (the IPersistStreamInit::Load()):

int length;
pStm->Read(&length, (ULONG)sizeof(int), &cb);
if( length > 0 ) {
   img.uname = SysAllocStringLen( 0, length );
   pStm->Read( img.uname, (ULONG)( length * sizeof( WCHAR) ), &cb);
} else {
   img.uname = 0;
}

Note that this code writes/reads string length and then writes/reads the payload that consists of Unicode characters. Unicode characters occupy more than one bytes each - hence the multiplication in IStream Read/Write methods call.

sharptooth
so you mean that i should take this out of the struct, if i'm not wrong: <pre>private: typedef struct { DOUBLE size; float color; float light; VARIANT origin; } Image; long imgNameLenght; long unameLenght; BSTR imgName; BSTR uname;</pre>
markitus82
Nope, alter the serialization code. Edited answer to clarify.
sharptooth
IT WORKED!!!! I gave up about 2 weeks ago, after struggling for a few days. now it's done in a while. thank you so much. I've just marked this answer as the correct one, and totally helpful.I'm sure this will be useful for many other programmers as there are no examples at all.AMAZING!thxAmillion!
markitus82