string encoding error from delphi 7 to xe - string

i found on internet some source for crypt string and i saw that on delphi 7 the string is crypted and decrypted well, and when i try to do the same things with delphi xe2,xe3,xe4,xe5 the encryption end decryption fail with this error "invalid buffer size for decryption"
i'm using aes.pas and eiaes.pas from here: http://code.google.com/p/jalphi-lib/source/browse/trunk/codemyth/delphi/dontuseit/?r=7
i think that the problem is the enconding of string.
Is it possible to solve this problem?

The AES library you provided the link to has not been updated to support the fact that "String" in later versions of Delphi (Delphi 2009 onward) is now a UnicodeString where each character is a WideChar.
You have 4 options:
Contact the library author and ask if a Unicode version is
planned/available
Attempt to modify the library to support Unicode yourself (or find
someone who can/will help do this)
Find an alternative encryption library that already supports Unicode
Ensure that you only use ANSI Strings with the library.
This last option may not be viable for you but if it is then you will still need to modify the AES library, but you will not need to do any code changes.
The problem is that "String" and "Char" in the later versions of Delphi are "Wide" types (2 bytes per 'character'). This difference is almost certainly causing problems with code in the AES library that assumes only ONE byte per character.
You can make these assumptions valid by making sure that the AES code is working with ANSI Strings.
If you choose to do this, then I would suggest that you introduce two new types:
type
AESString = ANSIString;
AESChar = ANSIChar;
PAESChar = ^AESChar;
You will then need to go through the AES library code replacing any reference to "String" with "AESString", "Char" with "AESChar" and "PChar" with "PAESChar".
This should then make AES an ANSI String library and it will still be usable in Delphi 7 (i.e. pre-Delphi 2009) if that's important for you.
If you then find in the future that you do need to fully support Unicode strings and then need to properly fix the AES library code itself, you can do this and then simply change the AESString and AESChar types to:
type
AESString = String;
AESChar = Char;
If this is then compiled with a non-Unicode version of Delphi, the library will automatically revert back to ANSI String ("String" == ANSIString pre-D2009), so your Unicode changes will need to take this into account if you need to support both Unicode and non-Unicode versions of Delphi. You do need to be careful, but this is not difficult.

Related

Converting Scandinavian letters from wstring to string

