Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Перехват ExtTextOut мусор или непонятная кодировка


Автор: -Сергей- 10.4.2014, 10:59
Внедряю код в процесс с целью перехватить текст, передаваемый в ExtTextOut.
Перехватываю обе функции ExtTextOutA и ExtTextOutW.
Для тестирования написал прогу, которая использует обе функции.
Функции перехватываются хорошо.
Для передачи отловленного текста использую WM_COPYDATA


ExtTextOutW перехватывается, но дело в том, что когда я сам вызываю ее в коде, текст получаю нужный, а вот контроллы которые рисуют текст, например TButton, с надписью "Кнопка" от нее я получаю непонятную кодировку "DzȕȖȗȒȈ" или текст "Button" приходит как "%XWWRQ"

C ExtTextOutA совсем все плачевно - там я получаю мусор вместе с Access violation. Вызываю я ее правильно, но перехватывается она как ExtTextOutW (в приемнике Msg.CopyDataStruct.dwData = 2) непонимаю почему.

Помогите разобраться в чем дело. Работаю в OC W7 Delphi XE5

Перехват и передача:
Код

procedure SendCopyData(hTargetWnd: HWND; ACopyDataStruct: TCopyDataStruct);
begin
  if hTargetWnd <> 0 then
    SendMessage(hTargetWnd, WM_COPYDATA, 0, Longint(@ACopyDataStruct));
end;

//Перехват ExtTextOutA 
function xSendtA(DC: HDC; X, Y: Integer; Options: LongWord; Rect: PRect; Str: LPCSTR; Count: LongWord; Dx: PInteger): Bool; stdcall;
var
  CopyDataStruct: TCopyDataStruct;
  hRecieverWnd: HWND;
begin
  Form1.Memo1.Lines.Add(Str);
  OutStr := PWideChar(((UnicodeString(Str))));

  with CopyDataStruct do
  begin
    dwData := 1; //  1-текст Ansi
    cbData := StrLen(Str) * SizeOf(PAnsiChar);  // длина данных +1 с учетом #0 (terminating)
    lpData := Str;
  end;
   hRecieverWnd := FindWindow('TMsgRcvr', nil);
  // Передаем нашу структуру приемнику
  SendCopyData(hRecieverWnd, CopyDataStruct);

   result := _pOldExtTextOutA(DC, X, Y, Options, Rect, Str, Count, Dx);
end;

//Перехват ExtTextOutW
function xSendtW(DC: HDC; X, Y: Integer; Options: LongWord; Rect: PRect; Str: LPCWSTR; Count: LongWord; Dx: PInteger): Bool; stdcall;
var
  CopyDataStruct: TCopyDataStruct;
  hRecieverWnd: HWND;
begin
  with CopyDataStruct do
  begin
    dwData := 2; //  2-текст Wide
   cbData := StrLen(Str) * SizeOf(PWideChar);
   lpData := Str;
  end;
   hRecieverWnd := FindWindow('TMsgRcvr', nil);
  // Передаем нашу структуру приемнику
  SendCopyData(hRecieverWnd, CopyDataStruct);
  result := _pOldExtTextOutW(DC, X, Y, Options, Rect, Str, Count, Dx);
end;


Принимающая сторона:
Код

procedure TMsgRcvr.WMCopyData(var Msg: TWMCopyData);

function StrLCopyA(Dest: PAnsiChar; const Source: PAnsiChar; MaxLen: Cardinal): PAnsiChar;
var
  Len: Cardinal;
begin
  Result := Dest;
  Len := StrLen(Source);
  if Len > MaxLen then
    Len := MaxLen;
  Move(Source^, Dest^, Len * SizeOf(AnsiChar));
  Dest[Len] := #0;
end;

function StrLCopyW(Dest: PWideChar; const Source: PWideChar; MaxLen: Cardinal): PWideChar;
var
  Len: Cardinal;

begin
  Result := Dest;
  Len := StrLen(Source);
  if Len > MaxLen then
    Len := MaxLen;
  Move(Source^, Dest^, Len * SizeOf(WideChar));
  Dest[Len] := #0;

end;

 var
   sTextA: array[0..260] of AnsiChar;
   sTextW: array[0..260] of WideChar;
 begin

  case Msg.CopyDataStruct.dwData of
    1:
      begin
        Memo1.Lines.Add('Message type 1');
        StrLCopyA(sTextA, PAnsiChar(Msg.CopyDataStruct.lpData), Msg.CopyDataStruct.cbData);
        Memo1.Lines.Add(sTextA);
      end;
    2:
      begin
        Memo1.Lines.Add('Message type 2');
        StrLCopyW(sTextW, PWideChar(Msg.CopyDataStruct.lpData), Msg.CopyDataStruct.cbData);
        Memo1.Lines.Add(sTextW);
      end;
  end;
 end;



Автор: CynicRus 10.4.2014, 14:37
Хм, можно попробовать проверить Options на:
Код

if Options and ETO_GLYPH_INDEX = ETO_GLYPH_INDEX then
...


