Delphi SHGetFolderPath and null terminated string - delphi

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.

Related

Delphi: Invalid typecast with Move

I am installing an old component pack TSCap32 and get error Invalid typecast in multiple location with Move command.
var
pCopiedDib: PChar;
...
Move(pOrigDibBmi^, TByteArray(pCopiedDib^)[0], BmiSize);
The unit itself declare TByteArray like bellow:
type
TByteArray = array[0..0] of Byte;
How to resolve this error?
The cast that you are using requires that the TByteArray and pCopiedDib^ types have the same size. They do not: TByteArray has size 1, and pCopiedDib^ has size 2. Thus the compiler rejects your code because it is not valid.
The code was presumably originally written for pre-Unicode Delphi where PChar is an alias for PAnsiChar. In that scenario the code compiles. Now, you use a Unicode Delphi and PChar is an alias for PWideChar, and hence the types have different size.
Exactly how to fix your code is unclear. If you define pCopiedDib as PAnsiChar then your code will compile, but whether or not it will work is a different matter altogther. I suggest that you study this code further now that you know the cause of the error.

Convert WideString to PWideChar

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));

Calling GetStringTypeW from non-unicode Delphi (7)

I'm attempting to call GetStringTypeW from a non-unicode delphi application and, no matter what I do, I get ERROR_INVALID_FLAGS back. I couldn't find any code sample of that function in use either.
I also had to redifine the function header because the ones provided in windows.pas incorrectly identifies the 3rd parameter as a boolean (it's an INT)
here is my definition for the function:
function GetStringTypeW(dwInfoType: DWORD; lpSrcStr: PWideChar; cchSrc: Integer; lpCharType: Pointer): BOOL;
(For some reason, that function isn't defined as stdcall. Trying it to define it as stdcall will result in an access violation.)
And my call:
var
aCharType: Array of WORD;
APassword: WideString
begin
{..}
SetLength(aCharType, Length(APassword));
if not GetStringTypeW(CT_CTYPE1, PWideChar(APassword[1]), Length(APassword), #aCharType[0]) then
RaiseLastOSError;
{..}
The error I get is
System Error. Code: 1004.
Invalid flags.
I've verified that CT_CTYPE1 is equal to 1.
Does anyone know what could be wrong or have a code sample for using this function ?
The declaration in Windows.pas is indeed wrong, but your correction is wrong, too. You've fixed the parameter types, but you need to fix the calling convention. It should be stdcall:
function GetStringTypeW(
dwInfoType: DWORD;
const lpSrcStr: PWideChar;
cchSrc: Integer;
lpCharType: PWordArray
): BOOL; stdacll;
Without the calling-convention fix, Delphi puts most of the parameters in registers, but the OS expects to find them on the stack. The stack doesn't contain the right values at the right places, to it fails. A bad calling convention can also lead to access violations because the stack is left in an inconsistent state, but since you immediately throw your own exception anyway, that might conceal the stack problems.
When you use stdcall, you get an access violation because you're passing a character and claiming it's a pointer. The OS attempts to dereference the "pointer," but since the character value doesn't represent a valid address, it fails. Type-cast the whole string, not just one character, when you call the function:
GetStringTypeW(CT_CTYPE1, PWideChar(APassword), Length(APassword), PWordArray(aCharType))
Your second parameter is wrong. You are type-casting a single WideChar value to a PWideChar, instead of obtaining the memory address of that WideChar. In other words, change this:
PWideChar(APassword[1])
To this:
PWideChar(APassword)
Or this (only if the length of APassword is never 0):
#APassword[1]

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