PIPE'lere ilk ihtiyaç duyduğum zaman, command prompt altında yaptığım "ping" sonuçlarını bire bir görmek istemiştim. Shell32Exec veya muadili execute api'leri ile komutu çalıştırabilyor ancak bir command-prompt penceresi açarak sonucu görebiliyordum. Bu da benim ve programımım görselliği açısından pek hoş olmuyordu. Amacım bu tarama sonuçlarını programıma ait pencerede kendisi için ayırdığım bir panel'de görünmesiydi.
Aslında itiraf etmek gerekirse bu iş için microsoftun bir api sağladığını ilk aşamada düşünmemiştim. Bende kendi basit çözümümü devreye koyup sonuç çıktısını bir dosyaya yazdırıp tekrar oradan okuyup ekrana yazdırıyordum ki, bu iğrenç çözüm hiçte içime yatmamıştı. Araştırmalarım esnasında processler hakkında okuduğum makalelerin birinde Pipe'ler ile tanıştım.
PIPE'ler Unnamed ve Named olarak iki kısımda irdelenir. Named olanlar biraz daha gelişmiş ipc tarzı işlem yapmak isteyenler için faydalıdır. Unnamed olanlar ise bizim ilgilendiğimiz ve her process için iki handle sağlayan tipleridir. Win32 Process mimarisinde her çalışan process alanı için PROCESS_INFORMATION ve STARTUP_INFORMATION structları vardır. Bu yapılar sayesinde Create edeceğiniz veya Create edilmiş işlemlerden bilgi yazabilirsiniz/okuyabilirsiniz.
Uygulamalarımızda Pipe'ler ile çalışabilmek için bazı ön hazırlıklar yapmalıyız. Bunlar yukarıda da yazdığım Structları tanımlamaktan ibarettir. Win32 uygulamanızda Unnamed pipe'ler ile çalışabilmek içi microsoftun bize sunduğu api "CreatePipe" api'sidir. SDK'da tanımlandığı gibi protoype'i aşağıda ki gibidir.
BOOL CreatePipe(
PHANDLE hReadPipe, // okunacak veriler icin kullanilicak handle'in adresi
PHANDLE hWritePipe, // yazilacak veriler icin kullanilicak handle'in adresi
LPSECURITY_ATTRIBUTES lpPipeAttributes, // security attributes yapısına pointer
DWORD nSize // pipe islemleri icin ayrilacak buffer (bu alanin miktarini siz belirteceksiniz)
);
Parametreler
hReadPipe
32 bitlik bir handle pointer'idir. Bu handle sayesinde pipe'mizin okuma ucundaki bilgileri ReadFile api'si ile istediğimiz bir hafıza alanına taşıyabiliriz. Uygulama içerisindeki kullanımı aşağıdaki örneklerde yer alıyor.
hWritePipe
32 bitlik bir handle pointer'idir. Bu handle sayesinde pipe'mizin yazma ucuna koyduğumuz bilgileri WriteFile api'si ile STARTUPINFO yapısı kullanılarak belirtilen hafıza alanına koyabiliriz.
lpPipeAttributes
Aslında bu yapının amacı belirtilen SECURITY_ATTRIBUTES niteliklerinin child process'lere(yani çağrılan processlere) de geçip geçmeyeceği ile ilgilidir. Eğer bu tanımlamayı yapmaz ve boş geçer iseniz üzülürsünüz. Bi daha gidip alt process için gereksiz yere SECURITY_ATTRIBUTES yapısı build edersiniz.
nSize
Pipe için buffer boyutunu belirtir. İstenilen boyut girilebilir ancak null geçip varsayılan boyutu sistemin sizin için ayarlamasınıda sağlayabilirsiniz.
Return Values
Fonksiyon herhangi bir problem ile karşılaşmaz ve görevini başarı ile yerine getirirse geriye sıfır harici rastgale bir sayı döndürür.
Eğer herhangi bir sebepten dolayı bir hata ile karşılaşır ise geriye sıfır değerini dönderir. Hata kodunun ne anlam içerdiğini bulabilmek için ise GetLastError api'si kullanılabilir.
Sizde uygulamanızda console uygulamaları ile i/o işlemi yapacak ve sonuçları özelleştirip programınızda kullanacaksanız Anonymous Pipe'ler bu işler için çok uygundur. Aşağıda kodlarını verdiğim VBScript ve Masm32 kodlarını derleğip nasıl çalıştıkları hakkında daha tefferruatlı bilgi sahibi olabilirsiniz.
;developed by StreAmeR bilgislem@hotmail.com 20:25 29.12.2004
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
.const
IDR_MAINMENU equ 101
IDM_ASSEMBLE equ 40001
.data
ClassName db "StrPipeClass",0
AppName db "Tek-yönlü Pipe Örnegi ve 3 Asamali Thread",0
EditClass db "EDIT",0
CreatePipeError db "Pipe olusumu esnasında hata ile karsilasldi",0
CreateProcessErr db "Islem olusturulurken hata ile karsilasildi",0
CommandLine db "c:\windows\system32\cmd /c cd&dir C: /s/a",0
Command2 db "c:\windows\system32\netstat -a",0
Command3 db "c:\windows\system32\ipconfig -all",0
.data?
hInstance HINSTANCE ?
hwndEdit dd ?
ThreadID DWORD ?
count db ?
.code
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
mov byte ptr[count],0
invoke WinMain,hInstance,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW+CS_VREDRAW
mov wc.lpfnWndProc,OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_APPWORKSPACE
mov wc.lpszMenuName,IDR_MAINMENU
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx,addr wc
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,
WS_OVERLAPPEDWINDOW+WS_VISIBLE,640,
480,400,200,NULL,NULL,
hInst,NULL
mov hwnd,eax
.while TRUE
invoke GetMessage,ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.endw
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT,wParam:WPARAM,lParam:LPARAM
LOCAL rect:RECT
LOCAL hdc:DWORD
.if uMsg == WM_CREATE
invoke CreateWindowEx,NULL,addr EditClass,NULL,
WS_CHILD+WS_VISIBLE+ES_MULTILINE+ES_AUTOHSCROLL+ES_AUTOVSCROLL,
0,0,0,0,hWnd,NULL,hInstance,NULL
mov hwndEdit,eax
.elseif uMsg==WM_CTLCOLOREDIT
invoke SetTextColor,wParam,Yellow
invoke SetBkColor,wParam,Black
invoke GetStockObject,BLACK_BRUSH
ret
.elseif uMsg==WM_SIZE
mov edx,lParam
mov ecx,edx
shr ecx,16
and edx,0ffffh
invoke MoveWindow,hwndEdit,0,0,edx,ecx,TRUE
.elseif uMsg==WM_COMMAND
.if lParam==0
mov eax,wParam
.if ax==IDM_ASSEMBLE
mov eax,OFFSET ThreadProc
invoke CreateThread,NULL,NULL,eax,
NULL,NORMAL_PRIORITY_CLASS,
ADDR ThreadID
invoke CloseHandle,eax
.endif
.endif
.elseif uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
WndProc endp
ThreadProc proc hWnd:HWND
LOCAL hRead:DWORD
LOCAL hWrite:DWORD
LOCAL startupinfo:STARTUPINFO
LOCAL pinfo:PROCESS_INFORMATION
LOCAL buffer[4096]:byte
LOCAL bytesRead:DWORD
LOCAL sat:SECURITY_ATTRIBUTES
mov sat.nLength,sizeof SECURITY_ATTRIBUTES
mov sat.lpSecurityDescriptor,NULL
mov sat.bInheritHandle,TRUE
invoke CreatePipe,addr hRead,addr hWrite,addr sat,addr buffer
.if eax==NULL
invoke MessageBox,hWnd,addr CreatePipeError,addr AppName,MB_ICONERROR+MB_OK
.else
mov startupinfo.cb,sizeof STARTUPINFO
invoke GetStartupInfo,addr startupinfo
mov eax,hWrite
mov startupinfo.hStdOutput,eax
mov startupinfo.hStdError,eax
mov startupinfo.dwFlags,STARTF_USESHOWWINDOW+STARTF_USESTDHANDLES
mov startupinfo.wShowWindow,SW_HIDE
inc byte ptr[count]
.if count==1
invoke CreateProcess,NULL,addr CommandLine,NULL,NULL,TRUE,NULL,NULL,NULL,
addr startupinfo,addr pinfo
.elseif count==2
invoke CreateProcess,NULL,addr Command2,NULL,NULL,TRUE,NULL,NULL,NULL,
addr startupinfo,addr pinfo
.elseif count==3
invoke CreateProcess,NULL,addr Command3,NULL,NULL,TRUE,NULL,NULL,NULL,
addr startupinfo,addr pinfo
mov byte ptr[count],0
.endif
.if eax==NULL
invoke MessageBox,hWnd,Addr CreateProcessErr,addr AppName,MB_ICONERROR+MB_OK
.else
invoke CloseHandle,hWrite
.while TRUE
invoke RtlZeroMemory,addr buffer,4096
invoke ReadFile,hRead,addr buffer,4095,addr bytesRead,NULL
.if eax==NULL
.break
.else
invoke SendMessage,hwndEdit,EM_SETSEL,-1,0
invoke SendMessage,hwndEdit,EM_REPLACESEL,FALSE,addr buffer
.endif
.endw
.endif
invoke CloseHandle,hRead
ret
.endif
ThreadProc endp
end start
Bunuda Excel'de macro alanında deneyebilirsiniz. Yaptığı iş ilgili hücreden aldığı ip adresine ping atıp sonuçları belirtilen hücrelere yazmaktır. Bir nevi ip scanner işte.
'Developed by StreAmeR with Win32Api
'bilgislem@hotmail.com; loginit@gmail.com
Option Base 1
Option Explicit
Private Declare Function CreatePipe Lib "Kernel32" ( _
phReadPipe As Long, _
phWritePipe As Long, _
lpPipeAttributes As Any, _
ByVal nSize As Long) As Long
Private Declare Function ReadFile Lib "Kernel32" ( _
ByVal hFile As Long, _
ByVal lpBuffer As String, _
ByVal nNumberOfBytesToRead As Long, _
lpNumberOfBytesRead As Long, _
ByVal lpOverlapped As Any) As Long
Private Declare Function GetSystemDirectoryA Lib "Kernel32" ( _
lpBuffer As String, iSize As Long) As Long
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type
Private Type STARTUPINFO
cb As Long
lpReserved As Long
lpDesktop As Long
lpTitle As Long
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadID As Long
End Type
Private Declare Function CreateProcessA Lib "Kernel32" (ByVal _
lpApplicationName As Long, ByVal lpCommandLine As String, _
lpProcessAttributes As Any, lpThreadAttributes As Any, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, _
lpStartupInfo As Any, lpProcessInformation As Any) As Long
Private Declare Function WaitForSingleObject Lib "Kernel32" _
(ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function CloseHandle Lib "Kernel32" (ByVal _
hObject As Long) As Long
Const SW_SHOWMINNOACTIVE = 7
Const SW_HIDE = 0
Const STARTF_USESHOWWINDOW = &H1
Const INFINITE = -1&
Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const STARTF_USESTDHANDLES = &H100&
Dim ExecCmd As String
Private Function ExecCmdPipe(ByVal CmdLine As String) As String
Dim proc As PROCESS_INFORMATION, ret As Long, bSuccess As Long
Dim start As STARTUPINFO
Dim sa As SECURITY_ATTRIBUTES
Dim hReadPipe As Long, hWritePipe As Long
Dim bytesread As Long, mybuff As String
Dim i As Integer
Dim sReturnStr As String
mybuff = String(10 * 1024, Chr$(65))
sa.nLength = Len(sa)
sa.bInheritHandle = 1&
sa.lpSecurityDescriptor = 0&
ret = CreatePipe(hReadPipe, hWritePipe, sa, 0)
If ret = 0 Then
ExecCmd = "Hata. CreatePipe icra edilmeye çalışılırken durdu. " & Err.LastDllError
Exit Function
End If
start.cb = Len(start)
start.hStdOutput = hWritePipe
start.dwFlags = STARTF_USESTDHANDLES + STARTF_USESHOWWINDOW
start.wShowWindow = SW_HIDE
ret& = CreateProcessA(0&, CmdLine$, sa, sa, 1&, _
NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)
If ret <> 1 Then
sReturnStr = "Hata: CreateProcess çalıştırılamadı ." & Err.LastDllError
End If
ret = WaitForSingleObject(proc.hProcess, INFINITE)
bSuccess = ReadFile(hReadPipe, mybuff, Len(mybuff), bytesread, 0&)
If bSuccess = 1 Then
sReturnStr = Left(mybuff, bytesread)
Else
sReturnStr = "Error: ReadFile durdu. " & Err.LastDllError
End If
ret = CloseHandle(proc.hProcess)
ret = CloseHandle(proc.hThread)
ret = CloseHandle(hReadPipe)
ret = CloseHandle(hWritePipe)
ExecCmd = sReturnStr
End Function
Sub Pipe()
Dim s, coz As String
Dim Aranacak, Aranan, bulunan, f, al, kez, i, satir
Dim say As Integer
satir = 2
kez = Sheets("Sayfa1").Range("E4")
Aranacak = Sheets("Sayfa1").Range("C4")
bulunan = InStr(1, Aranacak, ".", vbTextCompare)
bulunan = InStr(bulunan + 1, Aranacak, ".", vbTextCompare)
bulunan = InStr(bulunan + 1, Aranacak, ".", vbTextCompare)
al = Mid$(Aranacak, 1, bulunan)
bulunan = Mid$(Aranacak, bulunan + 1, 3)
For i = 1 To kez
s = al & bulunan
Sheets("Sayfa1").Range("C4") = s
s = "c:windowssystem32ping " & s & " -n 1"
ExecCmdPipe (s)
Aranan = "Kaybolan = 0"
coz = ExecCmd
f = InStr(1, coz, Aranan, vbTextCompare)
If f = 0 Then
Sheets("Sayfa1").Range("b" & satir) = Sheets("Sayfa1").Range("C4") & " ip'li makine deaktiv"
Else
Sheets("Sayfa1").Range("b" & satir) = Sheets("Sayfa1").Range("C4") & " ip'li makine aktif"
End If
satir = satir + 1
bulunan = bulunan + 1
Next
End Sub
Pipe'lerde bir de şöyle bir zaaf vardır ki çok uzun buffer gerektiren işlemlerde ve birebir aynı anda i/o gerektiren uygulamalarda yetersiz kalıyor. Örneğin tüm partition'in çıktısını almaya çalıştığınız bir "dir c: /s/a" işleminde süreç devam ediyor ancak çıktı alma işlemi yarıda kesilebiliyor. Ayrıca 7zip gibi sıkıştırma işlemini ekrana anlık olarak istatistik veren uygulamalarda da uzun süreli gecikmeler yaşanıyor. Ama yinede bu kadar kabahat kadı kızında da olur diyor ve kullanmaya devam ediyoruz.