-
Content Count
3057 -
Joined
-
Last visited
-
Days Won
139
Everything posted by Remy Lebeau
-
MacOS 12: OpenSSL with error "Clients should not load the unversioned libcrpto dylib as it does not have a stable ABI."
Remy Lebeau replied to philipp.hofmann's topic in Cross-platform
LibreSSL is backwards compatible with OpenSSL 1.0.1, so Indy's OpenSSL loading code has *very limited* support for detecting LibreSSL at runtime and not failing on it. But that is no guarantee that it will actually work correctly for all operations, so use at your own risk. Otherwise, you should install the actual OpenSSL dylibs directly in your app folder instead. -
SmartPointer implementations, Pros's and Con's
Remy Lebeau replied to Rollo62's topic in Algorithms, Data Structures and Class Design
Then it acts more like C++'s std::shared_ptr than std::unique_ptr. -
MacOS 12: OpenSSL with error "Clients should not load the unversioned libcrpto dylib as it does not have a stable ABI."
Remy Lebeau replied to philipp.hofmann's topic in Cross-platform
You are actually trying to use OpenSSL 1.0.0, not 1.0.2. 1.0.0 is quite old. That means the code is trying to load the dylibs using unversioned symlinks, which can map to versioned dylibs which you are not expecting, such as for OpenSSL 1.1.x, etc. Don't use symlinks when you need to load a specific version. Out of curiosity, are you using Indy to send your emails? If so, then on 'Nix platforms it has IdOpenSSLSetCanLoadSymLinks() and IdOpenSSLSetLoadSymLinksFirst() functions in the IdSSLOpenSSLHeaders unit. The defaults are both True, but you should set them to False in this situation. -
The documentStatus field is an array of OBJECTS, not an array of ARRAYS. Look at the JSON again more carefully, each element of the array is wrapped in curly braces (object), not square brackets (array). As such, when iterating the array, the JsonValueArr variable will point at each element of the array, so you MUST type-cast it to TJSONObject, NOT to TJSONArray. Otherwise your code will have undefined behavior due to an incorrect type-cast. If you were to change the type-cast to use 'as' instead, you would get a runtime error. You should not need to type-cast LItem as TJSONPair if you were to declare it as TJSONPair to begin with, rather than TJSONValue. Enumerating a TJSONObject in a for..in loop yields TJSONPair elements. Try this instead: var jasonArr: TJSONArray; JsonValueArr : TJSONValue; LItem: TJSONPair; partialAmount: Double; begin ... jasonArr := GetValue(JsonObject, 'documentStatus') as TJSONArray; for JsonValueArr in jasonArr do begin for LItem in TJSONObject(JsonValueArr){JsonValueArr as TJSONObject} do begin if LItem.JsonString.Value = 'statusCode' then odgovorFinaStatus.b2gStatus := LItem.JsonValue.Value else if LItem.JsonString.Value = 'statusText' then odgovorFinaStatus.b2gStatusTekst := LItem.JsonValue.Value else if LItem.JsonString.Value = 'statusTimestamp' then odgovorFinaStatus.b2gStatusVrijeme := LItem.JsonValue.Value else if LItem.JsonString.Value = 'note' then odgovorFinaStatus.b2gStatusNapomena := LItem.JsonValue.Value else if LItem.JsonString.Value = 'partialAmount' then begin if not TryStrToFloat(LItem.JsonValue.Value, partialAmount) then partialAmount := 0.0; odgovorFinaStatus.b2gDjelIzn := partialAmount; end; ...
-
JsonValueArr is pointing at a TJSONObject, not a TJSONArray, hence why you need to fix that typecast. Enumerating a TJSONObject in a for..in loop will give you TJSONPair elements. What do you have LItem declared as, and why are you type-casting it to TJSONPair inside the loop? JSON object- how to iterate through all properties without knowing their names?
-
Tarray , uses unit not found :-(
Remy Lebeau replied to FranzB's topic in Algorithms, Data Structures and Class Design
The non-generic TArray is a type in the System unit, and the generic TArray<T> is a type in the System.Generics.Collections unit. The uses clause is used to specify units only, not types. TArray is not its own unit, so don't specify TArray in your uses clause, eg you need to change 'System.Generics.Collections.TArray' to just 'System.Generics.Collections' in the uses clause. But don't change 'System.TArray' to 'System', just drop it entirely, as specify 'System' by itself in a uses clause will cause a compiler error (the System unit is already used implicitly in every unit). -
Almost. The documentStatus element is an array of objects, not an array of arrays, so you need to cast JsonValueArr to TJSONObject, not to TJSONArray: jasonArr := GetValue(JsonObject, 'documentStatus') as TJSONArray; for JsonValueArr in jasonArr do begin for LItem in TJSONObject(JsonValueArr) do begin ShowMessage(TJSONPair(LItem).JsonString.Value + '/' + TJSONPair(LItem).JsonValue.Value); end; end;
-
Using Delphi's stock JSON library, you already know how to access a named field of a TJSONObject. So, you can simply retrieve the TJSONObject for "b2GOutgoingInvoiceStatus", then retrieve the TJSONValue for its "documentStatus" field, then type-cast that to TJSONArray, and then iterate through its Items[] property, casting each TJSONValue in the array to TJSONObject. Using the JsonDataObjects library instead, you would have to do all of that using whatever its equivalent operations are.
-
How to iterate a TDictionary using RTTI and TValue
Remy Lebeau replied to John Kouraklis's topic in Delphi IDE and APIs
Asked and answered on StackOverflow: Do I need to free the enumerator returned by GetEnumerator? -
ParseJSONValue() is a 'class' function, so you don't need to instantiate an object to call it: JsonValue := TJSonObject.ParseJSONValue(memoRequest.Lines.Text); But more importantly: You are responsible for Free'ing the TJSONValue that ParseJSONValue() returns, so your example is creating a memory leak when it re-assigns the JsonValue variable. Try something more like this instead: var JsonValue: TJSONValue; JsonObject: TJSONObject; invoice: Integer; function GetValue(JsonObject: TJSONObject; const PairName: string): TJSONValue; var JsonPair: TJSONPair; begin JsonPair := JsonObject.Get(PairName); if JsonPair = nil then raise Exception.Create('Pair "' + PairName + '" not found'); Result := JsonPair.JsonValue; end; function GetObject(JsonObject: TJSONObject; const PairName: string): TJSONObject; begin Result := GetValue(JsonObject, PairName) as TJSONObject; end; begin ... invoice := 0; JsonValue := TJSONObject.ParseJSONValue(memoRequest.Lines.Text); if JsonValue = nil then begin // error handling ... end else try try JsonObject := JsonValue as TJSONObject; JsonObject := GetObject(JsonObject, 'b2GOutgoingInvoiceEnvelope'); JsonObject := GetObject(JsonObject, 'b2GOutgoingInvoiceProcessing'); JsonObject := GetObject(JsonObject, 'correctB2GOutgoingInvoice'); invoice := (GetValue(JsonObject, 'invoiceID') as TJSONNumber).AsInt; except // error handling ... end; finally JsonValue.Free; end; // use invoice as needed ... end; If you ever upgrade to XE6 or later, you can use the TJSONObject.FindValue() method instead: type TJSONObjectAccess = class(TJSONObject) end; var JsonValue, InvoiceJsonValue: TJSONValue; JsonObject: TJSONObject; invoice: Integer; begin ... invoice := 0; JsonValue := TJSONObject.ParseJSONValue(memoRequest.Lines.Text); if JsonValue = nil then begin // error handling ... end else try try JsonObject := JsonValue as TJSONObject; InvoiceJsonValue := TJSONObjectAccess(JsonObject).FindValue('$.b2GOutgoingInvoiceEnvelope.b2GOutgoingInvoiceProcessing.correctB2GOutgoingInvoice.invoiceID'); if InvoiceJsonValue = nil then raise Exception.Create('invoiceID not found'); invoice := (InvoiceJsonValue as TJSONNumber).AsInt; except // error handling ... end; finally JsonValue.Free; end; // use invoice as needed ... end;
-
Traybar application, get the last active windows on desktop
Remy Lebeau replied to mvanrijnen's topic in Windows API
Why not simply use GetForegroundWindow() or even GetGUIThreadInfo() instead? -
Exception classes implementing interfaces
Remy Lebeau replied to Wagner Landgraf's topic in RTL and Delphi Object Pascal
You have to use Exception.RaiseOuterException() to capture an InnerException, so it would look more like this: except on E: Exception do begin Exception.RaiseOuterException(EExtended.Create(AddInfo...)); end; end -
You are ignoring the OnWorkBegin and OnWorkEnd events. In particular, the OnWorkBegin event will tell you the actual total number of bytes that are about to be sent. You should be using that value to calculate your progress in the OnWork event, not limiting your progress to just the file size, which will obviously be smaller than the actual transmission. You are correct that there is going to be some extra overhead due to the formatting of the HTTP request, and your progress calculation is not reflecting those extra bytes. Also, you are ignoring the AWorkMode parameter of the events, which will tell you whether the event are being triggered when sending the request to the server or when reading the response from the server. You don't want the latter confusing your progress calculations. Keep in mind that sockets are buffered by default. When TIdHTTP is "sending" data to the server, it is really just putting the data into a kernel buffer. The actual data transmission happens in the background as the kernel sees fit to do so, and there is no way to track the actual transmissions (without hooking into the network hardware, anyway). So, what you are really reporting progress for is the rate in which you are able to queue data for transmission, not the actual transmission itself. If the buffer fills up, because the other party hasn't acknowledged transmitted data yet (ie, you are "sending" faster than the peer is reading), then queuing of new data will block until buffer space clears up as older data gets acknowledged. For most apps, this is adequate. But, if needed, you can try increasing the size of the kernel buffer (Indy doesn't have a property for that, but you can use the TIdHTTP.Socket.Binding.SetSockOpt() method to assign a value to the socket's SO_SNDBUF option manually), or you can try disabling some of the kernel's buffering by setting the TIdHTTP.UseNagle property to false.
-
Exception classes implementing interfaces
Remy Lebeau replied to Wagner Landgraf's topic in RTL and Delphi Object Pascal
I've never seen anyone do it, or even suggest it. But, I don't see any reason why it couldn't be done. -
What about this? uses ..., StrUtils; if StartsText('SELECT ', TrimLeft(Memo1.Text)) then MyQuery.Open else MyQuery.ExecSql; Or: if Memo1.Text.TrimLeft.StartsWith('SELECT ', True) then MyQuery.Open else MyQuery.ExecSql;
-
The native VCL TComboBox does not support a custom drop down. There is nothing like that for the native VCL TComboBox, so you will have to use a 3rd party solution (for instance, TMS has a TAdvTreeComboBox component, and there is (an older) TreeComboBox available on Torry.net). Otherwise, you would have to write your own component. No.
-
MARS running as Indy standalone Windows Service, correct cert locations
Remy Lebeau replied to marcovaldo's topic in MARS-Curiosity REST Library
Indy doesn't care where the certificate files are stored, as long as the paths you supply to them are valid. Using absolute paths should have worked fine. Unless Indy is not the one using the certificate files to begin with...- 7 replies
-
- indy service
- mars
-
(and 1 more)
Tagged with:
-
update indy to use tls 1.2 in c++builder 6?
Remy Lebeau replied to JeanCremers's topic in General Help
Not like it used to be. A few members are still lingering around the various forums. Several members have transitioned into MVPs, though some members (like me) haven't but are still recognized by a few Embarcadero employees to keep getting old TeamB perks (free versions, beta access, etc). Thanks, fixed. -
exception message : Error connecting with SSL. EOF was observed that violates the protocol.
Remy Lebeau replied to david_navigator's topic in Indy
Yes, I do, and that is a VERY old version of OpenSSL, you need to upgrade. Indy currently supports up to 1.0.2u dated 21/12/2019, which you can find at https://github.com/IndySockets/OpenSSL-Binaries/ Work on supporting 1.1.x is in progress: https://github.com/IndySockets/Indy/pull/299 The Method property is deprecated, you should be using the SSLVersions property instead. TLS 1.0 has been (almost) completely phased out of modern servers. Most modern servers support, if not require, TLS 1.2, so that really should be your minimum going forward nowadays. You should also have TLS 1.1 enabled as a fallback, just in case, but even that is being phased out as well. To enable multiple TLS versions with the Method property, you would have to set it to sslvSSLv23, which will set the SSLVersions property to all SSL/TLS versions supported by Indy (including SSL 2.0, SSL 3.0, and TLS 1.x), but it is better to use the SSLVersions property directly (ie, don't enable SSL 2.0 and 3.0 anymore), eg: SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; Having multiple TLS versions enabled at a time will allow OpenSSL to negotiate the highest version that both client and server support. That being said, on a side note, you do not need to assign ANY of the following SSLIOHandler properties, as Indy handles them internally for you: Setup the necessary Host/Port only in the client component, ie TIdIMAP4. The Connect() method will then copy them into the IOHandler as needed. -
MARS running as Indy standalone Windows Service, correct cert locations
Remy Lebeau replied to marcovaldo's topic in MARS-Curiosity REST Library
Why? What is the actual problem with having them in an application folder?- 7 replies
-
- indy service
- mars
-
(and 1 more)
Tagged with:
-
exception message : Error connecting with SSL. EOF was observed that violates the protocol.
Remy Lebeau replied to david_navigator's topic in Indy
I'm assuming you are using OpenSSL, which version? Also, what version of the SSL/TLS protocol are you using? Then you should be able to debug it. For instance, using Wireshark, you can see the details of the actual SSL/TLS handshake (at least until the connection is encrypted. It takes a few back-and-forths to get that far). At what stage in the handshake is the server disconnecting? The error means that the IMAP server is closing its end of the TCP connection while the SSL/TLS handshake is still in progress. Typically, this happens if the server encounters something it doesn't like in the client's SSL/TLS handshake, and the server decides to bail out without first sending a TLS alert to explain what is wrong. -
update indy to use tls 1.2 in c++builder 6?
Remy Lebeau replied to JeanCremers's topic in General Help
<sigh> Fine, then let's go the long way around, using a full wrapper class, forget TMethod (even though it will technically work at runtime, despite the compiler warnings): // SOURCE #define BUILDING_DLL #include "mydll.h" class eventHandlers { public: OnWorkBeginFunc onWorkBegin; OnWorkEndFunc onWorkEnd; OnWorkFunc onWork; void __fastcall OnWorkBeginHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax) { if (onWorkBegin) onWorkBegin((OnWorkMode)AWorkMode, AWorkCountMax); } void __fastcall OnWorkEndHandler(TObject *ASender, TWorkMode AWorkMode) { if (onWorkEnd) onWorkEnd((OnWorkMode)AWorkMode); } void __fastcall OnWorkHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) { if (onWork) onWork((OnWorkMode)AWorkMode, AWorkCount); } }; static bool sslPathSet = false; char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork) { char *result = NULL; try { if (!sslPathSet) { sslPathSet = true; IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0))); } TIdHTTP *http = new TIdHTTP; try { eventHandlers events; events.onWorkBegin = onWorkBegin; events.onWorkEnd = onWorkEnd; events.onWork = onWork; http->OnWorkBegin = &events.OnWorkBeginHandler; http->OnWorkEnd = &events.OnWorkEndHandler; http->OnWork = &events.OnWorkHandler; TIdSSLIOHandlerSocketOpenSSL *SSL = new TIdSSLIOHandlerSocketOpenSSL(http); SSL->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2; http->IOHandler = SSL; http->ReadTimeout = 10000; UTF8String s = http->Get(url); result = new char[s.Length()+1]; memcpy(result, s.c_str(), s.Length()+1); } __finally { delete http; } } catch (...) { return NULL; } return result; } void freedata(char *data) { delete[] data; } Odd, they should be. Especially since OnWorkEnd can't be called if OnWorkBegin is not called first. No. Correct, because the event is expecting a class method, not a standalone procedure. Using TMethod is the way to get around that. -
update indy to use tls 1.2 in c++builder 6?
Remy Lebeau replied to JeanCremers's topic in General Help
<sigh> Try this: // SOURCE #define BUILDING_DLL #include "mydll.h" struct userFunctions { OnWorkBeginFunc onWorkBegin; OnWorkEndFunc onWorkEnd; OnWorkFunc onWork; }; static void __fastcall MyOnWorkBeginHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax) { userFunctions *funcs = static_cast<userFunctions*>(AData); funcs->onWorkBegin((OnWorkMode)AWorkMode, AWorkCountMax); } static void __fastcall MyOnWorkEndHandler(void *AData, TObject *ASender, TWorkMode AWorkMode) { userFunctions *funcs = static_cast<userFunctions*>(AData); funcs->onWorkEnd((OnWorkMode)AWorkMode); } static void __fastcall MyOnWorkHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) { userFunctions *funcs = static_cast<userFunctions*>(AData); funcs->onWork((OnWorkMode)AWorkMode, AWorkCount); } template <typename EventType, typename HandlerType> static EventType makeEventHandler(void *data, HandlerType *handler) { TMethod m; m.Data = data; m.Code = handler; return reinterpret_cast<EventType&>(m); /* alternatively: EventType evt; TMethod &m = reinterpret_cast<TMethod&>(evt); m.Data = data; m.Code = handler; return evt; */ } static bool sslPathSet = false; char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork) { char *result = NULL; try { if (!sslPathSet) { sslPathSet = true; IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0))); } TIdHTTP *http = new TIdHTTP; try { userFunctions funcs; if (onWorkBegin) { funcs.onWorkBegin = onWorkBegin; http->OnWorkBegin = makeEventHandler<TWorkBeginEvent>(&funcs, &MyOnWorkBeginHandler); } if (onWorkEnd) { funcs.onWorkEnd = onWorkEnd; http->OnWorkEnd = makeEventHandler<TWorkEndEvent>(&funcs, &MyOnWorkEndHandler); } if (onWork) { funcs.onWork = onWork; http->OnWork = makeEventHandler<TWorkEvent>(&funcs, &MyOnWorkHandler); } TIdSSLIOHandlerSocketOpenSSL *SSL = new TIdSSLIOHandlerSocketOpenSSL(http); SSL->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2; http->IOHandler = SSL; http->ReadTimeout = 10000; UTF8String s = http->Get(url); result = new char[s.Length()+1]; memcpy(result, s.c_str(), s.Length()+1); } __finally { delete http; } } catch (...) { return NULL; } return result; } void freedata(char *data) { delete[] data; } -
update indy to use tls 1.2 in c++builder 6?
Remy Lebeau replied to JeanCremers's topic in General Help
My bad, too many *s in the DLL's event handlers. Try this instead: static void __fastcall MyOnWorkBeginHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax) { OnWorkBeginFunc func = static_cast<OnWorkBeginFunc>(AData); func(AWorkMode, AWorkCountMax); } static void __fastcall MyOnWorkEndHandler(void *AData, TObject *ASender, TWorkMode AWorkMode) { OnWorkEndFunc func = static_cast<OnWorkEndFunc>(AData); func(AWorkMode); } static void __fastcall MyOnWorkHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) { OnWorkFunc func = static_cast<OnWorkFunc>(AData); func(AWorkMode, AWorkCount); } -
update indy to use tls 1.2 in c++builder 6?
Remy Lebeau replied to JeanCremers's topic in General Help
Why not just install the latest Indy in C++Builder 6? No, not directly. Nor should you be trying to. What you should do instead is let the caller pass in its own function pointer(s), which your DLL then remembers, and can then call from inside its own OnWork event handler(s). For example, the following uses a little-known trick for assigning a standalone procedure as a VCL event handler, while also passing a user-defined parameter to it. There are other ways this could be implemented, but this is the most straight-forward way I can think of right now for your scenario: // HEADER #ifndef MY_DLL_H #define MY_DLL_H #ifdef __cplusplus extern "C" { #endif #ifdef BUILDING_DLL #define EXPORT __declspec(dllexport) #else #define EXPORT __declspec(dllimport) #endif enum OnWorkMode { WorkReading, WorkWriting }; typedef void (*OnWorkBeginFunc)(OnWorkMode AWorkMode, __int64 AWorkCountMax); typedef void (*OnWorkEndFunc)(OnWorkMode AWorkMode); typedef void (*OnWorkFunc)(OnWorkMode AWorkMode, __int64 AWorkCount); EXPORT char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork); EXPORT void freedata(char *data); #ifdef __cplusplus } #endif #endif // SOURCE #define BUILDING_DLL #include "mydll.h" static void __fastcall MyOnWorkBeginHandler(void *ASelf, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax) { OnWorkBeginFunc func = static_cast<OnWorkBeginFunc*>(ASelf); func(AWorkMode, AWorkCountMax); } static void __fastcall MyOnWorkEndHandler(void *ASelf, TObject *ASender, TWorkMode AWorkMode) { OnWorkEndFunc *func = static_cast<OnWorkEndFunc*>(ASelf); func(AWorkMode); } static void __fastcall MyOnWorkHandler(void *ASelf, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) { OnWorkFunc *func = static_cast<OnWorkFunc*>(ASelf); func(AWorkMode, AWorkCount); } template <typename EventType, typename FuncType, typename HandlerType> static EventType makeEventHandler(FuncType *func, HandlerType *handler) { TMethod m; m.Data = func; m.Code = handler; return reinterpret_cast<EventType&>(m); /* alternatively: EventType evt; TMethod &m = reinterpret_cast<TMethod&>(evt); m.Data = func; m.Code = handler; return evt; */ } static bool sslPathSet = false; char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork) { char *result = NULL; try { if (!sslPathSet) { sslPathSet = true; IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0))); } TIdHTTP *http = new TIdHTTP; try { if (onWorkBegin) http->OnWorkBegin = makeEventHandler<TWorkBeginEvent>(onWorkBegin, &MyOnWorkBeginHandler); if (onWorkEnd) http->OnWorkEnd = makeEventHandler<TWorkEndEvent>(onWorkEnd, &MyOnWorkEndHandler); if (onWork) http->OnWork = makeEventHandler<TWorkEvent>(onWork, &MyOnWorkHandler); TIdSSLIOHandlerSocketOpenSSL *SSL = new TIdSSLIOHandlerSocketOpenSSL(http); SSL->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2; http->IOHandler = SSL; http->ReadTimeout = 10000; UTF8String s = http->Get(url); result = new char[s.Length()+1]; memcpy(result, s.c_str(), s.Length()+1); } __finally { delete http; } } catch (...) { return NULL; } return result; } void freedata(char *data) { delete[] data; } And then you C++Builder 6 code can use it like this: #include "mydll.h" #pragma comment(lib, "mydll.lib") // or add the .lib file to your project void OnWorkBegin(OnWorkMode AWorkMode, __int64 AWorkCountMax) { //... } void OnWorkEnd(OnWorkMode AWorkMode) { //... } void OnWork(OnWorkMode AWorkMode, __int64 AWorkCount) { //... } char *data = getdata("url", &OnWorkBegin, &OnWorkEnd, &OnWork); if (data) { // ... freedata(data); } Yes, because System::String is an alias for System::UnicodeString in CB10.3, but is an alias for System::AnsiString in BCB6. Thus, you were indeed trying to pass a wchar_t* pointer to strcpy() rather than a char* pointer.