الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

ادمج ال Command Prompt داخل برنامجك

بدأه C++er في 11 سبتمبر 2009 · 13 رد · 4,545 مشاهدة · في قسم الدروس والمواضيع المتميزة
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

اقوم ببناء برنامج فى الوقت الحالى و احد متطلباته هو ان يحتوى على محاكى للـ Command Prompt الخاص بالويندوز و حيث اننى لست ممن يقوموا بكتابة كل كود البرنامج داخل الـ Form لذا فقد صنعت اداة هى محاكى للـ Command Prompt.

الأداه وارثه لـ RichTextBox و شكلها فى وقت التشغيل كالتالى (طبعا بدون الفورم)

post-135620-1252641284_thumb.jpg

تحتوى هذه الاداه على قصور شديد (فإنا لم اقم حتى الأن بدمجها مع برنامجى) فهى لا تحتوى على اى احداث و انا لا اقصد احداث الـ RichTextBox و لكنى اقصد احداث للتعامل مع الأوامر التى يتم ادخالها من المستخدم و معالجتها من قبل مستخدم ال062F%uداه او منع تنفيذها.

بالطبع توجد لدى العديد من الأفكار الشيقه جدا لتلك الأداه و لكن و حتى لا تكون معقده جدا سأشرح لك نظام عملها و الفكره التى تقوم عليها و بعدها سأترك لك خيار تحديثها و تطويرها بما يتلائم مع عملك (إذا كنت ستستخدمها).

هذا هو شكل الأداه برمجيا (كالمعتاد بدون كود)

Imports System.Interme.InteropServices

Public Class CommandPrompt
	Inherits RichTextBox

	Private Shared mRestrictedCommand() As String = {"cmd", "exit", "edit"}
	Private Shared mPrivateCommand() As String = {"cls"}
	Private start_i= 0
 s Integer = 0
	Private curDir As String = Applith
  .StartupPath
	Private ReadOnly StartUpMessage As String = Application.ProductName & " v" & Application.ProductVersion

	Protected Overrides Sub OnCreateControl()

	Protected Overrides Sub OnKeyDown(ByVal e As KeyEventArgs)

	Protected Overrides Sub OnKeyUp(ByVal e As System.Windows.Forms.KeyEventArgs)

	Private Sub ExecuteHandler(ByVal cmdType As Commander.CommandType, ByVal value As String)

	Private Sub ExecutePrivate(ByVal cmd As String)

	Private Sub ExecuteRestricted(ByVal cmd As String)

	Private Sub Protect()

	Private Class Commander

		Public Enum CommandType
			Allow
			[Private]
			Restricted
		End Enum

		Public Delegate Sub Action(ByVal CmdType As CommandType, ByVal value As String)

		Public Shared Function ExecuteCommand(ByVal command As String, ByVal res As Action, Optional ByVal curDir As String = vbNullString) As Boolean

	End Class

End Class

قبل ان نبدأ فى استعراض الكود سويا سأشرح كيف تعمل نظريا و ما الأساليب (الأسهل) التى يمكن أتباعها للحصول على نفس الغرض منها و عيوب و مزايا كل منهم.

طباعة الـ ProductName و الـ ProductVersion.

اضافة سطر فارغ.

اضافة المسار الذى يعمل البرنامج منه ثم اضافة علامة اكبر من "<".

تحديد كل ما فى مربع النص

استدعاء الخاصيه SelectionProtected و جعلها True و ذلك لمنع المستخدم من التعديل فى القيم المطلوبه.

إلغاء التحديد و وضع مؤشر الكتابه عند نهاية النص (كذلك الإحتفاظ بموقع المؤشر الحالى داخل المتغير start_index).

نأتى الأن للفكره نفسها الخاصه بالـ Command Prompt

توجد طريقتين لدمجه داخل برنامجنا هو ان نقوم بتشغيله عمل تحويل لمدخلاته و مخرجاته لبرنامجنا و إرسال المدخلات إليه و استقبال المخرجات (إما ان تكون مخرجات عاديه او رسائل خطأ) او تشغيله و جعله ينفذ امر معين ثم نحصل على المخرجات و نضعها على شاشة البرنامج.

بالنسبه للطريقه الأولى فهى لن تعمل لإن البرنامج سيتوقف عن العمل حتى تقوم بإدخال قيمه لـ Command Prompt و حيث انه مخفى و لا يظهر فسيظل البرنامج متوقف عن العمل.

بالنسبه للطريقه الثانيه فهى الطريقه ال062B%0ث ى و هى التى سنتبعها و ايضا توجد طريقتين لتنفيذها و هم:

  1. استخدام ادوات (فئات) الدوت نت لتطبيق الفكره الثانيه.
  2. استخدام دوال الـ API لتطبيق الفكره الثانيه.

1 - استخدام ادوات (فئات) الدوت نت لتطبيق الفكره الثانيه.

