I have put together this code for viewing Routing Table in My delphi App.
PMIB_IPFORWARDROW = ^TMibIpForwardRow;
TMibIpForwardRow=packed record
dwForwardDest:DWORD;
dwForwardMask:DWORD;
dwForwardPolicy:DWORD;
dwForwardNextHop:DWORD;
dwForwardIfIndex:DWORD;
dwForwardType:DWORD;
dwForwardProto:DWORD;
dwForwardAge:DWORD;
dwForwardNextHopAS:DWORD;
dwForwardMetric1:DWORD;
dwForwardMetric2:DWORD;
dwForwardMetric3:DWORD;
dwForwardMetric4:DWORD;
dwForwardMetric5:DWORD;
end;
PMIB_IPFORWARDTABLE=^TMibIpForwardTable ;
TMibIpForwardTable=packed record
dwNumEntries:DWORD;
table:array [0..ANY_SIZE-1] of TMibIpForwardRow;
end;
The Declaration of the Function is :
getIpForwardTable: function (pIpForwardTable:PMIB_IPFORWARDTABLE; pdwSize:PULONG; bOrder:BOOL):DWORD; stdcall;
And the Function for Implementation is:
procedure GetRouteTable;
var
Error:DWORD;
pRouteTable:PMIB_IPFORWARDTABLE ;
dwSize:ULONG;
//dwSize:ULONG;
TableSize:Integer;
begin
try
dwSize:=0;
GetMem(pRouteTable,SizeOf(TMibIpForwardTable));
Error:=GetIpForwardTable(pRouteTable,#dwSize,FALSE);// Error at this line
if Error=NO_ERROR then
begin
ShowMessage(IntToStr(Error));
end
else
ShowMessage(IntToStr(Error));
except
on E:Exception do
ShowMessage(E.ClassName+':'+E.Message);
end;
end;
I am getting Error code=122. Please help in correcting this Error.
Thanks in Advance
edit
I have tried this combo too but it didnt work
Error:=GetIpForwardTable(nil,#dwSize,FALSE);
if Error=ERROR_INSUFFICIENT_BUFFER then
ShowMessage(IntToStr(Error))
else
begin
GetMem(pRouteTable,dwSize);
Error:=getIpForwardTable(pRouteTable,#dwSize,False);
if Error=NO_ERROR then
begin
ShowMessage(IntToStr(Error));
end
else
ShowMessage(IntToStr(Error));
Error code 122 is ERROR_INSUFFICIENT_BUFFER. The documentation for GetIpForwardTable states that this error code is returned when the following occurs.
The buffer pointed to by the pIpForwardTable parameter is not large enough. The required size is returned in the DWORD variable pointed to by the pdwSize parameter.
The solution is to read the value of dwSize that is returned, and call again with a buffer of that size.
The other problem with your code is that you do not pass in a valid buffer. Notice that the variable pRouteTable is never assigned to. In your edit, you do call GetMem to allocate it, but in the wrong place. You need something like this:
Error := GetIpForwardTable(nil, #dwSize, False);
if Error<>ERROR_INSUFFICIENT_BUFFER then
RaiseLastOSError(Error);
GetMem(pRouteTable,dwSize);
Error := GetIpForwardTable(pRouteTable, #dwSize, False);
if Error<>ERROR_SUCCESS then
RaiseLastOSError(Error);
Related
In addition to this question I have made some tests and researches on the docwiki. My conclusion is that this kind of code should work without memory leaks:
function testResultObject: TClassA;
begin
Result := TClassA.Create;
Result.DoSomething;
end;
And then somewhere I can call the above code in this manner:
var k: TClassA;
begin
k := testResultObject;
try
//code code code
finally
k.Free;
end;
end;
As Remy suggested in the answer it's better to avoid this way of doing things and instead use something like testResultObject(x: TClassA): boolean. In this case the return true/false can tell me if everything went fine and I am passing an object already created.
Look at this code:
function testResultObject: TClassA;
begin
Result := TClassA.Create;
try
Result.DoSomething;
except
Result.Free;
end;
end;
The problem with the first version above of the function is that DoSomething could raise an exception and if so I'll leak memory. Can the second implementation with try-except be a solution? For sure later I'll have to check if the result is assigned or nil.
I agree that (as already said above) the testResultObject(x: TClassA): boolean would be better. I was just wondering if the return-a-class function way could be fixed as I've written.
Your code has serious problems. In case of an error, it swallows the exception, and returns an invalid object reference.
This is easy to fix. The canonical way is as follows:
function testResultObject: TClassA;
begin
Result := TClassA.Create;
try
Result.DoSomething;
except
Result.Free;
raise;
end;
end;
Either the function succeeds and returns a new object. Or it fails, cleans up after itself, and raises an exception.
In other words, this function looks and behaves just like a constructor. You consume it in the same way:
obj := testResultObject;
try
// do things with obj
finally
obj.Free;
end;
Your second approach works, but has 2 serious problems.
By swallowing all exceptions, (as J pointed out) you'll hide the fact that something went wrong.
There's no indication to the caller that you've created an object that the caller is responsible for destroying. This makes using the function more error prone; and easier to cause memory leaks.
I would recommend the following improvement on your second approach:
{Name has a clue that caller should take ownership of a new object returned}
function CreateObjectA: TClassA;
begin
{Once object is successfully created, internal resource protection is required:
- if no error, it is callers responsibility to destroy the returned object
- if error, caller must assume creation *failed* so must destroy object here
Also, by assigning Result of successful Create before *try*:
The object (reference) is returned
**if-and-only-if**
This function returns 'normally' (i.e. no exception state)}
Result := TClassA.Create;
try
Result.DoSomething; {that could fail}
except
{Cleanup only if something goes wrong:
caller should not be responsible for errors *within* this method}
Result.Free;
{Re-raise the exception to notify caller:
exception state means caller does not "receive" Result...
code jumps to next finally or except block}
raise;
end;
end;
The most important benefit of the above create function is that: as far as any caller/client code is concerned, it behaves exactly like a normal TObject.Create.
And so the correct usage pattern is exactly the same.
Note that I'm not keen on J's FreeAndNil suggestion because if calling code doesn't check if the result was assigned: it is likely to AV. And code that does check the result correctly will be a little messy:
var k: TClassA;
begin
k := testResultObject; {assuming nil result on failed create, next/similar is *required*}
if Assigned(k) then {Note how this differs from normal try finally pattern}
try
//code using k
finally
k.Free;
end;
end;
NB: It's important to note that you cannot ever have your caller simply ignore memory management; which brings me to the next section.
All the above aside, there is much less chance of making careless mistakes if your testResultObject takes an input object that you require the caller to create and manage its lifetime as needed. I'm not sure why you're resisting that approach so much? You cannot get simpler than the following without resorting to a different memory model.
var k: TClassA;
begin
k := TClassA.Create;
try
testResultObject(k); {Where this is simply implemented as k.DoSomething;}
//more code using k
finally
k.Free;
end;
end;
The only problem with this :
function testResultObject: TClassA;
begin
Result := TClassA.Create;
try
Result.DoSomething;
except
Result.Free;
end;
end;
Is that you have no way of knowing whether the function was successful. Freeing an object does not alter the reference; the variable will still point to the (now) invalid memory location where the object used to exist. You must explicitly set the reference to nil if you want the consumer to be able to test if the reference is valid. If you want to use this pattern (having the consumer test for nil) then you would need to do :
try
Result.DoSomething;
except
FreeAndNil(Result);
end;
This way the caller can test the result for nil (using Assigned or otherwise) as you intended. This still isn't a very clean approach, however, since you're still swallowing exceptions. Another solution might be to simply introduce a new constructor or alter the existing one. For example
TFoo = class
public
constructor Create(ADoSomething : boolean = false);
procedure DoSomething;
end;
constructor TClassA.Create(ADoSomething: Boolean = False);
begin
inherited Create;
if ADoSomething then DoSomething;
end;
procedure TClassA.DoSomething;
begin
//
end;
This way you can get rid of all of the exception handling and just call this as :
function testResultObject: TClassA;
begin
Result := TClassA.Create(true);
end;
Since you've now pushed the DoSomething execution into the constructor any exceptions will naturally automatically call the destructor and your memory management problems go away. The other answers also have good solutions.
I am using TcxGridDBBandedTableView and have two columns of type TcxGridDBBandedColumn.
vwABC : TcxGridDBBandedTableView
vwABCField1 : TcxGridDBBandedColumn
vwABCField2 : TcxGridDBBandedColumn
When I change anything in vwABCField1, vwABCField2 values should get cleared. For this I am using OnEditValueChanged property of vwABCField1 like this:
procedure TMyForm.vwABCField1PropertiesEditValueChanged(Sender: TObject);
begin
vwABCField2.EditValue := '';
end;
While debugging, when I come to vwABCField2.EditValue := ''; statement, I never return back and get trapped in infine loop and after some time I get stackoverflow error.
vwABCField2.EditValue := ''; is calling vwABCField1PropertiesEditValueChanged procedure again and again recursively infinite time. I don't know why. I have not declared anything on OnEditValueChanged event of vwABCField2.
Update
If I write anything else in the above function instead of vwABCField2.EditValue := '';, it will be called only once. For example
procedure TMyForm.vwABCField1PropertiesEditValueChanged(Sender:TObject);
begin
ShowMessage("hi");
end;
works fine. So I suspect that culprit is vwABCField2.EditValue := ''; statement.
As in the official documentation is stated:
Do not change the edit value in your OnEditValueChanged event handler, as this can result in stack overflow. Use this event to get notification that the edit value has changed.
Because when you change the edit value in this event, of course, your editvalue is changed and therefore calling the OnEditValueChanged event again and again and ...
I have encountered an access violation error [Access violation at address 5005F6E in module 'rtl170'. Read of address 0000000A] using the IInterfaceList.
My code looks something like this:
if not Assigned(FoRoutingCodes) then Exit;
for i := 0 to FoRoutingCodes.nCount - 1 do
begin
...
end;
the declaration of FoRoutingCodes is:
FoRoutingCodes : IRoutingCodeList;
and the definition of IRoutingCodeList
IRoutingCodeList = interface
function GetCount: Integer;
...
property nCount: Integer read GetCount;
end;
the implementation of nCount property is:
function TRoutingCodeList.GetCount: Integer;
begin
Result := 0;
if Assigned(FoItems) then
Result := FoItems.Count; // here i get the access violation error
end;
where TRoutingCodeList is the implementation of IRoutingCodeList and FoItems is declared as:
FoItems: IInterfaceList;
I have fixed this using
FoItems: TList<IRoutingCode>;
instead of
FoItems: IInterfaceList;
I am new to delphi, can anyone help me understand what was wrong with previous approach.
I don't know if this is relevant because there are many other changes is our product, but this issue appeared only after we moved from Delphi XE to Delphi XE3.
Thanks in advance.
Update: in response to Uwe Raabe
I have changed FoItems initialisation from
constructor TRoutingCodeList.Create;
begin
FoItems := TInterfaceList.Create;
end;
to
constructor TRoutingCodeList.Create;
begin
FoItems := TList<IRoutingCode>.Create;
end;
Considering only the code you posted so far, I can´t see the problem. However, since you know the exact location of the error, my advice is to set a breakpoint at that line in code and evaluate (CTRL+F7) the member FoItems to see what is it.
Usually, when I suspect I have a bad object reference, I evalute it´s ClassName (in your case, FoItems.ClassName). If the reference is invalid, this method usually raises an exception of returns a strange name.
Also, set a breakpoint in the constructor in order to be sure that FoItems is indeed being instantiated.
I Hope this helps!
I often use code along the lines of:
function GetNumber(Handle : THandle) : Integer;
begin
FLock.BeginRead;
try
if FMap.TryGetValue(Handle, Object) then
raise EArgumentException.Create('Invalid handle');
Result := Object.Number;
finally
FLock.EndRead;
end;
end;
Unfortunately the compiler gives me a warning for all these methods:
[DCC Warning] Unit.pas(1012): W1035 Return value of function 'GetNumber' might be undefined
I know this warning, but in this case I can't see any reason for it at all. Or is there a scenario that I am missing that would result in an undefined result value? I understand the warning in the case of try..except but for try..finally it does not make sense to me.
Questions:
Is there any reason for the warning?
How can I get rid of it (moving the Result := Object.Number line out of the lock is not an option, and I want to avoid writing an completely unnecessary Result := 0 line at the top of each function)
Thanks!
Is there any reason for the warning?
I can't see one but it is there because of the raise
How can I get rid of it (moving the Result := Object.Name line out of
the lock is not an option, and I want
to avoid writing an completely
unncessary Result := 0 line at the top
of each function)
Move the raise statement to a procedure of it's own.
function GetNumber(Handle : THandle) : Integer;
procedure InvHandle;
begin
raise EArgumentException.Create('Invalid handle');
end;
begin
FLock.BeginRead;
try
if FMap.TryGetValue(Handle, Object) then
InvHandle;
Result := Object.Number;
finally
FLock.EndRead;
end;
end;
That is a compiler bug. If the try/finally is removed then the warning is not emitted. The compiler has long been able to recognise that a raise absolves the coder of the obligation to assign the return value. For whatever reason the try/finally appears to confuse its analysis.
On second thoughts perhaps it's not a compiler bug. What if the code in the finally block stopped the exception propagating? Clearly there's not an except handler, but I rather suspect that one of the exception support routines in the System or SysUtils unit may be able to stop the exception progressing further.
An assignment
Result := ...;
in the finally block is missing.
I've declared the following function:
function next(current, next: string): Integer;
begin
form1.Label1.Caption := next;
form1.Label2.Caption := current;
form1.label3.Caption := clipboard.AsText+inttostr(c);
Result:=1;
end;
I try to execute it with this code:
if label1.Caption = '' then res := next('current', 'next');
I am getting the following error:
[Error] Unit1.pas(47): E2034 Too many
actual parameters
I think all parameters are good, so why am I getting that error?
I just tried your code on both Delphi 7 and Delphi 2010. If it works on those two, it should also work on Delphi 2005.
Conclusion: Delphi wants to use a different version of the "next" routine, because of code scope/visibility. Try ctrl+click-ing on "next" in "res := next();" and see where Delphi takes you. Alternatively post more code so we can tell you why Delphi is not choosing your version of the "next" routine. Ideally you should post a whole unit, starting from "unit name" to the final "end."
As specified by Cosmin Prund, the problem is because of the visibility.
TForm has a procedure with name Next which wont accept any parameters.
Your function uses the same name and as you are calling the function in TForm1 class implementation, compiler is treating the call as TForm1.Next and hence it was giving error.
To solve the problem, precede the unit name before the function name i.e., Unit1.Next().
So this should be your code
if label1.Caption = '' then res := Unit1.next('current', 'next');