Форум: "Потрепаться";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.03.21;
Скачать: [xml.tar.bz2];




Вниз

Ну тогда еще задачка: 


Алексей Петров   (2002-02-06 12:21) [0]

Напишите код, который определяет имя модуля, в котором он находится.

модуль читаем двояко (2 раздельнх задачи)

1. Модуль = unit - нужно получить полное имя исходного pas файла.
2. Модуль = module - получтьб полное имя exe, dll или bpl в котором код сидит в run-time



McSimm   (2002-02-06 12:27) [1]

Исходный pas файл?
Какой из?
Тот, в котором происходит выполнения кода в данный момент?

А какие ограничения?

Настройки компилятора и компоновщика меняем как хотим?
:)



Алексей Петров   (2002-02-06 12:31) [2]

function MyPasFileName: string;
и
function MyModuleName: string;

Ограничения? Настройки проекта менять и попадать в зависимость от них не стоит. А применять директивы компилятора - почему бы и нет.



Алексей Петров   (2002-02-06 12:34) [3]

Еще в догонку задача из той-же оперы:

В приложении, скомпелированном с run-time packages определить в каком BPL-е или exe описан данный класс:

function GetClassPackageFileName(aClass: TClass): string;



troits   (2002-02-06 12:53) [4]

По поводу второго вопроса:
GetModuleFileName(HInstance, ... подойдет?



Алексей Петров   (2002-02-06 12:56) [5]

> troits © (06.02.02 12:53)
вполне.
А для класса?



McSimm   (2002-02-06 12:58) [6]

Не даешь .map файл генерить, тогда:

const
ThisUnitName = "MyUnit.pas";

function MyPasFileName: string;
begin
Result := ThisUnitName
end

Нигде условия не нарушил? Все в рамках?
При изменении имени файла константу тоже поменять можно.
:)



Алексей Петров   (2002-02-06 13:03) [7]

> McSimm © (06.02.02 12:58)
Формально, конечно, удовлетворяет моей формулировке :)
Но код можно сделать инвариантным от его местоположения. Т.е. переносишь функцию без изменений в другой модуль, или просто модуль переносишь в другую директорию - и возвращаемый результат отражает изменения.



McSimm   (2002-02-06 13:06) [8]

И все это без отладочной информации?

И exe будет также работать на любой машине?



Алексей Петров   (2002-02-06 13:11) [9]

Почему-же без отладочной? Что-то можно локально включить.

А вот где exe-шник будет запущен - не известно. Функция должна сказать, где она лежала в момент компиляции.



McSimm   (2002-02-06 13:28) [10]

Тогда признаю поражение.
Не вижу способа чтобы
- не используя .map;
- не имея доступа к dcu;
- инвариантно к месту расположения функции в теле программы
получить путь и имя pas файла
- даже если exe запущен на другой машине.

Все. Здаюсь.



Алексей Петров   (2002-02-06 14:51) [11]

Что, всем слабо?



VuDZ   (2002-02-06 15:03) [12]

вообще-то должен быть способ - когда у меня пару раз вылетал winCom (он написан на делфи, то писал строку, где произошла ошибка)
в С есть такой вариант:
#define chMSG(desc) message(__FILE__ "(" chSTR(__LINE__) "):" #desc)
так что должно быть что-то, связанно с препроцессором...



Алексей Петров   (2002-02-06 15:06) [13]

> VuDZ © (06.02.02 15:03)
В С эта задача тривиальна: там есть соответствующие предопределенные макросы.
char * getMyName()
{
return __FILE__;
}

А в ObjectPascal-е препроцессора нет.



VuDZ   (2002-02-06 15:26) [14]

2Алексей Петров
ну и я об этом же (о "С")

а для делфи должно быть нечто подобное, иначе, что это за языкъ...



Алексей Петров   (2002-02-06 15:32) [15]

