COM 对象和不同版本的 DLL
我对 DLL 对象非常陌生,我到处搜索但找不到正确的答案。 我对 Microsoft RMS 做了一些插件,它会自动从我的 dll 中调用 Process 函数,并使用传递当前会话详细信息的 IDispach 参数。
我正在使用 QSRules.dll 的接口(组件 > 导入 > 组件 > 类型化库...添加到项目)。 它创建包含所有引用等的 TLB 文件。
procedure TRefreshScreenRefreshScreen.Process(const Session: IDispatch);
begin
CodeSite.Send( csmLevel1, '(Session as SessionClass).Cashier.Name', (Session as SessionClass).Cashier.Name );
CodeSite.Send( csmLevel1, '(Session as SessionClass).Cashier.Number', (Session as SessionClass).Cashier.Number );
end;
这与软件版本 2.01 完美配合,但当尝试在版本 2.02 上使用相同的功能时,它会崩溃并显示“接口不支持”。 QSRules.dll 已更新版本,所有类的 GUID 都不同。
我用以下代码尝试了这一点:
procedure TRefreshScreenRefreshScreen.Process(const Session: IDispatch);
begin
if Supports(Session, QSRules_TLB_2_0_0_151.SessionClass) then
Begin
CodeSite.Send( csmLevel1, '(Session as SessionClass).Cashier.Name', (Session as QSRules_TLB_2_0_0_151.SessionClass).Cashier.Name );
CodeSite.Send( csmLevel1, '(Session as SessionClass).Cashier.Number', (Session as QSRules_TLB_2_0_0_151.SessionClass).Cashier.Number );
end else
if Supports(Session, QSRules_TLB_2_0_0_105.SessionClass) then
Begin
CodeSite.Send( csmLevel1, '(Session as SessionClass).Cashier.Name', (Session as QSRules_TLB_2_0_0_105.SessionClass).Cashier.Name );
CodeSite.Send( csmLevel1, '(Session as SessionClass).Cashier.Number', (Session as QSRules_TLB_2_0_0_151.SessionClass).Cashier.Number );
end
end;
有 4 或 5 个不同版本的 dll 都具有不同的 GUID,但 98% 的代码在所有版本之间都是相同的。 以这种方式做到这一点没有必要增加代码。
有什么办法可以缩短它吗?
我也尝试过
procedure TRefreshScreenRefreshScreen.Process(const Session: IDispatch);
var
_Session: SessionClass;
begin
if Supports(Session, QSRules_TLB_2_0_0_151.SessionClass) then
_Session = (Session as QSRules_TLB_2_0_0_151.SessionClass)
else if Supports(Session, QSRules_TLB_2_0_0_105.SessionClass) then
_Session = (Session as QSRules_TLB_2_0_0_105.SessionClass);
with _Session do
Begin
CodeSite.Send( csmLevel1, '_Session.Cashier.Name', Cashier.Name );
CodeSite.Send( csmLevel1, '_Session..Cashier.Number', Cashier.Number );
End;
end;
但这不起作用,因为变量类型只能从唯一的单元分配。
任何帮助表示赞赏!
I'm very new to DLL objects and I search everywhere and can't find the right answer.
I doing little addon to Microsoft RMS, it automatically calls function Process from my dll with IDispach parameter passing current session details.
I'm using interface from QSRules.dll (Components > Import > Component > Typed Library ... Add To Project).
It creates TLB file with all references etc.
procedure TRefreshScreenRefreshScreen.Process(const Session: IDispatch);
begin
CodeSite.Send( csmLevel1, '(Session as SessionClass).Cashier.Name', (Session as SessionClass).Cashier.Name );
CodeSite.Send( csmLevel1, '(Session as SessionClass).Cashier.Number', (Session as SessionClass).Cashier.Number );
end;
That works perfectly with software version 2.01 but when trying to use the same function on version 2.02 it crash with "Interface not supported".
The QSRules.dll has updated version and GUID's for all classes are different.
I tried that with fallowing code:
procedure TRefreshScreenRefreshScreen.Process(const Session: IDispatch);
begin
if Supports(Session, QSRules_TLB_2_0_0_151.SessionClass) then
Begin
CodeSite.Send( csmLevel1, '(Session as SessionClass).Cashier.Name', (Session as QSRules_TLB_2_0_0_151.SessionClass).Cashier.Name );
CodeSite.Send( csmLevel1, '(Session as SessionClass).Cashier.Number', (Session as QSRules_TLB_2_0_0_151.SessionClass).Cashier.Number );
end else
if Supports(Session, QSRules_TLB_2_0_0_105.SessionClass) then
Begin
CodeSite.Send( csmLevel1, '(Session as SessionClass).Cashier.Name', (Session as QSRules_TLB_2_0_0_105.SessionClass).Cashier.Name );
CodeSite.Send( csmLevel1, '(Session as SessionClass).Cashier.Number', (Session as QSRules_TLB_2_0_0_151.SessionClass).Cashier.Number );
end
end;
There is 4 or 5 different versions of dll all with different GUID's bu 98% of code is the same between all of them.
Doing that in this way is unnessesary multiplying the code.
Is there any way that I can shorten it ?
I also tried
procedure TRefreshScreenRefreshScreen.Process(const Session: IDispatch);
var
_Session: SessionClass;
begin
if Supports(Session, QSRules_TLB_2_0_0_151.SessionClass) then
_Session = (Session as QSRules_TLB_2_0_0_151.SessionClass)
else if Supports(Session, QSRules_TLB_2_0_0_105.SessionClass) then
_Session = (Session as QSRules_TLB_2_0_0_105.SessionClass);
with _Session do
Begin
CodeSite.Send( csmLevel1, '_Session.Cashier.Name', Cashier.Name );
CodeSite.Send( csmLevel1, '_Session..Cashier.Number', Cashier.Number );
End;
end;
But this not work because variable type can be assigned only from only unit.
Any help appreciated !
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你说不同版本的接口有不同的guid。只要新的接口是从旧的接口派生出来的,那就完全没问题。事实真的是这样吗?如果这样做,那么您可以通过将 Session 对象强制转换为实际定义 Cashier 成员的任何接口来简化代码。您不需要将其转换为每个单独的接口类型,除非这些接口不是相互派生的。你能展示实际的接口声明吗?
You say that the interfaces have different guids in different versions. That is perfectly fine as long as the newer interfaces derive from the older interfaces. Is that actually the case? If they do, then you can simplify your code by casting your Session object to whatever interface actually defines the Cashier member. You do not need to cast it to each individual interface type unless the interfaces do not derive from each other. Can you show the actual interface declarations?
从 v2.0.0.105 开始的 Cashier 声明
从 v2.0.0.151 开始的 Cashier 声明
正如你所看到的,在更高版本中添加了一些东西,但毫无疑问,我在调用它们的函数时需要检查软件版本。 Cashier 只是 25-30 种类型中的一种,所以如果我必须为所有版本编写相同的基本实现......艰巨的任务,以及后期修改的可怕代码。
Cashier declaration from v2.0.0.105
Cashier declaration from v2.0.0.151
As you can see there is few things added in the later version but there is no doubt that I need to check the software version when calling them functions. Cashier that's only one of 25-30 types there so if I have to write the same basic implementation for all versions .... big task, and horrible code for modifications in later stage.
终于整理好了!只需分享答案,以防其他人寻找答案。
成功的关键是“后期绑定”,这意味着你不使用接口。
对于变体变量,函数不会由编译器检查,而是在运行时检查,因此您必须确保拼写正确,因为智能感知不会检查它。
就像做梦一样!
不管怎样,谢谢大家!
Finally got it sorted ! Just share the answer in case anyone else looking for it.
The key for success is "late binding", that means you don't use interface.
With variant variable the functions are not check by compiler but in runtime, so you have to make sure that the spelling is correct because intellisense is not checking it.
Works like a dream !
Thanks all of you anyway !