Sending Delphi string as a parameter to DLL - delphi

I want to call a DLL function in Delphi 2010. This function takes a string and writes it to a printer with an USB interface. I do not know in which language is the DLL developed. According to the documentation, the syntax of the function is:
int WriteUSB(PBYTE pBuffer, DWORD nNumberOfBytesToWrite);
How can I declare and use my function in Delphi?
I declare the function like this:
var
function WriteUSB(myP:pByte;n:DWORD): integer ; external 'my.dll';
Should I use stdcall or cdecl in the declaration?
I call the DLL function like this:
procedure myProc;
var
str : string:
begin
str := 'AAAAAAAAAAAAAAAAAAAAA';
WriteUSB(str,DWORD(length(tmp)));
end;
But this code give me exception all the time. I know that the problem is that String is Unicode and each character > 1 byte. I tried to convert to different string types ( AnsiChar and ShortString) but I failed.
What is the correct way to do this?

A couple things. First off, if this is a C interface, which it looks like it is, then you need to declare the import like this:
function WriteUSB(myP:pAnsiChar; n:DWORD): integer; cdecl; external 'my.dll';
Then to call the function, you need to use an Ansi string, and convert it to a PAnsiChar, like so:
procedure myProc;
var
str : AnsiString;
begin
str := 'AAAAAAAAAAAAAAAAAAAAA';
WriteUSB(PAnsiChar(str), length(str));
end;
(The cast to DWORD is unnecessary.) If you do it like this, it should work without giving you any trouble.

