Breaking the code inside a method assigns wrong online values - twincat

I have a function block, inside of which I have a method. When the method is called, it takes it's input REAL variable and sets the function block's internal variable, also type REAL. Now, for some reason, when i put a breakpoint inside the method, the value for that variable is completely wrong, and is always assigned the same value. Here is the code
METHOD PUBLIC LowerTheObject : BOOL
VAR_INPUT
nSpeedSetpoint : INT; // 0-32767 (0-21mm/min)
fInsertionDistance : REAL; // Milimeters
END_VAR
IF bEnable AND eMotionStateInternal = E_FeedState.IDLE AND bInhibitMovementDown THEN
LowerTheObject := TRUE;
eMotionStateInternal := E_FeedState.AUTOMATIC_LOWERING;
THIS^.fLoweringStartPosition := THIS^.fPosition;
THIS^.nSpeedSetpoint := nSpeedSetpoint;
THIS^.fInsertionDepth := TO_REAL(fInsertionDistance);
ELSE
LowerTheObject := FALSE;
END_IF
When I call this method here is what happens:
Now comes the interesting part, which took me a while to even see it. The value of the variable is WRONG only, when I break the code inside the method. Breaking the code inside the function block, the variable gets assigned correct value:
To wrap it up, I am as confused as ever. Why does braking the code assign the wrong value? This is very wrong, the code can't be debugged properly. Is this a bug in this build of the TwinCAT?
I can guarantee that no other place in the code sets the value of the fInsertionDepth. I even added the TO_REAL(), in case the compiler did something weird I am not seeing. It seems like a memory allocation issue to me, I have tried restarting the PC, cleaning solution, re-activating the configuration, nothing helps.
Does anyone have a clue what might be happening, why is the variable fInsertionDepth get the 9.4 * 10^-38 when using a breakpoint, no matter what value is beeing assinged to it? I am running the solution on a local development machine, windows10, 64 bit as well as 64 bit CPU, never saw these issues before. Using TwinCAT 3, build 4024.25.
EDIT:
I have managed to make a project where this is very obviously replicated - I am not sure how/where to add attachments, so here is the code:
PROGRAM MAIN
VAR
END_VAR
ProgramExecution();
// Program that will containt FBs
PROGRAM ProgramExecution
VAR
fbTest : FB_Base;
END_VAR
fbTest();
// Base FB
FUNCTION_BLOCK FB_Base
VAR
fbTest : FB_Sub;
fValue : REAL := 10.0;
bStart : BOOL;
END_VAR
IF bStart THEN
bStart := FALSE;
fbTest.Method1(fValue := fValue);
END_IF
fbTest();
// Second FB, that is instantiated inside the base FB
FUNCTION_BLOCK FB_Sub
VAR
fValue : REAL;
bCall : BOOL;
END_VAR
// Method within the second FB
METHOD PUBLIC Method1 : BOOL
VAR_INPUT
fValue : REAL;
END_VAR
IF TRUE THEN
Method1 := TRUE;
THIS^.fValue := fValue;
ELSE
Method1 := FALSE;
END_IF
Here is the resulting error when breaking on the expression where the value is beeing assinged:

It is a glitch in displays, this value is assigned at some point, because you working in the stack memory (because the variable is declared in a method). In the PLC world you don't have any garbage collector, you just overwrite the address in every cycle. The number what you can see in when you break is some remaining data. Sometime the debug display not fast enough to collect everything when you break, especially if you change tab on the break. (This display issue is depend on your engineering PC not on the target PLC.)

Related

Delphi: how can i get list of running applications with starting path?

