Jump to content
Roger Cigol

WideString.c_bstr() operation in 11.2

Recommended Posts

I have a working class to interface to a COM interface (created by a 3rd party). My working class is a wrapper around the type library functions I generated using C++ Builder. This code works 100% when compiled under C++ Builder 10.4. I have just used the same class in a new application running on the same machine as the working system but this time compiled using 11.2 patch 1. This doesn't work. The problem seems to be in passing pointers to BSTR. I use WideString.c_bstr() to get the pointer to the BSTR to pass to the COM interface. 

QUESTION: can anyone confirm that this approach works ok using 11.2 ? (or should I be looking into this at a lower level with a view to creating an Embarcadero RSP bug report?)

Share this post


Link to post
2 hours ago, Roger Cigol said:

I have just used the same class in a new application running on the same machine as the working system but this time compiled using 11.2 patch 1. This doesn't work. The problem seems to be in passing pointers to BSTR. I use WideString.c_bstr() to get the pointer to the BSTR to pass to the COM interface.

What is the exact problem you are having?  Can you provide an example?  Passing a BSTR (which is already a pointer type to begin with) by value for an [in] parameter is very different than passing a pointer to a BSTR for an [in,out]/[out] parameter.  The WideString::c_str() method will work fine for the former case, but you can't use it in the latter case, you would have to use WideString::operator& instead.

Quote

QUESTION: can anyone confirm that this approach works ok using 11.2 ? (or should I be looking into this at a lower level with a view to creating an Embarcadero RSP bug report?)

Not without seeing the actual code you are having trouble with.

Edited by Remy Lebeau

Share this post


Link to post

We have been chasing this problem. The COM interface we are talking to is for the CodeSoft printer software (supplied by Teklynx). We have this bit of code

String LabelName = "C:\FullPathToLabelFile.lab";
      ResetFloatingPoint();  // our function to reset FPU flags
      CodeSoftDocument = CodeSoft->Documents->Open(WideString(LabelName).c_bstr(), 0);
      if (!CodeSoftDocument) {
         MostRecentError = LPE_CantOpenLabelFile;
      }
      else {
         MostRecentError = LPE_Ok;
      }

If we compile this under 10.4.2 we get MostRecentError = LPE_Ok. (and the label prints) If we compile this under 11.2 we get MostRecentError = LPE_CantOpenLabelFile (and no label).

If trace this down into the wrapper for the type library we find the definition of the Open() function as follows

Labelmanager2_tlb::IDocument* __fastcall Open(BSTR strDocName/*[in]*/, 
                                                VARIANT_BOOL ReadOnly/*[in,def,opt]*/)
  {
    TDispID dispid(/ Open / DISPID(7));
    TAutoArgs<2> _args;
    _args[1] = strDocName /*[VT_BSTR:0]*/;
    _args[2] = ReadOnly /*[VT_BOOL:0]*/;
    OleFunction(_dispid, _args);
    return (Labelmanager2_tlb::IDocument* /*[C1]*/)(LPDISPATCH) /*[VT_DISPATCH:1]*/_args.GetRetVariant();
  }