ну и если оно соответствует, то декодировать это.

Пример разбора на C++
Код


 if ( fuOptions & ETO_GLYPH_INDEX )
{
LPGLYPHSET ranges;
WCHAR * allChars, * ptr, * restoredString;
WORD * allIndices;
unsigned x;
// Here we have to decode glyph indices back to chars. We do this
// by tedious and ineffective iteration.
//
ranges = malloc( GetFontUnicodeRanges( hdc, 0 ) );
GetFontUnicodeRanges( hdc, ranges );
// Render up all available chars into one ridiculously big string
allChars = malloc( ( ranges->cGlyphsSupported ) * sizeof( WCHAR ) );
allIndices = malloc( ( ranges->cGlyphsSupported ) * sizeof( WORD ) );
ptr = allChars;
for( x = 0; x < ranges->cRanges; ++x )
{
WCHAR c = ranges->ranges[ x ].wcLow;
unsigned y = ranges->ranges[ x ].cGlyphs;
while( y-- )
*ptr++ = c++;
}
// Amazing. Now get glyph indices for this one nice string.
GetGlyphIndicesW( hdc, allChars, ranges->cGlyphsSupported, allIndices,
GGI_MARK_NONEXISTING_GLYPHS );
// Fascinating. Now translate our original input string back into
// its readable form.
restoredString = malloc( cbString * sizeof( WCHAR ) );
for( x = 0; x < cbString; ++x )
{
unsigned y;
WORD idx = lpszString[ x ];
for( y = 0; y < ranges->cGlyphsSupported; ++y )
if ( allIndices[ y ] == idx )
{
restoredString[ x ] = allChars[ y ];
break;
}
if ( y == ranges->cGlyphsSupported )
{
// Not found
restoredString[ x ] = L'?';
}
}
// And we're done.
free( allIndices );
free( allChars );
free( ranges );


Автор: -Сергей- 14.4.2014, 05:13
Спасибо за пример!
Я не сильный знаток С, кое-что понимаю, кое-что не совсем.
Как на паскале будет это:
Код

while( y-- ) 
*ptr++ = c++; 


Так что ли?
Код

//for z := y downto 0 do
while z >=0 do
begin
  Inc(ptr);
  Inc(c);
  ptr := c;
  Dec(z)
end;


И здесь непонятно
Код

if ( allIndices[ y ] == idx )

allIndices это же не массив, а WORD

Автор: CynicRus 14.4.2014, 07:39
Цитата(-Сергей- @ 14.4.2014,  05:13)
Спасибо за пример!
Я не сильный знаток С, кое-что понимаю, кое-что не совсем.
Как на паскале будет это:
Код

while( y-- ) 
*ptr++ = c++; 


Так что ли?
Код

//for z := y downto 0 do
while z >=0 do
begin
  Inc(ptr);
  Inc(c);
  ptr := c;
  Dec(z)
end;


И здесь непонятно
Код

if ( allIndices[ y ] == idx )

allIndices это же не массив, а WORD

allIndices - указатель на Word массив же.

В случае с циклом - да, похоже на то.

Автор: -Сергей- 22.4.2014, 08:16
Перевел на Delphi, может кому пригодится.
Код

function ConvertGlyphIdxToString(Str: PWideChar; DC: HDC): PWideChar; stdcall;
var
  ranges: PGLYPHSET;
  restoredString: PWideChar;
  allChars : array of WCHAR;
  allIndices: array of WORD;
  x, y : cardinal;
  idx : WORD;
  Count: LongWord;
begin
  Result:= '';

  Count := Length(Str);

  ranges := AllocMem(GetFontUnicodeRanges(DC, nil));
  GetFontUnicodeRanges(DC, ranges);

  SetLength(allChars, ranges.cGlyphsSupported * sizeof(PWideChar));
  SetLength(allIndices, ranges.cGlyphsSupported * SizeOf(Word));

  for x := 0 to (ranges.cGlyphsSupported * sizeof(PWideChar)) -1 do
    allChars[x] := WCHAR(x);

  if GetGlyphIndicesW(DC, Pointer(allChars), ranges.cGlyphsSupported, Pointer(allIndices),
  GGI_MARK_NONEXISTING_GLYPHS ) = GDI_ERROR then exit;

  restoredString := AllocMem(Count * sizeof(PWideChar));

  for x := 0 to Count -1 do
  begin
    idx := Ord(Str[x]);
    for y := 0 to ranges.cGlyphsSupported -1 do
    begin
      if allIndices[y] = idx then
      begin
        restoredString[x] := allChars[y];
        Break;
      end;
      if  y = ranges.cGlyphsSupported then
      begin
        // Not found
        restoredString[ x ] := '?';
      end;
    end;
  end;

  Result := PWideChar(Copy(restoredString, 0, Count));

  SysFreeMem(ranges);
  SysFreeMem(allChars);
  SysFreeMem(@allIndices);
  SysFreeMem(restoredString);
end;

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)