#delphi #dll
#delphi #dll
Вопрос:
Я очень новичок в DLL-объектах, и я ищу везде и не могу найти правильный ответ. Я делаю небольшое дополнение к Microsoft RMS, оно автоматически вызывает функцию Process из моей dll с параметром 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;
Но это не работает, потому что тип переменной может быть назначен только из единственного модуля.
Любая помощь приветствуется!
Комментарии:
1. Звучит как довольно ужасный COM-интерфейс! Не можете ли вы распространять свое приложение с одной версией DLL и использовать side-by-side COM, чтобы убедиться, что вы получаете желаемую версию. Поддержка нескольких версий была бы ужасной. Как бы вы протестировали?
Ответ №1:
Вы говорите, что интерфейсы имеют разные идентификаторы guid в разных версиях. Это совершенно нормально, если новые интерфейсы являются производными от старых интерфейсов. Так ли это на самом деле? Если это так, то вы можете упростить свой код, приведя свой объект сеанса к любому интерфейсу, который фактически определяет элемент Cashier. Вам не нужно приводить его к каждому отдельному типу интерфейса, если только интерфейсы не являются производными друг от друга. Можете ли вы показать фактические объявления интерфейса?
Ответ №2:
Декларация кассира из версии v2.0.0.105
_Cashier = interface(IDispatch)
['{AA84B4FB-AA41-4423-A763-59D0723ED52B}']
function Get_Session: _SessionClass; safecall;
function Get_CashDrawer: _CashDrawer; safecall;
function Get_OverShortLimitType: overshortlimitEnum; safecall;
function Get_MaxOverShortAmount: Currency; safecall;
function Get_MaxOverShortPercent: Double; safecall;
function Get_SecurityLevel: Smallint; safecall;
function Get_HasPrivilege(var CashierPrivilege: cashierprivilegesEnum): WordBool; safecall;
function Get_FailedLogOnAttempts: Integer; safecall;
function Get_EmailAddress: WideString; safecall;
function Get_Messages: _CashierMessages; safecall;
function Get_UnreadMessageCount: Integer; safecall;
function Get_Name: WideString; safecall;
function Get_FirstName: WideString; safecall;
function Get_LastName: WideString; safecall;
function Get_ReturnLimit: Currency; safecall;
function Get_FloorLimit: Currency; safecall;
function Get_ID: Integer; safecall;
function Get_CashDrawerNumber: Smallint; safecall;
function Get_Loaded: WordBool; safecall;
function Get_Number: WideString; safecall;
property Session: _SessionClass read Get_Session;
property CashDrawer: _CashDrawer read Get_CashDrawer;
property OverShortLimitType: overshortlimitEnum read Get_OverShortLimitType;
property MaxOverShortAmount: Currency read Get_MaxOverShortAmount;
property MaxOverShortPercent: Double read Get_MaxOverShortPercent;
property SecurityLevel: Smallint read Get_SecurityLevel;
property HasPrivilege[var CashierPrivilege: cashierprivilegesEnum]: WordBool read Get_HasPrivilege;
property FailedLogOnAttempts: Integer read Get_FailedLogOnAttempts;
property EmailAddress: WideString read Get_EmailAddress;
property Messages: _CashierMessages read Get_Messages;
property UnreadMessageCount: Integer read Get_UnreadMessageCount;
property Name: WideString read Get_Name;
property FirstName: WideString read Get_FirstName;
property LastName: WideString read Get_LastName;
property ReturnLimit: Currency read Get_ReturnLimit;
property FloorLimit: Currency read Get_FloorLimit;
property ID: Integer read Get_ID;
property CashDrawerNumber: Smallint read Get_CashDrawerNumber;
property Loaded: WordBool read Get_Loaded;
property Number: WideString read Get_Number;
end;
Декларация кассира из версии v2.0.0.151
_Cashier = interface(IDispatch)
['{39B2C128-00F1-4834-B1A4-05197C708BD9}']
function Get_Session: _SessionClass; safecall;
function Get_CashDrawer: _CashDrawer; safecall;
function Get_OverShortLimitType: overshortlimitEnum; safecall;
function Get_MaxOverShortAmount: Currency; safecall;
function Get_MaxOverShortPercent: Double; safecall;
function Get_SecurityLevel: Smallint; safecall;
function Get_HasPrivilege(var CashierPrivilege: cashierprivilegesEnum): WordBool; safecall;
function Get_FailedLogOnAttempts: Integer; safecall;
function Get_EmailAddress: WideString; safecall;
function Get_Messages: _CashierMessages; safecall;
function Get_UnreadMessageCount: Integer; safecall;
function Get_Name: WideString; safecall;
function Get_FirstName: WideString; safecall;
function Get_LastName: WideString; safecall;
function Get_ReturnLimit: Currency; safecall;
function Get_FloorLimit: Currency; safecall;
function Get_ID: Integer; safecall;
function Get_CashDrawerNumber: Smallint; safecall;
function Get_Loaded: WordBool; safecall;
function Get_Number: WideString; safecall;
function Get_PasswordAge: Integer; safecall;
function Get_ReminderPeriod: Integer; safecall;
function Get_PasswordResetFlag: WordBool; safecall;
function Get_IsPasswordChanged: WordBool; safecall;
procedure Set_IsPasswordChanged(var Param1: WordBool); safecall;
function Get_TimecardID: Integer; safecall;
procedure Set_TimecardID(var Param1: Integer); safecall;
function ValidatePassword(var Password: WideString): WordBool; safecall;
function IsPwdDuplicated(var CashierNumber: Integer; var Password: WideString): WordBool; safecall;
property Session: _SessionClass read Get_Session;
property CashDrawer: _CashDrawer read Get_CashDrawer;
property OverShortLimitType: overshortlimitEnum read Get_OverShortLimitType;
property MaxOverShortAmount: Currency read Get_MaxOverShortAmount;
property MaxOverShortPercent: Double read Get_MaxOverShortPercent;
property SecurityLevel: Smallint read Get_SecurityLevel;
property HasPrivilege[var CashierPrivilege: cashierprivilegesEnum]: WordBool read Get_HasPrivilege;
property FailedLogOnAttempts: Integer read Get_FailedLogOnAttempts;
property EmailAddress: WideString read Get_EmailAddress;
property Messages: _CashierMessages read Get_Messages;
property UnreadMessageCount: Integer read Get_UnreadMessageCount;
property Name: WideString read Get_Name;
property FirstName: WideString read Get_FirstName;
property LastName: WideString read Get_LastName;
property ReturnLimit: Currency read Get_ReturnLimit;
property FloorLimit: Currency read Get_FloorLimit;
property ID: Integer read Get_ID;
property CashDrawerNumber: Smallint read Get_CashDrawerNumber;
property Loaded: WordBool read Get_Loaded;
property Number: WideString read Get_Number;
property PasswordAge: Integer read Get_PasswordAge;
property ReminderPeriod: Integer read Get_ReminderPeriod;
property PasswordResetFlag: WordBool read Get_PasswordResetFlag;
property IsPasswordChanged: WordBool read Get_IsPasswordChanged write Set_IsPasswordChanged;
property TimecardID: Integer read Get_TimecardID write Set_TimecardID;
end;
Как вы можете видеть, в более поздней версии добавлено несколько вещей, но нет никаких сомнений в том, что мне нужно проверять версию программного обеспечения при вызове их функций. Кассир, это только один из 25-30 типов, поэтому, если мне нужно написать одну и ту же базовую реализацию для всех версий …. большая задача и ужасный код для внесения изменений на более позднем этапе.
Ответ №3:
Наконец-то разобрался! Просто поделитесь ответом на случай, если кто-нибудь еще его ищет.
Ключом к успеху является «позднее связывание», что означает, что вы не используете интерфейс.
procedure TRefreshScreenRefreshScreen.Process(const Session: IDispatch);
var
_Session: Variant;
begin
_Session := Session;
CodeSite.Send( csmLevel1, '_Session.Cashier.Name', _Session.Cashier.Name );
CodeSite.Send( csmLevel1, '_Session.Cashier.Number', _Session.Cashier.Number );
end;
С переменной variant функции проверяются не компилятором, а во время выполнения, поэтому вы должны убедиться, что правописание правильное, потому что intellisense его не проверяет.
Работает как мечта!
В любом случае спасибо всем вам!