ControlGetHandle in AutoIT - autoit

Can anyone tell me what ControlGetHandle() does behind the scenes? What Windows API function does it invoke? How can I see it? (logs/debug mode).
It sometimes succeeds and sometimes fails and I don't understand why. I looked all over the place, including AutoIT .au3 include files, but I couldn't find any information.

So, I discovered this amazing tool called "API Monitor". It shows you API calls made to the OS. You can filter etc. When running AutoIT with "ControlGetHandle" you can see that it actually calls two functions:
EnumWindows
EnumChildWindows
With the relevant parameters to get the handle you wish.
Thanks!

I believe it uses GetDlgCtrlID among other things. If you are having trouble getting it to return a handle sometimes changing the controlID parameter will fix it. Also, make sure you are waiting for the control to load first. If the control exists and you are using the right controlID parameters AutoIt will be able to get a controls handle 99.9999% of the time.

The first thing that comes to mind, the function finds the window with matching caption, lists the controls, finds the control with suitable criteria( class name and text), and returns his HWnd. This is done using the API EnumWindows/GetWindowTextLength/GetWindowText,GetWindowClassName.
Here, I wrote a small example, but it is in Pascal ( Excuse me. later rewritten in AutoIt. ;) ;) ;)
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
fhw:hwnd;
cls,txt:string;
wind:hwnd;
implementation
{$R *.dfm}
function GetText(wnd:hwnd):string;
var
len:integer;
begin
len:=GetWindowTextLength(wnd)+1;
SetLength(result,len);
SetLength(Result,GetWindowText(wnd,pchar(result),len));
end;
function GetClsName(wnd:hwnd):string;
begin
SetLength(result,5000);
SetLength(result,GetClassName(wnd,pchar(result),5000));
end;
function EnumChildProc(wnd:HWnd; param:Integer):bool;stdcall;
var
wintext,wincls:string;
ccmp,tcmp:boolean;
begin
wintext:=gettext(wnd);
wincls:=getclsname(wnd);
if cls <> '' then
ccmp:=(comparetext(cls,wincls)=0)
else
ccmp:=true;
if txt <> '' then
tcmp:=(comparetext(txt,wintext)=0)
else
tcmp:=true;
result:=not (tcmp and ccmp);
if not result then
wind:=wnd;
end;
procedure GetControlHandle(title:string; wtext:string; clsname:string);
var
hw:hwnd;
begin
wind:=0;
hw:=findwindow(nil,pchar(title));
if hw <> 0 then
begin
cls:=clsname;
txt:=wtext;
EnumChildWindows(hw,#EnumChildProc,integer(pointer(result)));
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
w:hwnd;
begin
getcontrolhandle('New Project','','Button');
w:=wind;
CloseWindow(w);
end;
end.

Related

Ada: Manipulate a private type

I'm kinda new with Ada and recently got an error that I don't seem to know how to solve.
I have the following code:
data.ads
with Text_IO; use text_io;
with ada.Integer_Text_IO; use ada.Integer_Text_IO;
package data is
type file is private;
type file_set is array (Integer range <>) of file;
procedure file_Print (T : in out file); --Not used
private
type file is record
start, deadline : integer;
end record;
end data;
Main.adb
with ada.Integer_Text_IO; use ada.Integer_Text_IO;
procedure Main is
Num_files: integer:=3;
Files:file_set(1..Num_files);
begin
Files(1):=(2,10); -- Expected private type "file" defined at data.ads
for i in 1..Num_Files loop
Put(integer'Image(i));
New_Line;
data.File_Print(Files(i));
But I'm getting this error Expected private type "file" defined at data.ads
How can I access the file type and declare a new array of values in main?
That's right - you don't get to see or manipulate what's inside a private type. That would be breaking encapsulation. Bugs and security risks follow.
You can only interact with a private type via its methods : functions and procedures declared in the package where it's declared.
Now file_set is NOT a private type (you might consider making it private later, for better encapsulation, but for now ....) you can index it to access a file within it (using one of those procedures).
Files(1):=(2,10);
As you want to create a file here, you need a method to create a file ... a bit similar to a constructor in C++, but really more like the Object Factory design pattern. Add this to the package:
function new_file(start, deadline : integer) return file;
And implement it in the package body:
package body data is
function new_file(start, deadline : integer) return file is
begin
-- check these values are valid so we can guarantee a proper file
-- I have NO idea what start, deadline mean, so write your own checks!
-- also there are better ways, using preconditions in Ada-2012
-- without writing explicit checks, but this illustrates the idea
if deadline < NOW or start < 0 then
raise Program_Error;
end if;
return (start => start, deadline => deadline);
end new_file;
procedure file_Print (T : in out file) is ...
end package body;
and that gives the users of your package permission to write
Files(1):= new_file(2,10);
Files(2):= new_file(start => 3, deadline => 15);
but if they attempt to create garbage to exploit your system
Files(3):= new_file(-99,-10); -- Oh no you don't!
this is the ONLY way to create a file, so they can't bypass your checks.

How do I refactor this base class and split its functionality?

I have a base class TBuilder that inherits from TObjectList. TBuilder can do ADO related operations and populate its internal structure with the results. In addition to this, the same operation can be done over the web, via HTTP calls. The results returned is also parsed and the internal structure updated.
From here on I generate pas files from a database table to mimic its structure. Say I have a table called Company, I programmatically generate a TCompany object that also inherits from TBuilder which can then choose what it needs to be. At the moment I construct TCompany with a type that says I want it to do ADO operations or I want it to do HTTP operations. TBuilder would then typically have a load procedure and, based on the type, it will then generate SQL and load from the database (or http) and populate itself internally with the results.
Now what I'm trying to do now is to split TBuilder up so that one unit knows how to query the database via ADO and the other via HTTP. I was thinking to inherit these two classes from TBuilder as well. But I'm challanged with TCompany because it needs to inherit from either TBuilder, or TADOBuilder or TDSBuilder ( the latter two being the new units). If I inherit from TADOBuilder it can only represent the one type of object. I'm trying to make it so that TCompany can be either of the two at any time. I saw that you can only implement multiple inheritance with Interfaces, but I'm new to this and haven't been able to figure out how I can redesign it so that my TCompany can be both types of objects.
Any ideas how I can approach this? For the time being I'm stuck in Delphi 6 doing this.
This is how it looks:
TCompany = class(TBuilder) //I generate this programatically. This represents a table in the database
private
fUser: TSecurityUser;
function GetCompanyName: TBuilderField;
function GetCompanyAbbreviation: TBuilderField;
function GetCompanyID: TBuilderField;
function GetDateCreated: TBuilderField;
function GetDeleted: TBuilderField;
public
Property CompanyID:TBuilderField read GetCompanyID;
Property CompanyName:TBuilderField read GetCompanyName;
Property Abbreviation:TBuilderField read GetCompanyAbbreviation;
property DateCreated:TBuilderField read GetDateCreated;
property Deleted:TBuilderField read GetDeleted;
property User:TSecurityUser read fUser Write fUser;
constructor NewObject(psCompanyName,psAbbreviation:string);
constructor Create(conType:TConnectionType = conTypeSQLServer);override;
This is how the Load procedure looks like, at the moment that I'm trying to split out into seperate units, in a smarter way:
function TBuilder.Load(psSQL:string = ''; poLoadOptions:TLoadOptions = []; poConnectionType:TConnectionType = conNone): Boolean;
var
LoQuery:TADOQuery;
LoSQL:string;
LoConnectionType:TConnectionType;
begin
Result := True;
LoConnectionType := fConnectionType;
if poConnectionType <> conNone then
LoConnectionType := poConnectionType;
if LoConnectionType = conTypeSQLServer then
begin
LoQuery := TADOQuery.Create(nil);
Try
try
LoQuery.Connection := uBuilder.ADOConnection;
LoSQL := psSQL;
if LoSQL = '' then
LoSQL := BuildSelectSQL;
LoQuery.SQL.Text := LoSQL;
LoQuery.Open;
LoadFromDataset(LoQuery);
except on E:exception do
begin
Error := E.Message;
Result := False;
end;
end;
Finally
FreeAndNil(LoQuery);
end;
end else
if fConnectionType = conTypeDatasnap then
begin
fWebCallType := sqlSelect;
try
if Assigned(fParent) then
if fParent.Error <> '' then
Exit;
if Assigned(OnBusyLoadingHook) then
OnBusyLoadingHook('Busy loading...');
{Reset the msg}
if Assigned(OnDisplayVisualError) then
OnDisplayVisualError(imtRed,'');
if (poLoadOptions <> LoadOptions) then
LoadOptions := LoadOptions + poLoadOptions;
Result := InternalLoad(loFullyRecursiveLoad in LoadOptions);
finally
{ We're done loading }
if Assigned(OnBusyLoadingHook) then
OnBusyLoadingHook('');
end;
end;
end;
Then I'd use the object in this way:
var
LoCompany:TCompany;
begin
LoCompany := TCompany.Create(conTypeDatasnap);
LoCompany.CompanyName.Value := 'Test';
LoCompany.Load; //This will then generate the appropriate JSON and pass it via idhttp component to the server and the results will be parsed into its structure.
end;
If I change the type it will query the database directly.
Option 1)
Do not inherit TCompany from TBuilder. Add an FBuilder: TBuilder field/property to TCompany and set this to either a TADOBuilder or TDSBuilder instance. And then add the required methods to TCompany and those methods would need to call the required method on FBuilder. Of course the methods needed has to be declared as virtual on TBuilder and TADOBuilder would need to override those.
Option2)
Separate your business object (TCompany) from the persistence code (TBuilder, TADOBuilder). Hard to give concrete advice without knowing the details but the idea is that your TCompany should be independent from the persistence code. In general you add all the required business properties to TCompany (e.g. Name, Address) and use a separate class which loads data into a TCompany using a TBuilder or TADOBuilder etc.
EDIT
Here is how it would look like with Option1.
TBuilder = abstract class
procedure Load; virtual;
end;
TADOBuilder = class(TBuilder)
procedure Load; override;
end;
TDSBuilder = class(TBuilder)
procedure Load; override;
end;
TCompany = class
private
FBuilder: TBuilder;
public
constructor Create(aBuilder: TBuilder);
procedure Load;
end;
{ TCompany }
constructor TCompany.Create(aBuilder: TBuilder);
begin
inherited;
FBuilder := aBuilder;
end;
procedure TCompany.Load;
begin
FBuilder.Load;
end;
....
EDIT example for Option 2
TCompany = class
private
FId: Integer;
FName: string;
...
public
property Id: Integer read FId write FId;
property Name: string read FName write FName;
end;
TADOPerssiter = class
public
constructor Create(const aConnectionString: string);
// Creates and loads TCompany from ADO
function LoadCompany(aId: Integer): TCompany;
procedure SaveCompany(aCompany: TCompany);
end;
Add similar class for DS

Pin folder to the start menu with Inno Setup [duplicate]

I'm using the excellent Inno Setup installer and I notice that some Applications (often from Microsoft) get installed with their launch icon already highly visible ('pinned?') in the start menu (in Windows 7). Am I totally reliant on the most-recently-used algorithm for my icon to be 'large' in the start menu, or is there a way of promoting my application from the installer please?
It is possible to pin programs, but not officially. Based on a code posted in this thread (which uses the same way as described in the article linked by #Mark Redman) I wrote the following:
[Code]
#ifdef UNICODE
#define AW "W"
#else
#define AW "A"
#endif
const
// these constants are not defined in Windows
SHELL32_STRING_ID_PIN_TO_TASKBAR = 5386;
SHELL32_STRING_ID_PIN_TO_STARTMENU = 5381;
SHELL32_STRING_ID_UNPIN_FROM_TASKBAR = 5387;
SHELL32_STRING_ID_UNPIN_FROM_STARTMENU = 5382;
type
HINSTANCE = THandle;
HMODULE = HINSTANCE;
TPinDest = (
pdTaskbar,
pdStartMenu
);
function LoadLibrary(lpFileName: string): HMODULE;
external 'LoadLibrary{#AW}#kernel32.dll stdcall';
function FreeLibrary(hModule: HMODULE): BOOL;
external 'FreeLibrary#kernel32.dll stdcall';
function LoadString(hInstance: HINSTANCE; uID: UINT;
lpBuffer: string; nBufferMax: Integer): Integer;
external 'LoadString{#AW}#user32.dll stdcall';
function TryGetVerbName(ID: UINT; out VerbName: string): Boolean;
var
Buffer: string;
BufLen: Integer;
Handle: HMODULE;
begin
Result := False;
Handle := LoadLibrary(ExpandConstant('{sys}\Shell32.dll'));
if Handle <> 0 then
try
SetLength(Buffer, 255);
BufLen := LoadString(Handle, ID, Buffer, Length(Buffer));
if BufLen <> 0 then
begin
Result := True;
VerbName := Copy(Buffer, 1, BufLen);
end;
finally
FreeLibrary(Handle);
end;
end;
function ExecVerb(const FileName, VerbName: string): Boolean;
var
I: Integer;
Shell: Variant;
Folder: Variant;
FolderItem: Variant;
begin
Result := False;
Shell := CreateOleObject('Shell.Application');
Folder := Shell.NameSpace(ExtractFilePath(FileName));
FolderItem := Folder.ParseName(ExtractFileName(FileName));
for I := 1 to FolderItem.Verbs.Count do
begin
if FolderItem.Verbs.Item(I).Name = VerbName then
begin
FolderItem.Verbs.Item(I).DoIt;
Result := True;
Exit;
end;
end;
end;
function PinAppTo(const FileName: string; PinDest: TPinDest): Boolean;
var
ResStrID: UINT;
VerbName: string;
begin
case PinDest of
pdTaskbar: ResStrID := SHELL32_STRING_ID_PIN_TO_TASKBAR;
pdStartMenu: ResStrID := SHELL32_STRING_ID_PIN_TO_STARTMENU;
end;
Result := TryGetVerbName(ResStrID, VerbName) and ExecVerb(FileName, VerbName);
end;
function UnpinAppFrom(const FileName: string; PinDest: TPinDest): Boolean;
var
ResStrID: UINT;
VerbName: string;
begin
case PinDest of
pdTaskbar: ResStrID := SHELL32_STRING_ID_UNPIN_FROM_TASKBAR;
pdStartMenu: ResStrID := SHELL32_STRING_ID_UNPIN_FROM_STARTMENU;
end;
Result := TryGetVerbName(ResStrID, VerbName) and ExecVerb(FileName, VerbName);
end;
The above code first reads the caption of the menu item for pinning or unpinning applications from the string table of the Shell32.dll library. Then connects to the Windows Shell, and for the target app. path creates the Folder object, then obtains the FolderItem object and on this object iterates all the available verbs and checks if their name matches to the one read from the Shell32.dll library string table. If so, it invokes the verb item action by calling the DoIt method and exits the iteration.
Here is a possible usage of the above code, for pinning:
if PinAppTo(ExpandConstant('{sys}\calc.exe'), pdTaskbar) then
MsgBox('Calc has been pinned to the taskbar.', mbInformation, MB_OK);
if PinAppTo(ExpandConstant('{sys}\calc.exe'), pdStartMenu) then
MsgBox('Calc has been pinned to the start menu.', mbInformation, MB_OK);
And for unpinning:
if UnpinAppFrom(ExpandConstant('{sys}\calc.exe'), pdTaskbar) then
MsgBox('Calc is not pinned to the taskbar anymore.', mbInformation, MB_OK);
if UnpinAppFrom(ExpandConstant('{sys}\calc.exe'), pdStartMenu) then
MsgBox('Calc is not pinned to the start menu anymore.', mbInformation, MB_OK);
Please note that even though this code works on Windows 7 (and taskbar pinning also on Windows 8.1 where I've tested it), it is really hacky way, since there is no official way to programatically pin programs to taskbar, nor start menu. That's what the users should do by their own choice.
There's a reason there's no programmatic way to pin things to the taskbar/start menu. In my experience, I have seen the start menu highlight newly-created shortcuts, and that's designed to handle exactly this situation. When you see a newly-installed program show up on the start menu, it's probably because of that algorithm and not because the installer placed it there.
That said, if a new shortcut does not appear highlighted, it may be because the installer extracts a pre-existing shortcut and preserves an old timestamp on it, rather than using the API function to create a shortcut in the start menu.
Have a look at: http://blogs.technet.com/deploymentguys/archive/2009/04/08/pin-items-to-the-start-menu-or-windows-7-taskbar-via-script.aspx

Passing strings as task creation discriminants in Ada

I'm moving my first steps with Ada, and I'm finding that I struggle to understand how to do common, even banal, operations that in other languages would be immediate.
In this case, I defined the following task type (and access type so I can create new instances):
task type Passenger(
Name : String_Ref;
Workplace_Station : String_Ref;
Home_Station : String_Ref
);
type Passenger_Ref is access all Passenger;
As you can see, it's a simple task that has 3 discriminants that can be passed to it when creating an instance. String_Ref is defined as:
type String_Ref is access all String;
and I use it because apparently you cannot use "normal" types as task discriminants, only references or primitive types.
So I want to create an instance of such a task, but whatever I do, I get an error. I cannot pass the strings directly by simply doing:
Passenger1 := new Passenger(Name => "foo", Workplace_Station => "man", Home_Station => "bar");
Because those are strings and not references to strings, fair enough.
So I tried:
task body Some_Task_That_Tries_To_Use_Passenger is
Passenger1 : Passenger_Ref;
Name1 : aliased String := "Foo";
Home1 : aliased String := "Man";
Work1 : aliased String := "Bar";
begin
Passenger1 := new Passenger(Name => Name1'Access, Workplace_Station => Work1'Access, Home_Station => Home1'Access);
But this doesn't work either, as, from what I understand, the Home1/Name1/Work1 variables are local to task Some_Task_That_Tries_To_Use_Passenger and so cannot be used by Passenger's "constructor".
I don't understand how I have to do it to be honest. I've used several programming languages in the past, but I never had so much trouble passing a simple String to a constructor, I feel like a total idiot but I don't understand why such a common operation would be so complicated, I'm sure I'm approaching the problem incorrectly, please enlighten me and show me the proper way to do this, because I'm going crazy :D
Yes, I agree it is a serious problem with the language that discriminates of task and record types have to be discrete. Fortunately there is a simple solution for task types -- the data can be passed via an "entry" point.
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure Main is
task type Task_Passenger is
entry Construct(Name, Workplace, Home : in String);
end Passenger;
task body Task_Passenger is
N, W, H : Unbounded_String;
begin
accept Construct(Name, Workplace, Home : in String) do
N := To_Unbounded_String(Name);
W := To_Unbounded_String(Workplace);
H := To_Unbounded_String(Home);
end Construct;
--...
end Passenger;
Passenger : Task_Passenger;
begin
Passenger.Construct("Any", "length", "strings!");
--...
end Main;
Ada doesn't really have constructors. In other languages, a constructor is, in essence, a method that takes parameters and has a body that does stuff with those parameters. Trying to get discriminants to serve as a constructor doesn't work well, since there's no subprogram body to do anything with the discriminants. Maybe it looks like it should, because the syntax involves a type followed by a list of discriminant values in parentheses and separated by commas. But that's a superficial similarity. The purpose of discriminants isn't to emulate constructors.
For a "normal" record type, the best substitute for a constructor is a function that returns an object of the type. (Think of this as similar to using a static "factory method" instead of a constructor in a language like Java.) The function can take String parameters or parameters of any other type.
For a task type, it's a little trickier, but you can write a function that returns an access to a task.
type Passenger_Acc is access all Passenger;
function Make_Passenger (Name : String;
Workplace_Station : String;
Home_Station : String) return Passenger_Acc;
To implement it, you'll need to define an entry in the Passenger task (see Roger Wilco's answer), and then you can use it in the body:
function Make_Passenger (Name : String;
Workplace_Station : String;
Home_Station : String) return Passenger_Acc is
Result : Passenger_Acc;
begin
Result := new Passenger;
Result.Construct (Name, Workplace_Station, Home_Station);
return Result;
end Make_Passenger;
(You have to do this by returning a task access. I don't think you can get the function to return a task itself, because you'd have to use an extended return to set up the task object and the task object isn't activated until after the function returns and thus can't accept an entry.)
You say
"I don't understand how I have to do it to be honest. I've used several programming languages in the past, but I never had so much trouble passing a simple String to a constructor, I feel like a total idiot but I don't understand why such a common operation would be so complicated, I'm sure I'm approaching the problem incorrectly, please enlighten me and show me the proper way to do this, because I'm going crazy :D"
Ada's access types are often a source of confusion. The main issue is that Ada doesn't have automatic garbage collection, and wants to ensure you can't suffer from the problem of returning pointers to local variables. The combination of these two results in a curious set of rules that force you to design your solution carefully.
If you are sure your code is good, then you can always used 'Unrestricted_Access on an aliased String. This puts all the responsibility on you to ensure the accessed variable won't disappear from underneath the task though.
It doesn't have to be all that complicated. You can use an anonymous access type and allocate the strings on demand, but please consider if you really want the strings to be discriminants.
Here is a complete, working example:
with Ada.Text_IO;
procedure String_Discriminants is
task type Demo (Name : not null access String);
task body Demo is
begin
Ada.Text_IO.Put_Line ("Demo task named """ & Name.all & """.");
exception
when others =>
Ada.Text_IO.Put_Line ("Demo task terminated by an exception.");
end Demo;
Run_Demo : Demo (new String'("example 1"));
Second_Demo : Demo (new String'("example 2"));
begin
null;
end String_Discriminants;
Another option is to declare the strings as aliased constants in a library level package, but then you are quite close to just having an enumerated discriminant, and should consider that option carefully before discarding it.
I think another solution would be the following:
task body Some_Task_That_Tries_To_Use_Passenger is
Name1 : aliased String := "Foo";
Home1 : aliased String := "Man";
Work1 : aliased String := "Bar";
Passenger1 : aliased Passenger(
Name => Name1'Access,
Workplace_Station => Work1'Access,
Home_Station => Home1'Access
);
begin
--...

TCP send command and wait for output

I have the following situation:
function Mach3Code(Str: String): String;
var StrOut: String;
begin
StrOut := '';
try
IdTelnet1.Connect();
IdTelnet1.Write(Str);
StrOut := ''; // assign here return output;
finally
IdTelnet1.Disconnect;
end;
Result := StrOut;
end;
On the line "StrOut := '';" I need to get the text output of the server (which is a tcp server, written in vc 2008 by me as Mach3 plugin).
Normally, the client sends "COMMAND1" and the server replies with "ANSWER1#" or something like this. I need the code to wait for the answer and then return it, synchronously, so I can do something like:
StrResult := Mach3Code('G0X300Y200');
and read what the server part has sent to me.
any ideas how I can solve this problem?
TIdTelnet is an asynchronous componnt, it is not suited for what you are attempting to do. Unless you are dealing with the actual Telnet protocol, then you should use TIdTCPClient instead:
function Mach3Code(const Str: String): String;
begin
Result := '';
try
IdTCPClient1.Connect();
IdTCPClient1.WriteLn(Str);
StrOut := IdTCPClient1.ReadLn('#');
finally
IdTCPClient1.Disconnect;
end;
end;
to receive data assign an event handler of type TIdTelnetDataAvailEvent to the OnDataAvailable property of idtelnet1, i know this is not synchronous but i would re factor your code to work this way as this is how the telnet client is designed to work.
Failing that create your own TIdTCPClientCustom decendant and implement your own read thread with the relevent methods.

Resources