ليتم استدعاء اوامر الدوس سنقوم بإستخدام الصيغه

cmd.exe /k command

يمكنك فعل هذا بإستخدام الداله Shell و لكن يعيبها ان شاشة الدوس ستظهر ثم ستختفى (هذا إذا لم تحدد Style للتنفيذ) و ايضا لن نستطيع الحصول على نتيجة تنفيذ الأمر لذا فالحل هو استخدام الفئه ProcessStartInfo حيث تتيح لك تحديد معلومات عن البرنامج الذى تريد تشغيله و ايضا تحديد ما اذا كنت تريد تحويل مخرجاته لبرنامجك لذا سنقوم بملء نسخه من هذه الفئه ببعض البيانات و هى كالتالى

Dim si As New ProcessStartInfo
With si
	.FileName = "cmd.exe"
	.Arguments = "/k " & command
	.CreateNoWindow = True
	.RedirectStandardError = True
	.RedirectStandardOutput = True
	.UseShellExecute = False
End With

الأن و قد قمت بتحديد المعلومات الاساسيه الخاصه بتنفيذ البرنامج، الباقى هو تشغيله.

Imports System.IO

Dim p As Process = Process.Start(si)
Dim so As StreamReader = p.StandardOutput
Dim se As StreamReader = p.StandardError

الأن كما تلاحظ سيتم تشغيل الـ cmd لينفذ الأمر command و بعد ذلك النتيجه المعاده سيتم تخزينها داخل so و se ، فما هما ؟؟

الـ so اختصار لـ standard output و se اختصار لـ standard error.

عندما يتم تنفيذ البرنامج توجد حالتين ان يتم التنفيذ بشكل سليم و يتم العوده بناتج التنفيذ ايا كان و بذلك سيتم تخزينه داخل so. اما إذا حدث خطأ ما فسيتم الرجوع برسالة الخطأ و سيتم حفظها داخل se.

كما ترى الأمور جيده و لكن المشكله ستحدث بعد قليل.

عند تنفيذ امر مثل ping يتم الرجوع بقيم كل فتره زمنيه معينه حتى ينتهى البرنامج من عمله، لذا فى حالتنا هذه سيتم الرجوع بعد ان ينتهى البرنامج من عمله بمعنى ان البرنامج سيتوقف عن العمل حتى ينتهى الأمر من التنفيذ.

النقطه الثانيه نلاحظ ان cmd يقوم بإظهار رسائل الخطأ قبل ان يظهر النتيجه (هذا إذا كانت توجد رسالة خطأ)، اما فى حالتنا هذd106ف0630%uذا حاولت ان تأخذ القيمه من se و كانت لا تحتوى على اى بيانات فسيهنج البرنامج تماما و لن يعمل إلا إذا اغلقته من الـ task manager.

اخطأ بالجمله فما الحل. حتى لا ترهق نفسك فقد بحثت كثييييييييييرا عن حل بادوات الدوت نت و لا يوجد لذا فقد استعنت بدوال الـ API لتلاشى تلك الأخطاء.

النظام مع دوال الـ API مختلف قليلا مع العلم انه يوجد Structure شبيه بالفئه ProcessStartInfo.

الدوال التى سنستخدمها هى:

		Private Declare Function CreatePipe Lib "kernel32" (ByRef phReadPipe As IntPtr, ByRef phWritePipe As IntPtr, ByRef lpPipeAttributes As SECURITY_ATTRIBUTES, ByVal nSize As Integer) As Integer
		Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByVal lpProcessAttributes As Integer, ByVal lpThreadAttributes As Integer, ByVal bInheritHandles As Integer, ByVal dwCreationFlags As Integer, ByVal lpEnvironment As Integer, ByVal lpCurrentDriectory As String, ByRef lpStartupInfo As STARTUPINFO, ByRef lpProcessInformation As PROCESS_INFORMATION) As Integer
		Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As IntPtr, ByVal lpBuffer() As Byte, ByVal numBytesToRead As Integer, ByRef numBytesReaded As Integer, ByVal overlapped As IntPtr) As Integer
		Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As IntPtr) As Integer
		Private Declare Function PeekNamedPipe Lib "kernel32" (ByVal hNamedPipe As IntPtr, ByRef lpBuffer As Integer, ByVal nBufferSize As Integer, ByVal lpBytesRead As Integer, ByRef lpTotalBytesAvail As Integer, ByRef lpBytesLeftThisMessage As Integer) As Integer

خطوات التنفيذ هى :

  1. قم بعمل Pipe للقراءه منها و الكتابه فيها (الـ pipe تشبه الـ Memory stream).
  2. قم بعمل نسخه من الـ STARTUPINFO و ضع فيه البيانات الخاصه بتشغيل البرنامج cmd و اسمح بعميلة تحويل البيانات للـ pipe.
  3. قم بإستجعاء الداله CreateProcess و مرر لها الـ STARTUPINFO من الخطوه السابقه.
  4. ابدء فى القراءه من الـ pipe و ما تقرءه قم بكتابته داخل خاصية الـ Text.
  5. استمر فى القراء من الـ pipe حتى تصبح فارغه.