Using Delphi (windows app) i want to get list of other applications running currently. Here How to check if a process is running using Delphi? i've found great tutorial about geting filenames/names of running application, however it gives names only process name (for example NOTEPAD.EXE). I've used naturally part with
UpperCase(ExtractFileName(FProcessEntry32.szExeFile))
and
UpperCase(ExtractFilePath(FProcessEntry32.szExeFile))
and just
UpperCase(FProcessEntry32.szExeFile)
but obviously FProcessEntry32.szExeFile does not have a path to file/process
Is there a simply way of getting list with paths? Here's How to get the list of running processes including full file path? solution with JclSysInfo library, but i cant use it in place of work in project.
I looked at what I could in Google and what I found usually concerned just the application that is running or the application that is active, but I can't just find a list of all running applications. Maybe i'm missing something obvious?
I'm not looking for any complex procedures, I'm not much interested in process parrent, or if there is no access to the process path, I don't have it and don't bother.
Any simple hint?
OK, due to helpfull comment from #TLama i've combined topics above to take name and path of process:
function processExists(exeFileName: string): Boolean;
var
ContinueLoopP, ContinueLoopM: BOOL;
FSnapshotHandle1, FSnapshotHandle2: THandle;
FProcessEntry32: TProcessEntry32;
FMODULEENTRY32: TMODULEENTRY32;
begin
FSnapshotHandle1 := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
FMODULEENTRY32.dwSize := SizeOf(FMODULEENTRY32);
ContinueLoopP := Process32First(FSnapshotHandle1, FProcessEntry32);
ContinueLoopM := Module32First(FSnapshotHandle2, FMODULEENTRY32);
Result := False;
while Integer(ContinueLoopP) <> 0 do
begin
if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
UpperCase(ExeFileName)) or (UpperCase(FProcessEntry32.szExeFile) =
UpperCase(ExeFileName))) then
Result := True;
ShowMessage(FMODULEENTRY32.szExePath + FProcessEntry32.szExeFile);
ContinueLoopP := Process32Next(FSnapshotHandle1, FProcessEntry32);
ContinueLoopM := Module32Next(FSnapshotHandle2, FMODULEENTRY32);
end;
CloseHandle(FSnapshotHandle1);
CloseHandle(FSnapshotHandle2);
end;
But still FProcessEntry32.szExeFile returns empty string. What i'm doing wrong? Thank You in advance.
I cannot write comment (low score), so I need to write as "answer". Try this code,
using FProcessEntry32.th32ProcessID as parameter:
Function QueryFullProcessImageNameW(hProcess:THandle; dwFlags:Cardinal; lpExeName:PWideChar; Var lpdwSize:Cardinal) : Boolean; StdCall; External 'Kernel32.dll' Name 'QueryFullProcessImageNameW';
Function GetFullPath(Pid:Cardinal) : UnicodeString;
Var rLength:Cardinal;
Handle:THandle;
Begin Result:='';
Handle:=OpenProcess(PROCESS_QUERY_INFORMATION, False, Pid);
If Handle = INVALID_HANDLE_VALUE Then Exit;
rLength:=256; // allocation buffer
SetLength(Result, rLength+1); // for trailing space
If Not QueryFullProcessImageNameW(Handle, 0, #Result[1],rLength) Then Result:='' Else SetLength(Result, rLength);
End;
This is a simple way I think. If you want to get the loaded DLL's full name, use
FMODULEENTRY32.hModule with GetModuleFileNameW function.

Pointer dereference in VHDL

I have not been able to understand how to dereference a pointer in VHDL.
What I have in mind is a C code like :
int a;
int* ptr_a;
a = 42;
ptr_a=&a;
*ptr_a=451;// how can I do this ?
I tried to mimick this code in VHDL :
ptr_test : process
type ptr_integer is access integer;
variable a : integer;
variable ptr_a : ptr_integer;
begin
a := 42;
ptr_a := new integer'(a);
report "ptr now points to a : ptr=" & str(ptr_a.all);
ptr_a.all := 451;
report "ptr modified : ptr=" & str(ptr_a.all);
report "a is NOT modified : a =" & str(a);
wait;
end process;
So how can I correctly modify a value through a pointer ?
You can't directly. Access types are not "just like pointers" - they are to at least some extent distinct types of data storage.
This line does not create a pointer to a:
ptr_a := new integer'(a);
It creates a data object with the same value as a and sets up ptr_a to reference it.
If you were to create another access type variable :
variable ptr_b : ptr_integer;
and set it to to point to ptr_a:
ptr_b := ptr_a;
then changes to ptr_b.all will reflect in ptr_a.all.
"new" is the equivalent of the (C++ rather than C) "new" operation; invoking a constructor allocating an integer on the heap and initialising it to "a". (Naturally, you can "deallocate" it when done)
What you are looking for is ptr_a := a'access; which is the Ada way of accessing a global or local (stack) variable via a pointer : this is only legal if said variable has been declared "aliased" alerting the compiler to the fact there may be more than one view of it (and thus, preventing some nice optimisations). In C, everything is "aliased" whether you want it or not.
This is one of the aspects of Ada that didn't make it through the simplification process into VHDL : and it's difficult to see a good use for it. So there isn't an exact equivalent in VHDL.
Martin's answer just popped up : as he says, you CAN have 2 or more pointers to the same heap object.
Alternatively, explain what you are trying to achieve this way; there may be a VHDL alternative way of doing it.

Adding non-VCL window into VCL align queue

Some background (kind of a continuation of TLabel and TGroupbox Captions Flicker on Resize):
So, I have an application that loads different plugins and creates a
new tab on a TPageControl for each one.
Each DLL has a TForm associated with it.
The forms are created with their parent hWnd as the new TTabSheet.
Since the TTabSheets aren't a parent of the form as far as VCL is
concerned (didn't want to use dynamic RTL, and plugins made in
other languages) I have to handle resizes manually.
I just seem to be running into a lot of new issues (but great learning experiences) for this "plugin" type of application.
So, my current struggle is trying to have a plugin that doesn't get inserted into a TTabSheet but will be resized and aligned directly on the form.
Since this would be easier to explain with a picture:
Now I could manually do the alignment and the resize, but I'd much rather have the VCL alignment procedures (alClient, alTop, etc) do it for me. That way I would just have to set the plugins alignment on its form without thinking.
After looking through the VCL source I began to step through the align code and how it's called. Basically when a TControl gets a WM_RESIZE it will:
Call Realign() which calls AlignControl()
AlignControl() will get the client rect and call AlignControls()
AlignControls() will call DoAlign() for each TAlignment type in this order: alTop, alBottom, alLeft, alRight, alClient, alCustom, alNone
DoAlign() will loop through FControls and FWinControls (which are TLists) and will align them appropriately
So my thought process is that if I create a new TWinControl, set it's handle to the plugins form (window) handle, and insert it into the FControls list with the proper align it should do my work for me.
Of course I'm here, so it failed miserably. I even get an AV when exiting the application about an invalid window handle. My guess is that the TWinControl I created is trying to free the handle of the plugins form (window) which doesn't exist any more.
What I've tried:
procedure AddHandleToControlList(AHandle: DWORD; Align: TAlign);
var
NewWinControl : TWinControl;
begin
NewWinControl := TWinControl.Create(frmMain);
NewWinControl.WindowHandle := AHandle;
NewWinControl.Align := Align;
NewWinControl.Width := frmMain.ClientWidth;
NewWinControl.Height := 30;
NewWinControl.Parent := frmMain;
end;
procedure AddHandleToControlList(AHandle: DWORD; Align: TAlign);
var
NewWinControl : TWinControl;
begin
NewWinControl := TWinControl.Create(frmMain);
NewWinControl.WindowHandle := AHandle;
NewWinControl.Align := Align;
NewWinControl.Width := frmMain.ClientWidth;
NewWinControl.Height := 30;
TWinControl(frmMain).Insert(NewWinControl);
end;
Soooo, thoughts?
EDIT 1:
Ok, so this correctly adds the control to the list and conforms the the TAlign set (why is it that I spend 8 hours trying to figure something out, I post here, and then the answer just appears...oh well someone might find this question and my ramblings useful):
procedure AddHandleToControlList(AHandle: DWORD; AName: PChar; ATop, ALeft, AWidth, AHeight: Integer; AAlign: TAlign);
var
NewWinControl : TWinControl;
begin
NewWinControl := TWinControl.Create(frmMain);
With NewWinControl Do
begin
Name := AName;
Top := ATop;
Left := ALeft;
Width := AWidth;
Height := AHeight;
Align := AAlign;
WindowHandle := AHandle;
Visible := True;
end;
TWinControl(frmMain).InsertControl(NewWinControl);
end;
The issue now is that when the application closes, I get the invalid error AV...I shall continue!!
EDIT 2:
Ok, so it is TWinControl.DestroyWindowHandle that raises the AV because the window handle doesn't exist any more. I'm trying to think of a clean solution.
Derive a new class from TWinControl and override its virtual DestroyWindowHandle() method to not free the HWND you provide. The default implementation of TWinControl.DestroyWindowHandle() calls the Win32 API DestroyWnd() function.

TNetSharingManager access violation problem

I'm trying to compile this project in Delphi 2010, which uses TNetSharingManager. I have imported the type library and tried compiling it, but unfortunately I'm getting an Access Violation in this function:
function TNetSharingManager.GetDefaultInterface: INetSharingManager;
begin
if FIntf = nil then
Connect;
Assert(FIntf nil, 'DefaultInterface is NULL. Component is not connected to Server. You must call "Connect" or "ConnectTo" before this operation');
Result := FIntf;
end;
(part of NETCONLib_TLB)
The error is in : if FIntf = nil then for some odd reason..
The code which is calling it:
procedure TForm1.GetConnectionList(Strings,IdList: TStrings);
var
pEnum: IEnumVariant;
vNetCon: OleVARIANT;
dwRetrieved: Cardinal;
pUser: NETCONLib_TLB.PUserType1;
NetCon : INetConnection;
begin
Strings.Clear;
IdList.Clear;
pEnum := ( NetSharingManager.EnumEveryConnection._NewEnum as IEnumVariant);
while (pEnum.Next(1, vNetCon, dwRetrieved) = S_OK) do
begin
(IUnknown(vNetCon) as INetConnection).GetProperties(pUser);
NetCon := (IUnknown(vNetCon) as INetConnection);
if (pUser.Status in [NCS_CONNECTED,NCS_CONNECTING])//remove if you want disabled NIC cards also
and (pUser.MediaType in [NCM_LAN,NCM_SHAREDACCESSHOST_LAN,NCM_ISDN] )
and (GetMacAddress(GuidToString(pUser.guidId))'' ) then
begin
//we only want valid network cards that are enabled
Strings.Add(pUser.pszwName );
IdList.Add(GuidToString(pUser.guidId));
end;
end;
end;
I don't understand why I cannot compare with nil. Any ideas?
It is likely the TNetSharingManager object itself has actually died (or wasn't created in the first place) when that error is triggered. The FIntF = nil expression is the first reference to an actual field of the class, i.e. it will be pointing into invalid address space.
[Edit] I download the source and followed the steps to import the TLB (Delphi 2010). To execute the appilcation, I had to (a) run Delphi as an admin, because I'm not a power user by default and (b) had to add a check for pUser <> nil because the final getProperties returns a nil-structure, but other than that the code run fine. So unfortunately, I can't seem to reproduce your problem.
Rereading your question, are you getting an AV while compiling?

Memory leak using WMI in Delphi 7

I'm experiencing a memory leak when using WMI from Delphi 7 to query a (remote) pc. The memory leak only occurs on Windows 2003 (and Windows XP 64). Windows 2000 is fine, and so is Windows 2008. I'm wondering if anyone has experienced a similar problem.
The fact that the leak only occurs in certain versions of Windows implies that it might be a Windows issue, but I've been searching the web and haven't been able to locate a hotfix to resolve the issue. Also, it might be a Delphi issue, since a program with similar functionality in C# doesn't seem to have this leak. The latter fact has led me to believe that there might be another, better, way to get the information I need in Delphi without getting a memory leak.
I've included the source to a small program to expose the memory leak below. If the line sObject.Path_ below the { Leak! } comment is executed, the memory leak occurs. If I comment it out, there's no leak. (Obviously, in the "real" program, I do something useful with the result of the sObject.Path_ method call :).)
With a little quick 'n dirty Windows Task Manager profiling on my machine, I found the following:
Before N=100 N=500 N=1000
With sObject.Path_ 3.7M 7.9M 18.2M 31.2M
Without sObject.Path_ 3.7M 5.3M 5.4M 5.3M
I guess my question is: has anyone else encountered this problem? If so, is it indeed a Windows issue, and is there a hotfix? Or (more likely) is my Delphi code broken, and is there a better way to get the information I need?
You'll notice on several occasions, nil is assigned to objects, contrary to the Delphi spirit... These are COM objects that do not inherit from TObject, and have no destructor I can call. By assigning nil to them, Windows's garbage collector cleans them up.
program ConsoleMemoryLeak;
{$APPTYPE CONSOLE}
uses
Variants, ActiveX, WbemScripting_TLB;
const
N = 100;
WMIQuery = 'SELECT * FROM Win32_Process';
Host = 'localhost';
{ Must be empty when scanning localhost }
Username = '';
Password = '';
procedure ProcessObjectSet(WMIObjectSet: ISWbemObjectSet);
var
Enum: IEnumVariant;
tempObj: OleVariant;
Value: Cardinal;
sObject: ISWbemObject;
begin
Enum := (wmiObjectSet._NewEnum) as IEnumVariant;
while (Enum.Next(1, tempObj, Value) = S_OK) do
begin
sObject := IUnknown(tempObj) as SWBemObject;
{ Leak! }
sObject.Path_;
sObject := nil;
tempObj := Unassigned;
end;
Enum := nil;
end;
function ExecuteQuery: ISWbemObjectSet;
var
Locator: ISWbemLocator;
Services: ISWbemServices;
begin
Locator := CoSWbemLocator.Create;
Services := Locator.ConnectServer(Host, 'root\CIMV2',
Username, Password, '', '', 0, nil);
Result := Services.ExecQuery(WMIQuery, 'WQL',
wbemFlagReturnImmediately and wbemFlagForwardOnly, nil);
Services := nil;
Locator := nil;
end;
procedure DoQuery;
var
ObjectSet: ISWbemObjectSet;
begin
CoInitialize(nil);
ObjectSet := ExecuteQuery;
ProcessObjectSet(ObjectSet);
ObjectSet := nil;
CoUninitialize;
end;
var
i: Integer;
begin
WriteLn('Press Enter to start');
ReadLn;
for i := 1 to N do
DoQuery;
WriteLn('Press Enter to end');
ReadLn;
end.
I can reproduce the behaviour, the code leaks memory on Windows XP 64 and does not on Windows XP. Interestingly this occurs only if the Path_ property is read, reading Properties_ or Security_ with the same code does not leak any memory. A Windows-version-specific problem in WMI looks like the most probable cause of this. My system is up-to-date AFAIK, so there probably isn't a hotfix for this either.
I'd like to comment on your resetting all variant and interface variables, though. You write
You'll notice on several occasions, nil is assigned to objects, contrary to the Delphi spirit... These are COM objects that do not inherit from TObject, and have no destructor I can call. By assigning nil to them, Windows's garbage collector cleans them up.
This is not true, and consequently there is no need to set the variables to nil and Unassigned. Windows does not have a garbage collector, what you are dealing with are reference-counted objects, which are immediately destroyed once the reference count reaches 0. The Delphi compiler does insert the necessary calls to increment and decrement the reference count as necessary. Your assignments to nil and Unassigned decrement the reference count, and free the object when it reaches 0.
A new assignment to a variable, or the exiting of the procedure take care of this as well, so additional assignments are (albeit not wrong) superfluous and decrease the clarity of the code. The following code is completely equivalent and does not leak any additional memory:
procedure ProcessObjectSet(WMIObjectSet: ISWbemObjectSet);
var
Enum: IEnumVariant;
tempObj: OleVariant;
Value: Cardinal;
sObject: ISWbemObject;
begin
Enum := (wmiObjectSet._NewEnum) as IEnumVariant;
while (Enum.Next(1, tempObj, Value) = S_OK) do
begin
sObject := IUnknown(tempObj) as SWBemObject;
{ Leak! }
sObject.Path_;
end;
end;
I'd say one should explicitly reset interfaces only if this does actually free the object (so the current ref count has to be 1) and the destruction itself should really happen exactly at this point. Examples for the latter are that a large chunk of memory can be freed, or that a file needs to be closed or a synchronization object to be released.
you should store the return value of
sObject.Path_;
in a variable and make it SWbemObjectPath. This is necessary to make the reference counting right.

Resources