I am trying to use a disconnected ADO Recordset in XE6. The idea is that you open the recordset normally, then you set the recordset's ActiveConnection to your language's equivalent of null/Nothing/nil:
rs.Set_ActiveConnection(null);
The following example from Delphi 5 works fine:
var rs: _Recordset;
rs := CoRecordset.Create;
rs.CursorLocation := adUseClient; //the default for a Recordset is adUseServer (Connection.Execute's default is adUseClient)
rs.CursorType := adOpenForwardOnly; //the default
rs.Open(CommandText, Conn,
adOpenForwardOnly, //CursorType
adLockReadOnly, //LockType
adCmdText);
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(nil);
It works in Delphi 5
The issue is that I cannot make it work in Delphi XE6. In Delphi 5 i would successfully call:
rs.Set_ActiveConnection(nil);
and everything worked splendidly. It worked because _Recordset interface was declared as:
procedure Set_ActiveConnection(const pvar: IDispatch); safecall;
So it was valid to pass nil; and it worked.
In XE6 the delcaration changed to:
procedure Set_ActiveConnection(pvar: OleVariant); safecall;
To which you cannot pass nil. The question then becomes, what is the OleVariant equivalent of nil?
Try #1
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(nil); //E2010 Incompatible types: 'OleVariant' and 'Pointer'
Try #2
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(Null);
causes exception:
Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another
Try #3
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(EmptyParam);
causes exception:
Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another
Try #4
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(Unassigned);
causes exception:
Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another
Try #5
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(OleVariant(nil)); //E2089 Invalid typecast
Try #6
//Disconnect the recordset by setting the .ActiveConnection to null
rs.Set_ActiveConnection(OleVariant(Null));
causes exception:
Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another
Try #7
It's clear to me that Codebarcadero got the declaration wrong. It really is supposed to be an IDispatch. This means i need to trick the compiler into passing an OleVariant located at address 0x00000000 (i.e. nil). That way ADO will see the value 0x00000000 on the stack, and know i mean null:
rs.Set_ActiveConnection(POleVariant(nil)^); //access violation before call
I'm sure Bo..Imp...Co..Embarcadero has the intended way to call this; i just cannot figure it out.
Delphi 5 assembly
Dephi 5 does the correct thing; it pushes $00 (i.e. nil) onto the stack:
rs.Set_ActiveConnection(nil);
push $0 ;push nil
mov eax,[ebp-$08] ;get address of rs
push eax ;push "this"
mov eax,[eax] ;get VMT of IRecordset
call dword ptr [eax+$28] ;call offset $28 of VMT
Whereas Delphi XE6 is going through heroic efforts to do something i don't know what:
rs.Set_ActiveConnection(nil);
lea eax,[ebp-$000000d8]
call Null
lea edx,[ebp-$000000d8]
lea eax,[ebp-$000000c8]
call #OleVarFromVar
push dword ptr [ebp-$000000bc]
push dword ptr [ebp-$000000c0]
push dword ptr [ebp-$000000c4]
push dword ptr [ebp-$000000c8]
mov eax,[ebp-$04]
push eax
mov eax,[eax]
call dword ptr [eax+$2c]
Bonus Reading
MSDN: Disconnecting and Reconnecting the Recordset
KB184397: How To Create ADO Disconnected Recordsets in VBA/C++/Java
MSDN: Recordset Object (ADO)
MSDN: ActiveConnection Property (ADO)
In D7 (don't have D5 to hand), AdoInt.Pas contains two flavours of Set_ActiveConnection, e.g.
Recordset15 = interface(_ADO)
['{0000050E-0000-0010-8000-00AA006D2EA4}']
procedure Set_ActiveConnection(const pvar: IDispatch); safecall;
procedure _Set_ActiveConnection(pvar: OleVariant); safecall;
and in Delphi XE6:
Recordset15 = interface(_ADO)
['{0000050E-0000-0010-8000-00AA006D2EA4}']
//...
procedure _Set_ActiveConnection(const pvar: IDispatch); safecall;
procedure Set_ActiveConnection(pvar: OleVariant); safecall;
So try the other version in XE6. Personally, I'd have tried
Set_ActiveConnection(IDispatch(Nil))
first, but you say in comments that _Set_ActiveConnection works for you.
The reason I'd have tried Set_ActiveConnection(IDispatch(Nil)) first, for an interface which requires an OleVariant to be passed, is this: Ever since interfaces were added into Delphi (in D3?), iirc in the version after variant-based OLE automation was added (D2), the compiler has known how to generate code to convert in both directions between an OleVariant and an IDispatch interface. So the "problem" is how to pass an IDispatch interface as an OleVariant argument. That bit, to my simple-minded way of looking at it, is easy, just write IDispatch() where the argument is supposed to be an OleVariant, and leave the compiler to sort out the code to generate. And if the value we want to pass as the IDisaptch interface is actually Nil, we just need to write
SomeInterfaceMemberExpectingAnOleVariant(IDispatch(Nil))
Related
Hello using code below to call a function with asm and after call read the result. That works fine. Now,- how to read the argument changes afterwards, best regards
//push arguments:
for i:=lst.cnt downto 0 do begin //reverse order
ia:=longword(lst.fList^[i]);
asm push ia end; //push 32-Bit argument
end;
asm
call proc //call the routine
mov ia, eax //get result right after the call
end;
//how to read variable arguments here ?
edit:
The initial code has run for some time without any issues, even when using the loop. The reason for this question came, when i had to call a winapi routine like this:
function GetWindowRect(hWnd: HWND; var lpRect: TRect): BOOL; stdcall;
external user32 name 'GetWindowRect';
It has declared a variable record argument "lpRect" (size: 16 bytes). I was trying to (push) these 4 integers before call, and then read them after call. Turns out just passing a pointer to the data actually worked in this case.
Conclusion: So there is no such thing variable arguments, only pointers to value or record. Thanks for any inputs helped getting to this conclusion
If you pass the parameters by value, then list.fList^[i] contains the pointer to the value.
Just unreference and use it, for instance PInteger(list.fList^[i])^ for an integer parameter passed by reference (as var).
But note that your code is pretty awful, and using push within the loop is very risky. It would also need to follow the calling convention - a proc function defined with default Delphi register passes its first parameters into eax/ecx/edx. Check this reference material - your question seems to indicate that you are a bit confused with how it works.
In Delphi, how do I find out the the address of a COM method?
I can hardcode the offsets
//0 is the offset of the QueryInterface method
p := TPonterArray(pointer(SomeInterface)^)[0];
but I would prefer to use symbolic names. The folllowing obviously does not work:
var M : TMethod;
...
M := TMethod(SomeInterface.QueryInterface);
Thanks!
You can use the vmtoffset assembler directive to get the byte offset of an interface method relative to the start of the interface's method table. Take a look at the implementation of _IntfCast in System.pas, for example:
call dword ptr [eax] + vmtoffset IInterface.QueryInterface
...
call dword ptr [eax] + vmtoffset IInterface._Release
The first expression adds 0; the second, 8.
You cannot parameterize those expressions, though. They're compile-time constants, so you cannot choose which method you want at run time. You need to have all possible method names represented in advance.
All you really need to hook is QueryInterface. Once you have that, you can return whatever proxy object you want that can intercept calls to all the other methods.
I don't think Delphi supports that. Hardcoding the offsets is probably the only thing that will work, since the compiler doesn't count interface methods as symbols whose value can be assigned to a function pointer, the way object methods or standalone functions can.
Why are you trying to do this, BTW?
Your code is wrong because an interface reference is not a pointer to an interface method table but a pointer to pointer to an interface method table. That is how Delphi interfaces are implemented on binary level. It is hard to say more and point out to the error in your code because you have not given a code example that can be compiled. Use the following code to convert interface reference to method pointer correctly, the idea was taken from Barry Kelly's demonstration of creating a method pointer from a method reference:
procedure IntRefToMethPtr(const IntRef; var MethPtr; MethNo: Integer);
type
TVtable = array[0..999] of Pointer;
PVtable = ^TVtable;
PPVtable = ^PVtable;
begin
// QI=0, AddRef=1, Release=2, etc
TMethod(MethPtr).Code := PPVtable(IntRef)^^[MethNo];
TMethod(MethPtr).Data := Pointer(IntRef);
end;
If you prefer symbolic names for MethNo you are better to declare them yourself as offset constants
Two additional directives allow assembly code to access dynamic and
virtual methods: VMTOFFSET and DMTINDEX.
VMTOFFSET retrieves the offset in bytes of the virtual method pointer
table entry of the virtual method argument from the beginning of the
virtual method table (VMT). This directive needs a fully specified
class name with a method name as a parameter (for example,
TExample.VirtualMethod), or an interface name and an interface method
name.
DMTINDEX retrieves the dynamic method table index of the passed
dynamic method. This directive also needs a fully specified class name
with a method name as a parameter, for example,
TExample.DynamicMethod. To invoke the dynamic method, call
System.#CallDynaInst with the (E)SI register containing the value
obtained from DMTINDEX.
docwiki.embarcadero.com
Here the code to get the needed method pointer
function GetMethodPointer(const IntRef: IInterface): Pointer; assembler;
asm
mov eax, [IntRef]
add eax, vmtoffset ISomeInterface.MemberMethod
mov eax, [eax]
end;
For some reason, trying to create a TLanguages object provided by the SysUtils header by using the singleton or by calling the constructor directly is causing trouble in the wild, where some users report this error (X varies):
Access violation at address X. Write of address X (at address X)
... when the following seemingly innocent line of code is executed:
TLanguages.Create;
To clarify, this is not related to context. I can put this line in any place I like (as the only line of code of an empty program for example), but the problem remains.
The weird part is that this class is part of Delphi's standard headers, which should not fail (right?).
constructor TLanguages.Create;
type
TCallbackThunk = packed record
POPEDX: Byte;
MOVEAX: Byte;
SelfPtr: Pointer;
PUSHEAX: Byte;
PUSHEDX: Byte;
JMP: Byte;
JmpOffset: Integer;
end;
var
Callback: TCallbackThunk;
begin
inherited Create;
Callback.POPEDX := $5A;
Callback.MOVEAX := $B8;
Callback.SelfPtr := Self;
Callback.PUSHEAX := $50;
Callback.PUSHEDX := $52;
Callback.JMP := $E9;
Callback.JmpOffset := Integer(#TLanguages.LocalesCallback) - Integer(#Callback.JMP) - 5;
EnumSystemLocales(TFNLocaleEnumProc(#Callback), LCID_SUPPORTED);
end;
The constructor attempts to use a member function as the EnumSystemLocales callback, which seems to be causing the crashes, because copying the TLanguages.LocalesCallback function to global scope and passing that to EnumSystemLocales works perfectly fine.
The struct contains the following Intel x86 assembly, where each item is given by its opcode:
pop edx
mov eax Self
push eax
push edx
jmp JmpOffset
Can anyone explain how the trick works and tell me why it's not working as expected?
It appears to be a known issue with older Delphi versions, related to DEP, as I guessed in comments to the question. It's clear that the code in the RTL cannot work when DEP is enabled.
Here's a link to confirm the theory: http://codecentral.embarcadero.com/Item/23411
Although that CodeCentral article includes code to fix the problem in Delphi 5, it looks like it will work in Delphi 7 too. The fix works by hooking the SysUtils.Languages function. So make sure you always use that rather than calling TLanguages.Create yourself, for obvious reasons.
I asked a similar question about implicit interface variables not so long ago.
The source of this question was a bug in my code due to me not being aware of the existence of an implicit interface variable created by the compiler. This variable was finalized when the procedure that owned it finished. This in turn caused a bug due to the lifetime of the variable being longer than I had anticipated.
Now, I have a simple project to illustrate some interesting behaviour from the compiler:
program ImplicitInterfaceLocals;
{$APPTYPE CONSOLE}
uses
Classes;
function Create: IInterface;
begin
Result := TInterfacedObject.Create;
end;
procedure StoreToLocal;
var
I: IInterface;
begin
I := Create;
end;
procedure StoreViaPointerToLocal;
var
I: IInterface;
P: ^IInterface;
begin
P := #I;
P^ := Create;
end;
begin
StoreToLocal;
StoreViaPointerToLocal;
end.
StoreToLocal is compiled just as you would imagine. The local variable I, the function's result, is passed as an implicit var parameter to Create. The tidy up for StoreToLocal results in a single call to IntfClear. No surprises there.
However, StoreViaPointerToLocal is treated differently. The compiler creates an implicit local variable which it passes to Create. When Create returns, the assignment to P^ is performed. This leaves the routine with two local variables holding references to the interface. The tidy up for StoreViaPointerToLocal results in two calls to IntfClear.
The compiled code for StoreViaPointerToLocal is like this:
ImplicitInterfaceLocals.dpr.24: begin
00435C50 55 push ebp
00435C51 8BEC mov ebp,esp
00435C53 6A00 push $00
00435C55 6A00 push $00
00435C57 6A00 push $00
00435C59 33C0 xor eax,eax
00435C5B 55 push ebp
00435C5C 689E5C4300 push $00435c9e
00435C61 64FF30 push dword ptr fs:[eax]
00435C64 648920 mov fs:[eax],esp
ImplicitInterfaceLocals.dpr.25: P := #I;
00435C67 8D45FC lea eax,[ebp-$04]
00435C6A 8945F8 mov [ebp-$08],eax
ImplicitInterfaceLocals.dpr.26: P^ := Create;
00435C6D 8D45F4 lea eax,[ebp-$0c]
00435C70 E873FFFFFF call Create
00435C75 8B55F4 mov edx,[ebp-$0c]
00435C78 8B45F8 mov eax,[ebp-$08]
00435C7B E81032FDFF call #IntfCopy
ImplicitInterfaceLocals.dpr.27: end;
00435C80 33C0 xor eax,eax
00435C82 5A pop edx
00435C83 59 pop ecx
00435C84 59 pop ecx
00435C85 648910 mov fs:[eax],edx
00435C88 68A55C4300 push $00435ca5
00435C8D 8D45F4 lea eax,[ebp-$0c]
00435C90 E8E331FDFF call #IntfClear
00435C95 8D45FC lea eax,[ebp-$04]
00435C98 E8DB31FDFF call #IntfClear
00435C9D C3 ret
I can guess as to why the compiler is doing this. When it can prove that assigning to the result variable will not raise an exception (i.e. if the variable is a local) then it uses the result variable directly. Otherwise it uses an implicit local and copies the interface once the function has returned thus ensuring that we don't leak the reference in case of an exception.
But I cannot find any statement of this in the documentation. It matters because interface lifetime is important and as a programmer you need to be able to influence it on occasion.
So, does anybody know if there is any documentation of this behaviour? If not does anyone have any more knowledge of it? How are instance fields handled, I have not checked that yet. Of course I could try it all out for myself but I'm looking for a more formal statement and always prefer to avoid relying on implementation detail worked out by trial and error.
Update 1
To answer Remy's question, it mattered to me when I needed to finalize the object behind the interface before carrying out another finalization.
begin
AcquirePythonGIL;
try
PyObject := CreatePythonObject;
try
//do stuff with PyObject
finally
Finalize(PyObject);
end;
finally
ReleasePythonGIL;
end;
end;
As written like this it is fine. But in the real code I had a second implicit local which was finalized after the GIL was released and that bombed. I solved the problem by extracting the code inside the Acquire/Release GIL into a separate method and thus narrowed the scope of the interface variable.
If there is any documentation of this behavior, it will probably be in the area of compiler production of temporary variables to hold intermediate results when passing function results as parameters. Consider this code:
procedure UseInterface(foo: IInterface);
begin
end;
procedure Test()
begin
UseInterface(Create());
end;
The compiler has to create an implicit temp variable to hold the result of Create as it is passed into UseInterface, to make sure that the interface has a lifetime >= the lifetime of the UseInterface call. That implicit temp variable will be disposed at the end of the procedure that owns it, in this case at the end of the Test() procedure.
It's possible that your pointer assignment case may fall into the same bucket as passing intermediate interface values as function parameters, since the compiler can't "see" where the value is going.
I recall there have been a few bugs in this area over the years. Long ago (D3? D4?), the compiler didn't reference count the intermediate value at all. It worked most of the time, but got into trouble in parameter alias situations. Once that was addressed there was a follow up regarding const params, I believe. There was always a desire to move disposal of the intermediate value interface up to as soon as possible after the statement in which it was needed, but I don't think that ever got implemented in the Win32 optimizer because the compiler just wasn't set up for handling disposal at statement or block granularity.
You can not guarantee that compiler will not decide to create a temporal invisible variable.
And even if you do, the turned off optimization (or even stack frames?) may mess up your perfectly checked code.
And even if you manage to review your code under all possible combinations of project options - compiling your code under something like Lazarus or even new Delphi version will bring hell back.
A best bet would be to use "internal variables can not outlive routine" rule. We usually do not know, if compiler would create some internal variables or not, but we do know, that any such variables (if created) would be finalized when routine exists.
Therefore, if you have code like this:
// 1. Some code which may (or may not) create invisible variables
// 2. Some code which requires release of reference-counted data
E.g.:
Lib := LoadLibrary(Lib, 'xyz');
try
// Create interface
P := GetProcAddress(Lib, 'xyz');
I := P;
// Work with interface
finally
// Something that requires all interfaces to be released
FreeLibrary(Lib); // <- May be not OK
end;
Then you should just wrap "Work with interface" block into subroutine:
procedure Work(const Lib: HModule);
begin
// Create interface
P := GetProcAddress(Lib, 'xyz');
I := P;
// Work with interface
end; // <- Releases hidden variables (if any exist)
Lib := LoadLibrary(Lib, 'xyz');
try
Work(Lib);
finally
// Something that requires all interfaces to be released
FreeLibrary(Lib); // <- OK!
end;
It is a simple, but effective rule.
In Delphi, how do I find out the the address of a COM method?
I can hardcode the offsets
//0 is the offset of the QueryInterface method
p := TPonterArray(pointer(SomeInterface)^)[0];
but I would prefer to use symbolic names. The folllowing obviously does not work:
var M : TMethod;
...
M := TMethod(SomeInterface.QueryInterface);
Thanks!
You can use the vmtoffset assembler directive to get the byte offset of an interface method relative to the start of the interface's method table. Take a look at the implementation of _IntfCast in System.pas, for example:
call dword ptr [eax] + vmtoffset IInterface.QueryInterface
...
call dword ptr [eax] + vmtoffset IInterface._Release
The first expression adds 0; the second, 8.
You cannot parameterize those expressions, though. They're compile-time constants, so you cannot choose which method you want at run time. You need to have all possible method names represented in advance.
All you really need to hook is QueryInterface. Once you have that, you can return whatever proxy object you want that can intercept calls to all the other methods.
I don't think Delphi supports that. Hardcoding the offsets is probably the only thing that will work, since the compiler doesn't count interface methods as symbols whose value can be assigned to a function pointer, the way object methods or standalone functions can.
Why are you trying to do this, BTW?
Your code is wrong because an interface reference is not a pointer to an interface method table but a pointer to pointer to an interface method table. That is how Delphi interfaces are implemented on binary level. It is hard to say more and point out to the error in your code because you have not given a code example that can be compiled. Use the following code to convert interface reference to method pointer correctly, the idea was taken from Barry Kelly's demonstration of creating a method pointer from a method reference:
procedure IntRefToMethPtr(const IntRef; var MethPtr; MethNo: Integer);
type
TVtable = array[0..999] of Pointer;
PVtable = ^TVtable;
PPVtable = ^PVtable;
begin
// QI=0, AddRef=1, Release=2, etc
TMethod(MethPtr).Code := PPVtable(IntRef)^^[MethNo];
TMethod(MethPtr).Data := Pointer(IntRef);
end;
If you prefer symbolic names for MethNo you are better to declare them yourself as offset constants
Two additional directives allow assembly code to access dynamic and
virtual methods: VMTOFFSET and DMTINDEX.
VMTOFFSET retrieves the offset in bytes of the virtual method pointer
table entry of the virtual method argument from the beginning of the
virtual method table (VMT). This directive needs a fully specified
class name with a method name as a parameter (for example,
TExample.VirtualMethod), or an interface name and an interface method
name.
DMTINDEX retrieves the dynamic method table index of the passed
dynamic method. This directive also needs a fully specified class name
with a method name as a parameter, for example,
TExample.DynamicMethod. To invoke the dynamic method, call
System.#CallDynaInst with the (E)SI register containing the value
obtained from DMTINDEX.
docwiki.embarcadero.com
Here the code to get the needed method pointer
function GetMethodPointer(const IntRef: IInterface): Pointer; assembler;
asm
mov eax, [IntRef]
add eax, vmtoffset ISomeInterface.MemberMethod
mov eax, [eax]
end;