Goal
Converting a wstring containing ÅåÄäÖöÆæØø into a string in C++.
Environment
C++17, Visual Studio Community 2017, Windows 10 Pro 64-bit
Description
I'm trying to convert a wstring to string and has implemented the solution suggested at
https://stackoverflow.com/a/3999597/1997617
// This is the code I use:
// Convert a wide Unicode string to an UTF8 string
std::string toString(const std::wstring &wstr)
{
if (wstr.empty()) return std::string();
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
std::string strTo(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
return strTo;
}
So far so good.
My problem is that I have to handle scandinavian letters (ÅåÄäÖöÆæØø) in addition to the English ones. Regard the input wstring below.
L"C:\\Users\\BjornLa\\Å-å-Ä-ä-Ö-ö Æ-æ-Ø-ø\\AEther Adept.jpg"
When returned it has become...
"C:\\Users\\BjornLa\\Å-å-Ä-ä-Ö-ö Æ-æ-Ø-ø\\AEther Adept.jpg"
... which causes me some trouble.
Question
So I would like to ask an often asked question, but with a small addition:
How do I convert a wstring to a string when it contains Scandinavian characters?
So, I did some additional read-up and experimenting based on the comments I've got.
Turns at the solution is quite simple. Just change CP_UTF8 to CP_ACP!
However...
Microsoft suggest that one actually should use CP_UTF8, if you read between the lines at the MSDN method documentation. The note for CP_ACP reads:
This value can be different on different computers, even on the same
network. It can be changed on the same computer, leading to stored
data becoming irrecoverably corrupted. This value is only intended for
temporary use and permanent storage should use UTF-16 or UTF-8 if
possible.
Also, the note for the entire method reads:
The ANSI code pages can be different on different computers, or can be
changed for a single computer, leading to data corruption. For the
most consistent results, applications should use Unicode, such as
UTF-8 or UTF-16, instead of a specific code page, unless legacy
standards or data formats prevent the use of Unicode. If using Unicode
is not possible, applications should tag the data stream with the
appropriate encoding name when protocols allow it. HTML and XML files
allow tagging, but text files do not.
So even though this CP_ACP-solution works fine for my test-cases, it remains to see if it is an overall good solution.

Delphi: Upgrade from 6 to XE2 - TStringList

We have to upgrade to XE2 (from Delphi6).
I collected many informations about this, but one of them isn't clear for me.
We are using String - what is AnsiString in XE.
As I know we must replace all (P)Ansi[String/Char] in our libraries to avoid the side effects of Unicode converts, and to we can compile our projects.
It is ok, but we are also using TStringList, and I don't found any TAnsiStringList class to change it simply... ;-)
What do you know about this? Can this cause problems too? Or this class have an option to preserve the strings?
(Ok, it seems to be 3 questions, but it is one only)
The program / OS language is hungarian, the charset is WIN-1250, what have some strange characters, like Ő, and Ű...
Thanks for your every information, link, etc.
1) 1st of all - WHY should u use AnsiStringList, rather than converting all your project to unicode-aware TStringList ? That should have certain detailed reasons, to suggest viable alternatives.
Unicode is a superset of windows-1250, windows-1251 and such.
Normally all you locale-specific string would be just losslessly converted to Unicode. IT is the opposite, Unicode to AnsiString, convertion that may loose data.
Explicit or implicit (like AnsiChar reduction in "if char-var in char-set")
You may have type-unsafe API like in DLLs, where compiler cannot check if you pass PChar or PAnsiChar, but you anyway should not pass objects liek TStrings into DLLs, there are BPLs for that.
So you probably just do not need TAnsiStringList
2) you can take TJclAnsiStringList from Jedi Code Library
3) You can use XE2 stock TList<AnsiString> type

Delphi Embarcadero XE: tons of warnings with String and PAnsiChar

I'm trying to migrate from Delphi 2007 to Embarcadero RAD Studio XE.
I'm getting tons of warnings.
They all look like this: I have a procedure where I declare a "String":
procedure SendMail( ADestinataire,ASubject : String);
And I'm trying to call Windows API like:
Res := MAPIResolveName(Session, Application.Handle,
PAnsiChar(ADestinataire), MAPI_LOGON_UI, 0, PRecip);
So the warnings are:
W1044: Transtyping string into PAnsiChar suspect.
What am I doing wrong / how should I correct this (350 warnings...)?
Thank you very much
MAPIResolveName uses LPSTR parameter, which is PAnsiChar in Delphi. Simple MAPI does not support UTF16 strings (though it can be used with UTF8 strings), so if you adhere to simple MAPI you should use AnsiStrings, for example
procedure SendMail( ADestinataire,ASubject : AnsiString);
or better you can use
procedure SendMail( ADestinataire,ASubject : String);
and explicitly convert string parameters to AnsiStrings prior to calling MAPIResolveName
Update
The whole Simple MAPI is now deprecated; Simple MAPI can be used with UTF8 strings, but it requires some changes in code and registry.
So if the question is about quick porting old ANSI Simple MAPI to Unicode Delphi, the best is to adhere to AnsiStrings.
A more solid approach is to abandon Simple MAPI completely and use Extended MAPI instead.
Just write
Res := MAPIResolveName(Session, Application.Handle,
PChar(ADestinataire), MAPI_LOGON_UI, 0, PRecip);
If you have a string, that is, a Unicode string, that is, a pointer to a sequence of Unicode characters, you shouldn't cast it to PAnsiChar. PAnsiChar is a pointer to a sequence of non-Unicode characters. Indeed, a cast to a PSomethingChar type simply tells the compiler to interpret the thing inside the cast as a pointer of the specified type. It doesn't do any conversion. So, basically, right now you lie to the compiler: You have a Unicode string and instructs the compiler to interpret it as an ANSI (non-Unicode) string. That's bad.
Instead, you should cast it to PWideChar, a pointer to a sequence of Unicode characters. In Delphi 2009+, PChar is equivalent to PWideChar.
Of course, if you send a pointer to a sequence of Unicode characters to the function, then the function had better expect Unicode characters, but I am not sure if this is the case of the MAPIResolveName function. I suspect that it actually requires ANSI (that is, non-Unicode) characters. If this is the case, you need to convert the Unicode string to an ANSI (non-Unicode) string. This is easy, just write AnsiString(ADestinataire). Then you cast to a ANSI (non-Unicode) PAnsiChar:
Res := MAPIResolveName(Session, Application.Handle,
PANsiChar(AnsiString(ADestinataire)), MAPI_LOGON_UI, 0, PRecip);
Starting with Delphi 2009, the string data type is now implemented as a Unicode string. Previous versions implemented the string data type as an Ansi string (i.e. one byte per character).
This had significant implications when we ported our apps from 2007 to XE, and these article were very helpful:
Delphi in a Unicode World (first of three parts)
Delphi and Unicode
Delphi Unicode Migration for Mere Mortals
To step back a moment:
When using use the cast
============ ================
String PChar
AnsiString PAnsiChar
WideString PWideChar
String used to be an alias for AnsiString, which means it happened to work if you used PAnsiChar (even though you should have used PChar).
Now string is an alias for UnicodeString, which means using PAnsiChar (which was always wrong) is now really wrong.
Knowing this you can solve the question:
ADestinataire: String
PAnsiChar(ADestinataire)

