Думается, это слишком сложно. В 9х, как утверждает MATT PIETREK, все гораздо проще. Первое, что надо помнить — процесс ID в 9х — это всего лишь некоторое обратимое преобразование указателя на PDB процесса (xor with "Obsfucator" DWORD). Т.к. PDB находятся в shared memory, то pid гарантированно уникальны. Второе — то, что PDB текущего процесса можно получить некоторым образом (см. пример к главе 3 — у меня в электронной книге, к сожалению, эта часть отсутствует). Получая PDB, мы можем определить таблицу хендлов, по таблице хандлов можем получить указатель на PDB нужного нам процесса, а из него, используя пресловутое преобразование (1) получить искомый PID процесса.
Вот, примерно так.
ST>>Предлагаю свой изощренный способ (навеяно статьей в RSDN о перехвате API):
БП>Точно! В Win9x можно сделать xCreateRemoteThread(GetCurrentProcessId()). БП>Павел.
Здравствуйте, Аноним, Вы писали:
А>>>Чего то я не понял. Почему OpenProcess не заюзать? А>>>Передаешь туда PID и сравниваешь хэндлы. А>А если так:
А хоть как.
OpenProcess создает НОВЫЙ хэндл.
А хэндлы по своей природе не сравниваются друг с другом.
Аналогия — утверждение навернства 2-х дверей, если у этих дверей одинаковые ручки.
К тому же, если первый хэндл был валиден в Вашем ВАП, то OpenProcess НИКОГДА не вернет аналогичную величину, потка тот хэндл не будет закрыт, так как нумерация хэндлов для одного процесса сквозная независимо от их типа (процесс, поток, файл, событие и т.д.).
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, LeonV, Вы писали:
LV>>Как можно получить PID, если есть hProcess?
А>Чего то я не понял. Почему OpenProcess не заюзать? А>Передаешь туда PID и сравниваешь хэндлы.
OpenProcess вернет другой хэндл каждый раз про открытии того же самого процесса:
#include <windows.h>
main()
{
int i;
for (i = 0; i < 10; i++)
printf("process handle: %d\n",
OpenProcess(PROCESS_ALL_ACCESS, 0, GetCurrentProcessId()));
}
process handle: 2024
process handle: 2036
process handle: 2012
process handle: 2008
process handle: 2004
process handle: 2000
process handle: 1996
process handle: 1992
process handle: 1988
process handle: 1984
Точно, оно.
AS>Итак, посмотрим определение функций
AS>
AS>// то, что должно выступать в качестве функции треда:
AS>DWORD WINAPI ThreadProc(
AS> LPVOID lpParameter // thread data
AS>);
AS>// То, что мы ему передаем.
AS>DWORD WINAPI GetCurrentProcessId(VOID);
AS>
При вызове функций, описанных прототипами реализуется вариант неявного
преобразования типов. По-настоящему важным является лишь физический размер
возвращаемого результата. К нашей радости(?) функции Win32 API в основном
возвращают (да и принимают четырехбайтовые) значения.
Вопрос же их интерпретации — целиком на совести использующего.
AS>Соглашение о передаче параметров winapi предусматривает то, что вызываемая функция очищает стек параметров. Таким образом, мы получаем на выходе из функции неправильный стек.
А чем оно отличается от вызыва твоей ThreadFunc, которой также передается
параметр — адрес структуры InjectInfo?
Да и чего ему портиться, если послетовательность CRT и GCPI имеют одно
и тоже соглашение о вызове.
Ну, а верность этих теоретических воззрений легко опровергнуть отладчиком.
IMHO, действительной проблемой является подобный вызов в случае, когда
параметров более одного.
Тогда, действительно есть нужда в ручном формировании стека и копировании
тела функции в чужой процесс по методу Джефа Рихтера, или с помощью
более прозрачной техники by Prasad Dubak, описанной ранее тобой.
AS>И в любом случае — этот метод не решает проблемы с 9х системами.В общем, лучше, правильнее и быстрее использовать в случае winnt (Nt/Zw)QueryInformationProcess.
Это временная проблема, бо 9х системы обречены на скорое вымирание
Ну, и в любом случае, Nt/ZwQueryInformationProcess не смогут ее решить
1. Я не описывал никаких приемов формирования стека функции. Вы меня с кем-то путаете
2. По поводу 9х — смотрите ветку, решение там приведено.
3. По поводу NT тоже. И оно правильное, в отличие от этого.
4. Последнее, и самое главное — см. ниже:
AS>>Итак, посмотрим определение функций
AS>>
AS>>// то, что должно выступать в качестве функции треда:
AS>>DWORD WINAPI ThreadProc(
AS>> LPVOID lpParameter // thread data
AS>>);
AS>>// То, что мы ему передаем.
AS>>DWORD WINAPI GetCurrentProcessId(VOID);
AS>>
LT> При вызове функций, описанных прототипами реализуется вариант неявного LT> преобразования типов. По-настоящему важным является лишь физический размер LT> возвращаемого результата. К нашей радости(?) функции Win32 API в основном LT> возвращают (да и принимают четырехбайтовые) значения. LT> Вопрос же их интерпретации — целиком на совести использующего.
LT> А чем оно отличается от вызыва твоей ThreadFunc, которой также передается параметр — адрес структуры InjectInfo?
При вызове функций главным является соглашение о вызове (в данном случае обе winapi) и количество параметров, которые они в себя принимают. ThreadProc принимает 1 параметр (eq 4 байта на стеке), GetCurrentProcessId принимает 0 параметров. Итак, что происходит. Caller делает перед вызовом ThreadProc push lpParameter, ожидая, что по возвращению, ThreadProc вернет результат в eax и очистит стек (например, ret 2). Однако, GetCurrentProcessId этого не делает (просто ret), хотя и возвращает результат в eax. Итого — внешне все ОК, хотя на самом деле далеко не так — портится caller's стек.
И теперь — самое главное. MSDN:
process can obtain the return value of the ThreadProc of a thread it created with CreateThread by calling the GetExitCodeThread function. A process cannot obtain the return value from the ThreadProc of a thread it created with CreateRemoteThread.
Здравствуйте, LeonV, Вы писали:
LV>Как можно получить PID, если есть hProcess? Ситуация такова: стартует приложение через ShellExecuteEx. Что это за приложение — заранее неизвестно, так как в ShellExecuteEx я передаю путь к файлу, который требуется открыть.
LV>Далее необходимо послать сообщение окну того приложения, которое запустилось для открытия этого файла. После запуска процесса я делаю EnumWindows, а в функции EnumWindowsProc получаю PID процесса, в котором каждое окошко создано — GetWindowThreadProcessId. Один из этих PID — тот, который запустила ShellExecuteEx. По идее, их надо бы сравнить, но у меня на выходе ShellExecuteEx есть hProcess, а не пид. Как быть? Единственный способ узнать пид запущенного процесса, как я понял — вызвать CreateProcess и пид будет возвращен в структуре PROCESS_INFORMATION.
LV>Кто поможет?
<поскипано>
Чего то я не понял. Почему OpenProcess не заюзать?
Передаешь туда PID и сравниваешь хэндлы.
Здравствуйте, Andrew S, Вы писали:
AS>Caller — это то, что вызывает функцию потока. Она же не сама по себе вызывается, верно?
Не сам по себе. Но, по поводу того как формируется стек нового потока-
это всего лишь домыслы (мои, твои). Посмотри отладчиком, если интересно.
AS>Тем более, что по выходу из потока код возврата сохраняется в TDB.
Ну, а в чем тут "более"? Это уже ближе к той цитате из MSDN,
не доверяющей возвращаемому значению.
LT>> Если бы было так, как ты описываешь, то удаленному потоку никогда и не удалось LT>> воспользоваться полученным параметром. По-крайней мере, поток вызывал бы LT>> AV по выходу из функции.
AS>А кто сказал, что он этого не делает? Вы можете достоверно это определить на всех системах линейки NT + то, что это происходит в другом процессе?
Необработанные исключения в процессе вызывают его завершение.
Это я знаю про все win32
AS> Еще раз. Вам указан прототип функции, который ожидается в качестве функции потока. Любое отклонение от него — это грубейшая ошибка, которая может повлечь за собой все что угодно.
Дело не в том, что я могу ошибаться, а то, что описанная тобой теория не
позволяет, например, создавать функции потока без параметра.
AS>Гм. Вообще то, тут сказано, что GetExitCodeThread не работает именно для CreateRemoteThread, для CreateThread это все вполне работоспособно. Вы можете не доверять MSDN — это Ваше право, но не дай Бог Вам потом "вживую" встретить пострадавшего от этого недоверия пользователя вашей программы
Ну, если ты такой адепт MSDN, то почему не критиковал Read/WriteProcessMemory
без остановки потоков чужого процесса, если прямо указано, что они
для отладки. Кажется, обсуждаемый способ был признан правильным?
LT>> Это надо воспринимать несколько критически, иначе бы мы и не могли пользоваться LT>> ExitCode возвращаемым CreateThread, бо реализация его построена на CRT.
AS> Какой CRT? Если имеется ввиду C run time, то тут это не при чем, если что то еще — тогда я видимо не в курсе этого чего то еще
Скучно все время писать длинно CreateRemoteThread, пишу CRT — поверь не я это первый придумал.
Как можно получить PID, если есть hProcess? Ситуация такова: стартует приложение через ShellExecuteEx. Что это за приложение — заранее неизвестно, так как в ShellExecuteEx я передаю путь к файлу, который требуется открыть.
Далее необходимо послать сообщение окну того приложения, которое запустилось для открытия этого файла. После запуска процесса я делаю EnumWindows, а в функции EnumWindowsProc получаю PID процесса, в котором каждое окошко создано — GetWindowThreadProcessId. Один из этих PID — тот, который запустила ShellExecuteEx. По идее, их надо бы сравнить, но у меня на выходе ShellExecuteEx есть hProcess, а не пид. Как быть? Единственный способ узнать пид запущенного процесса, как я понял — вызвать CreateProcess и пид будет возвращен в структуре PROCESS_INFORMATION.
Кто поможет?
--------------------
PS.
Мной были испробован такой способ: (не смеяться!)
Моя прога:
1. Перед запуском процесса через ШелЕкзек я регистрю переменную окружения, допустим RunnerInvoked=1.
2. Создаю именованные объекты — событие и FileMapping.
3. Рождаю процесс через ЩелЕкзек, регистрю хук WH_GETMESSAGE.
4. Ожидаю поднятия события.
Теперь перенесемся в другой процесс :
5. Хук обрабатывается в DLL в которой сидит функция-обработчик хука WH_GETMESSAGE. Кроме того, при загрузке в процесс эта DLL проверяет переменную окружения RunnerInvoked и таким образом определяет, тот ли это процесс, который родился из под ShellExec. Ибо в остальные процессы эта длл тоже будет загружена. Переменная окружения от родителя наследуется только в тот процесс, который мне нужен .
6. Далее, если есть такая переменная, открываю именованный FileMapping и вписываю туда GetCurrentProcessId. Подымаю евент. Все.
А тут просыпается первоначальная прога, так как евент был поднят при загрузке длл в процесс, рожденный из под шел екзек:
7. Вычитываем из FileMapping значение пида.
Теперь я имею пид запущенного процесса, через такую вот задницу .
Верю, что есть способы получше (передавать PID через реестр с использованием выше описанной технологии не предлагать).
Здравствуйте, LeonV, Вы писали:
LV>Как можно получить PID, если есть hProcess? Ситуация такова: стартует приложение через ShellExecuteEx. Что это за приложение — заранее неизвестно, так как в ShellExecuteEx я передаю путь к файлу, который требуется открыть.
Предлагаю свой изощренный способ (навеяно статьей в RSDN о перехвате API):
type
TInjectCode = packed record
instr_call_getpid : WORD; // call []
adr_from_call_getpid: DWORD; // @GetCurrentProcessID
instr_mov_ax: BYTE; // mov,
instr_mov_opcode: BYTE; // ax,
adr_to_move : DWORD; // [target]
instr_push_exitthread_arg: BYTE; // push
exitthread_arg : DWORD; // 0
instr_call_exitthread : WORD; // call
adr_from_call_exitthread: DWORD; // @ExitThread
addr_getpid : DWORD; // Address of GetCurrentProcessID
addr_exitthread: DWORD; // Address of ExitThread
addr_ret_val: DWORD; // Here the pid will be writtenend;
function GetPIDByHandle(AProcessHandle: THandle): DWORD;
var
LInjectCode: TInjectCode;
LThread: THandle;
LThreadID: Cardinal;
P: Pointer;
N: Cardinal;
begin// Allocate memory for further writing and execution of machine instructions
P := VirtualAllocEx(AProcessHandle, nil, SizeOf(LInjectCode),
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// Fill the region with machine instructions
// call GetCurrentProcessID
// mov [adr_to_move], eax
// push 0
// call ExitThreadwith LInjectCode do
begin// Call GetCurrentProcessID in the context of target process
instr_call_getpid := $15ff; // call
adr_from_call_getpid := DWORD(P) + DWORD(@LInjectCode.addr_getpid) - DWORD(@LInjectCode); // GetCurrentProcessID
// Store the result for future fetching
instr_mov_ax := $89; // mov [target_address], eax
instr_mov_opcode := $05; //
adr_to_move := DWORD(P) + DWORD(@LInjectCode.addr_ret_val) - DWORD(@LInjectCode); // [target]
// Call ExitThread
instr_push_exitthread_arg := $68;
exitthread_arg := 0;
instr_call_exitthread := $15ff;
adr_from_call_exitthread := DWORD(P) + DWORD(@LInjectCode.addr_exitthread) - DWORD(@LInjectCode);
addr_getpid := DWORD(GetProcAddress(GetModuleHandle('kernel32.dll'), 'GetCurrentProcessId'));
addr_exitthread := DWORD(GetProcAddress(GetModuleHandle('kernel32.dll'), 'ExitThread'));
end;
WriteProcessMemory(AProcessHandle, P, @LInjectCode, SizeOf(LInjectCode), N);
// Create remote thread in the target process
LThread := CreateRemoteThread(AProcessHandle, nil, 0, P,
nil, 0, LThreadID);
WaitForSingleObject(LThread, INFINITE);
// Read back PID and free memory
ReadProcessMemory(AProcessHandle, P, @LInjectCode, SizeOf(LInjectCode), N);
VirtualFreeEx(AProcessHandle, P, SizeOf(LInjectCode), MEM_RELEASE);
Result := LInjectCode.addr_ret_val;
end;
Здравствуйте, EM, Вы писали:
БП>>Точно! В Win9x можно сделать xCreateRemoteThread(GetCurrentProcessId()).
EM>Как раз в Win9x — то и нельзя — CreateRemoteThread() есть только в NT/2000/XP
xCreateRemoteThread() это функция из библиотеки elirt (см google).
Работает замечательно и в 9.x и в NT3.1
DWORD TId = GetCurrentThreadId(); // Get ID of current thread
DWORD Unobsfucator = 0;
__asm
{
mov eax, fs:[0x18] // pTIB = FS:[18h] pointer to the thread information block
sub eax, 0x10 // pointer for the Thread Database 10h bytes upper
xor eax,[TId] // This is unobsfucator variable
mov [Unobsfucator], eax
}
Соответственнно, получить текущий PDB процесса можно: GetCurrentProcessId() ^ Unobsfucator.
Это для 95-х. Впрочем, должно работать и для других 9х систем. Возможно, с небольшими изменениями.
AS>Думается, это слишком сложно. В 9х, как утверждает MATT PIETREK, все гораздо проще. Первое, что надо помнить — процесс ID в 9х — это всего лишь некоторое обратимое преобразование указателя на PDB процесса (xor with "Obsfucator" DWORD). Т.к. PDB находятся в shared memory, то pid гарантированно уникальны. Второе — то, что PDB текущего процесса можно получить некоторым образом (см. пример к главе 3 — у меня в электронной книге, к сожалению, эта часть отсутствует). Получая PDB, мы можем определить таблицу хендлов, по таблице хандлов можем получить указатель на PDB нужного нам процесса, а из него, используя пресловутое преобразование (1) получить искомый PID процесса. AS>Вот, примерно так.
Здравствуйте, Sergey Ten, Вы писали:
ST>Здравствуйте, LeonV, Вы писали:
LV>>Как можно получить PID, если есть hProcess? Ситуация такова: стартует приложение через ShellExecuteEx. Что это за приложение — заранее неизвестно, так как в ShellExecuteEx я передаю путь к файлу, который требуется открыть.
ST>Предлагаю свой изощренный способ (навеяно статьей в RSDN о перехвате API):
// то, что должно выступать в качестве функции треда:
DWORD WINAPI ThreadProc(
LPVOID lpParameter // thread data
);
// То, что мы ему передаем.
DWORD WINAPI GetCurrentProcessId(VOID);
Соглашение о передаче параметров winapi предусматривает то, что вызываемая функция очищает стек параметров. Таким образом, мы получаем на выходе из функции неправильный стек. Что будет дальше? Неизвестно. Скорее всего, никто ничего не заметит. На данной системе с данным процессом. Но если случится (а это обязательно случится) большой бах — то он случится даже не в вашем, а чужом процессе.
И в любом случае — этот метод не решает проблемы с 9х системами.В общем, лучше, правильнее и быстрее использовать в случае winnt (Nt/Zw)QueryInformationProcess.
Здравствуйте, Sergey Ten, Вы писали:
ST>Здравствуйте, Аноним, Вы писали:
А>>Здравствуйте, LeonV, Вы писали:
LV>>>Как можно получить PID, если есть hProcess?
А>>Чего то я не понял. Почему OpenProcess не заюзать? А>>Передаешь туда PID и сравниваешь хэндлы.
ST>OpenProcess вернет другой хэндл каждый раз про открытии того же самого процесса:
А если так:
#include <windows.h>
main()
{
int i;
HANDLE h;
for (i = 0; i < 10; i++)
{
h = OpenProcess(PROCESS_ALL_ACCESS, 0, GetCurrentProcessId());
CloseHandle(h);
}
}
Re[5]: Получить ProcessID, имея hProcess?
От:
Аноним
Дата:
03.09.03 04:48
Оценка:
Здравствуйте, vasketsov, Вы писали:
V>Здравствуйте, Аноним, Вы писали:
А>>>>Чего то я не понял. Почему OpenProcess не заюзать? А>>>>Передаешь туда PID и сравниваешь хэндлы. А>>А если так: V>А хоть как. V>OpenProcess создает НОВЫЙ хэндл. V>А хэндлы по своей природе не сравниваются друг с другом. V>Аналогия — утверждение навернства 2-х дверей, если у этих дверей одинаковые ручки. V>К тому же, если первый хэндл был валиден в Вашем ВАП, то OpenProcess НИКОГДА не вернет аналогичную величину, потка тот хэндл не будет закрыт, так как нумерация хэндлов для одного процесса сквозная независимо от их типа (процесс, поток, файл, событие и т.д.).
AS>1. Я не описывал никаких приемов формирования стека функции. Вы меня с кем-то путаете
Я никого ни с кем не путал, IMHO.
Исторически первой была техника Джефа, а Прасад, как раз, предложил вариант, описанный тобой. Именно поэтому они в таком порядке и были упомянуты.
Во всяком случае, я считал, что это нужно знать каждому, кто заинтересовался этим вопросом.
LT>> А чем оно отличается от вызыва твоей ThreadFunc, которой также передается параметр — адрес структуры InjectInfo?
AS>При вызове функций главным является соглашение о вызове (в данном случае обе winapi) и количество параметров, которые они в себя принимают. ThreadProc принимает 1 параметр (eq 4 байта на стеке), GetCurrentProcessId принимает 0 параметров. Итак, что происходит. Caller делает перед вызовом ThreadProc push lpParameter, ожидая, что по возвращению, ThreadProc вернет результат в eax и очистит стек (например, ret 2). Однако, GetCurrentProcessId этого не делает (просто ret), хотя и возвращает результат в eax. Итого — внешне все ОК, хотя на самом деле далеко не так — портится caller's стек.
Какой-какой caller? Речь идет о стеке того самого потока.
Если бы было так, как ты описываешь, то удаленному потоку никогда и не удалось
воспользоваться полученным параметром. По-крайней мере, поток вызывал бы
AV по выходу из функции.
AS>И теперь — самое главное. MSDN: AS>
AS>process can obtain the return value of the ThreadProc of a thread it created with CreateThread by calling the GetExitCodeThread function. A process cannot obtain the return value from the ThreadProc of a thread it created with CreateRemoteThread.
Это надо воспринимать несколько критически, иначе бы мы и не могли пользоваться
ExitCode возвращаемым CreateThread, бо реализация его построена на CRT.
Caller — это то, что вызывает функцию потока. Она же не сама по себе вызывается, верно?
Тем более, что по выходу из потока код возврата сохраняется в TDB.
LT> Какой-какой caller? Речь идет о стеке того самого потока. LT> Если бы было так, как ты описываешь, то удаленному потоку никогда и не удалось LT> воспользоваться полученным параметром. По-крайней мере, поток вызывал бы LT> AV по выходу из функции.
А кто сказал, что он этого не делает? Вы можете достоверно это определить на всех системах линейки NT + то, что это происходит в другом процессе? Еще раз. Вам указан прототип функции, который ожидается в качестве функции потока. Любое отклонение от него — это грубейшая ошибка, которая может повлечь за собой все что угодно.
AS>>И теперь — самое главное. MSDN: AS>>
AS>>process can obtain the return value of the ThreadProc of a thread it created with CreateThread by calling the GetExitCodeThread function. A process cannot obtain the return value from the ThreadProc of a thread it created with CreateRemoteThread.
Гм. Вообще то, тут сказано, что GetExitCodeThread не работает именно для CreateRemoteThread, для CreateThread это все вполне работоспособно. Вы можете не доверять MSDN — это Ваше право, но не дай Бог Вам потом "вживую" встретить пострадавшего от этого недоверия пользователя вашей программы LT> Это надо воспринимать несколько критически, иначе бы мы и не могли пользоваться LT> ExitCode возвращаемым CreateThread, бо реализация его построена на CRT.
Какой CRT? Если имеется ввиду C run time, то тут это не при чем, если что то еще — тогда я видимо не в курсе этого чего то еще
Честно говоря, я устал говорить то же самое на протяжении 4-х постингов. Вы можете писАть для себя как угодно, я думаю, эта тема для меня (и не только) исчерпана.
Спасибо за внимание.
Приветствую, Andrew.
> Честно говоря, я устал говорить то же самое на протяжении 4-х постингов. Вы можете писАть для себя как угодно, я думаю, эта тема для меня (и не только) исчерпана.
Экий бука. А я хотел порадовать откровением самого Прасада.
Оно могло бы, IMHO, тебя утешить.
От:Prasad Dabak (pdabak@yahoo.com)
Заголовок:Re: getting command line of a process
Группы новостей:microsoft.public.win32.programmer.kernel
Число:2001-07-17 23:59:25 PST
Hello,
You are *absolutely* right about your suspicion.
The actual thread function is called from BaseThreadStart function in
KERNEL32.DLL and once your function returns, the BaseThreadStart
function calls ExitThread. Hence, I believe, it does not matter if
there is any extra parameter present on stack.
Hence it works.
The *safest* way would be to inject some function (containing no
relocatable instructions) in the target process, inject some data
structure containing all the required data and then create a remote
thread and pass it pointer to the data structure. After the thread
returns read back the data strcuture from the target process to get
the result.
e.g.
You can inject a data structure and a function like below.
RGetCommandLineA(PGETCMDLNDATA pGetCmdlnData)
{
char *p;
int i;
pGetCmdlnData->CmdLn[i]='\0'
p=(char *)pGetCmdlnData->pfnGetCommandLineA();
if (p) {
for (i=0; p[i]; i++) {
pGetCmdlnData->CmdLn[i]=p[i];
} pGetCmdlnData->CmdLn[i]='\0';
} return 0;
}
-Prasad
Daniel Lohmann <daniel@uni-koblenz.de> wrote in message news:<bqg9lto0h6gio4kbtreo6n0un6jgpk7mdh@4ax.com>... > On 17 Jul 2001 04:29:40 -0700, pdabak@yahoo.com (Prasad Dabak) wrote: > > >Hello, > > > >Here is the sample program to do it. It accepts process id on the > >command line and prints commandline for that process. You will have to > >enable debug privilege to access commandline for processes not running > >in your own security context. > > Nice example, Prasad > > But I have a question there. I thought about doing it exactly the same > way you suggested (calling GetCommandLineA/W() as thread function via > CreateRemoteThread), but decided it would not work: An ordinary thread > function accepts a single 32 bit parameter on the stack and because it > is called as _stdcall it *must* remove the parameter from the stack. > GetCommandLine() expects nothing to be passed on the stack and > therefore does not remove anything. > > Does this work only because the thread terminates after calling > GetCommandLine() and nobody worries about the not-cleaned-up stack or > I'm missing something? > > > Daniel
--
> Спасибо за внимание.
Здравствуйте, Leonid Troyanovsky, Вы писали:
>> Честно говоря, я устал говорить то же самое на протяжении 4-х постингов. Вы можете писАть для себя как угодно, я думаю, эта тема для меня (и не только) исчерпана.
LT> Экий бука.
Тема исчерпана, так как и для 9x и для NT есть более простые решения, чем CRT(GetCurrentProcessId()).
И совершенно не важно, будет ли этот способ работать или нет. Он никому не нужен.