Unicode Support
Unicode Support
I'm trying to read and write unicode to a SQL Server NText field. While the write seems to work well enough, reading in the text causes a TMemoField rather than a TWideMemoField to be created. I've also tried subclassing TSQLDataset, and overriding internalinitfielddefs, to create a TWideMemoField instead of a TMemoField, and I still don't get what I saved out to the database. Any ideas?
The dbExpress technology supports Unicode from Delphi 2006.
Use the Unicode extended driver option to enable or disable Unicode support.
If it is set to True, all character data from nchar and nvarchar columns is stored as WideStrings, and TStringField is replaced with TWideStringFiled.
Please see the ReadMe.html file in the DbxSda installation directory for more information.
Use the Unicode extended driver option to enable or disable Unicode support.
If it is set to True, all character data from nchar and nvarchar columns is stored as WideStrings, and TStringField is replaced with TWideStringFiled.
Please see the ReadMe.html file in the DbxSda installation directory for more information.
These are the changes I have done :
1) Added a 'Wide' property to TFieldDesc in MemData.pas
property Wide : Boolean read fWide write fWide;
2) in TOLEDBRecordSet.InternalInitFields (OLEDBAccess.pas), I set this property when a wide string is found.
if OLEDBType = DBTYPE_WSTR then
begin
Field.SubDataType := Field.SubDataType or dtWide;
Field.Wide:= True;
end
Note the sub-type is already being set as wide, but this is ignored when setting the fielddefs.
3) In TMemSQLCursor.FromDataType(Field: TFieldDesc; var puSubType: Word): : word; (dbexp.pas) I add the line :
if (puSubType = fldstMEMO) and (Field.Wide) then
puSubType := fldstWIDEMEMO;
The above creates a widememo instead of a memo if an ntext is encountered. But this is still not enough, data was still being lost so I had to do the foloowing :
4) in TData.ReadBlob (memdata.pas), the following lines mean we convert from unicode to ansi string, losing data
s := Marshal.PtrToStringUni(Ws, Result);
Buf := Marshal.StringToHGlobalAnsi(s);
CopyBuffer(Buf, Dest, Result);
I changed this to
CopyBuffer(Ws, Dest, Result)
5) Almost there, but I was only getting half my data. I noticed that TWideMemoField.AsWideString was dividing the blob size by two to get the number of charcters. But TData.GetBlobSize, was also doing this
if Blob.FIsUnicode then
Result := Result shr 1;
So in effect returning the number of characters, and not the blob size. So I got rid of these two lines, and changed the readblob slightly as now I had the number of bytes and not the number of characters
Ws := Marshal.AllocHGlobal(LenBytes);
Buf := nil;
try
Result := Blob.Read(Position shl 1, LenBytes, Ws); // Result is bytes len
Result := Result shr 1; // Result is WideChar len
CopyBuffer(Ws, Dest, Result shl 1);
Now it all works....I'd like to do the same for Oracle, but that's not working at all.....I'll leave that for another day!!!
1) Added a 'Wide' property to TFieldDesc in MemData.pas
property Wide : Boolean read fWide write fWide;
2) in TOLEDBRecordSet.InternalInitFields (OLEDBAccess.pas), I set this property when a wide string is found.
if OLEDBType = DBTYPE_WSTR then
begin
Field.SubDataType := Field.SubDataType or dtWide;
Field.Wide:= True;
end
Note the sub-type is already being set as wide, but this is ignored when setting the fielddefs.
3) In TMemSQLCursor.FromDataType(Field: TFieldDesc; var puSubType: Word): : word; (dbexp.pas) I add the line :
if (puSubType = fldstMEMO) and (Field.Wide) then
puSubType := fldstWIDEMEMO;
The above creates a widememo instead of a memo if an ntext is encountered. But this is still not enough, data was still being lost so I had to do the foloowing :
4) in TData.ReadBlob (memdata.pas), the following lines mean we convert from unicode to ansi string, losing data
s := Marshal.PtrToStringUni(Ws, Result);
Buf := Marshal.StringToHGlobalAnsi(s);
CopyBuffer(Buf, Dest, Result);
I changed this to
CopyBuffer(Ws, Dest, Result)
5) Almost there, but I was only getting half my data. I noticed that TWideMemoField.AsWideString was dividing the blob size by two to get the number of charcters. But TData.GetBlobSize, was also doing this
if Blob.FIsUnicode then
Result := Result shr 1;
So in effect returning the number of characters, and not the blob size. So I got rid of these two lines, and changed the readblob slightly as now I had the number of bytes and not the number of characters
Ws := Marshal.AllocHGlobal(LenBytes);
Buf := nil;
try
Result := Blob.Read(Position shl 1, LenBytes, Ws); // Result is bytes len
Result := Result shr 1; // Result is WideChar len
CopyBuffer(Ws, Dest, Result shl 1);
Now it all works....I'd like to do the same for Oracle, but that's not working at all.....I'll leave that for another day!!!
We couldn't reproduce the problem.
Please send us (evgeniym*crlab*com) a complete small test project to reproduce the problem;
it is desirable to use Northwind or Master schema objects, otherwise include definition of your own database objects; don't use third party components.
Also supply us the following information
- Exact version of Delphi or C++ Builder
- Exact version of DbxSda. You can see it in ReadMe.html
- Exact version of Microsoft SQL Server and OLE DB provider that you use. You can see it in version info of SQLOLEDB.DLL and SQLNCLI.DLL.
Please send us (evgeniym*crlab*com) a complete small test project to reproduce the problem;
it is desirable to use Northwind or Master schema objects, otherwise include definition of your own database objects; don't use third party components.
Also supply us the following information
- Exact version of Delphi or C++ Builder
- Exact version of DbxSda. You can see it in ReadMe.html
- Exact version of Microsoft SQL Server and OLE DB provider that you use. You can see it in version info of SQLOLEDB.DLL and SQLNCLI.DLL.