Lazarus

Free Pascal => Windows => Topic started by: Nitorami on March 10, 2023, 09:29:00 am

Title: GetConsoleTitle with pchar as argument ?
Post by: Nitorami on March 10, 2023, 09:29:00 am
GetConsoleTitle works with a shortstring as parameter but not with a pchar. Why ?

Code: Pascal  [Select][+][-]
  1. {$H-}
  2. uses windows;
  3.  
  4. var s: shortstring;
  5.     p: pchar;
  6. begin
  7.   writeln;
  8.   s :=  '##########################################################################';
  9.   writeln ('GetTitle returns ',GetConsoleTitle (@s[1],50));  //works
  10.   writeln (s+LineEnding);
  11.  
  12.   p :=  '##########################################################################';
  13.   writeln ('GetTitle returns ',GetConsoleTitle (p,50));      //doesn't work
  14.   writeln (p+LineEnding);
  15.   writeln ('GetTitle returns ',GetConsoleTitle (@p[0],50));  //doesn't either
  16.   writeln (p+LineEnding);
  17. end.
  18.  
  19.  

Output:
Running "c:\temp\getconsoletitle.exe "

GetTitle returns 24
Eingabeaufforderung - fp #################################################

GetTitle returns 0
##########################################################################

GetTitle returns 0
##########################################################################


Title: Re: GetConsoleTitle with pchar as argument ?
Post by: Red_prig on March 10, 2023, 10:08:28 am
Because pchar is a reference to data, first allocate the memory and put the reference in p, then when the data is no longer needed, free it accordingly
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: Nitorami on March 10, 2023, 10:10:20 am
Understood, but I initialised p with p := '####....', so it must have been allocated at a speciifc address ?
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: Red_prig on March 10, 2023, 10:11:36 am
'####....' is a constant and is write-protected
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: Nitorami on March 10, 2023, 10:31:23 am
You mean the content of pchar is stored in a specific write-protected area of memory which the API function cannot write to ?
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: Red_prig on March 10, 2023, 10:46:24 am
correct
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: JuhaManninen on March 10, 2023, 01:44:53 pm
Yes, memory must be allocated for PChar with StrAlloc(). Later it should be freed with StrDispose().
ShortString is a good type if the Title does not exceed 255 characters.
AnsiString can be used, but you must set its length first. After that it is freed automatically.
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: Nitorami on March 10, 2023, 02:57:21 pm
Thank you, that enlightens me. Maybe you can help me a bit further. I try to copy text to the windows clipboard from within a Console. I got it working in principle:


This seems to work, but I wonder if I could avoid the extra memory allocation via Global Alloc. I would need a memory handle for my own Pascal structure but cannot see a way how to do this.[/list]
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: JuhaManninen on March 10, 2023, 03:44:13 pm
I can only recommend the LCL unit Clipbrd and class TClipboard.
I don't use Windows myself and don't know much about WinAPI programming.
LCL pulls in a lot of code, thus in your console program you may want to avoid it.
If one GlobalAlloc is all you need for the task, it sounds like a reasonable addition.
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: Nitorami on March 10, 2023, 03:56:47 pm
Agreed it would be easier via Lazarus and the LCL. But I still use the textmode IDE because compliation speed is unsurpassed (unless the windows virusscanner intervenes). I often write small throw-away programs during my work, and the Strg-F9 button starts to wear out...

Can I use the LCL from within a Console program ? Rather not, I guess ?
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: JuhaManninen on March 11, 2023, 08:54:59 am
Can I use the LCL from within a Console program ? Rather not, I guess ?
You can but it may be difficult without the Lazarus IDE.
LCL has a "NoGUI" widgetset which does not call any real widgetset. For example the LazBuild console program uses it.
The same LazBuild can compile and build projects made with Lazarus. You could modify such a project with a textmode IDE and then run LazBuild. Compilation would not be super-fast any more because LCL pulls in lots of code.
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: Raskaton on July 03, 2023, 08:16:05 pm
Last time I use this:
Code: Pascal  [Select][+][-]
  1. uses
  2.   SysUtils;
  3. var
  4.   s: ShortString;
  5.   buf, p: PChar;
  6.   len: integer;
  7. begin
  8.   buf := StrAlloc(250); //temp buffer
  9.   len := GetConsoleTitle(buf,250);
  10.   //if len=0 then {Alloc to 'buf' more and repeat Get}
  11.  
  12.   p := StrAlloc(len);  //alloc real size
  13.   p := StrLCopy(p, buf, len); //get real data
  14.   StrDispose(buf);  //free buffer
  15.  
  16.   s := StrPas(p);
  17.  
  18.   WriteLn('Title = ', p); //automatic typecast will be done Alloc, StrPas and Dispose by ref
  19.   WriteLn('Len = ', StrLen(p));  //#0 not included
  20.   StrDispose(p);
  21.  
  22.   WriteLn('Title = ', s);
  23.   WriteLn('Len = ', Length(s));
  24. end;
  25.  