Delphi Translating Strings

I'm using Delphi 2007 and I wonder how the following problem can be solved:
I have to translate AComp.Caption for example, but the string that I want to assign to the caption, often depends on some data (for example a date or a number, that gets Formatted). Therefore I have to save the data and the string in a new variable for every translation, which is really annoying.
What I want to do is something like that:
// will add the string and data to an internal list of Translator
// and will then return a DynamicString, which represents the translated value
AComp.Caption := T.NewTranslatedString("Hello %s, do you like cheese?", User)
(Note that AComp.Caption ("Hello %s..") can be changed in different methods)
When switching to another language, you would call T.TranslateAgain() and the value of all strings will be translated and, if data given, formatted again.
Is this possible or do you know another way for solving the given problem?
Thanks in advance
Additional question:
Are strings normal objects, that I can subclass and add dynamic behaviour that changes the string itself in special cases?
Delphi strings are not objects, you can't add behaviours to them. You would need to develop your own class.
The Windows way to localize applications is to get advantage of resources, that can be changed (and loading redirected) without changes to the code (no need to call special functions or add new components), and without run-time calls but to load the resource. The only disadvantage of resources is they cannot be changed easily by the end user. The Delphi 2007 standard localization tools use this approach.
Anyway there are some libraries like dxGetText (which is a port of the GNU gettext library) or TsiLang, for example that use a more "intrusive" approach, requiring changes to your code or adding components. In exchange they can simplify end-user localization.
Before developing your own localization library, I would check if one of the existing ones fits youe needs.
Note: Be aware that Delphi localization tool has significant issues that weren't fixed until XE (which I didn't test yet). See for example QC #79449. Unluckily the fix was never backported to earlier releases.
You can use Delphi's own translator tool. It is able to extract strings and resourcestrings from your source code and form DFM files, and gives you a graphical user interface to translate them to any language. It then creates a resource DLL for each language. The DLL containing the translated strings and DFM data. You should deploy this translation DLL with your project to the destination machine.
In your case, your strings are divided into two groups; fixed strings which do not need any further processing, and parametrized strings which need some additional data to be formatted properly. For the fixed strings, you can just type in the translation into translator tool. For parametrized strings, save each one as a resourcestring and use the resourcestring for formatting them. For example:
resourcestring
strDoYouLikeCheese = 'Hello %s, do you like cheese?';
...
AComp.Caption := Format(strDoYouLikeCheese,[User]);
Now you can use the translator tool or any resource editor to translate the resourcestring into your desired language without the need for changing your source code or recompiling it.
What you want to do is to localize your application. Delphi has support for this, based around the resourcestring keyword. However, I've never done any localization myself so I recommend that you do some websearch for this topic or perhaps wait for the other experts here to supply more detailed help!
You could use a dictionary to keep track of the string mappings, something like this
TTranslator = class
private
FMappings : TDictionary <String, String>;
public
function Translate (const SrcStr : String) : String;
procedure SetMapping (const SrcStr, DestStr : String);
end;
function TTranslator.Translate (const SrcStr : String) : String;
begin
if not FMappings.TryGetValue (SrcStr, Result) then
Result := SrcStr;
end;
procedure TTranslator.SetMapping (const SrcStr, DestStr : String);
begin
FMappings.AddOrSetValue (SrcStr, DestStr);
end;
Translating would then be simply several calls to SetMappings. This gives you a lot of flexiblity. Anyway, you might consider using the built-in localization support or even third-party solutions.
EDIT: Just saw that you are using Delphi 2007, so you don't have TDictionary available. The idea should remain valid, just use any dictionary implementation or a list-based approach.
And to answer the other part of your question: no, strings are not normal object (actually they are not objects at all). They are special in various ways (memory management, copy-on-write behaviour) and it is not possible to subclass them. But that's not what you want anyway if I understood the question correctly.