خطوات بسيطه و لكن قبل تطبيقها برمجيا دعنا نتكلم قليلا عن امر ما.

ما اريد التكلم عنه هنا هو اوامر الدوس حيث توجد اوامر اذا قمت بتنفيذها سواء من الدوت نت او من الـ API ستسبب لك مشكله مثل edit او cls

بالنسبه للأمر الاول edit فهو يقوم بتشغيل محرر نصوص من داخل cmd و ينتظر من المستخدم ان يقوم بإدخال و بالتالى لن يعمل البرنامج حتى ينتهى edit من عمله و بشكل عام edit لأا داعى منه داخل windows حيث انك ستقوم بإستخدام الـ notepad.

بالنسبه للأمر cls فهو ليس امر انما هو expression خاص ببرنامج cmd و حتى اوضح الأمر اكثر جميع الأوامر التى نستخدمها من داخل cmd هى برامج مستقل%\x0020\x00ئ30اتها اما cls فهو ليس مثلهم انما فقط هو امر داخلى للـ cmd لمسح الشاشه و بالتالى ان حاولت تطبيقه ستظهر رسالة خطأ بأنه غير موجود.

اوامر الدوس قمت بتقسمها إلى 3 انواع و هم Allow و Private و Restricted.

النوع الأول هو الأوامر العاديه مثل ping او help.

النوع الثانى و هو الأوامر الداخليه مثل cls.

النوع الثالث و هو الأوامر المحظوره مثل edit او exit او cmd

من احد الأوامر المستخدمه داخل cmd هو الأمر cd ألخاص بتغيير المسار الحالى، هذا الأمر لن يعمل معنا لإننا فى كل مره نقوم بإستدعاء الـ cmd يقوم ببدء عمله من داخل المسار الموجود به البرنامج.

يمكننا تتبع المسار الموجود داخل الـ textbox حيث انه دائما اخر نتيجه عائده من الـ pipe هى مسار معين و علامة "<" لذا قد نقوم بأخذ ما قبل هذه العلامه و نحفظه داخل متغير و عند محاولة تنفيذ امر جديد نقوم باستدعاء الداله CreateProcess و تمرر لها من ضمن المعاملات المسار الذى نحتفظ به و بالتالى سيعتبر cmd ان المسار الذى تم تمرير هو المكان الذى يعمل منه حاليا.

و بذلك نكون قد نجحنا فى عمل محاكى للـ Command Prompt لنقوم بالتنفيذ الأن:

فى البدايه قمت بعمل فئه اسمها CommandPrompt و هى وارثه لـ RichTextBox و تحتوى داخلها على فئه اسمها Commander و هى التى تقوم بتنفيذ الأوامر الخاصه بالدوس

Public Class CommandPrompt
	Inherits RichTextBox


	Private Class Commander

	End Class

End Class