PS not tested
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: ASerge on July 03, 2023, 09:55:23 pm
PS not tested
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2. {$APPTYPE CONSOLE}
  3.  
  4. uses Windows;
  5.  
  6. var
  7.   Dummy: UnicodeChar;
  8.   Count: DWORD;
  9.   U: UnicodeString;
  10. begin
  11.   // GetConsoleTitleW expects the size in bytes, not in chars, but returns in chars
  12.   // The documentation is incorrect about this
  13.   // The size cannot be zero
  14.   Count := GetConsoleTitleW(@Dummy, SizeOf(Dummy));
  15.   SetString(U, nil, Count);
  16.   GetConsoleTitleW(Pointer(U), (Count + 1) * SizeOf(UnicodeChar)); // + #0
  17.   Writeln(U);
  18.   Readln;
  19. end.
Tested.
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: KodeZwerg on July 04, 2023, 09:23:32 am
Code: Pascal  [Select][+][-]
  1. var
  2.   Count: DWORD;
  3.   W: array[0..MAX_PATH -1] of WideChar;
  4. begin
  5.   Count := GetConsoleTitleW(@W, SizeOf(W));
  6.   W[Count] := #0;
  7.   Writeln(string(W));
  8.   Readln;
  9. end.
or
Code: Pascal  [Select][+][-]
  1. var
  2.   W: array[0..MAX_PATH -1] of WideChar;
  3. begin
  4.   FillChar(W, SizeOf(W), #0);
  5.   GetConsoleTitleW(@W, SizeOf(W));
  6.   Writeln(string(W));
  7.   Readln;
  8. end.
or
Code: Pascal  [Select][+][-]
  1. {$IF Defined(FPC) and Declared(FPC_VERSION) and (FPC_VERSION >= 3)}
  2.   {$DEFINE UniCode} { FreePascal UniCode support }
  3. {$IFEND}
  4.  
  5. function GetConsoleOriginalTitleA(lpConsoleTitle: LPTSTR; nSize: DWORD): DWORD;
  6.   external 'kernel32.dll' name 'GetConsoleOriginalTitleA';
  7.  
  8. function GetConsoleOriginalTitleW(lpConsoleTitle: LPWSTR; nSize: DWORD): DWORD;
  9.   external 'kernel32.dll' name 'GetConsoleOriginalTitleW';
  10.  
  11. function GetConsoleOriginalTitle(lpConsoleTitle: {$IF Defined(UniCode)}LPWSTR{$ELSE}LPTSTR{$ENDIF}; nSize: DWORD): DWORD;
  12.   external 'kernel32.dll' name 'GetConsoleOriginalTitle' + {$IF Defined(UniCode)}'W'{$ELSE}'A'{$ENDIF};
  13.  
  14. var
  15.   W: array[0..MAX_PATH -1] of {$IF Defined(UniCode)}WideChar{$ELSE}AnsiChar{$ENDIF};
  16. begin
  17.   FillChar(W, Length(W) * SizeOf({$IF Defined(UniCode)}WideChar{$ELSE}AnsiChar{$ENDIF}), #0);
  18.   GetConsoleOriginalTitle(@W, SizeOf(W));
  19.   Writeln({$IF Defined(UniCode)}PWideChar{$ELSE}PAnsiChar{$ENDIF}(W));
  20.   Readln;
  21. end.
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: KodeZwerg on July 04, 2023, 12:40:39 pm
Thank you, that enlightens me. Maybe you can help me a bit further. I try to copy text to the windows clipboard from within a Console. I got it working in principle:

  • First, get a window handle to the Console via GetConsoleTitle and FindWindow().
  • Using this handle, I can now OpenClipBoard() and EmptyClipBoard().
  • Then, use GlobalAlloc to reserve a memory region; it returns a handle, not a pointer. I can pass this handle to SetClipboardData() to copy the associated memory to the clipboard.
  • But to get something into this memory first, I need its address. I can get a pointer via GlobalLock (handle), and can now move() my own memory content (e.g. the string) to this memory.

This seems to work, but I wonder if I could avoid the extra memory allocation via Global Alloc. I would need a memory handle for my own Pascal structure but cannot see a way how to do this.[/list]
This is how I would do.
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. uses
  6.   Windows, SysUtils;
  7.  
  8. function GetTitle(Wnd: HWND): String;
  9. var
  10.  lpch: String;
  11.  len: DWORD;
  12. begin
  13.  len := GetWindowTextLength(Wnd);
  14.  SetLength(lpch, len);
  15.  GetWindowText(Wnd, PChar(lpch), Length(lpch) + 1);
  16.  Result := lpch;
  17. end;
  18.  
  19. function GetClipboardText(Wnd: HWND; var Str: string): Boolean;
  20. var
  21.  hData: HGlobal;
  22. begin
  23.  Result:=True;
  24.  if OpenClipboard(Wnd) then begin
  25.   try
  26.    hData:=GetClipboardData(CF_TEXT);
  27.    if hData<>0 then begin
  28.     try
  29.      SetString(Str, PChar(GlobalLock(hData)), GlobalSize(hData));
  30.     finally
  31.      GlobalUnlock(hData);
  32.     end;
  33.    end else Result:=False;
  34.    Str:=PChar(@Str[1]);
  35.   finally
  36.    CloseClipboard;
  37.   end;
  38.  end else Result:=False;
  39. end;
  40.  
  41. function SetClipboardText(Wnd: HWND; Value: string): Boolean;
  42. var
  43.  hData: HGlobal;
  44.  pData: pointer;
  45.  Len: integer;
  46. begin
  47.  Result:=True;
  48.  if OpenClipboard(Wnd) then begin
  49.   try
  50.    Len:=Length(Value)+1;
  51.    hData:=GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE, Len);
  52.    try
  53.     pData:=GlobalLock(hData);
  54.     try
  55.      Move(PChar(Value)^, pData^, Len);
  56.      EmptyClipboard;
  57.      SetClipboardData(CF_Text, hData);
  58.     finally
  59.      GlobalUnlock(hData);
  60.     end;
  61.    except
  62.     GlobalFree(hData);
  63.     raise
  64.    end;
  65.   finally
  66.    CloseClipboard;
  67.   end;
  68.  end else Result:=False;
  69. end;
  70.  
  71. var
  72.   s, ss: string;
  73.   wnd: HWND;
  74. begin
  75.   wnd := GetForegroundWindow;
  76.   ss := GetTitle(wnd);
  77.   WriteLn('Window Caption: ', ss);
  78.   s := '';
  79.   if SetClipboardText(wnd, ss) then
  80.     begin
  81.       WriteLn('Copy to clipboard success.');
  82.       if GetClipboardText(wnd, s) then
  83.         begin
  84.           WriteLn('Text from clipboard: ', s);
  85.         end
  86.         else
  87.           WriteLn('Unabled to get text from clipboard.');
  88.     end
  89.     else
  90.       WriteLn('Copy to clipboard failed.');
  91.   ReadLn;
  92. end.
I've missed a lot of pre-checkings, just a prototype to demonstrate.
Title: Re: GetConsoleTitle with pchar as argument ?
Post by: ASerge on July 04, 2023, 08:45:17 pm
Code: Pascal  [Select][+][-]
  1. var
  2.   Count: DWORD;
  3.   W: array[0..MAX_PATH -1] of WideChar;
  4. begin
  5.   Count := GetConsoleTitleW(@W, SizeOf(W));
  6.   W[Count] := #0;
  7.   Writeln(string(W));
  8.   Readln;
  9. end.
Unsafe. If the title length is longer than the buffer "W[Count] := #0" will mess up the program stack.
TinyPortal © 2005-2018