PAnsiChar, invalid typecast - delphi

I have problem with casting Strings to AnsiChars, function looks like
function PI_CST(ID: LongInt;
const szAxes: PAnsiChar;
const szNames: PAnsiChar): Bool cdecl ;
I want to use this funcion in this way:
bOk:=PI_CST(g_iD,'1',PAnsiChar(ComboBox1.Text));
but it doesn't work, when I call function in this way:bOk:=PI_CST(g_iD,'1','M-511.DD1') it works great (value beetween quotes is const), is there any way to use in this function with parameter choosen from ComboBox?

PAnsiChar(ComboBox1.Text)
This tells the compiler to interpret the string returned by the Text property as though it were a pointer to 8 bit ANSI characters. If the string is a Unicode string, then the cast is not valid because the underlying buffer is encoded with 16 bit text.
You need to convert the string to ANSI before casting:
PAnsiChar(AnsiString(ComboBox1.Text))

Related

UTF8 version of WIDESTRING

I have a text that I need to store it in a widestring variable. But my text is UTF8 and widestring doesn't support UTF8 and converts it to some chinese characters.
so is there any UTF8 version of WIDESTRING?
I always use UTF8string but in this case I have to use WideString
When you assign a UTF8String variable to a WideString variable, the compiler automatically inserts instructions to decode the string (in Delphi 2009 and later). It coverts UTF-8 to UTF-16, which is what WideString holds. If your WideString variable holds Chinese characters, then that's because your UTF-8-encoded string holds UTF-8-encoded Chinese characters.
If you want your string ws to hold 16-bit versions of the bytes in your UTF8String s, then you can by-pass the automatic conversion with some type-casting:
var
ws: WideString;
i: Integer;
c: AnsiChar;
SetLength(ws, Length(s));
for i := 1 to Length(s) do begin
c := s[i];
ws[i] := WideChar(Ord(c));
end;
If you're using Delphi 2009 or later (which includes the XE series), then you should consider using UnicodeString instead of WideString. The former is a native Delphi type, whereas the latter is more of a wrapper for the Windows BSTR type. Both types exhibit the automatic conversion behavior when assigning to and from AnsiString derivatives like UTF8String, though, so they type you use doesn't affect this answer.
In earlier Delphi versions, the compiler would attempt to decode the string using the system code page (which is never UTF-8). To make it decode the string properly, call Utf8Decode:
ws := Utf8Decode(s);

What is the CCHAR type equivalent in Delphi?

The ShortNameLength member of FILE_BOTH_DIR_INFORMATION structure is declared as follows:
typedef struct FILE_BOTH_DIR_INFORMATION {
...
CCHAR ShortNameLength;
...
};
From the explanation of CCHAR type, CCHAR is a 8-bit Windows (ANSI) character. So, it is equivalent to AnsiChar in Delphi, right? However, the description of ShortNameLength member of FILE_BOTH_DIR_INFORMATION structure says,
“ShortNameLength specifies the length, in bytes, of the short file name string.”
The statement makes me think that the CCHAR equivalent is Byte in Delphi. Another example is the NumberOfProcessors member of SYSTEM_BASIC_INFORMATION which is declared in winternl.h as follows:
typedef struct _SYSTEM_BASIC_INFORMATION {
BYTE Reserved1[24];
PVOID Reserved2[4];
CCHAR NumberOfProcessors;
}
Once again, the CCHAR type seems to be used in a Byte context, rather than AnsiChar context.
Now, I confuse, whether to use AnsiChar or Byte as a CCHAR equivalent in Delphi.
Note
JwaWinType.pas of JEDI Windows API declares CCHAR as AnsiChar.
It's a byte, or at least, it is used as a 1 byte integer. In C, chars can be used for this purpose. In Delphi, you couldn't do that without typecasting. So you could use Char, but then you would need to give it the value 'A' or Chr(65) to indicate a string of 65 characters. Now, that would be silly. :-)
To be able to pass it to the API it must have the same size. Apart from that, the callee will not even know how it is declared, so declaring it as a Delphi byte is the most logical solution. A choice backed up by the other declaration you found.
I believe the explanation of CCHAR is wrong. The C prefix indicates that this is a count of characters so this is probably a simple copy-paste error done by Microsoft when writing the explanation.
It is stored in a byte and it is used to count the number of bytes of a string of characters. These characters may be wide characters but the CCHAR value still counts the number of bytes used to store the characters.
The natural translation for this type is Byte. If you marshal it to a character type like AnsiChar you will have to convert the character to an integer value (e.g. a byte) before using it.