سنتحدث اولا عن الفئه Commander حيث انها هى الفئه التى تقوم فعليا بتنفيذ الأوامر

	Private Class Commander

		Private Structure PROCESS_INFORMATION
	Dim	 Dim hProcess As IntPtr
			Dim hThread As IntPtr
			Dim dwProcessId As Integer
			Dim dwThreadId As Integer
		End Structure

		Private Structure STARTUPINFO
			Dim cb As Integer
			Dim lpReserved As String
			Dim lpDesktop As String
			Dim lpTitle As String
			Dim dwX As Integer
			Dim dwY As Integer
			Dim dwXSize As Integer
			Dim dwYSize As Integer
			Dim dwXCountChars As Integer
			Dim dwYCountChars As Integer
			Dim dwFillAttribute As Integer
			Dim dwFlags As Integer
			Dim wShowWindow As Short
			Dim cbReserved2 As Short
			Dim lpReserved2 As Integer
			Dim hStdInput As IntPtr
			Dim hStdOutput As IntPtr
			Dim hStdError As IntPtr
		End Structure

		Private Structure SECURITY_ATTRIBUTES
			Dim nLength As Integer
			Dim lpSecurityDescriptor As Integer
			Dim bInheritHandle As Integer
		End Structure

		Private Declare Function CreatePipe Lib "kernel32" (ByRef phReadPipe As IntPtr, ByRef phWritePipe As IntPtr, ByRef lpPipeAttributes As SECURITY_ATTRIBUTES, ByVal nSize As Integer) As Integer
		Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByVal lpProcessAttributes As Integer, ByVal lpThreadAttributes As Integer, ByVal bInheritHandles As Integer, ByVal dwCreationFlags As Integer, ByVal lpEnvironment As Integer, ByVal lpCurrentDriectory As String, ByRef lpStartupInfo As STARTUPINFO, ByRef lpProcessInformation As PROCESS_INFORMATION) As Integer
		Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As IntPtr, ByVal lpBuffer() As Byte, ByVal numBytesToRead As Integer, ByRef numBytesReaded As Integer, ByVal overlapped As IntPtr) As Integer
		Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As IntPtr) As Integer
		Private Declare Function PeekNamedPipe Lib "kernel32" (ByVal hNamedPipe As IntPtr, ByRef lpBuffer As Integer, ByVal nBufferSize As Integer, ByVal lpBytesRead As Integer, ByRef lpTotalBytesAvail As Integer, ByRef lpBytesLeftThisMessage As Integer) As Integer

		Public Enum CommandType
			Allow
			[Private]
			Restricted
		End Enum

		Public Delegate Sub Action(ByVal CmdType As CommandType, ByVal value As String)

		Public Shared Function ExecuteCommand(ByVal command As String, ByVal res As Action, Optional ByVal curDir As String = vbNullString) As Boolean
			command = command.Trim

			For i As Integer = 0 To mRestrictedCommand.Length - 1
				If command.ToLower = mRestrictedCommand(i) Then
					res.Invoke(CommandType.Restricted, command)
					Return False
				End If
			Next

			For i As Integer = 0 To mPrivateCommand.Length - 1
				If command.ToLower = mPrivateCommand(i) Then
					res.Invoke(CommandType.Private, mPrivateCommand(i))
					Return False
				End If
			Next

			Const STARTF_USESTDHANDLES As Integer = &H100
			Const STARTF_USESHOWWINDOW As Integer = &H1
			Const SW_HIDE As Integer = 0
			Dim BUFFER_SIZE As Integer = 1024 * 10

			Dim sa As SECURITY_ATTRIBUTES

			sa.lpSecurityDescriptor = 0
			sa.bInheritHandle = -1
			sa.nLength = Marshal.SizeOf(GetType(SECURITY_ATTRIBUTES))


			Dim siStart As STARTUPINFO = Nothing
			Dim hOutRead As IntPtr
			Dim hOutWrite As IntPtr

			If CreatePipe(hOutRead, hOutWrite, sa, 0) <> 0 Then
				siStart.cb = Marshal.SizeOf(GetType(STARTUPINFO))
				siStart.hStdOutput = hOutWrite
				siStart.hStdError = hOutWrite
				siStart.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
				siStart.wShowWindow = SW_HIDE
			End If

			Dim piNewProc As PROCESS_INFORMATION = Nothing

			Dim bytesRead As Integer = 0
			Dim bytesLeft As Integer = 0

			If CreateProcess(vbNullString, "cmd.exe /k " & command, 0, 0, -1, 0, 0, curDir, siStart, piNewProc) <> 0 Then
				Threading.Thread.Sleep(50)

				While PeekNamedPipe(hOutRead, 0, 0, 0, bytesLeft, 0) <> 0

					If bytesLeft <> 0 Then
						If bytesLeft > BUFFER_SIZE Then bytesLeft = BUFFER_SIZE

						Dim buf(bytesLeft) As Byte

						If ReadFile(hOutRead, buf, bytesLeft, 0, IntPtr.Zero) <> 0 Then
							CloseHandle(hOutWrite)

							res.Invoke(CommandType.Allow, System.Text.UTF8Encoding.Default.GetString(buf))
						End If

					Else
						Application.DoEvents()
					End If

				End While

			End If

			Try
				Process.GetProcessById(piNewProc.dwProcessId).Close()
			Catch
			End Try
			CloseHandle(hOutRead)
			CloseHandle(hOutWrite)
			CloseHandle(piNewProc.hProcess)
			CloseHandle(piNewProc.hThread)

			Return True
		End Function

نبذه صغيره عن نظام عمل الفئه Commander

  1. تحتوى هذه الفئه على داله واحده اسمها ExecuteCommand تأخذ منك 3 معاملات هما
  2. command : و هو الأمر الذى تريد تنفيذه
  3. res : و هى عنوان لداله سيتم استدعائها و أرسال نوع الأمر كأول معامل و قيمه معينه كمعامل ثانى هذه القيمه تحدد بناءا على المعامل الأول فإذا كان Allow فالقيمه هى النتيجه المعاده من تنفيذ الأومر و غير ذلك يكون اسم الأمر نفسه.
  4. curDir : و هو معامل اختيارى القيمه الإفتراضيه له لاشئ و هو يمثل المسار الإفتراضى الذى سوف يتم تمريره لداله CreateProcess.

الداله ExecuteCommand تعود بـ True إذا كان نوع الأمر Allow و قد تم الإنتهاء من الحصول على نتيجة تنفيذه و تعود بـ False إذا كان نوع الأمر Private أو Restricted.

فى بداية الداله قمنا بتحويل الـ command إلى حروف صغيره بعدها قمنا بإختبار وجود الـ command داخل المصفوفه mRestrictedCommand فإن وجد نستدعى الداله res و نعود بـ False.

