Пишу компонент для вызова процедур посредством динамической загрузки модулей - в некотором роде аналог привычных многим исполняемых файлов C/C++, у которых есть процедура
main с параметрами командной строки
argc, argv.
Зачем? Это такой аналог майкрософтовского RunDll32, который умеет вызывать процедуры из библиотек. Для вызова процедур не требуется наличие BlackBox, нужен лишь загрузчик этих модулей. А далее за работу отвечает загруженный компонент, как типичная исполняемая программа.
У меня возникли проблемы с использованием модуля Meta. Подробности (и сам вопрос) - ниже.
Цепочка вызова такова:
- TestLoader (загрузчик модулей, исполняемый/EXE) - в него я буду передавать параметры с командной строки. В последствии этот модуль будет вызываться из исполняемого файла на XDS Oberon-2 (Windows/Linux).
- Loader (переработанный StdInterpreter) будет запускать соответствующие процедуры по полученным параметрам (имя модуля, имя процедуры, количество аргументов, указатель на массив аргументов).
- Исполняемый модуль (у меня это TestExeModule).
Вот в таком модуле мы объявляем для экспорта процедуру
main, в которую извне будут передаваться
Код:
MODULE TestExeModule;
IMPORT WinApi, Dynamics;
PROCEDURE ShowMessage (msg, title: ARRAY OF CHAR);
VAR res: INTEGER;
BEGIN
res := WinApi.MessageBoxW(0, msg, title, {});
END ShowMessage;
PROCEDURE main* (argc: INTEGER; argv: Dynamics.ArgList);
BEGIN
ShowMessage("Hello from ExeModule!", "Exe");
END main;
END TestExeModule.
Код:
MODULE Dynamics;
TYPE
String* = POINTER TO ARRAY OF CHAR;
ArgList* = POINTER TO ARRAY OF String;
END Dynamics.
Код:
MODULE TestLoader;
IMPORT WinApi, Dynamics, Loader, StdLog;
PROCEDURE ShowMessage (msg, title: ARRAY OF CHAR);
VAR res: INTEGER;
BEGIN
res := WinApi.MessageBoxW(0, msg, title, {});
END ShowMessage;
PROCEDURE ReportError (e: Loader.TLoaderError);
BEGIN
StdLog.String("===Error #"); StdLog.Int(e.n); StdLog.Ln;
StdLog.String(e.msg); StdLog.Ln;
StdLog.String(e.par0); StdLog.Ln;
StdLog.String(e.par1); StdLog.Ln;
StdLog.Ln
END ReportError;
PROCEDURE Run*;
VAR mod, proc: Dynamics.String;
BEGIN
NEW(mod, 64); NEW(proc, 256);
mod^ := "TestExeModule";
proc^ := "main";
Loader.Run(mod, proc, 0, NIL);
IF Loader.loaderError.n # 0 THEN
ReportError(Loader.loaderError)
END;
END Run;
END TestLoader.
TestLoader.RunКод:
MODULE Loader;
IMPORT WinApi, SYSTEM, Kernel, Meta, Strings, Dynamics, Dialog;
CONST
modNotFound = 10; procNotFound = 11; identExpected = 12; unknownIdent = 13;
depositExpected = 14; noDepositExpected = 15; syntaxError = 16;
lparenExpected = 17; rparenExpected = 18; containerExpected = 19; quoteExpected = 20;
fileNotFound = 21; noController = 22; noDialog = 23; cannotUnload = 24; commaExpected = 25;
incompParList = 26;
TYPE
IntValue = POINTER TO RECORD (Meta.Value)
int: INTEGER;
END;
StrValue = POINTER TO RECORD (Meta.Value)
str: Dialog.String;
END;
TLoaderError* = POINTER TO RECORD
n*: INTEGER;
errorMsg*,
msg*,
par0*, par1*: POINTER TO ARRAY OF CHAR
END;
VAR
loaderError*: TLoaderError;
errorMsg: ARRAY 256 OF CHAR;
par: ARRAY 100 OF POINTER TO Meta.Value;
PROCEDURE ShowMessage (msg, title: ARRAY OF CHAR);
VAR res: INTEGER;
BEGIN
res := WinApi.MessageBoxW(0, msg, title, {});
END ShowMessage;
PROCEDURE FillChar (VAR s: ARRAY OF CHAR; c: CHAR);
VAR i: INTEGER;
BEGIN
i := 0; WHILE s[i] # 0X DO s[i] := c; INC(i) END;
END FillChar;
PROCEDURE Init;
BEGIN
NEW(loaderError);
loaderError.n := 0;
NEW(loaderError.errorMsg, 256);
NEW(loaderError.msg, 256);
NEW(loaderError.par0, 256);
NEW(loaderError.par1, 256);
END Init;
PROCEDURE Error (res: INTEGER; msg, par0, par1: ARRAY OF CHAR);
VAR e, f: ARRAY 256 OF CHAR;
BEGIN
IF res # 0 THEN
loaderError.n := res;
IF errorMsg # "" THEN
loaderError.errorMsg^ := errorMsg$
ELSE
loaderError.errorMsg^ := "#System:CommandError"
END;
loaderError.msg^ := msg$;
loaderError.par0^ := par0$;
loaderError.par1^ := par1$;
(*
IF errorMsg # "" THEN
Dialog.MapString(errorMsg, e);
Dialog.MapParamString(msg, par0, par1, "", f);
IF e # " " THEN
f := e$ + ' ' + f$ (*Concat(e, f, f);*)
ELSE
Dialog.MapString("#System:CommandError", f)
END;
ShowMessage(f, "Error")
END
*)
END
END Error;
PROCEDURE Run* (_module, _proc: Dynamics.String; argc: INTEGER; argv: Dynamics.ArgList);
VAR
res, numPar: INTEGER;
errmsg: Dialog.String;
PROCEDURE ShowLoaderResult (IN mod: ARRAY OF CHAR);
VAR res: INTEGER; importing, imported, object: ARRAY 256 OF CHAR;
BEGIN
Kernel.GetLoaderResult(res, importing, imported, object);
CASE res OF
| Kernel.fileNotFound:
Error(res, "#System:CodeFileNotFound", imported, "")
| Kernel.syntaxError:
Error(res, "#System:CorruptedCodeFileFor", imported, "")
| Kernel.objNotFound:
Error(res, "#System:ObjNotFoundImpFrom", imported, importing)
| Kernel.illegalFPrint:
Error(res, "#System:ObjInconsImpFrom", imported, importing)
| Kernel.cyclicImport:
Error(res, "#System:CyclicImpFrom", imported, importing)
| Kernel.noMem:
Error(res, "#System:NotEnoughMemoryFor", imported, "")
ELSE
Error(res, "#System:CannotLoadModule", mod, "")
END
END ShowLoaderResult;
PROCEDURE CallProc (IN mod, proc: ARRAY OF CHAR);
VAR i, t: Meta.Item; ok: BOOLEAN;
BEGIN
ok := FALSE;
Meta.Lookup(mod, i);
IF i.obj = Meta.modObj THEN
i.Lookup(proc, i);
IF i.obj = Meta.procObj THEN
i.GetReturnType(t);
ok := (t.typ = 0) & (i.NumParam() = numPar);
IF ok THEN
i.ParamCallVal(par, t, ok)
ELSE
Error(incompParList, "#System:IncompatibleParList", mod, proc)
END
ELSE
Error(Kernel.commNotFound, "#System:CommandNotFoundIn", proc, mod)
END
ELSE
ShowLoaderResult(mod)
END;
IF ~ok THEN ShowMessage("CallProc error", "Error") END
END CallProc;
PROCEDURE ParamList ();
VAR iv: IntValue; sv: StrValue;
BEGIN
numPar := 0;
NEW(iv);
iv.int := argc;
par[numPar] := iv;
INC(numPar);
NEW(iv);
iv.int := SYSTEM.ADR(argv);
par[numPar] := iv;
INC(numPar);
END ParamList;
BEGIN
Init;
errmsg := "~";
ParamList ();
CallProc(_module, _proc (*"main"*))
END Run;
END Loader.
В принципе, частично уже работает: запускается процедура некоторого модуля, у которой нет параметров.
Допустим, если процедура main не имеет параметров и numPar = 0, то всё замечательно запускается. но как только я указываю параметры для передачи аргументов, тут начинаются проблемы.
Судя по всему, проблема заключается в подготовке параметров для передаче их процедуре ParamCallVal.
На строке
i.ParamCallVal(par, t, ok) я получаю ok = FALSE (неудача).
Единственное подозрение у меня вызывает строка
Код:
iv.int := SYSTEM.ADR(argv);
Подскажите.