Why the following character pointer types are being considered incompatible?

Consider the following snippet:
procedure TForm1.FormCreate(Sender: TObject);
{$REGION 'Sealed declarations'}
type WCh = WideChar; // (1)
type Str = ^WCh; // (2)
{ this routine accepts character pointer }
procedure Baz(Param: Str);
begin
end;
{$ENDREGION}
{ this one too, but character pointer type used directly }
procedure Bar(Param: PWideChar);
begin
end;
{ this constant should be compatible with strings and character pointers }
const FOO = 'FOO';
begin
Bar(FOO); // compiles!
Baz(FOO); // BAH! E2010 Incompatible types: 'Str' and 'string'
end;
How do i resolve this problem preserving both structured typing in declarations and the clarity and readability in the usage (i hope for no heavy typecasting)?
NB: By "sealed declarations" i really mean it. I prefer to not amend it unless it is absolutely necessary.
Internal handling of conversion between string and PChar varies from version to version, so environment might matter - i encountered this problem in Delphi XE.
As Rob Kennedy correctly noticed in comments, the question is about conversion from string literal, not string type.
To simplify coding Delphi allows implicit conversion from string literal to PChar type and PChar aliases.
To avoid typecasting you can use
type Str = PWideChar;
or use distict type
type Str = type PWideChar;
I have not noticed any difference in string literal --> PWideChar implicit conversion in Unicode Delphi versions (2009 and above).
Your WCh = WideChar definition creates a type alias for WideChar — they have type identity — but the subsequent Str = ^WCh definition does not create a type alias for PWideChar. When $T+ is in effect, they're compatible and assignment-compatible, but those aren't good enough in this situation. They are still distinct types.
The FOO constant is a string literal. The documentation for assignment compatibility says what types a string literal can be assigned to: "PAnsiChar, PWideChar, PChar or any string type." Str is not a string type. It's a pointer type, but it's not PWideChar, despite how similar their definitions are.
The type of a string literal adapts based on context. When the compiler needs a PWideChar, the string literal is a PWideChar. When the compiler needs an AnsiString, it's an AnsiString. (If the compiler needs both those types, then the literal will be stored in the program both ways.) String literals aren't assignable to your Str type, so, according to the error message, the compiler apparently chooses string as the type for the string literal in that situation. You can type-cast it to one of the other built-in types, but the better solution would be to avoid using custom-defined character-pointer classes at all.

Convert Delphi 7 code to work with Delphi 2009

I have a String that I needed access to the first character of, so I used stringname[1]. With the unicode support this no longer works. I get an error: [DCC Error] sndkey32.pas(420): E2010 Incompatible types: 'Char' and 'AnsiChar'
Example code:
//vkKeyScan from the windows unit
var
KeyString : String[20];
MKey : Word;
mkey:=vkKeyScan(KeyString[1])
How would I write this in modern versions of Delphi
The type String[20] is a ShortString of length 20, i.e. a ShortString that contains 20 characters. But ShortStrings behave like AnsiStrings, i.e. they are not Unicode - one character is one byte. Thus KeyString[1] is an AnsiChar, whereas the vkKeyScan function expects a WideChar (=Char) as argument. I really have no idea whatsoever why you want to use the type String[20] instead of String (=UnicodeString), but you could convert the AnsiChar KeyString[1] to a WideChar:
mkey := vkKeyScan(WideChar(KeyString[1]))
Off the top of my head: do you really need a string, which is equal to widestring in Delphi 2009?
One option is to have the definition
var KeyString: AnsiString;
then when you take KeyString[1] that would be an AnsiChar rather than a Char.

