Roger Cigol 107 Posted January 18, 2023 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
Remy Lebeau 1436 Posted January 18, 2023 (edited) 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 January 18, 2023 by Remy Lebeau Share this post Link to post
Roger Cigol 107 Posted February 6, 2023 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
Lars Fosdal 1793 Posted February 6, 2023 @Roger Cigol Did you switch to the new compiler? Share this post Link to post
Remy Lebeau 1436 Posted February 6, 2023 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
Roger Cigol 107 Posted February 8, 2023 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
Remy Lebeau 1436 Posted February 8, 2023 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
Roger Cigol 107 Posted February 8, 2023 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
Roger Cigol 107 Posted February 10, 2023 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
Lars Fosdal 1793 Posted February 11, 2023 Did you report the problem on Quality Portal? Share this post Link to post
Roger Cigol 107 Posted April 10, 2023 I've created an RSP. https://quality.embarcadero.com/browse/RSP-41374 1 Share this post Link to post
kiku 0 Posted August 9 Excuse me for interrupting. I ran into same issue, but I cannot open this link 'https://quality.embarcadero.com/browse/RSP-41374'. so, Could you tell me this the solution? Share this post Link to post
Lajos Juhász 295 Posted August 9 1 hour ago, kiku said: so, Could you tell me this the solution? The solution is this thread. Share this post Link to post
Remy Lebeau 1436 Posted August 9 8 hours ago, kiku said: I ran into same issue, but I cannot open this link 'https://quality.embarcadero.com/browse/RSP-41374'. so, Could you tell me this the solution? The ticket is still open. 6 hours ago, Lajos Juhász said: The solution is this thread. There is no solution presented in this thread. It is a regression bug somewhere in the RTL. Share this post Link to post
Chelly 0 Posted August 13 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(); } Its looks like the opterator= doesnt work. I tried step execution ,than i found _args[1] = strDocName /*[VT_BSTR:0]*/; this line is entering into the bool opertartor as follow OleVariant& operator =(const bool rhs) { PVariant(this)->operator=(rhs); return *this; } so ,maybe we can as follow Labelmanager2_tlb::IDocument* __fastcall Open(BSTR strDocName/*[in]*/, VARIANT_BOOL ReadOnly/*[in,def,opt]*/) { TDispID dispid(/ Open / DISPID(7)); TAutoArgs<2> _args; UnicodeString strChange = strDocName; _args[1] = strChange /*[VT_BSTR:0]*/; _args[2] = ReadOnly /*[VT_BOOL:0]*/; OleFunction(_dispid, _args); return (Labelmanager2_tlb::IDocument* /*[C1]*/)(LPDISPATCH) /*[VT_DISPATCH:1]*/_args.GetRetVariant(); } Share this post Link to post