Нормальный язык. А препроцессор - авторы считают, что не нужен. Правы или нет - не знаю? Но все, кто с ним работают - обходятся.



VuDZ   (2002-02-06 15:40) [16]

насколько я понял, суть проблемы такая: надо, что бы некая ф-ия знала, в котором модуле она находиться? так?

// типа, глобльная переменая для модуля
TString strMod = "my cool module.pas"

function .... (bla-bla-bla)
var
....
begin
....
OutputDebugString((TChar)"Proc called from ");
OutputDebugString((TChar)strMod);
....
end;

:D



McSimm   (2002-02-06 15:42) [17]

Вообще эта задачка в самый раз на призовую. Я про определение имени pas файла. (2All - ответ мне Алексей уже сказал).
В ней все для этого требования - и нестандартность мышления, и опыт/знания программирования на Делфи.

Вот только призы раздавать некому :)



Алексей Петров   (2002-02-06 15:43) [18]

> VuDZ © (06.02.02 15:40)
Чукча не читатель, чукча писатель :)

см. McSimm © (06.02.02 12:58) и Алексей Петров © (06.02.02 13:03)



VuDZ   (2002-02-06 16:12) [19]

2Алексей Петров
ya vol - мы мыслим глобально (или локально, смотря с какой стороны подходить)



vuk   (2002-02-06 19:07) [20]

Пойдеть? :o)

function GetUnitName : string;
var
s : string;
n, n2 : integer;
begin
Result := "";
{$C+}
try
Assert(false);
except
on e : exception do s := e.Message;
end;
{$C-}
n := Pos("(", s );
if n = 0 then exit;
n2 := Pos( ",", s );
if (n2 <= n ) then exit;
Result := copy( s, n+1, n2-n - 1);
end;



Shaman_Naydak   (2002-02-06 20:55) [21]

//1-я функция - та, о которой написал McSimm.
//Если передать ей HInstance, то она вернет имя модуля DLL/EXE.

function GetModuleFileName(Instance: LongWord) : string;
var
Buffer: array[0..261] of Char;
begin
SetString(Result, Buffer, Windows.GetModuleFileName(Instance, Buffer, SizeOf(Buffer)));
end;

// А вот эта ф-ция и ищет модуль класса, приколитесь
// проверить при компиляции с пакетом и без оного
function GetClassPackageFileName(aClass: TClass): string;
var Instance: LongWord;
begin
Result:="";
Instance:=FindClassHInstance(aClass);
if Instance <> 0 then
Result:=GetModuleFileName(Instance);
end;

>> Алексей Петров: Действительно, неплохая разминка для мозгов..
Может опубликуешь модуль с набором таких полезных ф-ций, а?



Алексей Петров   (2002-02-07 09:52) [22]

> vuk © (06.02.02 19:07)
Браво. Задача решена.
Но, позвольте одно мелкое замечие по коду: Вы {$C-} оставляете после себя, даже если до Вашей функции был {$C+}. Это не вполне корректно :)

ИМХО, для помещения фунции в прописи стоит заменить {$C+} и {$C-} на более сложные конструкции:

{$IFOPT C-}
{$C+}
{$DEFINE C_ACTIVATED}
{$ENDIF C-}

...

{$IFDEF C_ACTIVATED}
{$UNDEF C_ACTIVATED}
{$C-}
{$ENDIF}


Но это уже чисто техника, а задача решена отлично.


> Shaman_Naydak © (06.02.02 20:55)
В свете Вашего решения задача оказалась даже проще, чем я думал :) Мое решение было длиннее. Браво.
Дело в том, что я функцию FindClassHInstance нашел только вчера в свете обсуждения даннной задачи с MsSimm, а до того её всегда сам реализовывал. И вся её нетривиальность, на мой взгляд, заключалась именно в численном совпадении Hinstance с TMemInfo.AllocationBase, при использовании функции Win32 API VirtualQuery и именно это и используется в функции FindClassHInstance.

