Converting LTR to RTL? - c++

As you know many ui components and dev tools doesn't support rtl , we can call it flipping text , cause result is same example :
LTR
سلام salam متن راهنما word
RTL
word متن راهنما salam سلام
is there anyway to convert this LTR to RTL , i don't have any idea and language doesn't matter
Actually i am seeking for a solution to get this done in RAD Studio Firemonkey Application , as you may know firemonkey apps doesn't support rtl it's in roadmap of rad studio but not implemented yet

Under Windows, you can do that via the UniScribe API.
I've used this to convert Unicode text into set of glyphs, for our Open Source PDF writer.
You have source code sample in SynPdf.pas unit. See the TPdfWrite.AddUnicodeHexTextUniScribe method:
function TPdfWrite.AddUnicodeHexTextUniScribe(PW: PWideChar;
WinAnsiTTF: TPdfFontTrueType; NextLine: boolean; Canvas: TPdfCanvas): boolean;
var L, i,j: integer;
res: HRESULT;
max, count, numSp: integer;
Sp: PScriptPropertiesArray;
W: PWideChar;
items: array of TScriptItem;
level: array of byte;
VisualToLogical: array of integer;
psc: pointer; // opaque Uniscribe font metric cache
complex,R2L: boolean;
complexs: array of byte;
glyphs: array of TScriptVisAttr;
glyphsCount: integer;
OutGlyphs, LogClust: array of word;
procedure Append(i: Integer);
// local procedure used to add glyphs from items[i] to the PDF content stream
var L: integer;
W: PWideChar;
procedure DefaultAppend;
var tmpU: array of WideChar;
begin
SetLength(tmpU,L+1); // we need the text to be ending with #0
move(W^,tmpU[0],L*2);
AddUnicodeHexTextNoUniScribe(pointer(tmpU),WinAnsiTTF,false,Canvas);
end;
begin
L := items[i+1].iCharPos-items[i].iCharPos; // length of this shapeable item
if L=0 then
exit; // nothing to append
W := PW+items[i].iCharPos;
if not GetBit(complexs[0],i) then begin
// not complex items are rendered as fast as possible
DefaultAppend;
exit;
end;
res := ScriptShape(0,psc,W,L,max,#items[i].a,
pointer(OutGlyphs),pointer(LogClust),pointer(glyphs),glyphsCount);
case res of
E_OUTOFMEMORY: begin // max was not big enough (should never happen)
DefaultAppend;
exit;
end;
E_PENDING, USP_E_SCRIPT_NOT_IN_FONT: begin // need HDC and a selected font object
res := ScriptShape(Canvas.FDoc.GetDCWithFont(WinAnsiTTF),
psc,W,L,max,#items[i].a,
pointer(OutGlyphs),pointer(LogClust),pointer(glyphs),glyphsCount);
if res<>0 then begin // we won't change font if necessary, sorry
// we shall implement the complex technic as stated by
// http://msdn.microsoft.com/en-us/library/dd374105(v=VS.85).aspx
DefaultAppend;
exit;
end;
end;
0: ; // success -> will add glyphs just below
else exit;
end;
// add glyphs to the PDF content
// (NextLine has already been handled: not needed here)
AddGlyphs(pointer(OutGlyphs),glyphsCount,Canvas);
end;
begin
result := false; // on UniScribe error, handle as Unicode
// 1. Breaks a Unicode string into individually shapeable items
L := StrLenW(PW)+1; // include last #0
max := L+2; // should be big enough
SetLength(items,max);
count := 0;
if ScriptItemize(PW,L,max,nil,nil,pointer(items),count)<>0 then
exit; // error trying processing Glyph Shaping -> fast return
// 2. guess if requiring glyph shaping or layout
SetLength(complexs,(count shr 3)+1);
ScriptGetProperties(sP,numSp);
complex := false;
R2L := false;
for i := 0 to Count-2 do // don't need Count-1 = Terminator
if fComplex in sP^[items[i].a.eScript and (1 shl 10-1)]^.fFlags then begin
complex := true;
SetBit(complexs[0],i);
end else
if fRTL in items[i].a.fFlags then
R2L := true;
if not complex then begin
// no glyph shaping -> fast append as normal Unicode Text
if R2L then begin
// handle Right To Left but not Complex text
W := pointer(items); // there is enough temp space in items[]
W[L] := #0;
dec(L);
for i := 0 to L do
W[i] := PW[L-i];
AddUnicodeHexTextNoUniScribe(W,WinAnsiTTF,NextLine,Canvas);
result := true; // mark handled here
end;
exit;
end;
// 3. get Visual Order, i.e. how to render the content from left to right
SetLength(level,count);
for i := 0 to Count-1 do
level[i] := items[i].a.s.uBidiLevel;
SetLength(VisualToLogical,count);
if ScriptLayout(Count,pointer(level),pointer(VisualToLogical),nil)<>0 then
exit;
// 4. now we have enough information to start drawing
result := true;
if NextLine then
Canvas.MoveToNextLine; // manual NextLine handling
// 5. add glyphs for all shapeable items
max := (L*3)shr 1+32; // should be big enough - allocate only once
SetLength(glyphs,max);
SetLength(OutGlyphs,max);
SetLength(LogClust,max);
psc := nil; // cached for the same character style used
if Canvas.RightToLeftText then
// append from right to left visual order
for j := Count-2 downto 0 do // Count-2: ignore last ending item
Append(VisualToLogical[j]) else
// append from left to right visual order
for j := 0 to Count-2 do // Count-2: ignore last ending item
Append(VisualToLogical[j]);
end;
Of course, this is under Windows only. So it won't work on Mac OS X. You'll have to use another library under Mac OS X...

