Полезно ли е преписването от Интернет? :)

Преди 2-3 години ми се наложи да напиша програма, която стартира друго приложение в кода си. Логично, попитах google 🙂 . Намерих това, което ми трябва. Даже си го отделих в unit, който съдържа често използвани процедури и фунцкии. До тук добре.
Малко след това трябваше да се направи нишково приложение (Multi Threaded Аpplication), което да стартирапо 30външни приложения едновременно и постоянно – като цяло интензивнен процес. Направих необходимото и пуснах програмата в действие. За лоша моя изненада тулчето ми зависваше след няколко дни работа и състоянието на компютъра, на който се изпълняваше беше трагично: “Low Of System Resources”… Очевадно беше, че някъде съм забравил да освободя памет т.е. т.нар. Memory Leak (изтичане на памет). Прекарах доста време пред не чак толкова сложната си програма, но не постигнах никакъв положителен ефект – на пръв поглед всичко беше ОК. Последваха интензивни тестове, които показаха, че приложението ми яде памет…но къде?!?
Седмици след това намерих ключа от палатката: абсолютно НЕочевидно в процедурката за стартиране на външно приложение беше пропуснато нещо, довеждащо до memory leak. Интересното е, че въпросният отрязък от кода беше толкова (а и е в момента) разпространен във форумите и сайтовете за Delphi, че едва ли някой е обърнал внимание на проблема. Последният не се и забелязва ако използваш фунцкията 1-2 пъти в програмата си 🙂

За да не се бавим ще ви издам името на процедурата: WinExecAndWait или WinExecAndWait32. Като потърсите във www.google.com попадате мигновено на сайта http://www.hitekdev.com/delphi/winexecandwait.html(забележете: HI TEK DEV 🙂 ). И там виждате следния код:

[code lang=”pascal”]

function WinExecAndWait32(FileName:String; Visibility : integer):integer;
var
zAppName:array[0..512] of char;
zCurDir:array[0..255] of char;
WorkDir:String;
StartupInfo:TStartupInfo;
ProcessInfo:TProcessInformation;
begin
StrPCopy(zAppName,FileName);
GetDir(0,WorkDir);
StrPCopy(zCurDir,WorkDir);
FillChar(StartupInfo,Sizeof(StartupInfo),#0);
StartupInfo.cb := Sizeof(StartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := Visibility;
if not CreateProcess(nil,
zAppName, { pointer to command line string }
nil, { pointer to process security attributes }
nil, { pointer to thread security attributes }
false, { handle inheritance flag }
CREATE_NEW_CONSOLE or { creation flags }
NORMAL_PRIORITY_CLASS,
nil, { pointer to new environment block }
nil, { pointer to current directory name }
StartupInfo, { pointer to STARTUPINFO }
ProcessInfo) then Result := -1 { pointer to PROCESS_INF }
else begin
WaitforSingleObject(ProcessInfo.hProcess,INFINITE);
GetExitCodeProcess(ProcessInfo.hProcess,Result);
end;
end;

[/code]

{Thanks to Pat Ritchey for these functions.}

Обърнете внимание на завършващите редове:

[code lang=”pascal”]WaitforSingleObject(ProcessInfo.hProcess,INFINITE);
GetExitCodeProcess(ProcessInfo.hProcess,Result);

[/code]

Така да се каже няма нищо нередно. НО…какво е ProcessInfo.hProcess и ProcessInfo.hThread? Това са манипулатори (handles), които API функцията CreateProcess връща, за да можем да контролираме създадения процес (ако искаме!). Дефакто те заемат памет. А виждате ли някъде в кода тази памет да се освобождава? НЕ 🙂
За да бъде всичко наред трябва да “затворим” въпросните манипулатори, чрез точно два реда:
CloseHandle(ProcInfo.hProcess);
CloseHandle(ProcInfo.hThread);
Един пропуск и седмици мъка 🙂 Но откъде ги измислих тези два реда? Много ясно – пише си го в описанието на фунцкцията CreateProcess в MSDN:

lpProcessInformation [out]

A pointer to a PROCESS_INFORMATION structure that receives identification information about the new process.

!!! –> Handles in PROCESS_INFORMATION must be closed with CloseHandle when they are no longer needed.


Така че не приемайте всичко в Интернет за чиста монета. Особено ако пишете код четете документацията от производителя. Също така тествайте програмите си дълго. Могат да изкочат бъгове, които се проявяват рядко и/илисамопри голямо натоварване 🙂
п.п. MSDN (Microsoft Developer Network) е доста полезно място. Материалите са описани подробно и разбираемо (е…не е баш като за програмисти новаци, но пак става 🙂 ). Личното ми мнение е, че бъговете, на които се натъкваме в различните приложни програми/драйвери се дължат доста често в недочитане или пропускане на неща ОТ ПРОГРАМИСТИТЕ (!!!), които всъщност са добре документирани 🙂
Happy Coding!
Допълнителни материали:

Leave a Reply

Your email address will not be published. Required fields are marked *