I'm quite familiar with PHP dockblocks since it's been my job for the last 15+ years.
/**
* Description
*
* #tag bla bla
* #tag more bla bla
*/
What I'm trying to understand is if there is a standard like that for Delphi and/or FreePascal.
From my analysis on an awful lot of code I never seen any, but I could be dead wrong.
Delphi documentation tools
Using the XMLDoc tool for API documentation and HelpInsight with Delphi 2005
http://edn.embarcadero.com/article/32770
XML Documentation in Delphi 2006
http://tondrej.blogspot.com/2006/03/xml-documentation-in-delphi-2006.html
DelphiCodeToDoc
http://dephicodetodoc.sourceforge.net/
Doc-O-Matic
http://www.doc-o-matic.com/examplesourcecode.html
PasDoc
http://pasdoc.sipsolutions.net/
Pascal Browser
http://www.peganza.com/
Doxygen
http://www.doxygen.nl/
Pas2Dox
http://sourceforge.net/projects/pas2dox/
JADD - Just Another DelphiDoc
http://delphidoc.sourceforge.net/
Stackoverflow discussion
Is there a Delphi code documentor that supports current Delphi syntax?
https://stackoverflow.com/questions/673248/is-there-a-delphi-code-documentor-that-supports-current-delphi-syntax
Code documentation for delphi similar to javadoc or c# xml doc
Code documentation for delphi similar to javadoc or c# xml doc
Documenting Delphi
https://stackoverflow.com/questions/33336/documenting-delphi
Latest Delphis support parsing of XML documentation. They also use this information in hints (for example if you move the mouse cursor over a method name).
I'm using this template for method documentation:
///<summary></summary>
///<param name=''></param>
///<returns></returns>
///<exception cref=""></exception>
///<since>2009-04-15</since>
I prefer docs outside of the source (it always gets messy), and use the excellent fpdoc that comes with FPC. (FPC's own docs are written in it).
On a project I'm currently working on, we're using DelphiCodeToDoc, which works reasonably well. Its syntax looks like this:
type
{* This is an example class }
TMyClass = class
private
protected
public
{* Does some twiddling with AParam, and returns the result as String
#param AParam Input value
#return AParam incremented by 2, as String
#throws Exception 'Boo' if it's full moon }
function MyFunction(AParam: Integer): String;
end;
It looks like doxygen has a tool which can be used in conjunction to document Pascal and Delphi code. Maybe that will help you.
There are several standards, usually depending on the documentation tool being used. We use PasDoc, so we adhere mostly to its format http://pasdoc.sipsolutions.net/ which is based on JavaDoc.
Alternatively, there is XMLDoc as gabr pointed out and there are quite a few other tools, most having similar syntax with subtle differences.
Related
Trying to use MS Speech API v11 with a Japanese engine (MS Haruka) in Delphi 10.3.
I have a sample app with a form and a button. The click handler goes:
uses SpeechLib11_TLB; // Imported from "Microsoft Speech Object Library" v.B.0
procedure TForm1.Button1Click(Sender: TObject);
var
v: ISpeechVoice;
begin
v := CoSpVoice.Create();
//5.4 only but won't hurt
v.Voice := v.GetVoices('language=411', '').Item(0);
v.Speak('時間', SVSFDefault);
end;
That causes an error, "Catastrophic failure" (HRESULT 0x8000FFFF, E_UNEXPECTED). The code that I think should be equivalent works in a C++ project:
#include <windows.h>
#import "libid:d3c4a7f2-7d27-4332-b41f-593d71e16db1" rename_namespace("SAPI") //v11
//#import "libid:C866CA3A-32F7-11D2-9602-00C04F8EE628" rename_namespace("SAPI") //v5.4
int wmain()
{
CoInitializeEx(0, COINIT_APARTMENTTHREADED);
{
SAPI::ISpeechVoicePtr v;
v.CreateInstance(__uuidof(SAPI::SpVoice));
//Needed for 5.4 only, but won't hurt
SAPI::ISpeechObjectTokensPtr voices(v->GetVoices(L"language=411", L""));
v->Voice = voices->Item(0);
v->Speak(L"時間", SAPI::SVSFDefault);
}
CoUninitialize();
return 0;
}
That works and speaks. So the SAPI per se is not broken on the machine. The platform is Win32 in both projects, not Win64. The Japanese voice is the default one (no need to set it explicitly).
Same result with SAPI 5.4 proper (not OneCore), although the Japanese voice is not the default one and I had to add a couple of lines to set it as the default.
Further debugging reveals that on the Delphi side, as much as calling the v.Voice property getter immediately after the first line causes the same error, E_UNEXPECTED. Meanwhile, the Voice setter works if you pass it a valid voice token object from GetVoices(). It looks as if the voice object initializes itself to its defaults correctly in C++, but somehow skips that in the Delphi project.
Requesting v.Voice right after construction works though in Delphi with SAPI 5.4. Calling Speak() still throws a E_UNEXPECTED.
What could be the difference in process/threadwide execution context between C++ and Delphi? It's not the thread locale. The COM thread model is apartment in both.
The same Delphi code works with an English phrase and an English voice (MS Helen). So whatever init failure there might be, it's probably specific to Haruka.
The SAPI 11 runtime is available here. The language data for TTS are here.
Another data point. I've rewritten the SAPI logic in Delphi to use SAPI 5.4 OneCore instead (not SAPI 5.4 proper). Unlike 5.4 and 11, it doesn't expose an IDispatch-based interface, and it's somewhat clumsier specifically in Delphi, but Japanese TTS works. The question, as initially posed, is still unanswered, but at least there's a workaround. I'll write up an answer, but I won't accept it.
However, it's not the custom vs. dual distinction that's to blame. I've changed the logic to use custom interfaces instead of automation ones with SAPI 5.4 proper (the typelib defines both), still got E_UNEXPECTED from Speak(). There's no error info.
Here's another beautiful data point: SAPI 5.4 TTS with automation based API works and talks as expected in a Delphi console app. So it's not even Delphi specific, it's somehow VCL specific. What is it with Delphi GUI? Needless to say, I immediately retested the C++ snippet in a C++ GUI application, with a form and a button. The C++ one talks.
Not an answer, but a workaround.
Windows 10 comes with two flavors of 32-bit SAPI. There's SAPI 5.4 proper (in system32\speech), and also SAPI 5.4 OneCore (in system32\speech_onecore). The latter, even though it's superficially the same, exposes a different typelib - there's no automation support, all interfaces are custom instead of dual. What's more important, when you download the Japanese TTS voice in the Windows 10 Settings app, you end up with 3 voices under OneCore (Sayaka, somehow, missing) and only one, Haruka, under 5.4 proper.
Delphi can consume custom interfaces in a typelib, but the methods look somewhat clumsier. Also, the enumeration of voices in the automation API is cleaner. Anyway, here goes.
uses SpeechLib54Core_TLB; // This time it's OneCore
procedure TForm1.Button1Click(Sender: TObject);
var
v: ISpVoice;
cat: ISpObjectTokenCategory;
toks: IEnumSpObjectTokens;
tok: ISpObjectToken;
sno: LongWord;
hr: HResult;
n: Cardinal;
begin
v := CoSpVoice.Create();
hr := v.GetVoice(tok);
tok.GetCategory(cat);
cat.EnumTokens('language=411', '', toks); //411 means Japanese
toks.GetCount(n);
if n = 0 then
exit; // No Japanese voices installed
toks.Item(0, tok); //Take the first one - typically Ayumi
v.SetVoice(tok);
v.Speak('時間', 0, sno);
end;
Note that passing a Japanese string literal to a COM method works without an explicit cast to a wide string.
I am using FIB Plus components with Delphi 2010, just would like to know what closeopen(true) does. I tried googling but didn't get info on it.
FIBDataSet.closeopen(true);
procedure CloseOpen(const DoFetchAll:boolean);
This method reopen TpFIBDataSet. DoFetchAll parameter specifies whether to do a full fetch of data.
This quote is from russian version of document "FIbPlus 6.4 Appendix.PDF "
Unfortunately I can not find a link to the English version of the document;
I have imported a WSDL and use it to send a SOAP request. It looks like this:
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<Calculate xmlns="urn:xx.WSDL.xxxxxWebService">
<ContractdocumentIn>
<AL>
...More XML...
The problem is the xmlns="urn:xx.WSDL.xxxxxWebService" part in the Calculate element. The web service cannot accept this. The web service doesn't like namespaces like this...
Using SoapUI I found this request to work just fine:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:col="http://example.com.service.xxx/">
<SOAP-ENV:Body>
<col:Calculate>
<ContractdocumentIn>
<AL>
...More XML...
So, how do I change the request from the first to the second version? (Without using dirty tricks!) (Re-importing is not a problem if this would result in the proper request format.)
Again: no dirty tricks allowed, like hacking the request stream to modify it!
And while I haven't completely tested it, it seems that C#/VS2010 and Delphi 2010 are also unable to use the web service that I'm trying to call. A web service that seems to be written in Java. SoapUI happens to be written in Java, thus we have a Java client talking to a Java service, which seems to work just fine. But any other client?
Anyways, time to add two more tags: "Java", since it's a Java service, and "vs2010" because .NET also dislikes this service.And I was about to write a wrapper around this service in .NET, hoping that would work... It doesn't. So this is a very serious flaw, possibly a Java flaw...
If a Service expects:
<col:Calculate>
<ContractdocumentIn>
<AL>
and Delphi SOAP is sending...
<Calculate xmlns="urn:xx.WSDL.xxxxxWebService">
<ContractdocumentIn>
<AL>
... the problem is that ContractdocumentIn is an unqualified element and (until Delphi XE) Delphi SOAP did not support unqualified elements that are top level elements of an operation. Top level elements are parameters of the function and there is nowhere to store the fact that the underlying element must be unqualified; for elements that map to properties, we use the Index of the property to store away the IS_UNQL flag.
BTW, it's not necessary to use a prefix. The Service will (should) also accept:
<Calculate xmlns="urn:xx.WSDL.xxxxxWebService">
<ContractdocumentIn xmlns="">
<AL>
The latter is more verbose but it's equivalent to the prefix case.
In Delphi XE the importer stores away the fact that a particular parameter maps to an unqualified element and the runtime acts on this information. I've posted patches based on the XE implementation for D2010 and D2007 in the newsgroup when it came up in a thread recently:
https://forums.embarcadero.com/thread.jspa?threadID=43057
If someone needs access to them (they were in the attachments area but might have scrolled off), please email me and I'll make them available. [bbabet at embarcadero dot com]
Cheers,
Bruneau
OMG! It took lots of coffee and plenty of sleep depravation but I managed to solve my problem! It's reasonable simple too...First I import the WSDL, as expected. This will generate several TRemotable classes. Then, for each TRemotable which needs a different namespace, I override the ObjectToSOAP() method! (And include XMLIntf to the WSDL source.) In my case with code like this for several of the remotable types:
function AL2.ObjectToSOAP( RootNode, ParentNode: IXMLNode; const ObjConverter: IObjConverter; const NodeName, NodeNamespace, ChildNamespace: InvString; ObjConvOpts: TObjectConvertOptions; out RefID: InvString ): IXMLNode;
begin
Result := inherited ObjectToSOAP( RootNode, ParentNode, ObjConverter, NodeName, '', '', ObjConvOpts, RefID );
end;
Which worked in Delphi XE. In Delphi 2007 I had to use units XMLIntf and XMLDoc plus this code on the input type:
function ContractdocumentInType.ObjectToSOAP(RootNode, ParentNode: IXMLNode; const ObjConverter: IObjConverter; const Name, URI: InvString; ObjConvOpts: TObjectConvertOptions; out RefID: InvString): IXMLNode;
procedure AlterChildren(Child: IXMLNode);
var
I: Integer;
begin
if (Child.NodeType = ntElement) then Child.SetAttributeNS('xmlns', '', '');
for I := 0 to Pred(Child.ChildNodes.Count) do
AlterChildren(Child.ChildNodes[I]);
end;
begin
Result := inherited ObjectToSOAP(RootNode, ParentNode, ObjConverter, Name, '', ObjConvOpts, RefID);
AlterChildren(Result);
end;
It is a hack, in my opinion. But it's not a very dirty one. It's a bit of experimenting, capturing the SOAP requests and responses to check their content and to see if it uses the proper namespaces. Unfortunately, Delphi XE does a far better job at this than Delphi 2007.
Still, I keep this Q open for any better solutions...
Btw, to add the col: to the output, I also had to change this line in the WSDL RemClassRegistry.RegisterXSClass(Calculate, 'http://colan.ogconnect.service.wzp/', 'Calculate'); to this: RemClassRegistry.RegisterXSClass(Calculate, 'http://colan.ogconnect.service.wzp/', 'cal:Calculate');. The result then becomes <cal:Calculate xmlns:cal="http://example.webservice/">. Ont more thing needs to be done, though: moving that xmlns:cal to the xml header. But as it is now, it works for me.
Another note: for the WSDL I used the following settings: 'One Outparam is return', 'Unwind literal params', 'Generate destructors', 'Warning comments', 'emit literal types', 'Map string to widestring'. Other options are: 'Generate verbose information about types and interfaces', 'Ignore porttypes with HTTP Bindings', 'Validate enumeration members', 'Import fault types', 'Import header types', 'Process included and imported schemas', 'Generate class alias as class types', 'Allow Out parameters' and 'Process nillable and optional elements'. The emit literal types was practical because it generates a class around the single method that I was calling. Unfortunately, this won't help much either, although the class would help you to modify the SOAP request on the upper level within the envelope by overriding the ObjectToSOAP() method.Creation of the envelope itself is in the SOAPEnv unit and it's used in the OPToSOAPDomConv unit. Unfortunately, I haven't found an easy method to access the envelope itself to alter the header to add this additional namespace. Then again, I could override the TSOAPDomConv class with my own version that does add the additional namespace. But the code is working for me now, and as my father told me, when he learned me to program: never fix anything that isn't broken.
What tools would you recommend for unit testing in Delphi.
I've used FastMM4 for memoryleak testing.
And MadExcept, both rule, but it doesn't help me test the logic in my programs.
I would like some alternatives, so don't all rush to suggest DUnit :-).
Any suggestions?
The most commonly used is DUnit. It's actually included in modern versions of Delphi but if your version doesn't come with it you can download it from Sourceforge.
DUnit comes with D2007.
-->File -->New -->Other
Select Unit Test from the dialogue that pops up.
There are a couple of good demo videos on it's use, I'll see if I can dig one up.
This is a pretty good one and others come up on the right:
http://www.youtube.com/watch?v=nyZnfxDqThE
You could take a look at the unit testing classes available in our SynCommons open source unit. It's used in our Open-Source framework for all regression tests. It's perhaps not the best, but it's worth taking a look at it. See http://blog.synopse.info/post/2010/07/23/Unit-Testing-light-in-Delphi
In the up-to-come 1.13 version, there is also a new logging mechanism with stack trace of any raised exception and such, just like MadExcept, using .map file content as source.
It's now used by the unit testing classes, so that any failure will create an entry in the log with the source line, and stack trace:
C:\Dev\lib\SQLite3\exe\TestSQL3.exe 0.0.0.0 (2011-04-13)
Host=Laptop User=MyName CPU=2*0-15-1027 OS=2.3=5.1.2600 Wow64=0 Freq=3579545
TSynLogTest 1.13 2011-04-13 05:40:25
20110413 05402559 fail TTestLowLevelCommon(00B31D70) Low level common: TDynArray "" stack trace 0002FE0B SynCommons.TDynArray.Init (15148) 00036736 SynCommons.Test64K (18206) 0003682F SynCommons.TTestLowLevelCommon._TDynArray (18214) 000E9C94 TestSQL3 (163)
The difference between a test suit without logging and a test suit with logging is only this:
procedure TSynTestsLogged.Failed(const msg: string; aTest: TSynTestCase);
begin
inherited;
with TestCase[fCurrentMethod] do
fLogFile.Log(sllFail,'%: % "%"',
[Ident,TestName[fCurrentMethodIndex],msg],aTest);
end;
The logging mechanism can be used to trace recursive calls. It can use an interface-based mechanism to log when you enter and leave any method:
procedure TMyDB.SQLExecute(const SQL: RawUTF8);
var ILog: ISynLog;
begin
ILog := TSynLogDB.Enter(self,'SQLExecute');
// do some stuff
ILog.Log(sllInfo,'SQL=%',[SQL]);
end; // when you leave the method, it will write the corresponding event to the log
It will be logged as such:
20110325 19325801 + MyDBUnit.TMyDB(004E11F4).SQLExecute
20110325 19325801 info SQL=SELECT * FROM Table;
20110325 19325801 - MyDBUnit.TMyDB(004E11F4).SQLExecute
Here the method name is set in the code ('SQLExecute'). But if you have an associated .map file, the logging mechanism is able to read this symbol information, and write the exact line number of the event.
Note that by default you have time and date written to the log, but it's also possible to replace this timing with high-resolution timestamps. With this, you'll be able to profile your application with data coming from the customer side, on its real computer. Via the Enter method (and its auto-Leave feature), you have all information needed for this. ;)
Like this:
0000000000000B56 + TTestCompression(00AB3570).000E6C79 SynSelfTests.TTestCompression.TestLog (376)
0000000000001785 - TTestCompression(00AB3570).000E6D09 SynSelfTests.TTestCompression.TestLog (385)
I still need to write some tool to compute the profiling, but there is already a dedicated TSynLogFile class able to read the .log file, and recognize its content.
The first time the .map file is read, a .mab file is created, and will contain all symbol information needed. You can send the .mab file with the .exe to your client, or even embed its content to the .exe. This .mab file is optimized: a .map of 927,984 bytes compresses into a 71,943 .mab file.
So this unit could be recognized as the natural child of DUnit and MadExcept wedding, in pure OpenSource. :)
Additional information is available on our forum. Feel free to ask. Feedback and feature requests are welcome! Works from Delphi 6 up to XE.
I have the following declaration for DNSServiceRegister:
function DNSServiceRegister
(
var sdRef: TDNSServiceRef;
const flags: TDNSServiceFlags;
const interfaceIndex: uint32_t;
const name: PUTF8String; //* may be NULL */
const regType: PUTF8String;
const domain: PUTF8String; //* may be NULL */
const host: PUTF8String; //* may be NULL */
const port: uint16_t;
const txtLen: uint16_t;
const txtRecord: Pointer; //* may be NULL */
const callBack: TDNSServiceRegisterReply; //* may be NULL */
const context: Pointer //* may be NULL */
): TDNSServiceErrorType; stdcall; external DNSSD_DLL;
In my Bonjour framework I have the following response to an announced service being made active (i.e. to actually start announcing itself, via Bonjour):
procedure TAnnouncedService.Activate;
var
flags: Cardinal;
name: UTF8String;
svc: UTF8String;
pn: PUTF8String;
ps: PUTF8String;
begin
fPreAnnouncedServiceName := ServiceName;
inherited;
if AutoRename then
flags := 0
else
flags := kDNSServiceFlagsNoAutoRename; { - do not auto-rename }
if (ServiceName <> '') then
begin
name := ServiceName;
pn := PUTF8String(name);
end
else
pn := NIL;
svc := ServiceType;
ps := PUTF8String(svc);
CheckAPIResult(DNSServiceRegister(fHandle,
flags,
0 { interfaceID - register on all interfaces },
pn,
ps,
NIL { domain - register in all available },
NIL { hostname - use default },
ReverseBytes(Port),
0 { txtLen },
NIL { txtRecord },
DNSServiceRegisterReply,
self));
TBonjourEventHandler.Create(fHandle);
end;
This is more verbose than I think it strictly needs to be, certainly it was working perfectly well in Delphi 7 in a much less verbose form. I have expanded a lot of operations into explicit steps to facilitate debugging, e.g. to be able to identify any implicit transforms of string payloads that may be occuring "under the hood" in Delphi 2009.
Even in this untidy expanded form this code compiles and works perfectly well in Delphi 7, but if I compile and run with Delphi 2009 I get no announcement of my service.
For example, if I run this code as part of a Delphi 7 application to register a _daap._tcp service (an iTunes shared library) I see it pop-up in a running instance of iTunes. If I recompile the exact same application without modification in Delphi 2009 and run it, I do not see my service appearing in iTunes.
I get the same behaviour when monitoring with the dns-sd command line utility. That is, service code compiled with Delphi 7 behaves as I expect, compiled in Delphi 2009 - nothing.
I am not getting any errors from the Bonjour API - the DNSServiceRegisterReply callback is being called with an ErrorCode of 0 (zero), i.e. success, and if I supply a NIL name parameter with AutoRename specified in the flags then my service is allocated the correct default name. But still the service does not show up in iTunes.
I am at a loss as to what is going on.
As you might be able to tell from the expansion of the code, I have been chasing potential errors being introduced by the Unicode implementation in Delphi 2009, but this seems to be leading me nowhere.
The code was originally developed against version 1.0.3 of the Bonjour API/SDK. I've since updated to 1.0.6 in case that was somehow involved, without any success. afaict 1.0.6 merely added a new function for obtaining "properties", which currently supports only a "DaemonVersion" property for obtaining the Bonjour version - this is working perfectly.
NOTE: I'm aware that the code as it stands is not technically UTF8-safe in Delphi 7 - I have eliminated explicit conversions as far as possible so as to keep things as simple as possible for the automatic conversions that Delphi 2009 applies. My aim now is to get this working in Delphi 2009 then work backward from that solution to hopefully find a compatible approach for earlier versions of Delphi.
NOTE ALSO: I originally also had problems with browsing for advertised services, i.e. identifying an actual iTunes shared library on the network. Those issues were caused by the Unicode handling in Delphi 2009 and have been resolved. My Delphi 2009 code is just as capable of identifying an actual iTunes shared library and querying it's TXT records. It's only this service registration that isn't working.
I must be missing something stupid and obvious.
Does anyone have any ideas?!
UPDATE
Having returned to this problem I have now discovered the following:
If I have a pre-D2009 and a D2009+ IDE open (e.g D2006 and D2010) with the same project loaded into both IDE's concurrently:
Build and run under 2006: It works - my service announcement is picked up by iTunes
Switch to D2010 and run (without building): It does a minimal compile, runs and works.
Do a full build in D2010: It stops working
Switch back to D2006 and run (without building): It doesn't work
Do a full build in D2006: It works again
Does this give anyone any other ideas?
The answer to this is mind boggling. On the one hand I made a completely stupid, very simple mistake, but on the other hand it should never - as far as I can see - have worked in ANY version of Delphi!
The problem was nothing what-so-ever to do with the Unicode/non-unicodeness of any strings, but was actually due to a type mismatch in the PORT parameter.
I was passing in the result of ReverseBytes(Port) - that parameter expected a uint16_t, i.e. a Word value. My Port property was however declared (lazily) as an Integer!!
Once I fixed this and had Port declared as a Word, it now works on both D2007- and D2009+ versions of Delphi.
Very weird.
I can only think that some other edge-case behaviour of the compiler that might have somehow affected this was changed when Unicode support was introduced.
Based on the information that we have available here, the situation is this:
When calling the DLL with your code in Delphi 2007, it gives one result.
When calling the same DLL with your code in Delphi 2009, it gives another result.
The suspicion is, that it is related to the Delphi 2009 compiler.
Logically, the difference must therefore be, that Delphi 2009 sends different values as parameters. In order to make the debugging truly Delphi-independent, you therefore need to create a dummy DLL, which reports the values it gets. Other Delphi-dependent methods may be applied, like looking at the disassembly of the function-call into the DLL, and debugging it so that we know exactly what values are passed, and how, to the DLL, in both compilers.
I can't find the declaration instruction for the vars "ServiceName" and "ServiceType" in your code sample.
Assuming a String type (thus a unicode string), I guess (yes... no D2009 available to test this) lazy typecasting could be an issue:
name := ServiceName;
Why not use the following?
name := PAnsiChar(AnsiString(ServiceName))
Anyhow... just my 2 cts.
BTW:
I always use the pre defined "EmptyStr", "EmptyWideStr" ... so the test would look like:
if (ServiceName <> EmptyStr) then
which should be safe and avoid the confusion of types.
On the other side, Delphi may interpret the '' as an ANSIChar like the following declaration do:
const
MyParagraphChar = '§';
Not sure... I'm confused - should go home now ;)
If the DLL is not written using Delphi 2009, you may want to use something else than PUTF8String. The Delphi 2009 Utf8String type is different from Delphi 2007's UTF8String type.
If the DLL was written using C/C++, I strongly suggest to use PAnsiChar() instead of PUtf8String.