MSTSCLib_TLB \ OleCtrls 修正
delphi7 导入RDP AcitveX 生成的MSTSCLib_TLB.pas
delphi真的好惨啊,资料都是其他语言的。用到都得自己转换,自身导入生成的还有问题。或者用法根本就不一样。
第一处:
生成的是这样的: IMsRdpClientNonScriptable = interface(IMsTscNonScriptable) ['{2F079C4C-87B2-4AFD-97AB-20CDB43038AE}'] function NotifyRedirectDeviceChange(wParam: UINT_PTR; lParam: LONG_PTR): HResult; stdcall; function SendKeys(numKeys: Integer; var pbArrayKeyUp: WordBool; var plKeyData: Integer): HResult; stdcall; end; 通过导入生成的方法,是一次只能输入一个。不能多个一起输入。 根据微软的文档: https://learn.microsoft.com/vi-vn/windows/win32/termserv/imsrdpclientnonscriptable-sendkeys 这两个参数是数组才对,查阅资料后要改成下面这样 IMsRdpClientNonScriptable = interface(IMsTscNonScriptable) ['{2F079C4C-87B2-4AFD-97AB-20CDB43038AE}'] function NotifyRedirectDeviceChange(wParam: UINT_PTR; lParam: LONG_PTR): HResult; stdcall; function SendKeys(numKeys: Integer; pbArrayKeyUp: Pointer; plKeyData: Pointer): HResult; stdcall; 调用时也要注意: var keydatas: array[0..19] of integer; //一次最多发20个 keyupstate: array[0..19] of SmallInt; //c++中 VARIANT_BOOL 是COM 使用的布尔类型, 其实质上是 short 类型 不能使用boolen。c++的short对应的delphi的是SmallInt begin keydatas[0] := MapVirtualKey(65,0); keydatas[1] := MapVirtualKey(65,0); keydatas[2] := MapVirtualKey(66,0); keydatas[3] := MapVirtualKey(66,0); keydatas[4] := MapVirtualKey(67,0); keydatas[5] := MapVirtualKey(67,0); keyupstate[0] := 0; keyupstate[1] := 1; keyupstate[2] := 0; keyupstate[3] := 1; keyupstate[4] := 0; keyupstate[5] := 1; //注意状态要正确,不然会输入的结果。这里输入的是abc nonScriptable.SendKeys(6, @keyupda[0],@keydatas[0]); end
第二处:
TMsRdpClient9NotSafeForScriptingOnRemoteWindowDisplayed = procedure(ASender: TObject; vbDisplayed: WordBool; var hwnd: {??_RemotableHandle}OleVariant; windowAttribute: RemoteWindowDisplayedAttribute) of object; 这里只改9的,其他的版本也是一样的修改 改成不要var 用polevariant TMsRdpClient9NotSafeForScriptingOnRemoteWindowDisplayed = procedure(ASender: TObject; vbDisplayed: WordBool; hwnd: {??_RemotableHandle}POleVariant; windowAttribute: RemoteWindowDisplayedAttribute) of object; 因为控件是生成了,这里改完可以重新打开控件包,重新编译一下。 或者直接通过动态赋值事件的方式去绑定事件也可以 rdp.OnRemoteWindowDisplayed := rdpRemoteWindowDisplayed; procedure TForm1.rdpRemoteWindowDisplayed(ASender: TObject; vbDisplayed: WordBool; hwnd: POleVariant; windowAttribute: TOleEnum); var ph: PHandle; begin if vbDisplayed then begin if hwnd <> nil then begin ph := @hwnd; //这里就是remoteapp 的句柄了 mmo1.Lines.Add(inttostr(ph^)); end; end;
接一下来是OleCtrls.pas 修正(不知对不对,先记录。目前测试效果是对的,还不懂汇编,是问AI修复的)
procedure TOleControl.InvokeEvent(DispID: TDispID; var Params: TDispParams); var EventMethod: TMethod; Arg: TVariantArg; i: Integer; intval:integer; begin if ControlData.Version < 300 then D2InvokeEvent(DispID, Params) else begin GetEventMethod(DispID, EventMethod); if Integer(EventMethod.Code) < $10000 then Exit; try asm PUSH EBX PUSH ESI MOV ESI, Params MOV EBX, [ESI].TDispParams.cArgs TEST EBX, EBX JZ @@7 MOV ESI, [ESI].TDispParams.rgvarg MOV EAX, EBX SHL EAX, 4 // count * sizeof(TVarArg) XOR EDX, EDX ADD ESI, EAX // EDI = Params.rgvarg^[ArgCount] @@1: SUB ESI, 16 // Sizeof(TVarArg) MOV EAX, dword ptr [ESI] CMP AX, varInteger // 添加对varInteger的判断 //20231029 fix JE @@5 // 与varSingle处理方式相同 //20231029 fix CMP AX, varSingle // 4 bytes to push JA @@3 JE @@5 @@2: TEST DL,DL JNE @@2a MOV ECX, ESI INC DL TEST EAX, varArray JNZ @@6 MOV ECX, dword ptr [ESI+8] JMP @@6 @@2a: TEST EAX, varArray JZ @@5 PUSH ESI JMP @@6 @@3: CMP AX, varDate // 8 bytes to push JA @@2 @@4: PUSH dword ptr [ESI+12] @@5: PUSH dword ptr [ESI+8] @@6: DEC EBX JNE @@1 @@7: MOV EDX, Self MOV EAX, EventMethod.Data CALL EventMethod.Code POP ESI POP EBX end; except Application.HandleException(Self); end; end; end;
如果参数有类型是varInteger,原本的汇编没有对integer 判断。
对varInteger的情况,会出现问题:
对varInteger的情况,会出现问题:
1. varInteger是4字节整数类型
2. 但是代码里只有判断varSingle的情况会压入4字节
3. 所以对varInteger,会错误地当作8字节浮点数处理这确实是一个值得注意的问题。要修复这个问题,需要在代码的@@1标签处添加对varInteger的判断:
asm
@@1: SUB ESI, 16
MOV EAX, dword ptr [ESI]
CMP AX, varInteger // 添加对varInteger的判断
JE @@5 // 与varSingle处理方式相同
CMP AX, varSingle
JA @@3
JE @@5
添加一个判断varInteger的语句,如果是varInteger,也直接跳转到@@5标签进行4字节参数的PUSH操作。

浙公网安备 33010602011771号