CharPrinter in ZEBRA Printer XE7 - delphi

I used the CharPrinter.pas unit to send commands in ZPLII for a ZEBRA printer in Delphi RAD2007 and everything worked well, but I change to XE7 and tried to use the same functions and sending to the printer and does not work, and does not give any error message. Does it have to do the new data type versions XE ?

This happened cause Delphi switched to Unicode strings in Delphi 2009.
Unicode strings use 2 bytes for every char. Older Delphi versions used 1 byte for every char.
So you must make some changes in ChatPrinter.pas.
Try to edit this (not tested):
procedure TCharPrinter.SendData (aData : String);
var
Data : array[0..255] of char;
cnt : integer;
ss : TStringStream;
begin
try
ss := TStringStream.Create(aData,TEncoding.ANSI);
fStream.CopyFrom (ss,0);
finally
ss.Free;
end;
// for cnt := 0 to length(aData) - 1
// do Data[cnt] := aData[cnt+1];
end;
Or simpler:
procedure TCharPrinter.SendData (aData : String);
var
Data : AnsiString;
begin
Data := AnsiString(aData);
fStream.Write(PAnsiChar(Data)^, Length(Data));
end;

Related

TStringStream issue in Japanese localization

I have the following setup:
- Windows system language is English.
- I use Delphi 10.1 Berlin.
- In Windows Region & Language/Country set to Japan.
- Region/Administrative/Language for non-Unicode programs set to Japanese (Japan).
I have implemented communication client/server using strings.
Let's skip the question 'why not bytes' for now. I want to show the issue and find the reason why.
I write 2 things into TStringStream:
Header: which includes its size of Int64 (8 bytes), object size of Int64 (8 bytes) and class name: Header length - 2*SizeOf(Int64).
object (TComponent descendant)
procedure ComponentToStream(AComponent: TComponent; AStream: TStream; out HL,OL: Int64);
var
CN: TBytes;
MS1: TMemoryStream;
begin
MS1 := TMemoryStream.Create;
try
CN := TEncoding.Unicode.GetBytes(AComponent.ClassName);
SaveComponentToStream(MS1, AComponent);
OL := MS1.Size;
MS1.Position := 0;
HL := SizeOf(HL) + SizeOf(OL) + Length(CN);
AStream.Write(HL,SizeOf(HL));
AStream.Write(OL,SizeOf(OL));
AStream.Write(CN[0], Length(CN));
MS1.SaveToStream(AStream);
finally
FreeAndNil(MS1);
end;
end;
function PrepareDataBeforeSend(Component: TComponent): string;
var
HL, OL: Int64;
SS: TStringStream;
begin
SS := TStringStream.Create('', TEncoding.Unicode);
try
ComponentToStream(Component, SS, HL, OL);
Result := SS.DataString;
SS.SaveToFile('Orginal stream data.debug');
finally
FreeAndNil(SS);
end;
The result of this method saved in file here
click.
To verify the data I used code below right after calling of one above.
SS := TStringStream.Create({PrepareDataBeforeSend result}, TEncoding.Unicode);
SS.SaveToFile('New stream data.debug');
SS.Free;
Saved binary can be found here Click
And now 2 problems:
If I don't specify explicitly TEncoding.Unicode encoding in constructor of TStringStream, then TEncoding.Default will be used. But for Japanese code page it is ANSII and for English it is Unicode. As a result object size I read later
SS.Read(OL, SizeOf(OL));
is wrong.
Here's the binary to compare. See 8-15 bytes Click
OK, issue 1 was resolved, but still the binary I saved for verification does not match the original one: there is 1 byte missing at the end.
Can anyone tell where is a problem?
Important: there is no issues if I have English localization!!

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.

Delphi 2010 variant to unicode problem