К стати GetModuleFileName именно так описана в SysUtils.pas.



Алексей Петров   (2002-02-07 10:09) [23]

Еще немного. Меня несколько удивило, что EAssertFailure не содержит отдельно имени pas файла и № строки, а только в виде сообщения.

Обратите внимание, что обработкой Assert можно управлять самим. Я лично использовал Assert совершенно нетривиальным образом: Мне нужно было организовать трасиировку нескольких потоков и я перекрыл AssertErrorHanler на код, пишуший в лог что поток прошел через указанную точку, а Exception не генерировал.
После этого повсеместно в коде потока были расставлены Assert(False). - может кому идея пригодится :)

А вот код, демонстрирующий подмену обработки:

unit Assertions;

interface
uses
SysUtils;

type
EAssertionWithDetail = class(EAssertionFailed)
protected
FUnitName: string;
FLineNumber: Integer;
public
constructor Create(const Message, Filename: AnsiString; LineNumber: Integer);
property UnitName: string read FUnitName;
property LineNumber: Integer read FLineNumber;
end;

implementation
uses
SysConst;

{ EAssertionWithDetail }
function CreateAssertException(const Message, Filename: string;
LineNumber: Integer): Exception;
var
S: string;
begin
if Message <> "" then S := Message else S := SAssertionFailed;
Result := EAssertionWithDetail.Create(S, Filename, LineNumber);
end;

procedure RaiseAssertException(const E: Exception; const ErrorAddr, ErrorStack: Pointer);
asm
MOV ESP,ECX
MOV [ESP],EDX
MOV EBP,[EBP]
JMP System.@RaiseExcept
end;

procedure AssertErrorHandler(const Message, Filename: string;
LineNumber: Integer; ErrorAddr: Pointer);
var
E: Exception;
begin
E := CreateAssertException(Message, Filename, LineNumber);
RaiseAssertException(E, ErrorAddr, PChar(@ErrorAddr)+4);
end;

constructor EAssertionWithDetail.Create(const Message,
Filename: AnsiString; LineNumber: Integer);
begin
inherited Create(Format(SAssertError,[Message, Filename, LineNumber]));
FUnitName := FileName;
FLineNumber := LineNumber;
end;

initialization
AssertErrorProc := @AssertErrorHandler;
finalization
AssertErrorProc := nil;
end.



Алексей Петров   (2002-02-07 10:20) [24]

Немного комментариев к unit Assertions - здесь большая часть просто скопированна из SysUtils.pas.
После подключения такого модуля к проекту можно из AssertionFailure вытаскивать имя модуля и № строки как свойства.

С таким модулем функция для решения задачи №1 выглядит так:

function GetUnitName : string;
begin
Result := "";
{$IFOPT C-}
{$C+}
{$DEFINE C_ACTIVATED}
{$ENDIF C-}
try
Assert(False);
except
on E: EAssertionWithDetail do
Result := E.UnitName
end;
{$IFDEF C_ACTIVATED}
{$UNDEF C_ACTIVATED}
{$C-}
{$ENDIF}
end;



vuk   (2002-02-07 14:53) [25]

to Алексей Петров:
С директивами компилятора - это все понятно, обычное решение с сохранением и восстановлением значения директивы. Просто лень было ковыряться - это же просто пример... ;o)




Форум: "Потрепаться";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.03.21;
Скачать: [xml.tar.bz2];




Наверх





Память: 0.77 MB
Время: 0.028 c
7-67022           nika_rgups            2001-12-18 21:00  2002.03.21  
MP3 coder


1-66849           НЕО                   2002-03-05 15:08  2002.03.21  
Типа консольное приложение


1-66908           p-vlad                2002-03-06 13:36  2002.03.21  
Помощь


1-66915           Eraser                2002-03-06 10:26  2002.03.21  
поместить объект в TList


1-66857           PlaZZma               2002-03-05 12:22  2002.03.21  
Как ловить жмаканья клавиш?