بعد ذلك نتحقق من وجود الـ command داخل المصفوفه mPrivateCommand فإن وجد نستدعى الداله res و نعود بـ False.

بعدها نقوم بإنشاء نسخه من SECURITY_ATTRIBUTES بإسم sa ثم ملئها بالقيم الخاصه بها استعدادا لتمريرها للداله CreatePipe.

بعد ذلك نقوم بإنشاء المتغير si و هو من نوع STARTUPINFO و بعدها ننشى المتغيرين hOutRead و hOutWrite و اللذان فيما بعد سيحتويان على Handles الخاصه بالقراءه و الكتابه من الـ Pipe.

بعد ذلك نقوم بإستدعاء الداله CreatePipe و نمرر لها المتغيرين السابقين فإن عادت الداله CreatePipe بقيمه لا تساوى صفرفهذا يعنى انه تم انشاء الـ Pipe و المتغيرين يحتووا الأن على مقابض القراءه و الكتابه الخاصه بها و بالتالى نقوم بإسناد قيمة مقبض القراءه hOutRead للمتغيرين hStdOutput و hStdError الموجودين داخل si.

أيضا نقوم بوضع القيمه SW_HIDE داخل المتغير wShowWindow لعدم اظهار شاشة الدوس اثناء تنفيذ الأمرايضا نقوم بوضع حجم الـ STARTUPINFO داخل المتغير cb و نضع القيمه STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW لتحديد ان المتغيرين wShowWindow و متغيرات المقابص تحتوى على بيانات سليمه ليتم استخدامهم.

بعد ذلك نقوم بإنشاء المتغير piNewProc و هو من نوع PROCESS_INFORMATION و الذى سيحتوى فيما بعد على بيانات الـ Process الذى قمنا بتشغيله.

بعدها نقوم بإستدعاء الداله CreateProcess و نمرر لها المعاملات التى تريدها فإذا كانت القيمه العائده منها لا تساوى صفر فقد تم البدء فى تنفيذ الأمر الخاص بالدوس و بعدها ندخل فى حلقة while ان القيمه العائده من الداله PeekNamedPipe لا تساوى صفر. الداله PeekNamedPipe ستعود بصفر عندما ينتهى الـ Process من عمله.

الداله PeekNamedPipe تعود بقيمه داخل المتغير bytesLeft و هى تمثل عدد البيتات المتبقيه داخل الـ pipe و التى لم يتم قرائتها بعد.

بعدها إن كان bytesLeft تحتوى على صفر فنعلم البرنامج ان يستمر فى عمله و ذلك عن طريق الأمation.ication.DoEvents اما إن كان bytesLeاوى صفتساوى صفر تات اللتحقق بان عدد البيتات المتبقيه ليست اكبر من القيمه المخزنه داخل الثابت BUFFER_SIZE و الذى يحتوى على القيمه الإفتراضيه لعدد البايت التى سيتم قرائتها و هو 5 ميجا، فإذا كانت قيمة bytesLeft اكبر من BUFFER_SIZE يتم تخزين قيمة الـ BUFFER_SIZE داخل bytesLeft.

بعدها نستخدام الداله ReadFile مع مقبض القراءه الخاص بالـ Pipe لنقراء منها و نمرر لها ايضا المتغير bytesLeft ليتم القراءه بعدد القيمه المخزنه به ايضا نقوم بإنشاء المتغير buf و هى مصفوفه من نوع byte و حجمها يساوى الموجود داخل bytesLeft و نقوم بتمرير هذا المتغير للداله ليتم نسخ البايتات المقروءه داخله.

عندما تقوم الداله ReadFile بالقراءه تقوم بتحريك مؤشر القراءه لأخر مكان تقراء منه لذا عندما تقوم الداله PeekNamedPipe بحساب عدد البياتات التى لم يتم قرائتها تبدأ من مكان مؤشر القراءه و بالتالى ستتناقص القيمه تدريجيا.

القيم الموجوده داخل المصفوفه buf هى حروف ascii، لذا نريد تحويلها إلى نص و ذلك سيتم عن طريق استخدام الفئه System.Text.ding حcoding حيث سنقوم بإستخدام النسخه الإفتراضيه منها (المتغير default الموجود بها) حيث تحتوى على الداله GetString و التى تقبل مصفوفه bytes و تحولها لـ string.

لذا كل ما سنفعله الأن هو استدعاء الداله res ثم نمرر لها نوع الأمر Allowed و ايضا نمرر لها النتيجه العائده من الداله GetString.

فى النهايه نقوم بإغلاق الـ Process إذا كان مازال يعمل و ايضا نقوم بإغلاق كل الـ Handles المفتوحه.

انتهينا من شرح مجتويات الفئه Commander و نأتى الأن للفئه CommandPrompt.

