Convert WideString to PWideChar - delphi

I use Nicomsoft OCR library to OCR images in Delphi. It is good for my tasks and it has Delphi unit-wrapper so it's easy to use it in Delphi. However, Delphi debugger shows "Range Error" message when I pass empty string as parameter value to some OCR functions. I checked the wrapper code and found that DLL library functions accept PWideChars as parameter but wrapper accepts WideString. Inside of unit-wrapper there is the following conversion:
function CallSomeOCRFunction(a: WideString);
var b: PWideChar;
begin
b := #a[1];
CallSomeDLLFunction(b); //passing "b" to DLL function that accepts PWideChar
//.....
I did some research and discovered that many FAQs offer such conversion, for example: http://www.delphibasics.co.uk/RTL.asp?Name=PWideChar
It works if "a" is not empty string, but for empty string it cause "Range" error. How can I get pointer to first character of WideString variable correctly even if it is empty string? As far as I understand, even if string is empty it must contain zero character and PWideChar variable must point to it.

Use PWideChar() cast as described in the documentation. In your case it would be:
CallSomeDLLFunction(PWideChar(a));

Related

Delphi SHGetFolderPath and null terminated string

I am using this function to get user folder:
function LocalAppDataPath : string;
const
SHGFP_TYPE_CURRENT = 0;
var
path: array [0..MaxChar] of char;
begin
SHGetFolderPath(0,CSIDL_LOCAL_APPDATA,0,SHGFP_TYPE_CURRENT,#path[0]);
Result := StrPas(path);
end;
It is working great.
But after I add this lib to my project: OpenJpeg
The returned values from this function is: C. It seems like for some reason each char in the "path" array has #0 after it and for this reason the returned string it truncated right at the start.
When I remove: OpenJpeg from my uses it all works good.
Any idea why it happens?
At the very top of the OpenJpeg unit is found this code:
type
Char = AnsiChar;
This unit is therefore toxic when used in a Unicode Delphi. When you use Char in your code you are expecting a WideChar but this evil type alias confounds you.
My advice is to modify the library to remove that type alias and replace every use of Char with AnsiChar.
If you don't want to do that you can fully qualify Char as System.Char, or use WideChar explicitly in your code. However, this unit is, as I said, toxic, and it really needs to be remedied.
If you enable the Typed-Checked Pointers compiler option, the compiler will stop you from passing a PAnsiChar to a function expecting a PWideChar, and vice versa, when using the # address operator. Better to find your mistakes at compile time if possible.
You can simplify the code by passing path in place of #path[0].
Please don't use StrPas. It has long been deprecated. You can simply write
Result := path;
The array is one character longer than necessary. That is, assuming MaxChar is what I think it is. Anyway, the array should be
path: array [0..MAX_PATH-1] of char;
You also fail to perform any error checking on the API call. Please check for errors as described in the documentation.

how can i return pchar type from delphi dll function? [duplicate]

This question already has answers here:
How can I return a PChar from a DLL function to a VB6 application without risking crashes or memory leaks?
(7 answers)
Closed 8 years ago.
Vb6 code to run on the following error.
How can I fix the error?
How can I return a normal string?
vb6 code
Private Declare Function DllPchar Lib "C:\TEST\Project2.dll" (ByVal AStr As String) As String
Private Sub Command10_Click()
Dim tmp as String
tmp = DllPchar("123");
End Sub
Delphi7 code
function DllPchar( AStr: PChar) : PChar; stdcall;
Well, you haven't given much information about this problem. For instance, the error message. And the Delphi code for "DllPChar".
But never mind. The first thing I notice is that your Declare Function statement is returning a String. This will not work because VB is expecting a value of type BSTR to be returned. Delphi has a WideString type which is compatible with BSTR.
The reason why this is important is because VB strings are internally UTF-16, i.e. 2 byte per character Unicode Strings that are allocated by the COM memory manager. The Delphi PAnsiChar type is a pointer to an 8-bit ANSI character, and Delphi strings are allocated by Delphi's own memory manager. They are incompatible. However, there is a special case in VB6 where you can use Declare Function with a parameter ByVal ... As String, and VB handles and automatic conversion between a VB string and PAnsiChar before the call, and then between PAnsiChar and VB string after the call.
If you can't use BSTR in Delphi, your best bet is to rewrite DllPchar() so that it modifies the AStr parameter. Or alternatively, create a new parameter to return the value in.
If you can use BSTR, then you can modify AStr to pass it ByRef rather than ByVal. This will allow you to pass in a Unicode string from VB. You then return your result via the return value.

How to fetch japanese characters in Delphi 7

I have a problem with displaying Japanese Character , specifically Unicode character "5c" in my delphi application . I need to save the application names into the registry and then display it in some kind of popup.
I have narrowed down the problem to this code specifically :-
Var
Str : WideString;
Str2: WideString;
Str3 : WideString;
TntEdit5.Text := TntOpenDialog1.FileName; //correctly displayed
Str3 := TntEdit5.Text;
ShowMessage('Original =' + Str3);
Str := UTF8Encode(TntEdit5.Text) ;
ShowMessage('UTF8Encode =' + Str3);
Str2 := UTF8Decode(Str) ;
ShowMessage('UTF8Decode =' + Str3);
end;
I dont get the correct name in Str, Str2 and Str3 . So how to fetch the name in a string ?
I dont want to display the text but i want to use it to save to registry and other functions.
Instead of SHowMessage, I used MessageBoxW(Form1.Handle, PWChar( Str3 ), 'Path', MB_OK ); which gave me correct result.
But I want to use this string internally, like write the string into a file etc. How to do that ?
Thanks In Advance
The type of Str does not match the type of result of UTF8Encode - so the line Str := UTF8Encode damages data. Instead of Str you should declare and use variable with a datatype mathcing the one of Utf8Encode result.
Same is true for Str2 := UTF8Decode(Str) line with regard to wrong data type of Str parameter the. It should be replaced with another var of proper datatype.
Str3 is not declared, so the code won't even compile. Add the Str3: WideString; line.
ShowMessage does not work with UTF-16, so then you make your own popup function that does.
Make your own dialog containing Tnt unicode-aware Label to display the text. And your new ShowMessage-like function would set the label's caption and then display that dialog instead of stock unicode-unaware one.
You may look at http://blog.synopse.info/post/2011/03/05/Open-Source-SynTaskDialog-unit-for-XP%2CVista%2CSeven for exampel of such dialogs, but i don't know if they are UTF-16 aware on D7.
Another option is searching TnT Sources for a ready-made unicode-aware function like ShowMessage - there may be one, or not.
Yet another option is using Win32 API directly, namely the MessageBoxW function working with PWideChar variables for texts: see http://msdn.microsoft.com/en-us/library/windows/desktop/ms645505.aspx
#DavidHeffernan MessageBoxW needs a lot of boilerplate both due to using C-Strings and for giving too much flexibility. It may be considered kinda good replacement for MessageDlg but not so much for ShowMessage. Then i am sure that TnT has ShowMessage conversion and that implementing own dialog would be good for both application look-and-feel and topic-starter experience.
You may also switch from obsolete Delphi 7 to modern CodeTyphon that uses UTF-8 for strings out of the box. You should at very least give it a try.
To read and write WideString from registry using Delphi 7 RTL you can make two easy options:
Convert WideString to UTF8 AnsiString and save it via TRegistry.WriteString and do back conversion on reading.
Save WideString as binary data: Cardinal(Length) followed by array of WideChar using TRegistry.WriteBinaryData
You can also use function RegReadWideString(const RootKey: DelphiHKEY; const Key, Name: string): WideString; and RegWriteWideString courtesy of http://jcl.sf.net
Whatever approach you'd choose - you have to do your own class on top of TRegistry that would be uniformly implementing those new TYourNewRegistry.WriteWideString and TYourNewRegistry.ReadWideString methods, so that the string written would always be read back using the same method.
However, since you already got TNT installed - then look carefully inside,. there just should be ready-made unicode-aware class like TTntRegistry or something like that.

What is exact signature of System.Assign (for use in procedural expression)?

Unfortunately, exact Assign signature is not available in the RTL source, and my attempts to guess, like:
const
Assign: procedure (var F; const FileName: string) = System.Assign;
{ or }
Assign: function (var F; const FileName: string): Integer = System.Assign;
{ also I tried "internal" one from _AssignFile }
didn't yield any positive results, and compiler refuses to treat this contant expression as procedural and complains about right-value (E2029 '(' expected but ';' found)).
So, which type should I use to match Delphi RTL exactly?
Assign is a language remnant of Delphi tracing its origins to the original Turbo Pascal syntax. Long before function overloading was added to the language syntax, long before the RTL's text file and "file of type" internal data structures were documented, there was Assign.
Assign is a simple enough procedure to use, but when you look at what it has to do you'll see that it's pretty much impossible to implement without some sort of compiler magic. File of TFoo creates a file type that is distinct and incompatible with File of Integer. And yet, both can be passed as the first parameter to Assign.
Today, you could probably implement Assign using a generic type param for the file parameter. The generic type would conform to the type of the variable passed in. That's great and all, but we needed a way to associate a typed file variable with a string filename 25 years before generics were added to the mix.
Assign is a compiler intrinsic function. That means it lives outside of the Delphi/Pascal syntax space. Assign is essentially a type conforming procedure, which can't be represented in the strongly typed Delphi/Pascal language (without modern and complex language extensions such as generic types). It's not typeless, and it's not dynamically typed because the actual parameter type is fully determined at compile time.
You can't take the address of the Assign function. Even if you could, because it is amorphous, you won't be able to define a procedure type that accurately represents all of its possible call signatures.
Compiler magic functions exist outside the language to take care of tasks that could not be easily represented in the syntax available at the time. Assign is one example. Writeln() is another.
System.Assign is an intrinsic function. The official documentation of it is completely hopeless. It says:
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
I can only guess as to why the documentation generator cannot cope with this function. But the three identical overloads are clearly bogus. And it's not a function, rather it is a procedure.
No matter. Because it is an intrinsic, you cannot assign it to a function pointer. The solution is to wrap it up in a function of your own.
procedure MyAssign(var F: File; const FileName: string);
begin
Result := System.Assign(F, FileName);
end;
You can then assign that function to a variable or constant of procedural type.
const
AssignProc: procedure(var F: File; const FileName: string) = MyAssign;
The other variant of Assign takes a third parameter that specifies the code page, passed as a Word. You can only call that function if the first argument to Assign is a TextFile.
So, intrinsics are really a law unto themselves.
Note that the documentation does state that Assign should no longer be used. Instead you should use AssignFile. The documentation is no better there mind you!
If to open System.pas file and read it, in XE2 you have those internal procedures:
function _AssignFile(var t: TFileRec; const s: PChar): Integer;
function _AssignText(var t: TTextRec; const s: PChar; const CP: word): Integer;
And i don't think you have any global stable procedure w/o underscore in names - those listed are Delphi compiler internals, like invisible procedures, that are covertly called when you declare string or dynarray variable.
http://en.wikipedia.org/wiki/Intrinsic_function
Like a lot of procedures like _Write0LString which are implementing usual WriteLn calls.
So you would have to make a wrapper function that would call AssignFile and put reference to your wrapper into your variable instead;
However you did not put tag with your Delphi version, and different vesions may have different declarations for intrinsics. So look into sources of your particular Delphi.
Also, why would you need it ? I remember i did such a thing in TurboPascal 5.5 to make string-based file - make WriteLn act like C sprintf. But why would you need it in modern Delphi, when you can use TStream instead?

Porting mozilla's NSModule to Delphi

In extension to this question, I guess I'll best show what I've got so far.
What I'm trying to do is create a Firefox extension with Delphi, that'll work with the Firefox versions of the future that will use an exported NSModule structure, and no longer an NSGetModule function.
Main questions I'm struggling with for the moment is:
Is the code below correct? I may be wrong with how the pointers and arrays of records work.
How to debug this? If I build it and it runs then I'm kind of sure it'll work, but in debugging my library I can only check if my init code does its job. (and for now, Firefox 3.6 doesn't seem to pick up my #mozilla.org/network/protocol;1?name=xxm contract)
The code I'm trying to port is here:
http://mxr.mozilla.org/mozilla-central/source/xpcom/components/Module.h
type
TConstructorProcPtr=function(aOuter:nsISupports;const aIID:TGUID;var aResult:pointer):nsresult;
TLoadFuncPrt=function:nsresult;
TUnloadFuncPrt=procedure;
TCIDEntry=record
cid:TGUID;
service:boolean;
getFactoryProc:pointer;//TGetFactoryProcPtr;
constructorProc:TConstructorProcPtr;
end;
TContractIDEntry=record
contractid:PChar;
cid:TGUID;//PGUID?
end;
TCategoryEntry=record
category,entry,value:PChar;
end;
TXPCOMModule=packed record
kVersion:integer;//=1;
mVersion:cardinal;//kModuleVersion
mCIDs:^TCIDEntry;//pointer to first in array, last should be nil
mContractIDs:^TContractIDEntry;//pointer to first in array, last should be nil
mCategoryEntries:^TCategoryEntry;//pointer to first in array, last should be nil
getFactoryProcPtr:pointer;//TGetFactoryProcPtr;
loadProc:TLoadFuncPrt;
unloadProd:TUnloadFuncPrt;
end;
You almost certainly need the cdecl calling convention on all your procedure- and function-pointer declarations:
TConstructorProcPtr = function(aOuter: nsISupports; const aIID: TGUID; var aResult: Pointer): nsresult; cdecl;
TLoadFuncPrt = function: nsresult; cdecl;
TUnloadFuncPrt = procedure; cdecl;
I assume you've declared nsISupports as a Delphi interface. Otherwise, you need to make sure the aOuter parameter above is a pointer as it is in the C++ code.
For TContractIDEntry, and all the other places where you use PChar, I advise you to use PAnsiChar instead. The size of Delphi's Char type changed a couple of years ago, but the C++ char is and always will be one byte, so use Delphi's one-byte character type explicitly. Also, your comment wondering whether to declare the cid field as a PGUID was correct; asterisk means pointer.
TContractIDEntry = record
contractid: PAnsiChar;
cid: PGUID;
end;
The kVersion field should not be a member of the record you declare. In C++, it's a static member, which means it occupies no space in the structure itself; it's shared by all instances of that type. It's equivalent to a class field in a Delphi class, but I don't think records offer that feature. Make it a unit-level variable instead of a field.

Resources