How do I PInvoke a multi-byte ANSI string?

I'm working on a PInvoke wrapper for a library that does not support Unicode strings, but does support multi-byte ANSI strings. While investigating FxCop reports on the library, I noticed that the string marshaling being used had some interesting side effects. The PInvoke method was using "best fit" mapping to create a single-byte ANSI string. For illustration, this is what one method looked like:
[DllImport("thedll.dll", CharSet=CharSet.Ansi)]
public static extern int CreateNewResource(string resourceName);
The result of calling this function with a string that contains non-ASCII characters is that Windows finds a "close" character, generally this looks like it ends up being "???". If we pretend that 'a' is a non-ASCII character, then passing "cat" as a parameter would create a resource named "c?t".
If I follow the guidelines in the FxCop rule, I end up with something like this:
[DllImport("thedll.dll", CharSet=CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
public static extern int CreateNewResource([MarshalAs(UnmanagedType.LPStr)] string resourceName);
This introduces a change in behavior; now when a character cannot be mapped an exception is thrown. This concerns me because this is a breaking change, so I'd like to try and marshal the strings as multi-byte ANSI but I cannot see a way to do so. UnmanagedType.LPStr is specified to be a single-byte ANSI string, LPTStr will be Unicode or ANSI depending on the system, and LPWStr is not what the library expects.
How would I tell PInvoke to marshal the string as a multibyte string? I see there's a WideCharToMultiByte() API function, could I change the signature to expect an IntPtr to a string I create in unmanaged memory? It seems like this still has many of the problems that the current implementation has (it still might have to drop or substitute characters), so I'm not sure if this is an improvement. Is there another method of marshaling that I'm missing?
ANSI is multi-byte, and ANSI strings are encoded according to the codepage currently enabled on the system. WideCharToMultiByte works the same way as P/Invoke.
Maybe what you're after is conversion to UTF-8. Although WideCharToMultiByte supports this, I don't think P/Invoke does, since it's not possible to adopt UTF-8 as the system-wide ANSI code page. At this point you'd be looking at passing the string as an IntPtr instead, although if you're doing that, you may as well use the managed Encoding class to do the conversion, rather than WideCharToMultiByte.
Here is the best way I've found to accomplish this. Instead of marshalling as a string, marshal as a byte[]. Put the responsibility on the caller of the pinvoke function API to convert to a byte array in the most appropriate fashion. Most likely by using one of the Text.Encoding classes.
If you end up having to call WideCharToMultiByte manually, I would get rid of the p/invoke and manually marshal this using WideCharToMultiByte in a C++/CLI wrapper function. Managed C++ is much better at these interop scenarios than C# is.
Though, if this is the only p/invoke you have, it's probably not worth it.

Resources