Public Class CommandPrompt
	Inherits RichTextBox

	Private Shared mRestrictedCommand() As String = {"cmd", "exit", "edit", "doskey"}
	Private Shared mPrivateCommand() As String = {"cls"}

	Private start_index As Integer = 0
	Private curDir As String = Application.StartupPath

	Private ReadOnly StartUpMessage As String = Application.ProductName & " v" & Application.ProductVersion

	Protected Overrides Sub OnCreateControl()
		MyBase.OnCreateControl()
		If Not DesignMode Then
			ScrollBars = RichTextBoxScrollBars.ForcedVertical
			ShortcutsEnabled = False
			DetectUrls = False
			Text = StartUpMessage & vbNewLine & vbNewLine & curDir & ">"
			Protect()
		End If
	End Sub

	Protected Overrides Sub OnKeyDown(ByVal e As KeyEventArgs)

		If e.KeyCode = Keys.Enter Then
			e.Handled = True
			Dim cmd As String = Text.Substring(start_index)
			If Commander.ExecuteCommand(cmd, AddressOf ExecuteHandler, curDir) = True Then
				curDir = Lines(Lines.Length - 1).Split(">"c)(0)
				Protect()
			End If
		End If

		MyBase.OnKeyDown(e)
	End Sub

	Protected Overrides Sub OnKeyUp(ByVal e As System.Windows.Forms.KeyEventArgs)
		If SelectionStart >= start_index Then
			SelectionProtected = False
		End If

		MyBase.OnKeyUp(e)
	End Sub

	Private Sub ExecuteHandler(ByVal cmdType As Commander.CommandType, ByVal value As String)
		If cmdType = Commander.CommandType.Restricted Then
			ExecuteRestricted(value)
			Return
		ElseIf cmdType = Commander.CommandType.Private Then
			ExecutePrivate(value)
			Return
		End If

		If Text = vbNullString Then
			Text = value
		Else
			If value.EndsWith(vbNewLine) OrElse value.EndsWith(vbCr) OrElse value.EndsWith(vbCrLf) OrElse value.EndsWith(vbLf) Then
				Text += value
			Else
				Text += vbLf & value
			End If
		End If

	End Sub



	Private Sub ExecutePrivate(ByVal cmd As String)
		If cmd = "cls" Then
			Clear()
			Text = curDir & ">"
			Protect()
		End If
	End Sub

	Private Sub ExecuteRestricted(ByVal cmd As String)
		Text += vbNewLine & String.Format("command ""{0}"" is restricted, you can not use it.", cmd) & vbNewLine & vbNewLine
		Text += curDir & ">"
		Protect()
	End Sub

	Private Sub Protect()
		SelectAll()
		SelectionProtected = True

		start_index = Text.Length
		SelectionStart = start_index
		SelectionLength = 0
		SelectionProtected = False
	End Sub

End Class

فى الباديه داخل الداله OnCreateControl قمنا بالتحقق من إذا كانت الأداه ليست فى ةضع التصميم نقوم بتحديد الخصائص الخاصه بها مثل الـ scrollbars او النص الإفتراضى الذى يكتب داخل خاصيه الـ Text و هو بالمناسبه مزيج من المتغير StartUpMessage و curDir.

داخل الداله OnKeyDown إذا كان الزر هو Enter نقوم بمنع طبع قيمته أى لا نضيف سطر جديد ثم نقوم بإستخلاص الأمر المدخل و تمريره للداله ExecuteCommand وايضا نمرر لها عنوان الداله ExecuteHandler و الخاصه بإستقبال القيمه العائده من تنفيذ الأمر ايضا نقوم بتمرير المسار الحالى و الموجود داخل curDir.

إذا عادت الداله ExecuteCommand بـ True نقوم بأخذ المسار الحالى من اخر سطر فى خاصية Text و حتى نصل للعلامه "<". ايضا نقوم بإستدعاء الداله Protect و التى تقوم بحماية النص المكتوب داخل الخاصيه Text من التعديل ايضا تقوم بوضع المؤشر الخاص بالكتابه بعد اخر حرف مكتوب داخل خاصية الـ Text بالإضافه إلى ذلك يتم تحديث المتغير start_index إلى عدد الحروف فى مربع النص.

اذا قمت بالتحرك داخل اى منطقه من النص و كانت هذه المنطقه غير قابله للتعديل و قمت بعدها بالرجوع إلى اول منطقه قابله للكتابه فلن يتم الكتابه فيها و ستعتبر محميه و لمنع ذلك قمت بالتحقق من موقع المؤشر الخاص بالكتابه داخل الداله OnKeyUp فإن كان موقع المؤشر اكبر من او يساوى start_index فسيتم إلغاء الحمايه لأدعك تكتب كما يحلو لك.