I am working on a DLL in Delphi 2010. It exports a procedure that receives an array of variants. I want to be able to take one of these variants, and convert it into a string, but I keep getting ?????
I cannot change the input variable - it HAS to be an array of variants.
The host app that calls the DLL cannot be changed. It is written in Delphi 2006.
Sample DLL code:
Procedure TestArr(ArrUID : array of variant); stdcall;
var
i: integer;
s: string;
begin
s:= string(String(Arruid[0]));
showmessage(s);
end;
Using D2006 my DLL works fine. I have tried using VartoStr - no luck. When I check the VarType I am getting a varString. Any suggestions how to fix this?
You host application is sending an AnsiString and you dll is expecting a UnicodeString.
Unicode strings was introduced in Delphi 2009, it does not exist in Delphi 2006. How to fix it? Try [untested]:
Procedure TestArr(ArrUID : array of variant); stdcall;
var
i: integer;
s: AnsiString;
begin
s:= Ansistring(VarToStr(Arruid[0]));
showmessage(s);
end;
or maybe [also untested]:
Procedure TestArr(ArrUID : array of variant); stdcall;
var
i: integer;
s: AnsiString;
begin
s:= Ansistring(AnsiString(Arruid[0]));
showmessage(s);
end;
You can also check if theres is a function Like VarToStr that accepts AnsiStrings (maybe in the AnsiStrings unit?).
1/ How have you call the VarToStr() function ? VarToString(Arruid[0]) ?
2/ Does your Delphi2006 Application send AnsiString or WideString to the DLL ?
If so, and if (1) is not working, try to cast to AnsiString instead of string.

how to retrieve text from website text boxes

how can i retrieve text from a web sites text boxes in delphi for example suppose i type
''tiger'' in google's search box how do i retrieve text from that search box would wm_gettext or getwindowtext work?
i am using delphi 7
try this code.
Only works with internet explorer. tested in Windows Vista,IE8 y Delphi 2007.
Uses
SHDocVw,
mshtml;
procedure GetTextFromEditIExplorer(ListStr: TStringList);
var
ShellWindow : IShellWindows;
Web_Browser : IWebbrowser2;
reg_Shell_window : IDispatch;
Dummy : IHTMLDocument2;
ovElements : OleVariant;
Document : Variant;
WindowsCount : Integer;
ElementsCount : Integer;
FormsCount : Integer;
begin
ShellWindow := CoShellWindows.Create; //Provides access to the collection of open Shell windows
for WindowsCount := 0 to ShellWindow.Count do //iterate through number of windows in the Shell windows collection
begin
reg_Shell_window := ShellWindow.Item(WindowsCount); //Returns the registered Shell window for a specified index.
if reg_Shell_window = nil then Continue; //go to next reg window
reg_Shell_window.QueryInterface(iWebBrowser2, Web_Browser); // determines if an interface can be used with an object
if Web_Browser <> nil then
begin
Web_Browser.Document.QueryInterface(IHTMLDocument2, Dummy);
if Dummy <> nil then
begin
Web_Browser := ShellWindow.Item(WindowsCount) as IWebbrowser2;
Document := Web_Browser;
for FormsCount := 0 to Document.forms.Length - 1 do
begin
ovElements := Document.forms.Item(FormsCount).elements;
for ElementsCount := 0 to ovElements.Length - 1 do
begin
try
if (CompareText(ovElements.item(ElementsCount).tagName, 'INPUT') = 0) and (CompareText(ovElements.item(ElementsCount).type, 'text') = 0) then
ListStr.Add('Control Name ['+ovElements.item(ElementsCount).Name+']'+' Type -> '+ovElements.item(ElementsCount).Type+' -> Value ['+ovElements.item(ElementsCount).Value+']');
except
ListStr.Add('Error Reading element n° '+IntToStr(ElementsCount));
end;
end;
end;
end;
end;
end;
end;
procedure TForm1.btn1Click(Sender: TObject);
var
List : TStringList;
begin
List:=TStringList.Create;
GetTextFromEditIExplorer(List);
ShowMessage(List.Text);
end;
Edit :
Omar unfortunately there is no simple solution to your problem. This is because each browser uses a different interface to interact with the information.
Here are a couple of suggestions
Firefox uses XPCOM, you can research about that.
Try using DDE (dynamic data exchange).
You can use WatiN, is a .net library wich work with firefox and iexplorer. you can see this article to learn how interact with a .net Assembly in delphi win32.
Bye.
The simplest way would be by using a regular expression on the original HTML code from the page.