You could convert the string to AnsiString (as already mentioned) if you're only going to use Ansi characters but if you want to use unicode strings AND the DLL/printer will accept them you could try something along the lines of (untested but I think it's generally corrext):
procedure myProc;
var
str: string;
buff: TBytes;
begin
str := 'blahblahblah'; // plus additional unicode stuff
buff := TEncoding.Default.GetBytes(str); // of TEncoding.UTF8 or... etc
WriteUSB(#buff[0], Length(buff));
end;
Don't know whether this will work with this particular DLL but it is a more general way of coping with the shift to unicode strings rather than having to assume (and cast to) AnsiString everywhere.

Thanks a lot for all the feedbacks. I make it work by combining your feedbacks. The solution is:
Declaration (I add cdecl):
function WriteUSB( pc:pByte;n:DWORD): integer ; cdecl; external 'my.dll';
And the call:
Procedure myProc;
Var
str : string;
buff : TBytes;
begin
str := 'My string";
buff := TEncoding.Default.GetBytes(str); // of TEncoding.UTF8 or... etc
WriteUSB(pByte(#buff[0]), Length(buff))
...
End;
I do have some problems with Swedish characters but I will solve it. Now I know that the DLL call is correct.
Thanks again for all feedback. This is a great forum.
BR
Delphi User

Try casting it with pchar in your call:
WriteUSB(pchar(str),DWORD(length(tmp)));

Related

Covert from String to SHA256

I am a beginner of delphi.
In Delphi XE2, I can use my function to convert from String to Sha256. But my function is not work in Delphi7. Are there any functions in Delphi7 for converting from String to Sha256? This is my function:
function GetSHA256Str(const str : Ansistring) : Ansistring;
begin
IdSSLOpenSSL.LoadOpenSSLLibrary;
with TIdHashSHA256.Create do
try
Result := LowerCase( HashStringAsHex(str) );
finally
Free;
end;
IdSSLOpenSSL.UnLoadOpenSSLLibrary;
end;
I had the same problem and wrote these: http://yoy.be/md5.html (It says MD5, but there's SHA256 in there as well)

How to store string in stream in Delphi 7 and restore on mobile app in XE6?

I develop a server and a mobile client that communicate over HTTP. Server is written in Delphi 7 (because it has to be compatible with old code), client is mobile application written in XE6. Server sends to client stream of data that contains strings. A problem is connected to encoding.
On the server I try to pass strings in UTF8:
//Writes string to stream
procedure TStreamWrap.WriteString(Value: string);
var
BytesCount: Longint;
UTF8: string;
begin
UTF8 := AnsiToUtf8(Value);
BytesCount := Length(UTF8);
WriteLongint(BytesCount); //It writes Longint to FStream: TStream
if BytesCount > 0 then
FStream.WriteBuffer(UTF8[1], BytesCount);
end;
As it's written in Delphi7, Value is a single byte string.
On the client I read string in UTF8 and encode it to Unicode
//Reads string from current position of stream
function TStreamWrap.ReadString: string;
var
BytesCount: Longint;
UTF8: String;
begin
BytesCount := ReadLongint;
if BytesCount = 0 then
Result := ''
else
begin
SetLength(UTF8, BytesCount);
FStream.Read(Pointer(UTF8)^, BytesCount);
Result := UTF8ToUnicodeString(UTF8);
end;
end;
But it doesn't work, when I display the string with ShowMessage the letters are wrong. So how to store string in Delphi 7 and restore it in XE6 on the mobile app? Should I add BOM at the beginning of data representing the string?
To read your UTF8 encoded string in your mobile application you use a byte array and the TEncoding class. Like this:
function TStreamWrap.ReadString: string;
var
ByteCount: Longint;
Bytes: TBytes;
begin
ByteCount := ReadLongint;
if ByteCount = 0 then
begin
Result := '';
exit;
end;
SetLength(Bytes, ByteCount);
FStream.Read(Pointer(Bytes)^, ByteCount);
Result := TEncoding.UTF8.GetString(Bytes);
end;
This code does what you need in XE6, but of course, this code will not compile in Delphi 7 because it uses TEncoding. What's more, your TStreamWrap.WriteString implementation does what you want in Delphi 7, but is broken in XE6.
Now it looks like you are using the same code base for both Delphi 7 and Delphi XE6 versions. Which means that you may need to use some conditional compilation to handle the treatment of text which differs between these versions.
Personally I would do this by following the example of TEncoding. What you need is a function that converts a native Delphi string to a UTF-8 encoded byte array, and a corresponding function in the reverse direction.
So, let's consider the string to bytes function. I cannot remember whether or not Delphi 7 has a TBytes type. I suspect not. So let us define it:
{$IFNDEF UNICODE} // definitely use a better conditional than this in real code
type
TBytes = array of Byte;
{$ENDIF}
Then we can define our function:
function StringToUTF8Bytes(const s: string): TBytes;
{$IFDEF UNICODE}
begin
Result := TEncoding.UTF8.GetBytes(s);
end;
{$ELSE}
var
UTF8: UTF8String;
begin
UTF8 := AnsiToUtf8(s);
SetLength(Result, Length(UTF8));
Move(Pointer(UTF8)^, Pointer(Result)^, Length(Result));
end;
{$ENDIF}
The function in the opposite direction should be trivial for you to produce.
Once you have the differences in handling of text encoding between the two Delphi versions encapsulated, you can then write conditional free code in the rest of your program. For example, you would code WriteString like this:
procedure TStreamWrap.WriteString(const Value: string);
var
UTF8: TBytes;
ByteCount: Longint;
begin
UTF8 := StringToUTF8Bytes(Value);
ByteCount := Length(UTF8);
WriteLongint(ByteCount);
if ByteCount > 0 then
FStream.WriteBuffer(Pointer(UTF8)^, ByteCount);
end;
Instead of
Utf8 : String;
Use
Utf8 : Utf8String;
on client. Then conversion is Automatic.
EDIT: Since the client is on a mobile platform, and Embarcadero has decided to eliminate the 8-bit strings in mobile compilers, the above won't work for this particular case. But in other cases where you have an 8-bit UTF-8 encoded string, the Utf8String can be used to seamlessly convert back and forth between UTF-8 and Unicode strings without the need to use explicit UTF-8 conversion functions. Just use it like
UnicodeStringVariable := Utf8StringVariable;
or
Utf8StringVariable := UnicodeStringVariable;
and the compiler will insert the appropriate conversion.

Why doesn't my algorithm for inverting character values work when they're stored in a Delphi XE2 ShortString?

I have the following code :
user_s.username := EnDecrypt(Edit_username.Text);
The function is the following :
function EnDeCrypt(const Value : String) : String;
var
CharIndex : integer;
begin
Result := Value;
for CharIndex := 1 to Length(Value) do
Result[CharIndex] := chr(not(ord(Value[CharIndex])));
end;
the type of variable is :
TUser = record
access: char;
username: string[25];
password: string[25];
end;
In Delphi 2007 it works, in Delphi XE2 it fails. The curious thing is that it encrypts/decrypts well the password if it is 123456789. The issue must be related with unicode and the use of shortstring. I hope there is some way to make it work in Delphi XE2 as well..
Replace String type with AnsiString, and Char with AnsiChar, and the code will work exactly as in Delphi 2007. (String[25] should not be altered, though.) Of course, you will not get Unicode support.

How to use Delphi XE's TEncoding to save Cyrillic or ShiftJis text to a file?

I'm trying to save some lines of text in a codepage different from my system's such as Cyrillic to a TFileStream using Delphi XE. However I can't find any code sample to produce those encoded file ?
I tried using the same code as TStrings.SaveToStream however I'm not sure I implemented it correctly (the WriteBom part for example) and would like to know how it would be done elsewhere. Here is my code:
FEncoding := TEncoding.GetEncoding(1251);
FFilePool := TObjectDictionary<string,TFileStream>.Create([doOwnsValues]);
//...
procedure WriteToFile(const aFile, aText: string);
var
Preamble, Buffer: TBytes;
begin
// Create the file if it doesn't exist
if not FFilePool.ContainsKey(aFile) then
begin
// Create the file
FFilePool.Add(aFile, TFileStream.Create(aFile, fmCreate));
// Write the BOM
Preamble := FEncoding.GetPreamble;
if Length(Preamble) > 0 then
FFilePool[aFile].WriteBuffer(Preamble[0], Length(Preamble));
end;
// Write to the file
Buffer := FEncoding.GetBytes(aText);
FFilePool[aFile].WriteBuffer(Buffer[0], Length(Buffer));
end;
Thanks in advance.
Not sure what example are you looking for; may be the following can help - the example converts unicode strings (SL) to ANSI Cyrillic:
procedure SaveCyrillic(SL: TStrings; Stream: TStream);
var
CyrillicEncoding: TEncoding;
begin
CyrillicEncoding := TEncoding.GetEncoding(1251);
try
SL.SaveToStream(Stream, CyrillicEncoding);
finally
CyrillicEncoding.Free;
end;
end;
If I understand it's pretty simple. Declare an AnsiString with affinity for Cyrillic 1251:
type
// The code page for ANSI-Cyrillic is 1251
CyrillicString = type AnsiString(1251);
Then assign your Unicode string to one of these:
var
UnicodeText: string;
CyrillicText: CyrillicString;
....
CyrillicText := UnicodeText;
You can then write CyrillicText to a stream in the traditional manner:
if Length(CyrillicText)>0 then
Stream.WriteBuffer(CyrillicText[1], Length(CyrillicText));
There should be no BOM for an ANSI encoded text file.

How Do I Properly Call GetLongPathName Using Delphi 2009 and Unicode Strings?

I need to change the old Win98 short path names to long path names. I had a routine that worked fine with Delphi 4, but when I upgraded to Delphi 2009 and Unicode, it didn't work with Unicode strings.
I looked around and couldn't find a Unicode-compatible version of it.
It appears that the correct routine to use is GetLongPathName from the WinAPI. But it doesn't seem to be in the SysUtils library of Delphi 2009 and I have not been able to figure out how to declare it properly to access the WinAPI routine.
Also, it does seem that it may be tricky to call, because I've read the SO Question: Delphi TPath.GetTempPath result is cropped but that didn't help me get to first base.
Can someone please explain how to declare this function and use it properly passing a Unicode string in Delphi 2009?
Sure. You do need not a separate unit and can declare GetLongPathName anywhere:
function GetLongPathName(ShortPathName: PChar; LongPathName: PChar;
cchBuffer: Integer): Integer; stdcall; external kernel32 name 'GetLongPathNameW';
function ExtractLongPathName(const ShortName: string): string;
begin
SetLength(Result, GetLongPathName(PChar(ShortName), nil, 0));
SetLength(Result, GetLongPathName(PChar(ShortName), PChar(Result), length(Result)));
end;
procedure Test;
var
ShortPath, LongPath: string;
begin
ShortPath:= ExtractShortPathName('C:\Program Files');
ShowMessage(ShortPath);
LongPath:= ExtractLongPathName(ShortPath);
ShowMessage(LongPath);
end;

Resources