We have our own glue-layer-code-thingamajig that allows us to host the .NET runtime in our Win32 Delphi program. This has allowed us to do a gradual transition to .NET over time.
But, we have some problems with it from time to time, and yesterday I saw an answer here on SO that referred to Jcl's .NET host implementation, so I thought I'd take a look to see if there's some obvious differences.
Turns out there is, but I don't understand what it does, why, and whether I need to do the same. I'll certainly try it, but I'd very much like for someone else that understands the reason behind this odd code to tell me what it does.
In time, we might switch to using the Jcl implementation, but since we have an impending release, unless it's absolutely necessary in order to fix the current problems, then a major overhaul at this level of the code is not justified, so please don't just suggest we switch.
Anyway, the difference revolves around how they call into the .NET functions to load and bind to the .NET runtime, basically how they call the exported functions from the .NET dll.
Here's my code:
type
TCorBindToRuntimeEx = function(pwszVersion: PWideChar;
pwszBuildFlavor: PWideChar;
startupFlags: DWord; rclsid, riid: PGUID;
out ppv: IUnknown): Integer; stdcall;
...
var
CorBindToRuntimeEx : TCorBindtoRuntimeEx = nil;
...
CorBindToRuntimeEx := GetProcAddress(Runtimehandle, 'CorBindToRuntimeEx');
...
clsid := CLASS_CorRuntimeHost;
iid := IID_ICorRuntimeHost;
rc := CorBindToRuntimeEx('v2.0.50727', 'wks', 0, @clsid,
@iid, UnkRuntimeEngine);
Now, here I simply use GetProcAddress to load the address of the exported function into a variable, typed to be a stdcall
function pointer, and then I call it. This works, sort of. As I said, some problems with odd error messages in a few cases.
Ok, here's their code, and pay particular attention to the function with the assembler code.
function CorBindToRuntimeEx(pwszVersion, pwszBuildFlavor: PWideChar;
startupFlags: DWORD; const rclsid: TCLSID; const riid: TIID;
out pv): HRESULT; stdcall;
{$EXTERNALSYM CorBindToRuntimeEx}
...
var
_CorBindToRuntimeEx: Pointer = nil;
function CorBindToRuntimeEx;
begin
GetProcedureAddress(_CorBindToRuntimeEx, mscoree_dll,
'CorBindToRuntimeEx');
asm
mov esp, ebp
pop ebp
jmp [_CorBindToRuntimeEx]
end;
end;
...
OleCheck(CorBindToRuntimeEx(PWideCharOrNil(ClrVer),
PWideChar(ClrHostFlavorNames[Flavor]), Flags,
CLASS_CorRuntimeHost, IID_ICorRuntimeHost,
FDefaultInterface));
Note that I've reformatted the code slightly to avoid horizontal scrollbars here on SO, but only to add a few linebreaks and some indentation, the code is as-is.
The final call is probably irrelevant, it's basically going to pass the same parameters that we do (note that we pass 0 as the options value, but we've also tried with the same specific arguments the Jcl code uses, and the problems are still present).
So, my question is, what the he** does the assembler code do? I know what it does in the technical sense, I've been programming assembly before, so it manipulates the stack pointers.
The question is why does it have to do this. I just don't get it.
Could it be that the stack-frames are not quite stdcall
after all?
Please teach me something today.
Edit: Ok, changed my code accordingly, but the problem we have still exists, so that wasn't it. Looks like I'll be doing some WinDbg digging into third-party code after all.