I'm using Lazarus/FPC and I'm looking for a way to get a list of pointing devices in Windows - and then ultimately to be able to disable and enable particular devices.
A bit of Googling turned up this on MSDN and this on the FreePascal wiki.
These look like a good starting point but unfortunately I'm falling at the first hurdle... I can't figure out how to create the manager object that is referred to in the example.
The MSDN example is (C#):
private void PopulatePointers(TreeView tvDevices)
{
//Add "Pointer Devices" node to TreeView
TreeNode pointerNode = new TreeNode("Pointer Devices");
tvInputDevices.Nodes.Add(pointerNode);
//Populate Attached Mouse/Pointing Devices
foreach(DeviceInstance di in
Manager.GetDevices(DeviceClass.Pointer,EnumDevicesFlags.AttachedOnly))
{
//Get device name
TreeNode nameNode = new TreeNode(di.InstanceName);
nameNode.Tag = di;
TreeNode guidNode = new TreeNode(
"Guid = " + di.InstanceGuid);
//Add nodes
nameNode.Nodes.Add(guidNode);
pointerNode.Nodes.Add(nameNode);
}
}
Which I have partially translated to Pascal as:
uses windows, DirectInput;
procedure getPointingDevices();
begin
for pointingDevice in Manager.GetDevices(DeviceType.Keyboard,EnumDevicesFlags.AttachedOnly) do
begin
devicesTree.Items.AddChild(devicesTree.Items.TopLvlItems[0],pointingDevice.InstanceName);
end;
devicesTree.Items.TopLvlItems[0].Expand(true);
end;
and I have included DirectInput.pas, DirectX.inc, DXTypes.pas, Jedi.inc, Xinput.pas (some of which may not actually be needed, I'll work that out later) in the project.
Obviously I need to create the Manager object to be able to access its methods, but I have no idea how to do that from the documentation I've read so far.
What you are looking for is the DirectInput IDirectInput8 COM interface.
To enumerate input devices, obtain the IDirectInput8 interface using the DirectInput8Create() function, and then use its EnumDevices() or EnumDevicesBySemantics() method. For example:
uses
Windows, DirectInput;
function MyEnumCallback(lpddi: LPCDIDEVICEINSTANCE; pvRef: Pointer): BOOL; stdcall;
var
Tree: TTreeView;
begin
Tree := TTreeView(pvRef);
Tree.Items.AddChild(Tree.Items.TopLvlItems[0], lpddi.tszInstanceName);
end;
procedure getPointingDevices;
var
DI: IDirectInput8;
begin
OleCheck(DirectInput8Create(HInstance, DIRECTINPUT_VERSION, IDirectInput8, #DI, nil));
OleCheck(DI.EnumDevices(DI8DEVCLASS_POINTER, #MyEnumCallback, devicesTree, DIEDFL_ATTACHEDONLY));
devicesTree.Items.TopLvlItems[0].Expand(true);
end;
Related
I have an outliner application (in Delphi 10.2 Tokyo) that I use for taking notes (I'll call it NoteApp). I have another application that I use for editing plain text (TextApp). Since I switch between these applications a lot, I decided to integrate note-taking abilities within TextApp.
I copy/pasted the code from NoteApp to TextApp, and I put the components (one TTreeView, one TRichEdit, and one TActionToolbar) on TextApp.Form_Main .
OnCustomDrawItem event of the TTreeView is set to change FontStyle of each Node based on the NoteType of the corresponding note item which is an array of simple records:
type
///
/// Note types
///
TNoteType = (ntNote, ntTodo, ntDone, ntNext, ntTitle) ;
///
///
///
TNote = Record
Text ,
Attachment ,
Properties ,
CloseDate : String ;
NoteType : TNoteType ;
End;
Our array:
var
Notes: Array of TNote ;
And the event:
procedure TForm_Main.TreeView_NotesCustomDrawItem(Sender: TCustomTreeView;
Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
///
/// First check to see if the application allows us to change visuals. If the
/// application is in processing mode, visual updates are not allowed.
///
if ChangeAllowed AND Node.IsVisible then
begin
///
/// Check NoteType of the corresponding note:
///
case Notes[Node.AbsoluteIndex].NoteType of
ntTitle:
begin
TreeView_Notes.Canvas.Font.Style := [fsBold] ;
end;
//ntNote:
// begin
// end;
ntTodo:
begin
TreeView_Notes.Canvas.Font.Style := [fsBold] ;
end;
ntNext:
begin
TreeView_Notes.Canvas.Font.Style := [fsUnderline] ;
end;
ntDone:
begin
TreeView_Notes.Canvas.Font.Style := [fsStrikeOut] ;
end;
end;
end;
end;
When I open a note file in NoteApp, it works perfectly. When I open the same file in TextApp, TTreeView refreshes slowly. The top items in the TTreeView are alright, but the lower you go, the lower the refreshing rate.
The properties of all of the components are identically set.
I suspect I have made a mistake somewhere. I set the visibility of all of the other components on TextApp to false, but the TTreeView is still abysmally slow.
If I remove the code above, it becomes fast again. I don't use runtime themes in TextApp.
Okay, I found the answer to the question.
The answer was hidden in the code above, and what I posted was enough to answer the question. Turns out the code that I have posted was MCVE after all. I am posting the answer in case it happens to someone else.
Answer:
Turns out Node.AbsoluteIndex is incredibly slow. It shouldn't be used as an index.
Solution 1:
I used Node.Data as an index, and now it is very fast.
Solution 2:
An alternative solution that I tried and worked:
TTreeNodeNote = class(TTreeNode)
public
Note: TNote;
end;
procedure TForm_Main.TreeView_NotesCreateNodeClass(Sender: TCustomTreeView;
var NodeClass: TTreeNodeClass);
begin
NodeClass := TTreeNodeNote;
end;
Then we store the data in the Note property of each Node, instead of a separate array. Works like a charm.
I'm writing a windows service that performs operations according to different rules, one of which is based on the requesting user identity.
It thus receives the requesting user SID and then compares it to its internal list of SIDs to decide what operation it will perform. Using the EqualSID API function makes this very easy.
However, I am now faced with the situation where some SIDs in the service list are group SIDs and not user SIDs.
This means that I have to find a way to test if the received SID is either equal to the one in the list or belongs to the group that is represented by the SID in the list.
I looked around to see what APIs would be available and found about CheckTokenMembership which requires a token handle. That's where I'm a bit lost because as the service is not necessarily located on the same machine, I can't seem to find a way to create a valid token handle from the SID that I have received.
The service itself runs under the default "NT Service" account and I would prefer if it could stay this way.
What API would you suggest I use?
The target language is Delphi but I can understand examples in plain C.
Well, after looking around at various other things I finally managed to find a way to achieve this. In short, the answer is Active Directory Service Interfaces also known as ADSI
To give a bit more details should someone else be looking at that, here is a series of steps to achieve this in Delphi:
Import the Active DS Type Library set of objects into Delphi. This will create the ActiveDs_TLB unit with all the necessary interfaces
Declare AdsGetObject like so
function ADsGetObject(lpszPathName: WideString; const riid: TGUID; out ppObject): HRESULT; safecall;
Retrieve an IADSUser instance with the SID syntax:
var
SIDUser: IADSUser;
User: IADSUser;
SIDGroup: IADSGroup;
Group: IADSGroup;
begin
// Bind using the SID
AdsGetObject('LDAP://<SID=S-1-5-7>', IADSUser, SIDUser);
// rebind using the distinguished name as suggested by MSDN
AdsGetObject('LDAP://' + SIDUser.Get('distinguishedName'), IADSUser, User);
// Use the User instance
ShowMessage(User.FullName);
// Same method for group
AdsGetObject('LDAP://<SID=S-1-5-32-545>', IADSGroup, SIDGroup);
AdsGetObject('LDAP://' + SIDGroup.Get('distinguishedName'), IADSGroup, Group);
// IsMember does not seem to work with LDAP
// https://groups.google.com/forum/#!topic/microsoft.public.adsi.general/2d-e4HPXGfA
// http://www.rlmueller.net/Programs/IsMember4.txt
// if Group.IsMember(User.ADsPath) then
if AdsIsMember(User, 'S-1-5-32-545') then
ShowMessage('InGroup');
end;
As you can see, one would want to use the IsMember method of IADSGroup but clearly it does not work because it should return True in the above case (S-1-5-32-545 is the world group).
So as suggested by the link give in the comment, I wrote my own IsMember like so:
function ADsIsMember(const User: IADSUser; const GroupSID: string): Boolean;
const
TokenGroupsId = 'tokenGroups';
var
PropNames: array of OleVariant;
TokenGroups: OleVariant;
TokenGroupLow: Integer;
TokenGroupHigh: Integer;
TokenGroupIndex: Integer;
SIDBytes: array of Byte;
SIDAsString: PChar;
begin
Result := False;
SetLength(PropNames, 1);
PropNames[0] := TokenGroupsId;
User.GetInfoEx(PropNames, 0);
TokenGroups := User.Get(TokenGroupsId);
TokenGroupLow := VarArrayLowBound(TokenGroups, 1);
TokenGroupHigh := VarArrayHighBound(TokenGroups, 1);
for TokenGroupIndex := TokenGroupLow to TokenGroupHigh do
begin
SIDBytes := TokenGroups[TokenGroupIndex];
ConvertSidToStringSid(#SIDBytes[0], SIDAsString);
if GroupSID = SIDAsString then
Exit(True);
end;
end;
With all this, I can now check if a given SID belongs to a group defined by its SID.
In my simulation, I want to have RW access to signals whereever there are in the project. To get the write access, I use the "signal_force" procedure from the modelsim_lib library. But to get the read access I havn't find the corresponding function.
The reason why signal_force fit my needs is that I'm working with input text files, so I have the name and the value of the signal from a "string" or a "line" variable and I can directly give these variable to the fonction.
I cannot use the "init_signal_spy" procedure because this procedure doesn't give back a value into a string but just duplicates the behavior of a signal onto an other. As my project has to be as generic as possible, I work with variables declared into procedures and I cannot link a signal onto a variable.
Thanks for your help
edited
Sorry, I win the "did not read very carefully" award for the day...
Just for completeness, I'm leaving the part of my answer that deals with signal spy (which is a proprietary ModelSim method), even though you said it wouldn't work for you:
library modelsim_lib;
use modelsim_lib.util.all;
architecture ...
signal local_sig ...
begin
process
begin
init_signal_spy("/sim/path/to/signal/internal_sig", "local_sig");
With VHDL-2008 (if you have support for it), the standard way to access signals not in scope is hierarchical/external names, and as a bonus, it does both "write" and "read". I may be a bit rusty on the nuances, but you access them like:
<<signal .sim.path.to.signal.internal_sig : std_logic>>
And you should be able to use that in place of any normal in-scope identifier, I believe. Aliases, assignments, etc.
If you're comfortable writing C code it should be straightforward to achieve what you want using the VHPI, although sadly despite being part of the VHDL standard Mentor are not planning to implement it. However it will also be possible using FLI although you're locked into a proprietary interface.
Something like this:
procedure get_signal_value_as_string(
vhdl_path : IN string;
vhdl_value: OUT string);
attribute FOREIGN of get_signal_value_as_string : procedure is “my_func mylib.so”;
procedure get_signal_value_as_string(
vhdl_path : IN string;
vhdl_value: OUT string) is
begin
report “ERROR: foreign subprogram get_signal_value_as_string not called”;
end;
Then in C:
#include <stdio.h>
#include "mti.h"
/* Convert a VHDL String array into a NULL terminated string */
static char *get_string(mtiVariableIdT id)
{
static char buf[1000];
mtiTypeIdT type;
int len;
mti_GetArrayVarValue(id, buf);
type = mti_GetVarType(id);
len = mti_TickLength(type);
buf[len] = 0;
return buf;
}
void my_func (
mtiVariableIdT vhdl_path /* IN string */
mtiVariableIdT vhdl_value /* OUT string */
)
{
mtiSignalIdT sigID = mti_FindSignal(get_string(vhdl_path));
mtiInt32T value = mti_GetSignalValue(sigID);
...
}
Plenty of example code in the FLI manual.
I have done some really serious refactoring of my text editor. Now there is much less code, and it is much easier to extend the component. I made rather heavy use of OO design, such as abstract classes and interfaces. However, I have noticed a few losses when it comes to performance. The issue is about reading a very large array of records. It is fast when everything happens inside the same object, but slow when done via an interface. I have made the tinyest program to illustrate the details:
unit Unit3;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
const
N = 10000000;
type
TRecord = record
Val1, Val2, Val3, Val4: integer;
end;
TArrayOfRecord = array of TRecord;
IMyInterface = interface
['{C0070757-2376-4A5B-AA4D-CA7EB058501A}']
function GetArray: TArrayOfRecord;
property Arr: TArrayOfRecord read GetArray;
end;
TMyObject = class(TComponent, IMyInterface)
protected
FArr: TArrayOfRecord;
public
procedure InitArr;
function GetArray: TArrayOfRecord;
end;
TForm3 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
MyObject: TMyObject;
implementation
{$R *.dfm}
procedure TForm3.FormCreate(Sender: TObject);
var
i: Integer;
v1, v2, f: Int64;
MyInterface: IMyInterface;
begin
MyObject := TMyObject.Create(Self);
try
MyObject.InitArr;
if not MyObject.GetInterface(IMyInterface, MyInterface) then
raise Exception.Create('Note to self: Typo in the code');
QueryPerformanceCounter(v1);
// APPROACH 1: NO INTERFACE (FAST!)
// for i := 0 to high(MyObject.FArr) do
// if (MyObject.FArr[i].Val1 < MyObject.FArr[i].Val2) or
// (MyObject.FArr[i].Val3 < MyObject.FArr[i].Val4) then
// Tag := MyObject.FArr[i].Val1 + MyObject.FArr[i].Val2 - MyObject.FArr[i].Val3
// + MyObject.FArr[i].Val4;
// END OF APPROACH 1
// APPROACH 2: WITH INTERFACE (SLOW!)
for i := 0 to high(MyInterface.Arr) do
if (MyInterface.Arr[i].Val1 < MyInterface.Arr[i].Val2) or
(MyInterface.Arr[i].Val3 < MyInterface.Arr[i].Val4) then
Tag := MyInterface.Arr[i].Val1 + MyInterface.Arr[i].Val2 - MyInterface.Arr[i].Val3
+ MyInterface.Arr[i].Val4;
// END OF APPROACH 2
QueryPerformanceCounter(v2);
QueryPerformanceFrequency(f);
ShowMessage(FloatToStr((v2-v1) / f));
finally
MyInterface := nil;
MyObject.Free;
end;
end;
{ TMyObject }
function TMyObject.GetArray: TArrayOfRecord;
begin
result := FArr;
end;
procedure TMyObject.InitArr;
var
i: Integer;
begin
SetLength(FArr, N);
for i := 0 to N - 1 do
with FArr[i] do
begin
Val1 := Random(high(integer));
Val2 := Random(high(integer));
Val3 := Random(high(integer));
Val4 := Random(high(integer));
end;
end;
end.
When I read the data directly, I get times like 0.14 seconds. But when I go through the interface, it takes 1.06 seconds.
Is there no way to achieve the same performance as before with this new design?
I should mention that I tried to set PArrayOfRecord = ^TArrayOfRecord and redefined IMyInterface.arr: PArrayOfRecord and wrote Arr^ etc in the for loop. This helped a lot; I then got 0.22 seconds. But it is still not good enough. And what makes it so slow to begin with?
Simply assign the array to a local variable before iterating through the elements.
What you're seeing is that the interface methods calls are virtual and have to be called through an indirection. Also, the code has to pass-through a "thunk" that fixes up the "Self" reference to now point to the object instance and not the interface instance.
By making only one virtual method call to get the dynamic array, you can eliminate that overhead from the loop. Now your loop can go through the array items without the extra overhead of the virtual interface method calls.
You're comparing oranges with apples, as the first test reads a field (FArr), while the second test reads a property (Arr) that has a getter assigned with it. Alas, interfaces offer no direct access to their fields, so you really can't do it any other way than like you did.
But as Allen said, this causes a call to the getter method (GetArray), which is classified as 'virtual' without you even writing that because it's part of an interface.
Thus, every access results in a VMT-lookup (indirected via the interface) and a method call.
Also, the fact that you're using a dynamic array means that both the caller and the callee will do a lot of reference-counting (you can see this if you take a look at the generated assembly code).
All this is already enough reasons to explain the measured speed difference, but can indeed easily be overcome using a local variable and read the array only once. When you do that, the call to the getter (and all the ensueing reference counting) is taking place only once. Compared to the rest of the test, this 'overhead' becomes unmeasurable.
But note, that once you go this route, you'll loose encapsulation and any change to the contents of the array will NOT reflect back into the interface, as arrays have copy-on-write behaviour. Just a warning.
Patrick and Allen's answers are both perfectly correct.
However, since your question talks about improved OO design, I feel a particular change in your design that would also improve performance is appropriate to discuss.
Your code to set the Tag is "very controlling". What I mean by this is that you're spending a lot of time "poking around inside another object" (via an interface) in order to calculate your Tag value. This is actually what exposed the "performance problem with interfaces".
Yes, you can simply deference the interface once to a local variable, and get a massive improvement in performance, but you'll still be poking around inside another object. One of the important goals in OO design is to not poke around where you don't belong. This actually violates the Law of Demeter.
Consider the following change which empowers the interface to do more work.
IMyInterface = interface
['{C0070757-2376-4A5B-AA4D-CA7EB058501A}']
function GetArray: TArrayOfRecord;
function GetTagValue: Integer; //<-- Add and implement this
property Arr: TArrayOfRecord read GetArray;
end;
function TMyObject.GetTagValue: Integer;
var
I: Integer;
begin
for i := 0 to High(FArr) do
if (FArr[i].Val1 < FArr[i].Val2) or
(FArr[i].Val3 < FArr[i].Val4) then
begin
Result := FArr[i].Val1 + FArr[i].Val2 -
FArr[i].Val3 + FArr[i].Val4;
end;
end;
Then inside TForm3.FormCreate, //APPROACH 3 becomes:
Tag := MyInterface.GetTagValue;
This will be as fast as Allen's suggestion, and will be a better design.
Yes, I'm fully aware you simply whipped up a quick example to illustrate the performance overhead of repeatedly looking something up via interface. But the point is that if you have code performing sub-optimally because of excessive accesses via interfaces - then you have a code smell that suggests you should consider moving the responsibility for certain work into a different class. In your example TForm3 was highly inappropriate considering everything required for the calculation belonged to TMyObject.
your design use huge memory. Optimize your interface.
IMyInterface = interface
['{C0070757-2376-4A5B-AA4D-CA7EB058501A}']
function GetCount:Integer:
function GetRecord(const Index:Integer):TRecord;
property Record[Index:Integer]:TRecord read GetRecord;
end;
Here is code:
procedure DisableContrlOL(const cArray : array of string; ReEnable : boolean = False);
// can be called from VKP / RAW / Generation clicks
var
AComponent: TComponent;
CompListDis, CompListEna : TStringList;
begin
CompListDis := TStringList.Create;
CompListEna := TStringList.Create;
for i := Low(cArray) to High(cArray) do begin
AComponent := FindComponent(cArray[i]);
if Assigned(AComponent) then
if (AComponent is TControl) then begin
if TControl(AComponent).Enabled then
CompListEna.Add(TControl(AComponent).Name)
else
CompListDis.Add(TControl(AComponent).Name);
ShowMessage(TControl(AComponent).Name);
if ReEnable then begin // if reenabling needed, then all whi
if not TControl(AComponent).Enabled then
TControl(AComponent).Enabled := True;
end else if (TControl(AComponent).Enabled) then
TControl(AComponent).Enabled := False;
end;
end;
end;
I think no more explanations are needed.
The ShowMessage correctly shows name of each component, but nothing is added in StringLists. Why?
UPDATE: As question has gone pretty wild, I did confirm answer, which a bit helped me.
I understand that I did write things pretty unclear, but I am very limited, because these code lines is part of commercial project, and my hobby and heart thing. The main problem was found already 6h ago, but Rob just wanted to extend this whole question :D No, no offense, mate, it's OK. I am happy to receive so willing and helpful posts. Thanks again.
How do you know that nothing is added to the lists? You create them in this code and the only references to them are in local variables. The objects are leaked when this function returns, so you never actually use the lists anywhere.
You've said you have code for "modular testing." Since that code isn't here, I must assume the code is not part of this function. But if you have external code that's supposed to check the contents of the lists, then the lists can't be just local variables. No other code can access them. You need to either return those lists or accept lists from outside that you then fill. Here's an example of the latter:
procedure DisableContrlOL(const cArray: array of string;
Reenable: Boolean
CompListDis, CompListEna: TStrings);
// can be called from VKP / RAW / Generation clicks
var
AComponent: TComponent;
AControl: TControl;
i: Integer;
begin
for i := Low(cArray) to High(cArray) do begin
AComponent := FindComponent(cArray[i]);
if not Assigned(AComponent) or not (AComponent is TControl) then
continue;
AControl := TControl(AComponent);
if AControl.Enabled then
CompListEna.Add(AControl.Name)
else
CompListDis.Add(AControl.Name);
ShowMessage(AControl.Name);
AControl.Enabled := Reenable;
end;
end;
The caller of this function will need to provide a TStrings descendant for each list. They could be TStringList, or they could be other descendants, such as TMemo.Lines, so you can directly observe their contents in your program. (They can't be just TStrings, though, since that's an abstract class.)
As you can see, I made some other changes to your code. All your code using the Reenable parameter can be simplified to a single statement. That's because enabling a control that's already enabled, and disabling a control that's already disabled, are no-ops.
Also, Name is a public property of TComponent. You don't need to type-cast to TControl before reading that property, but since you're type-casting so often elsewhere, it made sense to introduce a new variable to hold the type-casted TControl value, and that can make your code easier to read. Easier-to-read code is easier-to-understand code, and that makes it easier to debug.
Emphasizing that this is largely based on Rob's excellent suggestions, it looks as though you could simplify the code to:
procedure DisableContrlOL(const cArray : array of string;
ReEnable : boolean = False);
var
AComponent: TComponent;
begin
for i := Low(cArray) to High(cArray) do
begin
AComponent := FindComponent(cArray[i]);
if Assigned(AComponent) then
if (AComponent is TControl) then
begin
ShowMessage(TControl(AComponent).Name);
TControl(AComponent).Enabled := ReEnable;
end;
end;
end;
Not clear what the stringlists were for, since their contents were lost when execution left the scope of this procedure. If you want to return them, you should create and free them in the calling code.
That sure looks like it ought to work. This is the sort of thing that the debugger can probably help with more than we can here.
Try breaking the problematic line down into multiple lines, like so:
if TControl(AComponent).Enabled then
CompListEna.Add(TControl(AComponent).Name)
else CompListDis.Add(TControl(AComponent).Name);
Rebuild with the "Use Debug DCUs" option on, and place a breakpoint on the if statement. Then use F7 to trace your way through the logic and see what's going on.