اثناء تنفيذ الداله ExecuteCommand يتم استدعاء الداله ExecuteHandler و التى تتحقق فى اولها من نوع الأمر فإن كان Private تقوم بإستددعاء الداله ExecutePrivate و تمرر لها اسم الأمر و التى تقوم بدورها بتنفيذ كود بناءا على هذا الامر و فى حالتنا هذه لا يوجد إلا الأمر cls اما إذا كان نوع الأمر Restricted فسيتم إستدعاء الداله ExecuteRestricted و التى تظهر رساله تفيد ان هذا الأمر غير قابل للتنفيذ.

اذا كان نوع الأمر Allowed ستقوم الداله بإختبار اخر حرف فى القيمه التى ستوضع داخل خاصية Text فإن كانت تمثل سطر جديد فيتم إضافتها للنص كما هى و غير ذلك يتم اضافة سطر جديد يليه القيمه المستقبله.

الصوره الموجوده بالأعلى هو صورة المثال الموجود بالمرفقات.

هذه الاداه ليست متكامله فلا تعتقد انها فابله للعمل كما هى حيث انه توجد بها بعض القصور و التى حتى الأن لما اعالجها بها فتذكر ان تجربها جيدا ان كنت ستستخدمها فى برامجك

هذا المرفق غير سليم للتحميل قم بالذهاب للمشركه رقم 12 من هنا و قم بتحميل اما الملف الثالث او الرابع

MyCommandPrompt.rar

و الله ولى التوفيق

تم تعديل هذه المشاركة بواسطة Muhammad alaa في 12 سبتمبر 2009 في 22:03

1

مدونتي: C++ Tips and Tricks

#2

الله يعطيك العافية ويزيدك من علمه - ( مقال وفكرة ) ممتازة ;), وعلى فكرة الكود دا يعني أقول إيه ( ++C ) ;) .

جاري التجربة :)

بالتوفيق

The important thing is not so much

that every child should be taught,

as that every child should be given the wish to learn.

#3

ذكرتنى ببرنامج SQLPlus الخاص بالأوراكل

جارى التقييم ,, :wink:

تم تعديل هذه المشاركة بواسطة GHOST2010 في 11 سبتمبر 2009 في 09:26

إن قلت قال الله قال رسولـه همزوك همز المنكر المتعالي
أو قلت قد قال الصحابة والألـى تبعاً لهم بالقول والأعمال
أو قلت قـال الشافعي وأحمد و أبو حنيفة والإمام الغالي
صدوا عن وحي الإله ودينـه واحتالوا على حرام الله بالإحلال
يا أمةً لعبت بدين نبيها كتلاعب الصبيان في الأوحال
حاشا رسول الله يحكم بالهوى تلك إذاً حكومة الضلال

feed.1.gif

#4
اقتباس
ذكرتنى ببرنامج SQLPlus الخاص بالأوراكل

او Command Window الخاصه بالـ Visual Studio ;)

مدونتي: C++ Tips and Tricks

#5

يابنى حرام عليك ,, كل ماترفع ملف أنزله يضرب ,, مش عارف تضغط ملف :D

post-47088-1252655705_thumb.jpg

باقولك ايه ياحمادة ,, إيه رأيك تغير شكل الـCaret يكون زى بتاع الـCommand Prompt

ده مثال بسيط يظهر مؤشر الكتابة على الفورم

  1. Imports System.Runtime.InteropServices
  2. Public Class Form1
  3.  
  4. <DllImport("user32.dll")> _
  5. Private Shared Function CreateCaret(ByVal hwnd As IntPtr, ByVal hBitmap As IntPtr, ByVal nWidt
    h As Integer, ByVal nHeight As Integer) As Integer
  6. End Function
  7.  
  8. <DllImport("user32.dll")> _
  9. Private Shared Function ShowCaret(ByVal hwnd As IntPtr) As Integer
  10. End Function
  11.  
  12. <DllImport("user32.dll")> _
  13. Private Shared Function SetCaretPos(ByVal x As Integer, ByVal y As Integer) As Integer
  14. End Function
  15.  
  16. Private Sub Form1_MouseClick(ByVal sender As Object, ByVal e As MouseEventArgs) Handles Me.Mou
    seClick
  17. CreateCaret(Me.Handle, IntPtr.Zero, 7, 2)
  18. ShowCaret(Me.Handle)
  19. SetCaretPos(e.X, e.Y)
  20. End Sub
  21.  
  22. End Class
  23.  

تم تعديل هذه المشاركة بواسطة GHOST2010 في 11 سبتمبر 2009 في 11:06

إن قلت قال الله قال رسولـه همزوك همز المنكر المتعالي
أو قلت قد قال الصحابة والألـى تبعاً لهم بالقول والأعمال
أو قلت قـال الشافعي وأحمد و أبو حنيفة والإمام الغالي
صدوا عن وحي الإله ودينـه واحتالوا على حرام الله بالإحلال
يا أمةً لعبت بدين نبيها كتلاعب الصبيان في الأوحال
حاشا رسول الله يحكم بالهوى تلك إذاً حكومة الضلال

feed.1.gif

#6

