Форум: "Потрепаться";
Текущий архив: 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)
Страницы: 1 вся ветка
Форум: "Потрепаться";
Текущий архив: 2002.03.21;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.005 c