Casting Delphi 2009/2010 string literals to PAnsiChar

So the question is whether or not string literals (or const strings) in Delphi 2009/2010 can be directly cast as PAnsiChar's or do they need an additional cast to AnsiString first for this to work?
The background is that I am calling functions in a legacy DLL with a C interface that has some functions that require C-style char pointers. In the past (before Delphi 2009) code like the following worked like a charm (where the param to the C DLL function is a LPCSTR):
either:
LegacyFunction(PChar('Fred'));
or
const
FRED = 'Fred';
...
LegacyFunction(PChar(FRED));
So in changing to Delphi 2009 (and now in 2010), I changed the call to this:
LegacyFunction(PAnsiChar('Fred'));
or
const
FRED = 'Fred';
...
LegacyFunction(PAnsiChar(FRED));
This seems to work and I get the correct results from the function call. However there is some definite instability in the app that seems to be occurring mostly the second or third time through the code that calls the legacy functions (that was not present before the move to the 2009 version of the IDE). In investigating this, I realized that the native string literal (and const string) in Delphi 2009/2010 is a Unicode string so my cast was possibly in error. Examples here and elsewhere seem to indicate this call should look more like this:
LegacyFunction(PAnsiChar(AnsiString('Fred')))
What confuses me is that with the code above in the second examples, casting the string literal directly to a PAnsiChar does not generate any compiler warnings. If instead of a string literal, I was casting a string var, I would get a suspicious cast warning (and the string would be mangled). This (and the fact that the string is usable in the DLL) leads me to believe the compiler is doing some magic to correctly interpret the string literal as the intended string type. Is this what is happening or is the double cast (first to AnsiString, then to PAnsiChar) really necessary and the lack of it in my code the reason for the hard to track down instability? And does the same answer hold true for const strings as well?
For type-inferred constants (only initializable from literals) the compiler changes the actual text at compile-time, rather than at runtime. That means it knows whether or not the conversion loses data, so it doesn't need to warn you if it doesn't.
To 'visualize' Barry Kelly and Mason Wheeler words:
const
FRED = 'Fred';
var
p: PAnsiChar;
w: PWideChar;
begin
w := PWideChar(Fred);
p := PAnsiChar(Fred);
In ASM:
Unit7.pas.32: w := PWideChar(Fred);
00462146 BFA4214600 mov edi,$004621a4
// no conversion, just a pointer to constant/"-1 RefCounted" UnicodeString
Unit7.pas.33: p := PAnsiChar(Fred);
0046214B BEB0214600 mov esi,$004621b0
// no conversion, just a pointer to constant/"-1 RefCounted" AnsiString
As you can see in both cases PWideChar/PChar(FRED) and PAnsiChar(FRED), there is no conversion and Delphi compiler make 2 constant strings, one AnsiString and one UnicodeString.
Constants, including string literals, are untyped by default, and the compiler will fit them into whatever format works in the context you're using them in. As long as there are no non-ANSI characters in your string literal, the compiler won't have any trouble generating the string as ANSI instead of Unicode in this situation.
As Mason Wheeler points out all is fine as long as you don't have non-ANSI characters in your string const. If you have things like:
const FRED = 'Frédérick';
I'm pretty sure Delphi 2009/2010 will either issue charset hints (and apply a string conversion automatically - thus the hint) or fail at comparing ('Frédérick' is different in ISO-8859-1 than UTF-16).
If you can have "special" characters in your consts you will need to call string conversion.
Here are some basic examples with TStringList:
TStringList.SaveToFile(DestFilename, TEncoding.GetEncoding(28591)); //ISO-8859-1 (Latin1)
TStringList.SaveToFile(DestFilename, TEncoding.UTF8);

Resources