CodeSoft has a debug mode - and this shows us that under 11.2 the label file name is being interpreted as a NULL BSTR (hence it can't open the label file).

Share this post


Link to post
3 hours ago, Roger Cigol said:

 


String LabelName = "C:\FullPathToLabelFile.lab";

 

You need to escape the '\' character in a string literal, eg:

String LabelName = "C:\\FullPathToLabelFile.lab"; // <-- note the double slashes!

/*
NOTE: you really should be using the L"" prefix or _D() macro instead!

String LabelName = L"C:\\FullPathToLabelFile.lab";
or:
String LabelName = _D("C:\\FullPathToLabelFile.lab");
*/

Or, when using a Clang compiler, you can use a raw string literal instead, which doesn't require escaping, eg:

String LabelName = LR"(C:\FullPathToLabelFile.lab)";

/*
Alternatively, using the _D() macro:

String LabelName = _D(R"(C:\FullPathToLabelFile.lab)");
*/

Other than that, the rest of that code snippet looks fine.  So, whatever problem you are having is not in your code, but is in the RTL itself.

3 hours ago, Roger Cigol said:

If trace this down into the wrapper for the type library we find the definition of the Open() function as follows

...

CodeSoft has a debug mode - and this shows us that under 11.2 the label file name is being interpreted as a NULL BSTR (hence it can't open the label file).

Since Open() takes a raw BSTR pointer, the problem has to be at the call site, ie with the construction of the UnicodeString or WideString objects.

 

You are using a narrow string literal when initializing the UnicodeString, instead of using a wide string literal.  So, that will invoke a narrow-to-wide data conversion at runtime, which could cause the UnicodeString to end up empty if something goes wrong in the conversion.  Did you check for that?

 

The WideString constructor that takes a UnicodeString will set a null BSTR pointer if the UnicodeString is empty.  Otherwise, it simply calls the Win32 SysAllocStringLen() function, which returns a null BSTR pointer on failure.  Did you check for that?

 

So, either the UnicodeString is empty to begin with (which it should not be in your case, given the code shown), or SysAllocStringLen() is failing due to insufficient system memory.  Did you check for that?

 

Try something like this to help narrow down the problem further:

String LabelName = "C:\\FullPathToLabelFile.lab";
if (LabelName.IsEmpty()) {
    throw ...;
}
WideString wLabelName(LabelName);
if (wLabelName.IsEmpty()) {
    throw ...;
}
...
CodeSoftDocument = CodeSoft->Documents->Open(wLabelName.c_bstr(), 0);
...

 

Share this post


Link to post

Thanks to all for input. SINCERE APOLOGIES : I accidentally created a "red herring" when I quickly replaced the actual test label path (which is long with lots of customer sensitive directory names) with "C:\FullPathToLabelFile.lab" and forgot to use the double \\. The actual label file path is correctly formated (ie includes \\).

@Lars Fosdal The 10.4.2 (working code) uses the classic compiler. The 11.2 (patch 1) code does not work with either the classic compiler of the clang32. I am running this on a remote PC (at the customer's premises) and need to interrupt their production to use the PC so testing is a bit tricky (and it also feels like I am hanging out dirty washing for everyone to see, each time I do a test and can't get it to work). I didn't think to try compiling under 10.4.2 using the clang32 compiler. I  will try to do this next time I "log in". [ I use Supremo for remote access to the PC ].

@Remy Lebeau By using the IDE debugger I have confirmed that the BSTR inside the call to Open() does indeed contain the correct label path. I accept all your other observations as good programming hints (although constructing a String (aka UnicodeString) by passing a constant array of 8 bit chars can't be a bad thing - as long as the 8 bit chars are all simple ASCII chars). But they are not the cause of my interesting problem. I don't feel a need to check for the [ exceedingly unlikely ]  failure of the internal call to the Win32 SysAllocStringLen() function because if this does fail the resulting error of "Can't open the label file" is going to be good enough. The key thing is that when the BSTR passed to Open() is a valid path to the file then it needs to work (as it does under 10.4.2).

Share this post


Link to post
5 hours ago, Roger Cigol said:

By using the IDE debugger I have confirmed that the BSTR inside the call to Open() does indeed contain the correct label path.

So, the real problem is that the BSTR is empty inside of the actual CodeSoft COM object's Open() method, rather than the TLB wrapper's Open() method?  If so, then that implies the problem is with either TAutoArgs or OleFunction() not passing the BSTR to the COM object correctly.

5 hours ago, Roger Cigol said:

I don't feel a need to check for the [ exceedingly unlikely ]  failure of the internal call to the Win32 SysAllocStringLen() function

If the BSTR is not empty inside of the TLB wrapper method, then WideString is working properly, and the problem is elsewhere.

Share this post


Link to post

Yes. I agree exactly with your diagnosis. It looks to me as if something has changed with the TAutoArgs or OleFunciton() handling of the BSTR type with the "jump" from 10.4.2 to 11.2.

Share this post


Link to post

I have now confirmed that with RAD Studio 10.4.2 I can get my COM interface to work correctly when compiled with either "Classic" or Clang32 compiler. And to repeat: neither compiler produces working results when compiled with 11.2.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×