It's complicated. If you want to do it correctly, you must use the Bidi Library from the International Components for Unicode.

If you use MFC, here is how to set both righting direction and alignment. Assuming your CEdit control is named m_TextEdit:
void MyDialog::SetLangDirection(bool RTL)
{
DWORD w_dwStyle;
w_dwStyle = GetWindowLong(m_TextEdit.GetSafeHwnd(), GWL_EXSTYLE);
if (RTL)
{
w_dwStyle -= WS_EX_LEFT | WS_EX_LTRREADING;
w_dwStyle |= WS_EX_RIGHT | WS_EX_RTLREADING;
}
else
{
w_dwStyle -= WS_EX_RIGHT | WS_EX_RTLREADING;
w_dwStyle |= WS_EX_LEFT | WS_EX_LTRREADING;
}
SetWindowLong(m_TextEdit.GetSafeHwnd(), GWL_EXSTYLE, w_dwStyle);
}
See my tip.

Related

How do I combine FreeArc compression with Inno Setup? [duplicate]

I've been trying to make an installer by Inno Setup which only supports zip/bzip/lzma/lzma2 compression methods. I packed my archive by FreeArc (output file extension is .arc but renamed it to .bin) but Inno Setup is not able to extract it. I searched on internet how to implant arc decompression into Inno Setup but all sites refer to FreeArc official website which is dead for a while.
All I need is the code to use the necessary dll files to give Inno Setup the ability to decompress arc archives plus the list of those dll files needed to do so.
I appreciate any help.
This answer has been superseded by Inno Setup - How to add cancel button to decompressing page? that uses unarc.dll instead of driving the console Arc.exe.
I'm keeping this answer, as its concept can be useful for other archive types.
See the example below. It:
takes an ARC file, embeds it to the installer
during installation, the ARC file is extracted to a temporary folder
the files from the ARC file is extracted to the target folder
#define ArcArchive "test.arc"
[Files]
Source: {#ArcArchive}; DestDir: "{tmp}"; Flags: nocompression deleteafterinstall
Source: Arc.exe; Flags: dontcopy
[Code]
function BufferToAnsi(const Buffer: string): AnsiString;
var
W: Word;
I: Integer;
begin
SetLength(Result, Length(Buffer) * 2);
for I := 1 to Length(Buffer) do
begin
W := Ord(Buffer[I]);
Result[(I * 2)] := Chr(W shr 8); // high byte
Result[(I * 2) - 1] := Chr(Byte(W)); // low byte
end;
end;
function SetTimer(
Wnd: LongWord; IDEvent, Elapse: LongWord; TimerFunc: LongWord): LongWord;
external 'SetTimer#user32.dll stdcall';
function KillTimer(hWnd: LongWord; uIDEvent: LongWord): BOOL;
external 'KillTimer#user32.dll stdcall';
var
ProgressPage: TOutputProgressWizardPage;
ProgressFileName: string;
procedure UpdateProgressProc(
H: LongWord; Msg: LongWord; Event: LongWord; Time: LongWord);
var
S: AnsiString;
L: Integer;
P: Integer;
Max: Integer;
Progress: string;
Buffer: string;
Stream: TFileStream;
Percent: Integer;
Found: Boolean;
begin
Found := False;
if not FileExists(ProgressFileName) then
begin
Log(Format('Progress file %s does not exist', [ProgressFileName]));
end
else
begin
try
// Need shared read as the output file is locked for writting,
// so we cannot use LoadStringFromFile
Stream :=
TFileStream.Create(ProgressFileName, fmOpenRead or fmShareDenyNone);
try
L := Stream.Size;
Max := 100*2014;
if L > Max then
begin
Stream.Position := L - Max;
L := Max;
end;
SetLength(Buffer, (L div 2) + (L mod 2));
Stream.ReadBuffer(Buffer, L);
S := BufferToAnsi(Buffer);
finally
Stream.Free;
end;
if S = '' then
begin
Log(Format('Progress file %s is empty', [ProgressFileName]));
end;
except
Log(Format('Failed to read progress from file %s - %s', [
ProgressFileName, GetExceptionMessage]));
end;
end;
if S <> '' then
begin
P := Pos('Extracted', S);
if P > 0 then
begin
Log('Extraction done');
Percent := 100;
Found := True;
end
else
begin
P := Pos('%', S);
if P > 0 then
begin
repeat
Progress := Copy(S, 1, P - 1);
Delete(S, 1, P);
P := Pos('%', S);
until (P = 0);
P := Length(Progress);
while (P > 0) and
(((Progress[P] >= '0') and (Progress[P] <= '9')) or
(Progress[P] = '.')) do
begin
Dec(P);
end;
Progress := Copy(Progress, P + 1, Length(Progress) - P);
P := Pos('.', Progress);
if P > 0 then
begin
Progress := Copy(Progress, 1, P - 1);
end;
Percent := StrToInt(Progress);
Log(Format('Percent: %d', [Percent]));
Found := True;
end;
end;
end;
if not Found then
begin
Log('No new data found');
// no new progress data, at least pump the message queue
ProgressPage.SetProgress(ProgressPage.ProgressBar.Position, 100);
end
else
begin
ProgressPage.SetProgress(Percent, 100);
ProgressPage.SetText(Format('Extracted: %d%%', [Percent]), '');
end;
end;
procedure ExtractArc;
var
ArcExtracterPath: string;
ArcArchivePath: string;
TempPath: string;
CommandLine: string;
Timer: LongWord;
ResultCode: Integer;
S: AnsiString;
Message: string;
begin
ExtractTemporaryFile('Arc.exe');
ProgressPage :=
CreateOutputProgressPage('Decompression', 'Decompressing archive...');
ProgressPage.SetProgress(0, 100);
ProgressPage.Show;
try
Timer := SetTimer(0, 0, 250, CreateCallback(#UpdateProgressProc));
TempPath := ExpandConstant('{tmp}');
ArcExtracterPath := TempPath + '\Arc.exe';
ArcArchivePath := TempPath + '\{#ArcArchive}';
ProgressFileName := ExpandConstant('{tmp}\progress.txt');
Log(Format('Expecting progress in %s', [ProgressFileName]));
CommandLine :=
Format('"%s" x -y -o+ -dp"%s" "%s" > "%s"', [
ArcExtracterPath, ExpandConstant('{app}'), ArcArchivePath,
ProgressFileName]);
Log(Format('Executing: %s', [CommandLine]));
CommandLine := Format('/C "%s"', [CommandLine]);
if not Exec(ExpandConstant('{cmd}'), CommandLine, '', SW_HIDE,
ewWaitUntilTerminated, ResultCode) then
begin
RaiseException('Cannot start extracter');
end
else
if ResultCode <> 0 then
begin
LoadStringFromFile(ProgressFileName, S);
Message :=
Format('Arc extraction failed failed with code %d', [ResultCode]);
Log(Message);
Log('Output: ' + S);
RaiseException(Message);
end
else
begin
Log('Arc extraction done');
end;
finally
// Clean up
Log('Arc extraction cleanup');
KillTimer(0, Timer);
ProgressPage.Hide;
DeleteFile(ProgressFileName);
end;
Log('Arc extraction end');
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
ExtractArc;
end;
end;
The code needs arc.exe (I've taken it from PeaZip portable package).
For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library (and you need Unicode version of Inno Setup 5).
Alternatively, to avoid double extraction, you can distribute the arc file along the installer.
Just use {src} to resolve its path:
ArcArchivePath := ExpandConstant('{src}\{#ArcArchive}');
And remove the {#ArcArchive} entry from the [Files] section.
It would be more robust to implement the extraction using unarc.dll, like seen in the FreeArc+InnoSetup package ISFreeArcExtract v.4.0.rar.
Freearc Actually Comes with Inno Extraction Example
http://freearc2.azurewebsites.net/InnoSetup.aspx

FireMonkey drawing with tPath does not fill the shape

I want to use a TPathData to draw shapes and fill them with an arbitrary color. I'm using the following code, at Button1Click, wich I extracted from a sample at Embarcadero documentation:
procedure TformPathDrawing.Button1Click(Sender: TObject);
var path: TPathData;
begin
Image1.Bitmap.Canvas.Fill.Color := TAlphaColorRec.Blue;
path := TPathData.Create;
path.Data := 'M 01,00 L 02,01 L 01,02 L 00,01 L 01,00';
Image1.Bitmap.Clear ($FFFFFF);
Image1.Bitmap.Canvas.BeginScene;
Image1.Bitmap.Canvas.FillPath (path, 200);
Image1.Bitmap.Canvas.EndScene;
end;
When this code is executed, as expected, a romboid is rendered, but it is not filled up with the color set in the first command. Anyone knows what is wrong? Thanks.
Here is right code. You can find another details in FMX.Objects.pas procedure TCustomPath.UpdateCurrent and other; or just Debug TPath visual component.
{aPath - is vector path of image;
aStretch - how to draw vector data - true - stretch draw, false = fit to bitmap
aBitmap - must be created. In bitmap you should specify:
Width, Height, Bitmap.Canvas.Fill.Color, Bitmap.Canvas.Stroke.Color
and, if you need, another drawing stuff like Gradient, stroke thikness,
background texture) }
procedure TForm1.DrawVectorPath(const aPath: string; aBitmap: TBitmap; aStretch: boolean);
var
vPath: TPathData;
R: TRectF;
begin
Assert(aBitmap <> nil);
vPath := TPathData.Create;
try
vPath.Data := aPath;
aBitmap.Clear($FFFFFF);
if aStretch then
begin
R := vPath.GetBounds;
vPath.Translate(-R.Left, -R.Top);
vPath.Scale(aBitmap.Width / R.Width, aBitmap.Height / R.Height);
end
else // Fit image
begin
R := TRect.Create(0, 0, aBitmap.Width, aBitmap.Height);
vPath.FitToRect(R);
end;
aBitmap.Canvas.BeginScene;
aBitmap.Canvas.FillPath(vPath, 1);
aBitmap.Canvas.DrawPath(vPath, 1);
aBitmap.Canvas.EndScene;
finally
vPath.Free;
end;
end;
Because comments can't have code (at least not formatted). Here is my code, that works.
procedure TForm7.RadioButton6Click(Sender: TObject);
var path: TPathData;
begin
Image1.Bitmap.Canvas.Fill.Color := TAlphaColorRec.Blue;
Image1.Bitmap.Canvas.Stroke.Color := TAlphaColorRec.black;
path := TPathData.Create;
try
path.Data := 'M 01,00 L 20,01 L 10,20 L 00,10 L 01,00';
Image1.Bitmap.Clear ($FFFFFF);
Image1.Bitmap.Canvas.BeginScene;
Image1.Bitmap.Canvas.FillPath (path, 200);
Image1.Bitmap.Canvas.EndScene;
finally
path.Free;
end;
end;
Note that the stroke color is set as well. That's the only difference I can make out here.
Follows code that works (in my case):
procedure TformPathDrawing.Button1Click(Sender: TObject);
begin
Image1.Bitmap.Canvas.Fill.Color := TAlphaColorRec.Blue;
path.Clear;
path.Data := 'M 01,00 L 02,01 L 01,02 L 00,01 L 01,00';
Image1.Bitmap.Canvas.BeginScene;
Image1.Bitmap.Canvas.FillPath (path, 1);
Image1.Bitmap.Canvas.EndScene;
end;
In my code, variable path is created outside the painting code. Since path.Data; is additive, it is mandatory, before adding the current path.Data;, to place a path.Clear; statement, to clean out whatever remains into the point array. Hope this help other people.

BeginPath Textout EndPath draws inverted text

here is the code that I have in OnPaint event of my form:
int elementCount;
String tStr = L"15:00";
::BeginPath(Canvas->Handle);
::TextOut(Canvas->Handle, 5, 5, tStr.c_str(), tStr.Length());
::EndPath(Canvas->Handle);
elementCount = ::GetPath(Canvas->Handle, NULL, NULL, 0);
Canvas->Brush->Color = clBlue;
Canvas->Pen->Color = clYellow;
Canvas->Pen->Width = 4;
if(0 < elementCount)
{
boost::scoped_array<TPoint> mPoints(new TPoint[elementCount]);
boost::scoped_array<BYTE> mTypes(new BYTE[elementCount]);
::GetPath(Canvas->Handle, mPoints.get(), mTypes.get(), elementCount);
::FillPath(Canvas->Handle);
::PolyDraw(Canvas->Handle, mPoints.get(), mTypes.get(), elementCount);
}
else
::StrokeAndFillPath(Canvas->Handle);
but here is what I get on the form:
as you can see the text comes out inverted (the text has to be blue and background gray but it is the other way around and the yellow line is around the background instead of text). Does anyone know how I can fix this?
I am using C++ Builder 10 Seattle but if anyone knows that Delphi or pure C++ trick, I can work with that as well.
Thank you
This is explained in TextOut's documentation:
When the TextOut function is placed inside a path bracket, the
system generates a path for the TrueType text that includes each
character plus its character box. The region generated is the
character box minus the text, rather than the text itself. You can
obtain the region enclosed by the outline of the TrueType text by
setting the background mode to transparent before placing the
TextOut function in the path bracket. Following is sample code that demonstrates this procedure.
The below is a Delphi adaption of the mentioned sample code and your snippet, draws yellow outlined blue text:
procedure TForm1.FormPaint(Sender: TObject);
var
elementCount: Integer;
mPoints: array of TPoint;
mTypes: array of Byte;
const
tStr = '15:00';
begin
BeginPath(Canvas.Handle);
Canvas.Brush.Style := bsClear;
TextOut(Canvas.Handle, 5, 5, PChar(tStr), Length(tStr));
EndPath(Canvas.Handle);
Canvas.Brush.Color := clBlue;
Canvas.Pen.Color := clYellow;
Canvas.Pen.Width := 4;
elementCount := GetPath(Canvas.Handle, Pointer(nil)^, Pointer(nil)^, 0);
if elementCount > 0 then begin
SetLength(mPoints, elementCount);
SetLength(mTypes, elementCount);
GetPath(Canvas.Handle, mPoints[0], mTypes[0], elementCount);
Canvas.Brush.Style := bsSolid;
SelectClipPath(Canvas.Handle, RGN_AND);
Canvas.FillRect(ClientRect);
SelectClipRgn(Canvas.Handle, 0);
PolyDraw(Canvas.Handle, mPoints[0], mTypes[0], elementCount);
end else
StrokeAndFillPath(Canvas.Handle);
end;

Can anyone help me with translation of this struct to delphi record?

I need help to transcribe a struct that has another nested struct to Delphi. Below is a struct:
#define CHANNEL_TAG_LENGTH 17
struct stChannelInfo
{
char ChannelTag[CHANNEL_TAG_LENGTH]; // Tag (máx 16 caracteres)
char ChannelEnabled; // Habilitado se diferente de "0"
};
// Structure with information about channels
struct stChannel
{
int ChannelNumber; // Número de canais no buffer
struct stChannelInfo *ChannelInfo; // Buffer com informações dos canais
};
In Borland C + + 6, the example uses the following code to read the value of ChannelTag:
stChannels = this->deviceInterface->LookForAvailableChannels(EdDirectorySource->Text.c_str(), iSn, dateTimeStart, dateTimeEnd);
for(int i = 0; i < stChannels.ChannelNumber; i++)
{
CLbChannels->Items->Add(stChannels.ChannelInfo[i].ChannelTag); // Add to list the values found
}
I wish I could do the same in Delphi. How should I transcribe structs?
Thanks and sorry because English is not my native language
EDIT
I was wrong not to post what I had done on Delphi. Follow my attempt:
// record who receive the values
type stChannelInfo = record
ChannelTag : string[16];
ChannelEnabled : char ;
end;
type stChannel = record
ChannelNumber:integer; // Númber of buffer channels
ChannelInfo : ^stChannelInfo ;
end;
And so i tried to read :
Var DadosCanais : stChannel; // defined in var section of procedure onclick Button.
DadosCanais:=LookForAvailableChannels (Pwidechar(dirroot) , sn , datepickerinicial.DateTime,datepickerfinal.DateTime);
for i := 0 to (DadosCanais.ChannelNumber-1) do
begin
Showmessage(String(DadosCanais.ChannelInfo^.ChannelTag));
inc(DadosCanais.ChannelInfo);
end;
I get the record, but I can not correctly read ​​ChannelTag values. It seems that the size is incorrect, because the strings is truncated and always lose the first character of the name.
Maybe this clarify a little the question. Thanks again
SOLUTION
Following advice from Remy , i do this :
sn:=strtoint(lstdirMaquinas.Items[lstdirMaquinas.Itemindex]);
Dadoscanais := LookForAvailableChannels(PChar(dirroot) , sn , datepickerinicial.DateTime,datepickerfinal.DateTime);
for i:=0 to DadosCanais.ChannelNumber-1 do
begin
ListboxChannel.Items.add(String(DadosCanais.ChannelInfo[i].ChannelTag));
end;
For now this resolves my problem. Thanks all.
{$POINTERMATH ON}
Type
PstChannelInfo = ^stChannelInfo;
stChannelInfo = record
ChannelTag: array[0..CHANNEL_TAG_LENGTH-1] of AnsiChar; // Tag (máx 16 caracteres)
ChannelEnabled: AnsiChar; // Habilitado se diferente de "0"
end;
// Structure with information about channels
stChannel = record
ChannelNumber: Integer; // Número de canais no buffer
ChannelInfo: PstChannelInfo; // Buffer com informações dos canais
end;
stChannels := Self.deviceInterface.LookForAvailableChannels(PChar(EdDirectorySource.Text), iSn, dateTimeStart, dateTimeEnd);
for i := 0 to stChannels.ChannelNumber-1 do begin
CLbChannels.Items.Add(stChannels.ChannelInfo[i].ChannelTag); // Add to list the values found
end;
Alternatively:
Type
PstChannelInfo = ^stChannelInfo;
stChannelInfo = record
ChannelTag: array[0..CHANNEL_TAG_LENGTH-1] of AnsiChar; // Tag (máx 16 caracteres)
ChannelEnabled: AnsiChar; // Habilitado se diferente de "0"
end;
// Structure with information about channels
stChannel = record
ChannelNumber: Integer; // Número de canais no buffer
ChannelInfo: PstChannelInfo; // Buffer com informações dos canais
end;
PstChannelInfoList = ^TstChannelInfoList;
TstChannelInfoList = [0..(MaxInt div SizeOf(stChannelInfo))-1] of stChannelInfo;
stChannels := Self.deviceInterface.LookForAvailableChannels(PChar(EdDirectorySource.Text), iSn, dateTimeStart, dateTimeEnd);
for i := 0 to stChannels.ChannelNumber-1 do begin
CLbChannels.Items.Add(PstChannelInfoList(stChannels.ChannelInfo)^[i].ChannelTag); // Add to list the values found
end;

is it possible to change Caret Symbol(Text Cursor) dynamically in windows system wide?

I want to change the Caret Symbol(Text Cursor) dynamically in windows and independently of applications(system wide).
i mean this one:
but i don't know if it is possible to make such utility-tool.
only i found in google was to tweak registry to change the Caret Symbol.
but once it is changed in registry, i must restart my computer.
i do not want to restart my computer for changing Caret Symbol.
is it possible to change caret sybol in windows without restart?
It is possible, if you are using delphi.
function GetCaretPosition(var APoint: TPoint): Boolean;
var w: HWND;
aID, mID: DWORD;
begin
Result:= False;
w:= GetForegroundWindow;
if w <> 0 then
begin
aID:= GetWindowThreadProcessId(w, nil);
mID:= GetCurrentThreadid;
if aID <> mID then
begin
if AttachThreadInput(mID, aID, True) then
begin
w:= GetFocus;
if w <> 0 then
begin
Result:= GetCaretPos(APoint);
ClientToScreen(w, APoint);
end;
AttachThreadInput(mID, aID, False);
end;
end;
end;
end;
//Small demo: set cursor to active caret position
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Clear();
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
Pt: TPoint;
begin
if GetCaretPosition(Pt) then
begin
ListBox1.Items.Add(Format('Caret position %d %d', [Pt.x, Pt.y]));
// SetCursorPos(Pt.X, Pt.Y);
end;
end;
end.