للأسف موضوع تغيير الـ Caret مش متاح فى الدوت نت، متاح فقط فى الـ VB6 ...... تخيل

انت ممكن بس تعمل DestroyCaret للـ TextBox العادى. اما بالنسبه للـ RichTextBox غير متاح و مصممى الدوت نت قالوا انهم هياخدوا النقطه دى فى الإعتيار مع الإصدارات الجديده.

بالنسبه للمرفق فلا حول و لا قوة إلا بالله

قمت بإبداله و لا اعرف ماذا يحدث (يبدو ان السرفر مستقصدنى عمال يبوظ الملفات اللى برفعها لكم بس انا ان شاء الله هخدعه المره اللى جايه) :lol:

و الله ولى التوفيق

مدونتي: C++ Tips and Tricks

#7

شكل مايكروسوفت هتعقدنا

أخبار درس الشل إيه ياريس ,, مش ناوى تكمل؟ :D

إن قلت قال الله قال رسولـه همزوك همز المنكر المتعالي
أو قلت قد قال الصحابة والألـى تبعاً لهم بالقول والأعمال
أو قلت قـال الشافعي وأحمد و أبو حنيفة والإمام الغالي
صدوا عن وحي الإله ودينـه واحتالوا على حرام الله بالإحلال
يا أمةً لعبت بدين نبيها كتلاعب الصبيان في الأوحال
حاشا رسول الله يحكم بالهوى تلك إذاً حكومة الضلال

feed.1.gif

#8

انا كنت قلتلك على اللى حصل

بس الحمد لله بدءت فيه من جديد امبارح

تم تعديل هذه المشاركة بواسطة Muhammad alaa في 11 سبتمبر 2009 في 19:10

مدونتي: C++ Tips and Tricks

#9
Muhammad alaa كتب:
انا كنت قلتلك على اللى حصل

بس الحمد لله بدءت فيه من جديد امبارح

أيوه أنا عارف اللى حصل بس بافكرك :D :D

إن قلت قال الله قال رسولـه همزوك همز المنكر المتعالي
أو قلت قد قال الصحابة والألـى تبعاً لهم بالقول والأعمال
أو قلت قـال الشافعي وأحمد و أبو حنيفة والإمام الغالي
صدوا عن وحي الإله ودينـه واحتالوا على حرام الله بالإحلال
يا أمةً لعبت بدين نبيها كتلاعب الصبيان في الأوحال
حاشا رسول الله يحكم بالهوى تلك إذاً حكومة الضلال

feed.1.gif

#10

انا شغال فيه بإيدى و اسنانى و بإذن الله هيخلص على بكره ليلا

و بعد مراجعته و تجهيز الصور و ما إلى ذلك ممكن نقول هرفعه على المنتدى بعد بكره الفجر (زى دلوقتى)

و الله ولى التوفيق

مدونتي: C++ Tips and Tricks

#11

محمد ,, حسبى الله ونعم الوكيل

المرة الأولى كان ملف واحد بس اللى بيضرب المرة دى ملفين :S :S

post-47088-1252741753_thumb.jpg

ربنا يهديك يامحمد ويتوب عليك

إن قلت قال الله قال رسولـه همزوك همز المنكر المتعالي
أو قلت قد قال الصحابة والألـى تبعاً لهم بالقول والأعمال
أو قلت قـال الشافعي وأحمد و أبو حنيفة والإمام الغالي
صدوا عن وحي الإله ودينـه واحتالوا على حرام الله بالإحلال
يا أمةً لعبت بدين نبيها كتلاعب الصبيان في الأوحال
حاشا رسول الله يحكم بالهوى تلك إذاً حكومة الضلال

feed.1.gif

#12

محمد انا رفعت من الملف اربع نسخ شوف اللى شغاله فيهم و احذف الموجوده و حطها مكانها

cmd.rar

cmd2.rar

cmd3.rar

cmd4.rar

:mad: :mad: :mad:

و الله ولى التوفيق

مدونتي: C++ Tips and Tricks

#13

طيب انت مالك متعصب ليه :D

المرفق الأول والثانى بيضربوا

الثالث والرابع تمام :P :P

إن قلت قال الله قال رسولـه همزوك همز المنكر المتعالي
أو قلت قد قال الصحابة والألـى تبعاً لهم بالقول والأعمال
أو قلت قـال الشافعي وأحمد و أبو حنيفة والإمام الغالي
صدوا عن وحي الإله ودينـه واحتالوا على حرام الله بالإحلال
يا أمةً لعبت بدين نبيها كتلاعب الصبيان في الأوحال
حاشا رسول الله يحكم بالهوى تلك إذاً حكومة الضلال

feed.1.gif

#14

قمت بالتنويه عن المرفق فى المشاركه الأولى و لا داعى لنقل المرفقات (حتى لا تتلف :blush: )

و الله ولى التوفيق

مدونتي: C++ Tips and Tricks

مواضيع مشابهة