File MD5 checksum

In this question is mentioned the wcrypt2.
What I need is simply calculate the MD5 of a file. It would be perfect if I could calculate it without having to save it because it is a downloaded file in stream format.
I would like to have the most straightforward way to do that.
Thanks!
Here is a working code for Indy 10:
function MD5File(const FileName: string): string;
var
IdMD5: TIdHashMessageDigest5;
FS: TFileStream;
begin
IdMD5 := TIdHashMessageDigest5.Create;
FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Result := IdMD5.HashStreamAsHex(FS)
finally
FS.Free;
IdMD5.Free;
end;
end;
Regards,
OscaR1
Based on #dummzeuch answere I wrote this function:
function getMD5checksum(s: TStream): string;
var
md5: TIdHashMessageDigest5;
hash : T4x4LongWordRecord;
begin
md5 := TIdHashMessageDigest5.Create;
s.Seek(0,0);
hash := md5.HashValue(s);
result := IntToHex(Integer(hash[0]), 4) +
IntToHex(Integer(hash[1]), 4) +
IntToHex(Integer(hash[2]), 4) +
IntToHex(Integer(hash[3]), 4);
end;
Indy comes with functions for calculating several hashes, MD5 is one of them. Indy is included in all versions of Delphi since at least Delphi 2006 and available as a free download for older versions.
What about:
function GetFileMD5(const Stream: TStream): String; overload;
var MD5: TIdHashMessageDigest5;
begin
MD5 := TIdHashMessageDigest5.Create;
try
Result := MD5.HashStreamAsHex(Stream);
finally
MD5.Free;
end;
end;
function GetFileMD5(const Filename: String): String; overload;
var FileStream: TFileStream;
begin
FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Result := GetFileMD5(FileStream);
finally
FileStream.Free;
end;
end;
As you mentioned, the post you linked to talks about wcrypt2, which is a library of cryptographic routines, including MD5. The post you linked to also seems to indicate that it is available for Delphi 7 since the asker includes output labeled "Delphi 7." You have tagged this question delphi7, so I assume that's the version you're using, too. So what's stopping you from using wcrypt2?
The question links to a copy of wcrypt2.pas, and the copyright dates in that file appear to indicate that the unit was available by the time Delphi 7 was released. Check your installation; you might already have it. If not, then the unit also says that it was obtained via Project Jedi, so you could try looking there for the unit as well.
The answers to your referenced question include example Delphi code and the names of units that come with Delphi for doing MD5. They come with Delphi 2009, so you should check whether they're also available for your version.
Take a look at this implementation of MD5SUM in Delphi. It requires a string for input, but I imagine you can easily make it work with a stream.
MessageDigest_5 would work for this as well.
I use the following function in Delphi 7 with Indy 10.1.5
uses IdHashMessageDigest, idHash, Classes;
...
function cc_MD5File(const p_fileName : string) : string;
//returns MD5 has for a file
var
v_idmd5 : TIdHashMessageDigest5;
v_fs : TFileStream;
v_hash : T4x4LongWordRecord;
begin
v_idmd5 := TIdHashMessageDigest5.Create;
v_fs := TFileStream.Create(p_fileName, fmOpenRead OR fmShareDenyWrite) ;
try
v_hash := v_idmd5.HashValue(v_fs);
result := v_idmd5.AsHex(v_hash);
finally
v_fs.Free;
v_idmd5.Free;
end;
end;
If you use Overbyte http://www.overbyte.eu/frame_index.html just add unit and call function FileMD5 with name of file
uses OverbyteIcsMd5;
....
function GetMd5File:String;
begin
Result := FileMD5(FileName);
end;

Resources