ساخت بافر و play کردن آن : تاکنون ما توانستيم DirectSound را initial کنيم . همانطور که می دانيد در تمام component های DirectX داده ها در يکسری بافر ذخيره می شوند . در مورد DirectSound نيز ما يک بافر با نام DirectSoundSecondaryBuffer8 می سازيم و داده های صوتی را در آن قرار می دهيم . برخی پارامتر ها هستند که بايد برای بافر تنظيم شوند مثل : stereo يا mono بودن بافر ، ۸ بيتی يا ۱۶ بيتی بودن بافر ، فرکانس صوتی ( 22khz ، 44khz و غيره ) . اگر اين پارامترها را مشخص نکنيم DirectSound از اطلاعات فايل صوتی استفاده می کند .
در يک کاربرد ساده ، ما تنها يک بافر صوتی از يک فايل ايجاد می کنيم اما امکان ايجاد چندين بافر بطور همزمان و نيز پخش چندين صدا بطور همزمان نيز وجود دارد :
DSBDesc.lFlags = DSBCAPS_CTRLFREQUENCY Or DSBCAPS_CTRLPAN Or DSBCAPS_CTRLVOLUME
Set DSBuffer = DS.CreateSoundBufferFromFile(App.Path & "\Sample.wav", DSBDesc)x
MsgBox "SOUND BUFFER CREATED:"x
MsgBox "Buffer Size: " & DSBDesc.lBufferBytes & "bytes (" & Round(DSBDesc.lBufferBytes / 1024, 3) & "kb)"x
MsgBox "Buffer Channel Count:" & DSBDesc.fxFormat.nChannelsIIf(DSBDesc.fxFormat.nChannels = 1, " (Mono)", " (Stereo)")x
MsgBox "Buffer Bits per channel: " & DSBDesc.fxFormat.nBitsPerSample & " bits"x
در بالا يک بافر صوتی ايجاد شده و اطلاعات صدا از فايل به بافر load شده است .
حال بايستی داده صوتی موجود در بافر را play کنيم :
دستور لازم برای Play کردن بافر بصورت loop :
DSBuffer.Play DSBPLAY_LOOPING
دستور لازم برای Play کردن بافر بدون loop :
DSBuffer.Play DSBPLAY_DEFAULT
دستورات لازم برای Stop کردن بافر :
DSBuffer.Stop
DSBuffer.SetCurrentPosition 0
دستور لازم برای Pause کردن بافر :
DSBuffer.Stop
تنظيم خصوصيات بافر : سه خصوصيت وجود دارد که در مورد بافر تنظيم می شود pannig ، volume و frequency
محدوده مقادير pannig بين اعداد زير است :
DSBPAN_LEFT = -10,000
DSBPAN_CENTER = 0
DSBPAN_RIGHT = 10,000
توسط متد SetPan می توان pannig بافر را تنظيم کرد :
DSBuffer.SetPan yourValue
DirectSound صدا را تقويت نمی کند بلکه آنرا تضعيف می نمايد بنابراين ماکزيمم volume عبارت است از volume ای که فايل صوتی با آن ضبط شده است . بعبارت ديگر محدود مقادير volume بين اعداد زير است :
DSBVOLUME_MAX = 0
DSBVOLUME_MIN = -10000
توسط متد SetVolume می توان volume بافر را تنظيم کرد :
DSBuffer.SetVolume yourValue
محدود فرکانسی DirectSound عبارت است از :
DSBFREQUENCY_MIN = 100 (hz)x
DSBFREQUENCY_MAX = 100000 (hz) = 100khz x
توسط متد SetFrequency می توان فرکانس بافر را تنظيم کرد :
DSBuffer.SetFrequency yourValue
موضوع : پخش موزيک توسط DirectMusic
مقدمه :
در اولين درس از آموزش DirectXAudio با چگونگي پخش افکتهاي صوتي آشنا شديد . اکنون اين توانايي را داريد که يک engine ساده صوتي بنويسيد . در اين بخش مباني پخش موزيک را فرا خواهيد گرفت . پس از اين درس شما مي توانيد يک ماژوال براي پخش موزيکهاي پس زمينه و افکتهاي صوتي براي برنامه هايتان ايجاد کنيد .
Initil کردن DirectMusic8 :
قبل از هر کار بايستي ماژول DirectMusic8 را مقداردهي اوليه کنيد . اينکار بصورت زير انجام مي شود :
Option ExplicitImplements DirectXEvent8
Private oDX As DirectX8
Private oDMPerf As DirectMusicPerformance8
Private oDMLoader As DirectMusicLoader8
Private oDMSeg As DirectMusicSegment8
Dim dmParams As DMUS_AUDIOPARAMS
Set oDX = New DirectX8
Set oDMPerf = oDX.DirectMusicPerformanceCreate
Set oDMLoader = oDX.DirectMusicLoaderCreate
oDMPerf.InitAudio frmMain.hWnd, DMUS_AUDIOF_ALL, dmParams, Nothing, DMUS_APATH_DYNAMIC_STEREO, 128
oDMPerf.SetMasterAutoDownload True
شي DirectMusicLoader8 کمک مي کند تا موزيک درون بافر load شود .
شي DirectMusicSegment8 مموزيکي را که بايد پخش شود ذخيره مي کند .
کد فوق کافي است يکبار زمانيکه برنامه آغاز مي شود ، اجرا گردد .
اکنون ما يک واسط مقدار دهي شده از DirectMusic داريم اما قبل از اينکه موزيک را Load کرده و پخش کنيم چگونگي terminate کردن DirectMusic را در زير مي بينيد :
If ObjPtr(oDMSeg)Then Set oDMSeg = Nothing
If ObjPtr(oDMLoader)Then Set oDMLoader = Nothing
If Not (oDMPerf Is Nothing) Then
oDMPerf.CloseDown
Set oDMPerf = Nothing
End If
If ObjPtr(oDX) Then Set oDX = Nothing
پيغامها :
در برخي از component هاي DirectX8 مثل Input , Sound , Music و Play برنامه شما بايستي يک سيستم messaging را برپا کند تا DirectX زمان وقوع برخي رخدادهاي خاص را بشما گزارش دهد . اين مطلب بخصوص زمانيکه يک موزيک را پخش مي کنيد مفيد است براي مثال مي تواند زمان خاتمه يافتن موزيک را به شما اطلاع دهد و آنگاه شما مي توانيد قطعه موزيک بعدي را پخش کنيد .
پيغامها توسط يک سيستم callback انجام مي شوند . کد زير را در تابع InitDMusic تان پس از initial کردن DirectMusic8 قرار دهيد :
oDMPerf.AddNotificationType DMUS_NOTIFY_ON_SEGMENT
hEvent = oDX.CreateEvent(Me)x
oDMPerf.SetNotificationHandle hEvent
اولين سطر به DirectMusic مي گويد چه نوع پيغامهايي را مي خواهيد به برنامه تان بفرستد . چندين نوع پيغام وجود دارد :
DMUS_NOTIFY_ON_SEGMENT = اطلاعات موزيک فعلي ( شروع پخش ، پايان پخش و غيره )
DMUS_NOTIFY_ON_CHORD = اطلاعات تغيير chord موزيک
DMUS_NOTIFY_ON_COMMAND = زمانيکه يک event فرماني صدا زده شود .
DMUS_NOTIFY_ON_MEASUREANDBEAT = اطلاعات beat/measure مربوط به موزيک فعلي
DMUS_NOTIFY_ON_PERFORMANCE = که event مربوط به سطح performance می باشد .
DMUS_NOTIFY_ON_RECOMPOSE = که recomposition event می باشد .
آخرين بخش از پيغام دهي ، تابع اصلي آن مي باشد . همانطور که در بخش Initial کردن DirectMusic ديديد يک توصيف بصورت Implements DirectXEvent8 داشتيم . بخش اصلي تابع callback مربوط به DirectXEvent8 ، شامل يک select case است که بين پيغامهاي مختلف سوئيچ می کند :
Private Sub DirectXEvent8_DXCallback(ByVal eventid As Long)x
If eventid = hEvent Then
Dim dmMSG As DMUS_NOTIFICATION_PMSG
If Not oDMPerf.GetNotificationPMSG(dmMSG) Then
Else
Select Case dmMSG.lNotificationOption
Case DMUS_NOTIFICATION_SEGABORT
Case DMUS_NOTIFICATION_SEGALMOSTEND
Case DMUS_NOTIFICATION_SEGEND
Case DMUS_NOTIFICATION_SEGLOOP
Case DMUS_NOTIFICATION_SEGSTART
Case Else
End Select
End If
End If
End Sub
پخش موزيک / متوقف کردن موزيک :
براي پخش يک موزيک ابتدا بايستي آنرا load کنيد . اينکار توسط کد زير انجام مي شود :
oDMLoader.SetSearchDirectory App.Path & "\"x
Set oDMSeg = oDMLoader.LoadSegment(App.Path & FILENAME)oDMSeg.SetStandardMidiFile
DirectMusic تنها چهار نوع فرمت صوتي را مي پذيرد : WAV ، MID ، RMI و SEG .
براي پخش فايلهاي MP3 بايستي از DirectXShow استفاده کنيد که آنرا در درسهاي بعدي خواهيد ديد .
اکنون که داده هاي فايل صوتي درون بافر load شد مي توانيد آنرا پخش کنيد :
oDMSeg.SetRepeats 0
oDMPerf.PlaySegmentEx oDMSeg, DMUS_SEGF_DEFAULT, 0
تعداد پخش شدن فايل را با متد SetRepets تنظيم کنيد . اگر اين مقدار صفر باشد ، آهنگ تنها يکبار پخش مي شود و اگر 1- باشد بطور ممتد پخش خواهد شد .
براي متوقف کردن موزيک از کد زير استفاده کنيد :
oDMPerf.StopEx oDMSeg, 0, DMUS_SEGF_DEFAULT
براي تنظيم ميزان صدا از متد SetMasterVolume استقاده کنيد :
oDMPerf.SetMasterVolume yourvalue
رنج صدا بين 20+ دسی بل تا 200- دسي بل است .
براي تنظيم Tempo از متد SetMasterTempo استفاده کنيد :
oDMPerf.SetMasterTempo yourvalue/ 100
بطور نرمال tempo برابر 1 مي باشد . عدد 2 سرعت را دو برابر مي کند و عدد 0 موزيک را قطع مي کند .
موضوع : ايجاد صدای سه بعدی توسط DirectSound3D
مقدمه
تاکنون با چگونگي پخش افکتهاي صوتي و موسيقي پس زمينه توسط DirectXAudiuo آشنا شديد . اين مطالب براي کاربردهاي ساده مناسبند اما اينکه فقط ما صداي استريو داشته باشيم کافي نيست و در کاربردهاب حرفه اي بايستي از صداهاي کاملاً سه بعدي استفاده کنيم .
با استفاده از افکتهاي صوتي سه بعدي مي توانيم صدا را در تمام جهتها براي کاربر شبيه سازي کنيم اما با همه مزاياي صداي سه بعدي ، دو اشکال براي آن وجود دارد : اول اينکه پخش صداي سه بعدي پيچيده تر از پخش صداي عادي است و تنها کارت هاي سخت افزاري جديد بطور کاملاً واقعي از آن پشتيباني مي کنند و دوم اينکه صداي سه بعدي با 4 بلندگو يا بيشتر حاصل مي شود – کيفيت حالت 2 بلندگو بد نيست اما در مقايسه با حالت 4 بلندگو ، بسيار کيفيت صداي سه بعدي پايين است .
برپاسازي DirectSound3D
برپاسازي صداي سه بعدي چندان پيچيده نيست اما هر بافر صوتي که براي يک صداي سه بعدي مي سازيد ، يک overhead را به سيستم تان اضافه مي کند . همچنين برخي درايورها هستند که تنها اجازه ايجاد تعداد محدودي بافر سه بعدي را در يک لحظه مي دهند و نيز اغلب درايورها تعداد بافرهاي سه بعدي که مي توان در يک لحظه پخش کرد را محدود مي کنند ( معمولاً 8 تا 16 بافر ) .
اولين قدم در استفاده از صداي سه بعدي تعريف متغيرها و اشيا زير است :
Dim DSBuffer As DirectSoundSecondaryBuffer8
Dim DSBuffer3D As DirectSound3DBuffer8
Dim DSBListener As DirectSound3DListener8
تنها دو شي آخر براي شما جديد هستند . شي DirectSound3dBuffer8 يک ارائه سه بعدي از بافرهاي عادي است . ما همچنان از DirectSoundSecondaryBuffer8 براي نگهداري داده صوتي استفاده مي کنيم و از DirectSound3Dbuffer8 براي نگهداري پارامترهاي سه بعدي و تنظيمات سه بعدي استفاده مي کنيم . شي DirectSound3Dlistener8 نيز يک listener است و براي تنظيم کردن سرعت و جهت صدا و برخي پارامترهاي ديگر استفاده مي شود .
مرحله دوم ، ساخت بافر صوتي است . اين کار در دو بخش انجام مي شود . اول ما يک بافر صوتي نرمال مي سازيم و سپس يک واسط بافر صوتي سه بعدي را از آن بدست مي آوريم :
If Not (DSBuffer Is Nothing) Then DSBuffer.Stop
Set DSBuffer = Nothing
DSBDesc.lFlags = DSBCAPS_CTRL3D Or DSBCAPS_CTRLVOLUME
Set DSBuffer = DS.CreateSoundBufferFromFile(App.Path & "\blip.wav", DSBDesc)x
If DSBDesc.fxFormat.nChannels > 1 Then
MsgBox "You can only use mono (1 channel) sounds with DirectSound3D"x
End If
If optLow.Value Then DSBDesc.guid3DAlgorithm = GUID_DS3DALG_NO_VIRTUALIZATION
If optMedium.Value Then DSBDesc.guid3DAlgorithm = GUID_DS3DALG_HRTF_LIGHT
If optHigh.Value Then DSBDesc.guid3DAlgorithm = GUID_DS3DALG_HRTF_FULL
Set DSBuffer = DS.CreateSoundBufferFromFile(App.Path & "\blip.wav", DSBDesc)x
Set DSBuffer3D = DSBuffer.GetDirectSound3DBuffer()x
سه نکته است که بايد به آن دقت شود :
1 – اضافه کردن DSBCAPS_CTRL3D بسيار مهم است . شما اگر اين پارامتر را بکار نبريد ، قادر نخواهيد بود که واسط سه بعدي را بدست آوريد .
2 – ما بايستي تنها از افکتهاي صوتي Mono ( تک کاناله ) استفاده کنيم زيرا افکت صوتي استريو در صداي سه بعدي معنا ندارد زيرا صدا از يک نقطه در فضاي سه بعدي مي آيد .
3 – سطح الگوريتم سه بعدي – که در پارامتر DSBDesc.guid3Dalgorhthm آمده . حالت NO VIRTULIZATION تنها از CPU استفاده مي کند و روي تمام سيستم ها کار مي کند اما افکتها مينيمم هستند . حالت HRTF LIGHT هم از CPU و هم سخت افزار کارت صوتي استفاده مي کند و کيفيت بهتري را نسبت به خالت اول ارائه مي دهد . حالت HRTF FULL بهترين حالت است اما در صورتي درست کار مي کند که يک سخت افزار سه بعدي داشته باشيد .
آخرين پارامتري که بايد تنظيم کنيم شي listener است :
DSBDesc_2.lFlags = DSBCAPS_CTRL3D Or DSBCAPS_PRIMARYBUFFER
Set DSBPrimary = DS.CreatePrimarySoundBuffer(DSBDesc_2) x
Set DSBListener = DSBPrimary.GetDirectSound3Dlistener
DSBListener.SetOrientation 0#, 0#, 1#, 0#, 1#, 0#, DS3D_IMMEDIATE
تا اينجا صداي سه بعدي ما آماده است و مي توانيم برخي پخش بافر را مشابه درسهاي قبلي شروع کنيد .
پارامترهاي اختياري :
چند پارامتر وجود دارد که مي توان آنها را تغيير داد :
1 – Volume : عدد 0 بيشترين ميزان صدا و عدد 3000 - کمترين ميزان صدا را دارد :
If DSBuffer Is Nothing Then Exit Sub
DSBuffer.SetVolume scrlVolume.Value
2 – Position : تنظيم محل listener :
DSBuffer3D.SetPosition Src_X, 0, Src_Y, DS3D_IMMEDIATE
DSBListener.SetPosition Src_X, 0, Src_Y, DS3D_IMMEDIATE
3 – Velocity : تنظيم سرعت و جهت منبع صدا :
DSBuffer3D.SetVelocity X, Y, Z, DS3D_IMMEDIATE
DSBListener.SetVelocity X, Y, Z, DS3D_IMMEDIATE
4 – Dppler Effect : انحراف صدا از مسيري که مي پيمايد انحراف سرعت حرکت صدا :
DSBListener.SetDopplerFactor CSng(scrlDoppler.Value), DS3D_IMMEDIATE
5 – Rolloff Effect : rolloff چگونگي تضعيف صدا با تغيير فاصله است
DSBListener.SetRolloffFactor CSng(scrlRolloff.Value), DS3D_IMMEDIATE
6 – Distance : ماکزيمم فاصله اي که يک صدا مي تواند شنيده شود :
DSBuffer3D.SetMaxDistance 250, DS3D_IMMEDIATE
DSBuffer3D.SetMinDistance 0.01, DS3D_IMMEDIATE
رجيستري چيست ؟
سيستم عامل ويندوز تنظيمات سخت افزاري و نرم افزاري خود را بطور مرکزي در يک بانک اطلاعاتي با ساختار سلسله مراتبي ذخيره مي کند که رجيستري نام دارد . رجيستري جايگزيني براي بسياري از فايلهاي پيکربندي INI ، SYS و COM است که در نسخه هاي اوليه ويندوز موجود بود . رجيستري ، سيستم عامل را با مهيا کردن اطلاعات موردنيز براي اجراي برنامه ها و load شدن component ها ، کنترل مي کند .
رجيستري شامل انواع مختلفي از اطلاعات مي باشد مثل :
- اطلاعات سخت افزارهاي نصب شده روي سيستم
- اطلاعات درايورهاي نصب شده روي سيستم
- اطلاعات برنامه هاي نصب شده روي سيستم
- اطلاعات پروتکلهاي شبکه اي مورد استفاده در سيستم
ساختار رجيستري شامل چندين مجموعه رکورد است که داده هاي اين رکوردها توسط بسياري از برنامه ها و اجزاي سيستم عامل خوانده و يا نوشته مي شود .
اجزاي رجيستري
اجزاي تشکيل دهنده رجيستري عبارتند از :
1 – subtree : Subtree ها همانند folder هاي موجود در ريشه يک درايو هارد هستند . رجستری ويندوز داراي پنج subtree مي باشد :
- HKEY_LOCAL_MACHINE : شامل تمام داده هاي پيکربندي براي کامپيوتر مي باشد و شامل 5 key است :Hardware ، SAM ، Security ، Software و System
- HKEY_USERS : شامل داده هاي مربوط به تنظيمات سيستم عامل براي هر user است مثل تنظيمات desktop و محيط ويندوز
- HKEY_CURRENT_USER : شامل داده هاي کاربر فعلي سيستم
- HKEY_CLASSES_ROOT : شامل اطلاعات پيکربندي نرم افزار است مثل داده هاي OLE و داده هاي کلاسهاي متناظر با فايل
- HKEY_CURRENT_CONFIG : شامل اطلاعات مورد نياز براي تنظيمات داريورهاي سخت افزاري و غيره
2 – Key : key ها همانند folder ها و subfolder هاي روي هارد هستند . هر key متناظر با object هاي نرم افزاري يا سخت افزاري مي باشد . subkey ها key هايي هستند که درون يکسري key قراردارند .
3 – Entry : هر key داراي يک يا چند entry است . هر entry داراي سه بخش مي باشد :
- نام Name
- نوع داده اي Data Type : مقدار هر entry يکي از انواع داده هاي زير است :
REG_DWORD ، REG_SZ ، REG_EXPAND_SZ ، REG_BINARY ،
REG_MULTI_SZ ، REG_FULL_RESOURCE_DESCRIPTOT
- مقدار Value
نکته 1 : براي مشاهده رجيستري و اعمال تغييرات در آن ( لطفاً اگر هيچ تجربه اي در تنظيم کردن رجيستري نداريد اطلاعات آنرا تغيير ندهيد ) ، مي توانيد از برنامه regedit.exe و يا regedt32.exe موجود در ويندوز استفاده کنيد . براي اينکار کافيست نام برنامه را در کادر Run وارد کنيد .
براي کار با رجيستري در ويژوال بيسيک کلاس Registery.bas را مطابق مطالب زير ايجاد کرده و در پروژه هاي خود از آن استفاده کنيد :
1 - تعريف ثابتهاي مورد نياز : براي نوشتن اين کلاس نياز به تعريف چهار دسته ثابت داريم :
- ثابتهاي مربوط به تعريف data type هاي entry هاي رجيستري :
Global Const REG_SZ As Long = 1
Global Const REG_DWORD As Long = 4
- ثابتهاي مربوط به تعريف key هاي رجيستري
Global Const HKEY_CLASSES_ROOT = &H80000000
Global Const HKEY_CURRENT_USER = &H80000001
Global Const HKEY_LOCAL_MACHINE = &H80000002
Global Const HKEY_USERS = &H80000003
- ثابتهاي مربوط به خطاهاي کار با رجيستري
Global Const ERROR_NONE = 0
Global Const ERROR_BADDB = 1
Global Const ERROR_BADKEY = 2
Global Const ERROR_CANTOPEN = 3
Global Const ERROR_CANTREAD = 4
Global Const ERROR_CANTWRITE = 5
Global Const ERROR_OUTOFMEMORY = 6
Global Const ERROR_INVALID_PARAMETER = 7
Global Const ERROR_ACCESS_DENIED = 8
Global Const ERROR_INVALID_PARAMETERS = 87
Global Const ERROR_NO_MORE_ITEMS = 259
- ثابتهاي متفرقه
Global Const KEY_ALL_ACCESS = &H3F
Global Const REG_OPTION_NON_VOLATILE = 0
2 - Declare کردن Api هاي مورد نياز : براي کار با رجيستري از توابع کتابخانه Advapi32.dll استفاده مي کنيم . اين توابع عبارتند از :
- تابع RegCloseKey : آزاد کردن handle مربوط به يک key
Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long
- تابع RegCreateKeyEx : ساخت يک key در رجيستري ( اگر key قبلاً وجود داشته باشد ، اين تابع آنرا باز مي کند ) :
Declare Function RegCreateKeyEx Lib "advapi32.dll" Alias "RegCreateKeyExA" (ByVal hKey As Long, ByVal lpSubKey As String, ByVal Reserved As Long, ByVal lpClass As String, ByVal dwOptions As Long, ByVal samDesired As Long, ByVal lpSecurityAttributes As Long, phkResult As Long, lpdwDisposition As Long) As Long
- تابع RegOpenKeyEx : باز کردن يک key
Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" (ByVal hKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, ByVal samDesired As Long, phkResult As Long) As Long
- تابع RegQueryValueExLong : استخراج type و data ي يک نام متناظر با يک key باز شده
Declare Function RegQueryValueExString Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, ByVal lpData As String, lpcbData As Long) As Long
Declare Function RegQueryValueExLong Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, lpData As Long, lpcbData As Long) As Long
Declare Function RegQueryValueExNULL Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, ByVal lpData As Long, lpcbData As Long) As Long
- تابع RegSetValueEx : ذخيره يک مقدار در فيلد value يک کليد باز
Declare Function RegSetValueExString Lib "advapi32.dll" Alias "RegSetValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, ByVal lpValue As String, ByVal cbData As Long) As Long
Declare Function RegSetValueExLong Lib "advapi32.dll" Alias "RegSetValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, lpValue As Long, ByVal cbData As Long) As Long
- تابع RegDeleteKey : پاک کردن يک کليد و کليه اطلاعات مرتبط با آن
Private Declare Function RegDeleteKey& Lib "advapi32.dll" Alias "RegDeleteKeyA" (ByVal hKey As Long, ByVal lpSubKey As String)
- تابع RegDeleteValue : حذف مقدار يک key
Private Declare Function RegDeleteValue& Lib "advapi32.dll" Alias "RegDeleteValueA" (ByVal hKey As Long, ByVal lpValueName As String)
3 - توابع کمکي : براي نوشتن توابع اصلي کار با رجيستري نياز به نوشتن توابع کمکي زير است :
- تابع SetValueEx : با توجه به نوع داده يک کليد ، مقدار موجود در آنرا در يک متغير ذخيره مي کند :
Public Function SetValueEx(ByVal hKey As Long, sValueName As String, lType As Long, vValue As Variant) As Long
Dim lValue As Long
Dim sValue As String
Select Case lType
Case REG_SZ ' type of value is string
sValue = vValue
SetValueEx = RegSetValueExString(hKey, sValueName, 0&, lType, sValue, Len(sValue))x
Case REG_DWORD ' type of value is Double word
lValue = vValue
SetValueEx = RegSetValueExLong(hKey, sValueName, 0&, lType, lValue, 4)x
End Select
End Function
- تابع QueryValueEx : سايز و نوع داده اي يک داده را که بايد خوانده شود مشخص مي کند .
Function QueryValueEx(ByVal lhKey As Long, ByVal szValueName As String, vValue As Variant) As Long
Dim cch As Long
Dim lrc As Long
Dim lType As Long
Dim lValue As Long
Dim sValue As String
lrc = RegQueryValueExNULL(lhKey, szValueName, 0&, lType, 0&, cch)x
Select Case lType
' For strings
Case REG_SZ:
sValue = String(cch, 0)x
lrc = RegQueryValueExString(lhKey, szValueName, 0&, lType, sValue, cch)x
If lrc = ERROR_NONE Then
vValue = Left$(sValue, cch)x
Else
vValue = Empty
End If
' For DWORDS
Case REG_DWORD:
lrc = RegQueryValueExLong(lhKey, szValueName, 0&, lType, lValue, cch)x
If lrc = ERROR_NONE Then vValue = lValue
Case Else
'all other data types not supported
lrc = -1
End Select
QueryValueExExit:
QueryValueEx = lrc
Exit Function
QueryValueExError:
Resume QueryValueExExit
End Function
4 - توابع اصلي : توابع مربوط به پاک کردن يک کليد از رجيستري ، ساخت يک کليد جديد در رجيستري و مقداردهي به يک کليد :
- تابع DeleteKey : اين تابع يک کليد از رجيستري را حذف مي کند . داراي دو پارامتر ورودي است :
Location که يکي از مقادير HKEY_CLASSES_ROOT ، HKEY_CURRENT_USER
، HKEY_LOCAL_MACHINE و يا HKEY_USERS است .
KeyName که نام کليدي است که بايد از رجيستري حذف شود . اين کليد ممکنست شامل subkey هايي نيز باشد مثلاً Key1\SubKey1
Public Function DeleteKey(lPredefinedKey As Long, sKeyName As String)x
Dim lRetVal As Long
lRetVal = RegDeleteKey(lPredefinedKey, sKeyName)x
DeleteKey = lRetVal ' return function value
End Function
- تابع DeleteValue : اين تابع يک entry را از کليد حذف مي کند . داراي سه پارامتر ورودي است : Location ، KeyName و ValueName که نام آن value را مشخص مي کند .
Public Function DeleteValue(lPredefinedKey As Long, sKeyName As String, sValueName As String)x
Dim lRetVal As Long
Dim hKey As Long
lRetVal = RegOpenKeyEx(lPredefinedKey, sKeyName, 0, KEY_ALL_ACCESS, hKey)x
lRetVal = RegDeleteValue(hKey, sValueName)x
RegCloseKey (hKey)x
DeleteValue = lRetVal
End Function
- تابع CreateNewKey : اين تابع يک کليد جديد ايجاد مي کند . داراي دو پارامتر ورودي است : Location و KeyName
Public Function CreateNewKey(lPredefinedKey As Long, sNewKeyName As String)x
Dim hNewKey As Long
Dim lRetVal As Long
lRetVal = RegCreateKeyEx(lPredefinedKey, sNewKeyName, 0&, vbNullString, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0&, hNewKey, lRetVal)x
RegCloseKey (hNewKey)x
CreateNewKey = lRetVal
End Function
- تابع SetKeyValue : اين تابع پارامتر data يک entry را تنظيم مي کند . داراي 5 پارامتر ورودي است : Location ، KeyName ، ValueName ، ValueSetting و ValueType
Public Function SetKeyValue(lPredefinedKey As Long, sKeyName As String, sValueName As String, vValueSetting As Variant, lValueType As Long)x
Dim lRetVal As Long
Dim hKey As Long
lRetVal = RegOpenKeyEx(lPredefinedKey, sKeyName, 0, KEY_ALL_ACCESS, hKey)x
lRetVal = SetValueEx(hKey, sValueName, lValueType, vValueSetting)x
RegCloseKey (hKey)x
SetKeyValue = lRetVal
End Function
- تابع QueryValue : اين تابع فيلد داده يک entry را برمي گرداند . داراي سه پارامتر ورودي است : Location ، KeyName و ValueName
Public Function QueryValue(lPredefinedKey As Long, sKeyName As String, sValueName As String)x
Dim lRetVal As Long
Dim hKey As Long
Dim vValue As Variant
lRetVal = RegOpenKeyEx(lPredefinedKey, sKeyName, 0, KEY_ALL_ACCESS, hKey)x
lRetVal = QueryValueEx(hKey, sValueName, vValue)x
QueryValue = vValue
RegCloseKey (hKey)x
End Function
ساخت يک انتصاب فايل يا File Association به يک برنامه
در اين درس می خواهم با استفاده از کلاسی که در درس قبل معرفی شد تابعی بسازيم که توسط آن بتوانيم فايلهای با پسوندی مشخص را به يک برنامه اختصاص دهيم . بعبارت ديگر تابعی بنويسيم که اطلاعات لازم برای باز شدن فايلهايی با پسوند xxx را توسط برنامه MyApp.exe در رجيستری ثبت کند .
Public Sub CreateAssociation(sExtension As String, sApplication As String, sAppPath As String)x
Dim sPath, sAppExe As String
CreateNewKey "." & sExtension, HKEY_CLASSES_ROOT
SetKeyValue HKEY_CLASSES_ROOT, "." & sExtension, "", sApplication & ".Document", REG_SZ
CreateNewKey sApplication & ".Document\shell\open\command", HKEY_CLASSES_ROOT
SetKeyValue HKEY_CLASSES_ROOT, sApplication & ".Document", "", sApplication & " Document", REG_SZ
sPath = sAppPath & " %1"x
sAppExe = sApplication & ".exe"x
SetKeyValue HKEY_CLASSES_ROOT, sApplication& ".Document\shell\open\command", "", sPath, REG_SZ
CreateNewKey "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\." & sExtension, HKEY_CURRENT_USER
SetKeyValue HKEY_CURRENT_USER, "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\." & sExtension, "Application", sAppExe, REG_SZ
CreateNewKey "Applications\" & sAppExe & "\shell\open\command", HKEY_CLASSES_ROOT
SetKeyValue HKEY_CLASSES_ROOT, "Applications\" & sAppExe & "\shell\open\command", "", sPath, REG_SZ
End Sub
کاربرد اين تابع بصورت زير است :
CreateAssociation("xxx","MyApp","c:\MyApp.exe")x
اجرا شدن يک برنامه در هنگام راه اندازی سيستم
فرض کنيد می خواهيم برنامه ای بنويسيم که هر بار در هنگام راه اندازي سيستم بطور خودكار اجرا شود. البته نمي خواهم در startup ويندوز ديده شود .
براي اين كار بايد برنامه موردنظر را در StartUp رجيستري قرار دهيم . به اين ترتيب كه در يكي از كليدهاي زير يك مقدار رشته اي جديد(String Value) ايجاد کنيم و آدرس برنامه را در آن وارد كنيم :
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
براي مثال اگه اسم برنامه مورد نظر MyApp و مسيرش C:\Windows\MyApp.exe است بايد بصورت زير عمل کرد :
SetKeyValue HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows\CurrentVersion\Run", "MyApp", "C:\MyApp.exe", REG_SZ
نکته : البته دو تا راه ديگر برای اينکار وجود دارد که برخی تروجان ها هم از اين روشها استفاده می کنند تا روی سيستم باقی بمانند :
يكي استفاده از win.ini و نوشتن نام فايل جلوي = run و ديگري استفاده از system.ini و نوشتن نام برنامه جلوي خط explorer.exe .
آشنايي با Windows API
قصد دارم در مورد API هاي ويندوز و چگونگي استفاده از آنها در ويژوال بيسيک بطور خلاصه توضيح دهم و همچنين دو مثال پراستفاده را نيز در اين زمينه بيان کنم که عبارتند از چگونگي پخش فايلهاي Wav و ساخت يک تايمر با دقت بالا :
۱ - آشنايي با Windows API : واژه API مخفف Application Programming Interface مي باشد . API هاي ويندوز مجموعه اي از توابع از پيش آماده موجود در سيستم عامل هستند که شما مي توانيد آنها را در برنامه هاي خود فراخواني کنيد . اين توابع در چندين کتابخانه DLL ويندوز ذخيره شده اند . براي دسترسي به اين توابع در ويژوال بيسيک ابتدا بايد آنها را برنامه خود declare کنيد . براي مثال :
Declare Function sndPlaySound Lib "winmm.dll" Alias "sndPlaySoundA" (ByVal lpszSoundName As String, ByVal uFlags As Long) As Long
همانطور که مي بينيد مثال فوق يک Declare از تابع sndPlaySound مي باشد که اين تابع در کتابخانه Winmm.dll موجود است . کلمه Alias نشان مي دهد که اين تابع نام ديگري در dll دارد . ساير بخشها مربوط به تعريف پارامترهاي تابع مي باشند که در مورد مثال فوق ، اين تابع دو پارامتر ورودي و يک خروجي از نوع Long دارد .
پس از Delare کردن API در برنامه مي توانيد از آن استفاده نمائيد .
۲ - پخش فايلهاي Wav : تابعي که براي پخش فايلهاي Wav استفاده مي شود تابع sndPlaySound است که در بالا با آن آشنا شديد . پارامتر lpzSoundName نام و مسير فايل Wavو پارامتر uFlags چگونگي پخش فايل را مشخص مي کند . مقادير ممکن اين پارامتر عبارتند از :
- SND_ASYNC : اجازه مي دهد طوري فايل Wav پخش شود که آنرا بتوان وقفه داد . بعبارت ديگر قادر خواهيد بود فايل Wav تان را هر زمان که بخواهيد پخش کنيد و مطمئن باشيد که حتماً شنيده مي شود .
- SND_LOOP : فايل Wav را بطور ممتد پخش مي کند .
- SND_NODEFAULT : اگر فايل Wav پيدا نشود صداي ديگري پخش نخواهد شد ( مثلاً برخي صداهاي default ويندوز )
- SND_SYNC : در طول پخش فايل Wav کنترل به برنامه داده نمي شود . اين پارامتر در زمانيکه مي خواهيد فايل Wav اي را در پس زمينه برنامه تان پخش کنيد مناسب نمي باشد .
- SND_NOSTOP : اگر فايل Wav اي قبلاً در حال پخش باشد ، فايل Wav شما آنرا دچار وقفه نمي کند . از اين پارامتر زماني استفاده مي شود که بخواهيم فايل Wav مان هيچوقت در وسط کار قطع نشود .
اگر بخواهيد از بيش از يکي از اين پارامترها استفاده کنيد توسط Or آنها را ترکيب نمائيد مثال :
sndPlaySound App.path & "\ding.wav", SND_ASYNC or SND_LOOP
نکته : براي استفاده از توابع صوتي پيچيده تر بايستي از DirectSound که يکي از اجزاي DirectX مي باشد استفاده کنيد . در مورد DirectSound بعداً صحبت خواهم کرد .
۳ - ساخت يک تايمر با دقت بالا : شايد تا بحال از کنترل تايمر موجود در نوار ابزار ويژوال بيسيک استفاده کرده باشيد . اين تايمر داراي دقت حدود ۵۵ ميلي ثانيه است . براي دستيابي به زمانهاي با دقت بالاتر اين کنترل مفيد نخواهد بود .
تابع GetTickCount يک API موجود در کتابخانه Kernel32.dll است . اين تابع طول زماني را که سيستم شروع به کار کرده است را برحسب ميلي ثانيه برمي گرداند :
Private Declare Function GetTickCount Lib "kernel32" () As Long
براي بررسي طي شدن يک مدت زماني خاص شما ابتدا بايد مقدار اين تابع را در يک متغير کمکي مثل TempTime قرار دهيد سپس در يک حلقه Do-Loop بايد اختلاف زمان GetTickCount جديد و زمان TempTime را با مقدار زماني که مي خواهيد سپري شود مقايسه کنيد :
TempTime = GetTickCount()x
Do While DesiredTime < GetTickCount() - TempTime
Do some things'
Loop
توسط کد بالا مي توان يک عمليات خاص را براي يک مدت زماني مشخص اجرا کرد .
کد زير نشان مي دهد که چگونه مي توان دستورات خاصي را در فواصل زماني خاص اجرار کرد :
ExitFunction = False
TempTime = GetTickCount()x
Do While not(ExitFunction)x
If DesiredTime < GetTickCount() - TempTime then
Reset the temporary variable'
TempTime = GetTickCount()x
Do some things'
End If
Loop
همچنين از تابع GetTickCount مي توان براي benchmark برنامه ها استفاده کرد . بعبارت ديگر مي توان زمان اجراي يکسري دستورات خاص را بدست آورد
مباحث پيشرفته Direct3D
موضوع : ساخت يک موتور گرافيکي سه بعدي
قبل از شروع مباحث جديد برنامه نويسي Direct3D ، با هم مروري بر مباحث قبلي خواهيم داشت .( مباحث قبلي در آرشيو موجود مي باشند
در اين درس با استفاده از مطالب قبلي يک Engine سه بعدي ساخته و از امکانات آن در يک برنامه نمونه استفاده خواهيم کرد .
اين engine داراي دو کلاس است :
1 – کلاس MainD3D
2 – کلاس D3Dobject
در کلاس MainD3D متغيرها و توابع لازم براي ساخت يک device سه بعدي ، تنظيمات ماتريسي ، تابع رندر و غيره موجود مي باشد .
متغيرهاي عمومي اين کلاس عبارتند از :
Public g_DX As New DirectX8
Public g_D3D As Direct3D8
Public g_D3DX As New D3DX8
Public g_D3DDevice As Direct3DDevice8
Public NTextures As Long
روتين ها و توابع اين کلاس عبارتند از :
1 - InitD3D : اين روتين ، اشيا D3D و D3Ddevice را مي سازد و پارامترهاي آنها را تنظيم مي کند .
2 – ApplyCameraChanges : روتين ايجاد ماتريس View
3 – SetupMatrices : روتين ايجاد ماتريس Projection
4 – StartRender : در اين روتين عمليات لازم براي شروع عمل رندر صورت مي گيرد .
5 – RenderObject : اين تابع ، يک شي سه بعدي از نوع کلاس D3Dobject را مي گيرد و بردارهاي مورد نياز و نيز بافت شي را تنظيم مي کند و در پايان شي را ترسيم مي کند .
6 – FinishRender : در اين روتين به عمليات رندر پايان داده مي شود .
7 – Cleanup: روتين از بين بردن اشيا Direct3D
8 – CreateVector : تابع ساخت يک بردار سه بعدي
9 – CreateTextures : روتين ساخت يک بافت جديد
10 – InitTexture: تابع مقداردهي به يک بافت
در کلاس D3Dobject متغيرها و توابع لازم براي ايجاد يک شي سه بعدي و اختصاص بافت به آن موجود مي باشد .
در اين کلاس دو type عمومي تعريف شده است :
1 - NormalVERTEX
2 - TeturedVERTEX
همچنين روتين ها و توابع اين کلاس عبارتند از :
1 – InitObject : تابعي که تنظيمات اوليه vertex ها و بافت شي را انجام مي دهد .
2 – Vertex : روتين ايجاد vertex هاي مورد نياز
3 – GetRenderingMode: تابعي که مد رندر را مشخص مي کند .
و نيز يکسري تابع ساخت vertex نرمال و ساخت vertex داراي بافت و غيره
اين دو کلاس در يک پروژه ويژوال بيسيک قرارداده شده و پروژه با نام D3Dengine.dll کامپايل شده است .
حال با استفاده از اين engine مي خواهيم يک منظره سه بعدي را ايجاد کنيم :
اين منظره شامل سه object است : ديوار ، آسمان و زمين.
ابتدا بايد يک شي از کلاس MainD3D تعريف کنيم :
Dim D3D8Main As MainD3D8
در متد Form Load نيز سه شي Floor ، Sky و Wall را بصورت زير تعريف مي کنيم :
Dim Floor As D3DObject
Dim Sky As D3DObject
Dim Walls As D3Dobject
سپس اين سه شي را به اضافه شي D3D8Main ، ايجاد مي کنيم :
Set D3D8Main = New D3DEngine.MainD3D8
Set Floor = New D3DEngine.D3DObject
Set Sky = New D3DEngine.D3DObject
Set Walls = New D3DEngine.D3Dobject
در ابتدا شي MainD3D را Initial مي کنيم و سپس بافتهاي مورد نيز خود را مي سازيم :
D3D8Main.InitD3D True, Me.hWnd
D3D8Main.CreateTextures 3
D3D8Main.InitTexture 1, App.Path + "\floor.jpg"
D3D8Main.InitTexture 2, App.Path + "\sky.bmp"
D3D8Main.InitTexture 3, App.Path + "\wall.bmp"
حال به سراغ ايجاد و مقداردهي vertex هاي floor مي رويم . floor شامل شش vertex مي باشد و بنابراين دو face مثلثي دارد :
Floor.InitObject 6, 2, TriangleList, True, 1
Floor.Vertex 0, -55, -2, -55, vbWhite, 0, 10
Floor.Vertex 1, 55, -2, -55, vbWhite, 10, 10
Floor.Vertex 2, 55, -2, 55, vbWhite, 10, 0
Floor.Vertex 3, -55, -2, -55, vbWhite, 0, 10
Floor.Vertex 4, 55, -2, 55, vbWhite, 10, 0
Floor.Vertex 5, -55, -2, 55, vbWhite, 0, 0
سپس به سراغ ايجاد و مقداردهي vertex هاي wall مي رويم . wall شامل بيست و چهار vertex مي باشد و بنابراين هشت face مثلثي دارد :
Walls.InitObject 24, 8, TriangleList, True, 3
Walls.Vertex 0, -55, -2, -55, &HBCE8FC, 0, 1
Walls.Vertex 1, 55, -2, -55, &HBCE8FC, 5, 1
Walls.Vertex 2, 55, 8, -55, &HBCE8FC, 5, 0
Walls.Vertex 3, -55, -2, -55, &HBCE8FC, 0, 1
Walls.Vertex 4, 55, 8, -55, &HBCE8FC, 5, 0
Walls.Vertex 5, -55, 8, -55, &HBCE8FC, 0, 0
Walls.Vertex 6, -55, -2, 55, &HBCE8FC, 0, 1
Walls.Vertex 7, 55, -2, 55, &HBCE8FC, 5, 1
Walls.Vertex 8, 55, 8, 55, &HBCE8FC, 5, 0
Walls.Vertex 9, -55, -2, 55, &HBCE8FC, 0, 1
Walls.Vertex 10, 55, 8, 55, &HBCE8FC, 5, 0
Walls.Vertex 11, -55, 8, 55, &HBCE8FC, 0, 0
Walls.Vertex 12, -55, -2, 55, &HBCE8FC, 0, 1
Walls.Vertex 13, -55, -2, -55, &HBCE8FC, 5, 1
Walls.Vertex 14, -55, 8, -55, &HBCE8FC, 5, 0
Walls.Vertex 15, -55, -2, 55, &HBCE8FC, 0, 1
Walls.Vertex 16, -55, 8, -55, &HBCE8FC, 5, 0
Walls.Vertex 17, -55, 8, 55, &HBCE8FC, 0, 0
Walls.Vertex 18, 55, -2, 55, &HBCE8FC, 0, 1
Walls.Vertex 19, 55, -2, -55, &HBCE8FC, 5, 1
Walls.Vertex 20, 55, 8, -55, &HBCE8FC, 5, 0
Walls.Vertex 21, 55, -2, 55, &HBCE8FC, 0, 1
Walls.Vertex 22, 55, 8, -55, &HBCE8FC, 5, 0
Walls.Vertex 23, 55, 8, 55, &HBCE8FC, 0, 0
حال به سراغ ايجاد و مقداردهي vertex هاي sky مي رويم . sky شامل شش vertex مي باشد و بنابراين دو face مثلثي دارد :
Sky.InitObject 6, 2, TriangleList, True, 2
Sky.Vertex 0, -55, 8, -55, &HBCE8FC, 0, 1
Sky.Vertex 1, 55, 8, -55, &HBCE8FC, 0, 1
Sky.Vertex 2, 55, 8, 55, &HBCE8FC, 0, 1
Sky.Vertex 3, -55, 8, -55, &HBCE8FC, 0, 1
Sky.Vertex 4, 55, 8, 55, &HBCE8FC, 0, 1
Sky.Vertex 5, -55, 8, 55, &HBCE8FC, 0, 1
در پايان تابع رندر را صدا مي کنيم . البته در هر بار عمل رندر کردن ، دوربين يک درجه در صفحه X-Z دوران مي کند تا کل ديوار قابل مشاهده باشد :
Dim Angle As Double
PI = 3.1415
Angle = 0
Do
DoEvents
D3D8Main.StartRender vbBlack
D3D8Main.RenderObject Sky
D3D8Main.RenderObject Floor
D3D8Main.RenderObject Walls
D3D8Main.FinishRender
If Sqr(Angle ^ 2) = 360 Then Angle = 0
Angle = Angle + 1
D3D8Main.CamLookAtX = Sin((Angle * 2 * PI) / 360)
D3D8Main.CamLookAtZ = Cos((Angle * 2 * PI) / 360)
D3D8Main.ApplyCameraChanges
Loop
موضوع : استفاده از object هاي 3D Studio Max در Direct3D
تا بحال ما هر شيي را که مي خواستيم در Direct3D بسازيم خودمان بوسيله کد نويسي آنرا توصيف کرده ايم . ممکنست اين سوال برايتان پيش آمده باشد که بازيهاي تجاري براي توليد کاراکترهاي و اشيا پيچيده سه بعدي چگونه عمل مي کنند ؟
منطقي بنظر نمي رسد که اينگونه مدلهاي پيچيده بصورت کد وارد برنامه شده اند زيرا نياز به هزاران خط برنامه براي هر فريم خواهد بود . بجاي اينکار ما object هاي خود را توسط برنامه هاي ديگري مي سازيم و آنها را در برنامه خودمان load مي کنيم سپس بافتها و material هاي مورد نظر را به آنها اختصاص داده و در پايان آنها را رندر مي کنيم . مزيت ديگر اينکار اينست که شما مي توانيد براحتي فايل object خود را تغيير دهيد و مدلهايي با جزئيات متفاوت براي برنامه خود قرار دهيد .
مراحل ساخت چنين برنامه هايي بصورت زير است :
۱ - ساخت object سه بعدي :
اولين چيزي که بايستي بدانيد داشتن دانش پايه اي از چگونگي مدلسازي سه بعدي است . همچنين نياز به يک نرم افزار مدلسازي مثل 3D Studio Max داريد .
بعد از ساخت مدل خود در Max نياز به يک Convertor داريد تا فايلهاي Max را به فايلهاي Direct3D که با فرمت "X." هستند تبديل کنيد .
Convertor هاي زيادي براي تبديل فايلهاي نرم افزارهاي مدلسازي به فايلهاي "X." وجود دارند که برخي از آنها عبارتند از :
- برنامه PolyTrans3D System Translation
- برنامه Deep Exploration 2.0
- برنامه Quick3D
- برنامه 3DWin
- DirectX Explorer Plugin
- ابزارهاي موجود در DirectX 8.0 SDK که عبارتند از :
برنامه Conv3DS براي تبديل فايلهاي 3DS به فايلهاي X
DX SDK Exporter Plugin براي تبديل فايلهاي 3DS و Max به فايلهاي X
از بين اين برنامه ها و plugin ها من برنامه Deep Exploration را به شما پيشنهاد مي کنم .
2 - Load کردن يک Object ساخته شده :
زمانيکه فايل X شي مورد نظر را ساختيد ، load کردن آن در direct3D ساده است . براي اينکار نياز به يک مش داريم که اطلاعات شي ما را نگهداري کند :
Dim Mesh As D3DXMesh
همچنين براي اختصاص material و texture به شي ، نياز به تعريف متغيرهاي زير داريم :
Dim MeshMaterial As D3DMATERIAL8
Dim MeshTexture As Direct3DTexture8
حال به سراغ بازنويسي روتين InitGeometry مي رويم :
- تعريف متغيرهاي مورد نياز :
Dim mtrlBuffer as D3DXBuffer
Dim TextureFile as String
Dim n as Long
- گرفتن داده هاي شي از فايل X :
Set Mesh=D3DX.LoadMeshFromX app.path&"\"&"yourfilename",D3DMESH_MANAGED,D3DDevice,Nothing,mtrlBuffer,n
- استخراج اطلاعات materiasl شي و تنظيم پارامتر Ambient :
D3DX.BufferGetMaterial mtrlBuffer,0,MeshMaterial
MeshMaterial.Ambient=MeshMaterial.Diffuse
- استخراج نام بافت بکار رفته براي شي :
TextureFile=D3DX.BufferGetTextureName(mtrlBuffer,0)x
- ساخت بافت :
If TextureFile<>"" Then
Set MeshTexture=D3DX.CreateTextureFromFile D3DDevice,app.path&"\"&TextureFile,128,128,D3DX_DEFAULT,0,
D3DFMT_UNKNOWN,D3DPOOL_MANAGED,D3DX_FILTER_LINEAR,D3DX_FILTER_LINEAR,0,Byval 0,Byval 0
End If
۳ - رندر نمودن شي : رندر نمودن شي چندان مشکل نيست اما همچنان بايد ماتريسها و تبديلاتي را که مي خواهيد ، خودتان مديريت کنيد .
D3DDevice.SetMaterial MeshMaterial
D3DDevice.SetTexture 0,MeshTexture
Mesh.DrawSubset 0
موضوع : مباحث تکميلي نورپردازي در Direct3D
در بخش اول آموزش Direct3D با مباني نورپردازي آشنا شديد . در اين درس قصد دارم آن مباحث را کاملتر برايتان مطرح کنم .
نورپردازي يکي از بخشهاي مهم طراحي يک بازي و يا يک انيميشن سه بعدي است . بمنظور پياده سازي نورپردازي يک صحنه ابتدا بايد با تئوري آن آشنا شويد .
تئوري نورپردازي : نورپردازي در Direct3D تخميني از چگونگي عملکرد نور در دنياي واقعي مي باشد . چهار نوع اصلي نور در Direct3D قابل استفاده است ( همچنين شما مي توانيد خودتان انواع جديدي از نور ايجاد کنيد که موضوع ما نيست ) :
۱ - Point Light : توسط يک نقطه در فضاي سه بعدي ايجاد مي شود و داراي سه پارامتر رنگ ، دامنه و تضعيف مي باشد . دامنه يک نور مسافتي است که نور مي تواند طي کند . تضعيف ، مقدار کاهش نور در اثر افزايش مسافت مي باشد . نور نقطه اي در تمام جهات تششع مي کند - شبيه يک لامپ حبابي و يا يک شمع
۲ - Spot Light : داراي يک موقعيت و يک جهت است و تنها نور را در يک جهت خاص مي تاباند - شبيه يک چراغ قوه . اين نور داراي يک زاويه مخروطي و يک دامنه است .
۳ - Directional Light : داراي موقعيت نيست و براي پياده سازي نورهايي که از فاصله بسيار دور مي آيند - مثل خورشيد - مناسب است .
۴ - Ambient Light : اين نور تضمين مي کند که تمام vertex هاي يک صحنه تاريکتر از يک رنگ خاص نباشند .
عملي کردن نورپردازي : ضمن اينکه اغلب کارت هاي گرافيک سه بعدي از نورپردازي پشتيباني مي کنند اما اين نکته بايد مورد توجه قرار گيرد که با افزايش تعداد نور در يک صحنه محاسبات Direct3D بيشتر مي شود و اين باعث کند شدن رندر صحنه خواهد شد و بنابراين کارت هاي گرافيکي سه بعدي نيز داراي يک ماکزيمم تعداد نور هستند - مثلاً ۱۶ نور در GeForce 2 - همچنين توجه داشته باشيد که نورهاي مختلف داراي زمان پردازشي متفاوتي هستند . نور ambient سريعترين زمان پردازشي را دارد ، سپس نورdirectional ، سپس نور point و کندترين آنها Spot Light است .
همچنين نکته ديگري که بايد توجه کنيد دامنه نور است . اگر نور ، يک منطقه بزرگي را پوشش دهد بر تعداد زيادي از vertex ها تاثير مي گذارد و اين باعث افزايش محاسبات مي شود .
نورپردازي Specular - که در درسهاي بعدي در مورد آن صحبت مي کنم و براي ايجاد اشيا درخشان استفاده مي شود - نيز زمان پردازشي زيادي دارد و بهتر است کمتر از آن استفاده شود .
پارامتر ديگري که بايد در نظر بگيريد جزئيات هندسه شما مي باشد . هر چه پيچيدگي صحنه بيشتر باشد ، نورپردازي نيز زمان بيشتري را مصرف مي کند .
سايه زني نيز يک بخش بسيار پيچيده در مدل سازي نور است و محاسبات آن بسيار زمان گير خواهد بود بنابراين Direct3D مستقيماً محاسبات سايه زني را انجام نمي دهد بلکه رنگ نور را بر مبناي جهت هر مثلث scale مي کند بنابراين قسمت پشتي يک شي که رو به نور نيست ، هيچ نوري را دريافت نمي کند .
بردار نرمال : Direct3D هر vertex را بر مبناي بک بردار نرمال نورپردازي مي کند و نوري که يک vertex دريافت مي کند به زاويه بين نور و بردار نرمال آن vertex بستگي دارد . بردار نرمال توسط سه vertex يک face مثلثي ايجاد مي شود و اين بردار نرمال ساخته شده به vertex ها اختصاص مي يابد . بردار نرمال در واقع سمت يک مثلث را مشخص مي کند بنابراين اگر نور پشت مثلث باشد ، مثلث هيچ نوري را دريافت نميکند . بردار نرمال بايستي داراي طول ۱ باشد .
مراحل توليد بردار نرمال يک face مثلثي :
۱ - مطمئن شويد که face در جهت عقربه هاي ساعت ساخته شده است .
۲ - يک بردار از vertex شماره صفر به vertex شماره يک بسازيد .
۳ - يک بردار از vertex شماره صفر به vertex شماره دو بسازيد .
۴ - حاصلضرب برداري ( cross droduct ) اين دو بردار را بدست آوريد .
۵ - نتيجه حاصلضرب را نرمال کنيد .
Private Function GenerateTriangleNormals(p0 As UnlitVertex, p1 As UnlitVertex, p2 As UnlitVertex) As D3DVECTOR
Dim v01 As D3DVECTOR
Dim v02 As D3DVECTOR
Dim vNorm As D3DVECTOR
D3DXVec3Subtract v01, MakeVector(p1.X, p1.Y, p1.Z), MakeVector(p0.X, p0.Y, p0.Z)x
D3DXVec3Subtract v02, MakeVector(p2.X, p2.Y, p2.Z), MakeVector(p0.X, p0.Y, p0.Z)x
D3DXVec3Cross vNorm, v01, v02
D3DXVec3Normalize vNorm, vNorm
GenerateTriangleNormals.X = vNorm.X
GenerateTriangleNormals.Y = vNorm.Y
GenerateTriangleNormals.Z = vNorm.Z
End Function
اگر دو face در يک vertex مشترک باشند ( مثل گوشه دو ديوار ) براي توليد نرمال اين vertex ابتدا نرمال دو face را با روش فوق بدست آوريد سپس دو بردار نرمال را با هم جمع کنيد و در پايان بردار حاصلجمع را نرمال کنيد .
برپاسازي نورپردازي : اولين چيزي که قبل از برپاسازي نورپردازي بايستي اعمال کنيم تغيير ساختار vertex است . براي اينکار بايد پارامتر color را از ساختار vertex حذف و سه پارامتر را براي نگهداري نرمال اضافه کنيم :
Private Type UnlitVertex
X As Single
Y As Single
Z As Single
nx As Single
ny As Single
nz As Single
tu As Single
tv As Single
End Type
Const Unlit_FVF = (D3DFVF_XYZ Or D3DFVF_NORMAL Or D3DFVF_TEX1)x
همچنين بايد براي تمام vertex هاي شي خود بردار نرمال را محاسبه کنيد براي مثال اگر شي شما يک مکعب است براي هر ۱۲ face آن بردار نرمال را بدست آوريد . در زير من کد لازم براي ساخت نرمال يکي از اين face ها را نوشته ام :
Cube2(0) = CreateVertex(-1, -1, 1, 0, 0, 0, 0, 0)x
Cube2(1) = CreateVertex(1, 1, 1, 0, 0, 0, 1, 1)x
Cube2(2) = CreateVertex(-1, 1, 1, 0, 0, 0, 0, 1)x
vN = GenerateTriangleNormals(Cube2(0), Cube2(1), Cube2(2))x
Cube2(0).nx = vN.X: Cube2(0).ny = vN.Y: Cube2(0).nz = vN.Z
Cube2(1).nx = vN.X: Cube2(1).ny = vN.Y: Cube2(1).nz = vN.Z
Cube2(2).nx = vN.X: Cube2(2).ny = vN.Y: Cube2(2).nz = vN.Z
براي برپا سازي نور ابتدا بايستي يک material به device خود اضافه کنيد :
Dim Mtrl As D3DMATERIAL8, Col As D3DCOLORVALUE
Col.a = 1: Col.r = 1: Col.g = 1: Col.b = 1
Mtrl.Ambient = Col
Mtrl.diffuse = Col
D3DDevice.SetMaterial Mtrl
سپس بايستي طوري device خود را تنظيم کنيد که نور شما را بشناسد - lights يک شي از نوع D3DLight8 است - يکبار که اين خط را بنويسيد مي توانيد از نور استفاده کنيد اما اگر خصوصيات نور را تغيير دهيد بايستي دوباره اين دستور را فراخواني کنيد :
D3DDevice.SetLight 0, Lights
حال بايد نور را روشن کنيد :
D3DDevice.LightEnable 0, 1
و در پايان بايد به Direct3D بگوئيد که نورپردازي را براي شما انجام دهد :
D3DDevice.SetRenderState D3DRS_LIGHTING, 1
چگونگي ايجاد يک نور : براي ايجاد هر يک از ۴ نوع اصلي نور بايد به روشي خاص عمل کنيد :
۱ - نورپردازي Ambient : اين نوع نورپردازي بسيار ساده است و تنها با فراخواني تابع SetRenderState ايجاد مي شود . رنگ ambient يک عدد هگزادسيمال بصورت RRGGBB است :
D3DDevice.SetRenderState D3DRS_AMBIENT, &H202020
۲ - نورپردازي Directional : داراي دو پارامتر رنگ و جهت مي باشد :
Lights.Type = D3DLIGHT_DIRECTIONAL
Lights.diffuse.r = 1
Lights.diffuse.g = 1
Lights.diffuse.b = 1
Lights.Direction = MakeVector(0, -1, 0)x
3 - نورپردازي Point : داراي سه پارامتر موقعيت ، رنگ و تضعيف مي باشد :
Lights.Type = D3DLIGHT_POINT
Lights.position = MakeVector(5, 0, 2)x
Lights.diffuse.b = 1
Lights.Range = 100
Lights.Attenuation1 = 0.05
۴ - نورپردازي Spot : اين نور داراي دو مخروط است که نقاط خارج مخروط اول روشنتر از نقاط داخل آن هستند . دو زاويه براي مخروط وجود دارد - زاويه داخلي theta و زاويه خارجي phi - که برحسب راديان هستند :
Lights.Type = D3DLIGHT_SPOT
Lights.position = MakeVector(-4, 0, 0)x
Lights.Range = 100
Lights.Direction = MakeVector(1, 0, 0)x
Lights.Theta = 30 * (Pi / 180)x
Lights.Phi = 50 * (Pi / 180)x
Lights.diffuse.g = 1
Lights.Attenuation1 = 0.05
موضوع : استفاده از Index Buffer براي ذخيره سازي اشکال سه بعدي
مقدمه : مکعبي که در درسهاي قبلي ساختيم را درنظر بگيريد . با دانشي که اکنون داريد ، دو راه براي ساخت يک مکعب داريم : ۱ - استفاده از 36 عدد vertex براي تعريف face هاي مکعب ۲ - ساخت مکعب با استفاده از يک مدلساز و ذخيره آن با فرمت X
روش اول غيرکارامد است زيرا شما بايستي از تعداد زيادي vertex براي يک شکل بسيار ساده استفاده کنيد . روش دوم مناسب است اما زمانيکه بخواهيم رنگها و بافتها را تغيير دهيم دچار مشکل خواهيم شد . روش جديدي که امروز در مورد آن صحبت مي کنم استفاده ار Index Buffer است .
Index Buffer شامل يکسري عدد integer است که اين اعداد مرجعي براي vertex هاي ذخيره شده در يک Vertex Buffer هستند . براي مثال فرض کنيد يک Vertex Buffer شامل 8 عدد vertex داريم که يک مکعب را براي ما توصيف مي کند . ما مي توانيم يک Index Buffer با ۳۶ عضو بسازيم بطوريکه ترتيب اتصال vertex ها را براي ما مشخص کنند . مثلاً Index هاي ۰ و ۱و ۳ براي مشخص کردن face شماره ۱ مکعب بکار مي روند . بنابراين بجاي استفاده از ۳۶ عدد vertex مي توانيم مکعب را با ۸ عدد vertex و يک Index Buffer بسازيم .
گرچه استفاده از Index Buffer بسيار کارامد است اما چندين محدوديت در استفاده از آن وجود دارد . مهمترين آنها اينست که تمام انديسهايي که يک vertex مشابه را share مي کنند بايستي خصوصيات مشابهي داشته باشند - موقعيت ، رنگ ، بافت و نرمال يکسان - براي مثال نمي توانيد مکعبي بسازيد که هر face آن يک رنگ داشته باشد .
ساخت Index Buffer : ابتدا به متغيرهاي زير نياز داريم :
Dim VBuffer as Direct3DVertexBuffer8
Dim IBuffer as Direct3DIndexBuffer8
Dim Vlist(0 to 7) as LITVERTEX
Dim Ilist(0 to 35) as Integer
تابع InitGeometry بصورت زير بازنويسي مي شود:
۱- توليد هشت vertex براي مکعب :
Vlist(0) = CreateLitVertex(-1, -1, -1, &HFF0000, 0, 0, 0)x
Vlist(1) = CreateLitVertex(-1, 1, -1, &HFF00&, 0, 0, 0)x
Vlist(2) = CreateLitVertex(1, -1, -1, &HFF&, 0, 0, 0)x
Vlist(3) = CreateLitVertex(1, 1, -1, &HFF00FF, 0, 0, 0)x
Vlist(4) = CreateLitVertex(-1, -1, 1, &HFFFF00, 0, 0, 0)x
Vlist(5) = CreateLitVertex(-1, 1, 1, &HFFFF, 0, 0, 0)x
Vlist(6) = CreateLitVertex(1, -1, 1, &HFFCC00, 0, 0, 0)x
Vlist(7) = CreateLitVertex(1, 1, 1, &HFFFFFF, 0, 0, 0)x
۲ - ايجاد Vertex Buffer توسط تابع CreateVertexBuffer :
Set VBuffer = D3DDevice.CreateVertexBuffer(Len(Vlist(0)) * 8, 0, Lit_FVF, D3DPOOL_DEFAULT)x
D3DVertexBuffer8SetData VBuffer, 0, Len(Vlist(0)) * 8, 0, Vlist(0)x
۳ - توليد index ها :
front '
Ilist(0) = 0: Ilist(1) = 1: Ilist(2) = 2
Ilist(3) = 1: Ilist(4) = 3: Ilist(5) = 2
Right '
Ilist(6) = 2: Ilist(7) = 3: Ilist(8) = 6
Ilist(9) = 3: Ilist(10) = 7: Ilist(11) = 6
Back '
Ilist(12) = 6: Ilist(13) = 7: Ilist(14) = 4
Ilist(15) = 7: Ilist(16) = 5: Ilist(17) = 4
Left '
Ilist(18) = 4: Ilist(19) = 5: Ilist(20) = 0
Ilist(21) = 5: Ilist(22) = 1: Ilist(23) = 0
Top '
Ilist(24) = 1: Ilist(25) = 5: Ilist(26) = 3
Ilist(27) = 5: Ilist(28) = 7: Ilist(29) = 3
Bottom '
Ilist(30) = 2: Ilist(31) = 6: Ilist(32) = 0
Ilist(33) = 6: Ilist(34) = 4: Ilist(35) = 0
۴ - ايجاد Index Buffer توسط تابع CreateIndexBuffer :
Set IBuffer = D3DDevice.CreateIndexBuffer(Len(Ilist(0)) * 36, 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT)x
D3DIndexBuffer8SetData IBuffer, 0, Len(Ilist(0)) * 36, 0, Ilist(0)x
تابع Render : براي رندر کردن اين مکعب دو روش وجود دارد :
۱ - استفاده از تابع DrawIndexedPrimitive : در اين روش از VBuffer و IBUffer و آرايه vertex ها استفاده مي شود :
Public Sub Render()x
D3DDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET Or D3DCLEAR_ZBUFFER, 0, 1#, 0
D3DDevice.BeginScene
D3DDevice.SetStreamSource 0, VBuffer, Len(Vlist(0))x
D3DDevice.SetIndices IBuffer, 0
D3DDevice.DrawIndexedPrimitive D3DPT_TRIANGLELIST, 0, 36, 0, 12
D3DDevice.EndScene
D3DDevice.Present ByVal 0, ByVal 0, 0, ByVal 0
End Sub
۲ - استفاده از تابع DrawIndexedPrimitiveUP : در اين روش از آرايه هاي vertex و index استفاده مي شود :
Public Sub Render()x
D3DDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET Or D3DCLEAR_ZBUFFER, 0, 1#, 0
D3DDevice.BeginScene
D3DDevice.DrawIndexedPrimitiveUP D3DPT_TRIANGLELIST, 0, 8, 12, Ilist(0), D3DFMT_INDEX16, Vlist(0), Len(Vlist(0))x
D3DDevice.EndScene
D3DDevice.Present ByVal 0, ByVal 0, 0, ByVal 0
End Sub
موضوع : Vertex/Mesh Animation
در اين درس در مورد روشهاي ساخت انيميشن در Direct3D صحبت خواهيم کرد . انيميشن در فضاي سه بعدي در حالتهاي مختلفي مي تواند ايجاد شود که بسته به engine گرافيکي شما و ابزارهايي که ايجاد کرده ايد ، دارد . سه روش اصلي ساخت انيميشن وجود دارد که عبارتند از :
- Tween سازي دستي / درون يابي خطي ( manual tweening/linear interpolation )
- درون بابي برداري ( vector interpolation )
- درون يابي بر اساس فريم کليدي ( keyframe interpolation )
1 – روش اول يکي از ساده ترين راههاي ساخت انيميشن است . اين روش در زمانيکه با مدلهاي پيچيده سر و کار داريد مناسب نيست – و يا مدلهايي با تعداد زيادي vertex – اين روش نوعي tween کردن است که از مزيت index buffer ها استفاده مي کند .
درون يابي ، چگونگي تغييرات شيي در طول يک زمان مشخص مي باشد . در درسهاي قبلي شما درون يابي رنگ را روي يک شي ديديد که در آن يک رنگ بطور ملايم به رنگ ديگري تبديل مي شد ( fadeشدن ( . درون يابي خطي نيز مشابه آن است . براي درون يابي خطي از موقعيت A به موقعيت B از فرمول زير استفاده مي شود :
(B*V)+A*(1-V)
که A و B مختصاتهاي مبدا و مقصد هستند و V ضريب درون يابي است که عددي بين صفر و يک مي باشد . اين فرمول مختصات نقطه tween را در هر لحظه مشخص مي کند .
همانطور که مي بينيد بکار بردن اين فرمول براي يک شي با تعداد زيادي vertex بسيار وقت گير بوده و fram rate را پايين مي آورد .
تابع زير دو vertex و يک مقدار ضريب درون يابي را مي گيرد تا نقطه tween را محاسبه کند :
Private Function TweenVertices(Source As LITVERTEX, Dest As LITVERTEX, TweenAmount As Single) As LITVERTEX
TweenVertices.X = (Dest.X * TweenAmount) + Source.X * (1# - TweenAmount)x
TweenVertices.Y = (Dest.Y * TweenAmount) + Source.Y * (1# - TweenAmount)x
TweenVertices.Z = (Dest.Z * TweenAmount) + Source.Z * (1# - TweenAmount)x
TweenVertices.color = Source.color
End Function
اگر شما از vertex هاي UNLIT استفاده کنيد – vertex هايي با بردار نرمال – در اينصورت بايد کد فوق را تغيير دهيد و بايد tween را از نرمال مبدا به نرمال مقصد نيز انجام دهيد .
همانطور که مي بينيد رنگ tween vertex نيز تنظيم شده است . در يک تابع tweening مناسبتر مي توانيد رنگها ، مختصات بافت و مقادير specular را نيز tween کنيد .
محدوديتي که اين روش دارد اينست که خطي است و براي مدل کردن حرکتهاي غير خطي درست کار نمي کند .
حال مي خواهيم از تابع tween استفاده کنيم تا يک مکعب را در يک انيميشن به يک هرم تبديل کنيم . ابتدا سه شي را بصورت زير تعريف مي کنيم :
در ابتداي انيميشن ، شي current cube همان source cube است’
CubeVertices(0) = CreateLitVertex(-1, -1, -1, &HFF0000, 0, 0, 0)x
CubeVertices(1) = CreateLitVertex(-1, 1, -1, &HFF00&, 0, 0, 0)x
CubeVertices(2) = CreateLitVertex(1, -1, -1, &HFF&, 0, 0, 0)x
CubeVertices(3) = CreateLitVertex(1, 1, -1, &HFF00FF, 0, 0, 0)x
CubeVertices(4) = CreateLitVertex(-1, -1, 1, &HFFFF00, 0, 0, 0)x
CubeVertices(5) = CreateLitVertex(-1, 1, 1, &HFFFF, 0, 0, 0)x
CubeVertices(6) = CreateLitVertex(1, -1, 1, &HFFCC00, 0, 0, 0)x
CubeVertices(7) = CreateLitVertex(1, 1, 1, &HFFFFFF, 0, 0, 0)x
مکعب اوليه’
CubeVerticesSource(0) = CreateLitVertex(-1, -1, -1, &HFF0000, 0, 0, 0)x
CubeVerticesSource(1) = CreateLitVertex(-1, 1, -1, &HFF00&, 0, 0, 0)x
CubeVerticesSource(2) = CreateLitVertex(1, -1, -1, &HFF&, 0, 0, 0)x
CubeVerticesSource(3) = CreateLitVertex(1, 1, -1, &HFF00FF, 0, 0, 0)x
CubeVerticesSource(4) = CreateLitVertex(-1, -1, 1, &HFFFF00, 0, 0, 0)x
CubeVerticesSource(5) = CreateLitVertex(-1, 1, 1, &HFFFF, 0, 0, 0)x
CubeVerticesSource(6) = CreateLitVertex(1, -1, 1, &HFFCC00, 0, 0, 0)x
CubeVerticesSource(7) = CreateLitVertex(1, 1, 1, &HFFFFFF, 0, 0, 0)x
هرم مقصد’
CubeVerticesDest(0) = CreateLitVertex(-1, -1, -1, &HFF0000, 0, 0, 0)x
CubeVerticesDest(1) = CreateLitVertex(-0.1, 1, -0.1, &HFF00&, 0, 0, 0)x
CubeVerticesDest(2) = CreateLitVertex(1, -1, -1, &HFF&, 0, 0, 0)x
CubeVerticesDest(3) = CreateLitVertex(0.1, 1, -0.1, &HFF00FF, 0, 0, 0)x
CubeVerticesDest(4) = CreateLitVertex(-1, -1, 1, &HFFFF00, 0, 0, 0)x
CubeVerticesDest(5) = CreateLitVertex(-0.1, 1, 0.1, &HFFFF, 0, 0, 0)x
CubeVerticesDest(6) = CreateLitVertex(1, -1, 1, &HFFCC00, 0, 0, 0)x
CubeVerticesDest(7) = CreateLitVertex(0.1, 1, 0.1, &HFFFFFF, 0, 0, 0)x
حال بايد در يک حلقه با استفاده از تابع twen پيکسلهاي CubeVertices را update کنيم :
Private Sub UpdateAnimation()x
Dim I As Integer
به روز کردن پارامترهاي زمان و جهت'
If AnimTweenDir = True Then
AnimTweenFactor = AnimTweenFactor + (((GetTickCount() - LastTimeTweened) / 1000)*1#)
LastTimeTweened = GetTickCount
If AnimTweenFactor >= 1# Then
AnimTweenFactor = 1#
AnimTweenDir = False
End If
Else
AnimTweenFactor = AnimTweenFactor - (((GetTickCount() - LastTimeTweened) / 1000)*1#)
LastTimeTweened = GetTickCount
If AnimTweenFactor <= 0# Then
AnimTweenFactor = 0#
AnimTweenDir = True
End If
End If
به روز کردن اطلاعات vertex ها '
For I = 0 To 7
CubeVertices(I) = TweenVertices(CubeVerticesSource(I), CubeVerticesDest(I), AnimTweenFactor)x
Next I
به روز کردن بافر vertex’
If D3DVertexBuffer8SetData(VBuffer, 0, Len(CubeVertices(0)) * 8, 0, CubeVertices(0)) = D3DERR_INVALIDCALL Then GoTo Error:
Exit Sub
Error:
Debug.Print “Error occured whilst updating the animation…”x
End Sub
زمان پايه انيميشن توسط عبارت زير تنظيم مي شود :
(((GetTickCount() - LastTimeTweened) / 1000) * 1#)
همانطور که مي دانيد دو نوع انيميشن وجود دارد : انيميشن بر مبناي frame و انيميشن بر مبناي زمان . در انيميشن بر مبناي frame شماره فريم با يک مقدار ثابت در زمان افزايش مي يابد اما اگر اينکار باعث مي شود کيفيت انيميشن در کامپيوترهاي با سرعت متفاوت تغيير کند . بنابراين انيميشن را بر مبناي زمان توليد کرده ايم . انيميشن هاي بر مبناي زمان بجاي " 1 فريم در هر سيکل " ، " 30 فريم در هر ثانيه " هستند
2 – روش دوم از توابع کتابخانه D3DX براي انجام عمل tweening استفاده مي کند و بنابراين بهبودي در سرعت انيميشن نسبت به روش بالا حاصل مي شود . با استفاده از کتابخانه D3DX مي توانيم عمل درون يابي خطي را براي تمام اجزا اصلي يک vertex انجام دهيم . ليست زير توابعي را براي اينکار نشان مي دهد :
- تابع D3DXVec3Lerp : انجام درون يابي براي موقعيت و نرمال :
D3DXVec3Lerp( VOut as D3DVECTOR, V1 as D3DVECTOR, V2 as D3DVECTOR, S as Single)x
- VOut = The result of the interpolation
- V1 = The source coordinates
- V2 = The destination coordinates
- S = The interpolation amount - between, but not limited to, 0.0 - 1.0 scale; where 0 is the source and 1 is the destination
- تابع D3DXColorLerp : انجام درون يابي براي رنگهاي vertex :
D3DXColorLerp( COut as D3DCOLORVALUE, C1 as D3DCOLORVALUE, C2 as D3DCOLORVALUE, S as Single)x
- COut = The resulting colour
- C1 = The source colour
- C2 = The destination colour
- S = The interpolant,
on a 0.0 to 1.0 scale
- تابع D3DXVec2Lerp : انجام درون يابي براي مختصاتهاي دوبعدي :
- VOut = The result of this interpolation
- V1 = The source coordinates
- V2 = The destination coordinates
- S = The interpolant on a 0.0 to 1.0 scale
- تابع D3DXVec3Hermite : توليد يک مسير منحني که از دو نقطه کنترل عبور مي کند :
D3DXVec3Hermite( VOut as D3DVECTOR, V1 as D3DVECTOR, T1 as D3DVECTOR, V2 as D3DVECTOR, T2 as D3DVECTOR, S as Single)x
- VOut = The Result
- V1 = The Source Coordinate
- T1 = The Tangent at the Source coordinate, this is the direction and speed the line will leave the source point.
- V2 = The Destination Coordinate
- T2 = The Tangent at the Destination coordinate, this is the direction and speed the line will enter the destination point.
- S = The Interpolant Value
براي اينکه بتوانيم از کتابخانه D3DX استفاده کنيم بايد توصيف vertex هايمان را تغيير دهيم و بايستي يکسري مقادير ARGB اضافي را به ساختار vertex اضافه کنيم :
Private Type LITVERTEX
X As Single
Y As Single
Z As Single
color As Long
specular As Long
tu As Single
tv As Single
ColorEx As D3DCOLORVALUE
End Type
حال تابع tween را بصورت زير مي نويسيم :
Private Function TweenVertices(Source As LITVERTEX, Dest As LITVERTEX, TweenAmount As Single) As LITVERTEX
Dim vResult As D3DVECTOR
Dim vResult2 As D3DVECTOR2
Tween کردن موقعيت vertex ها ‘
D3DXVec3Lerp vResult, MakeVector(Source.X, Source.Y, Source.Z), MakeVector(Dest.X, Dest.Y, Dest.Z), TweenAmount
TweenVertices.X = vResult.X
TweenVertices.Y = vResult.Y
TweenVertices.Z = vResult.Z
Tween کردن اطلاعات texture ’
D3DXVec2Lerp vResult2, MakeVector2D(Source.tu, Source.tv), MakeVector2D(Dest.tu, Dest.tv), TweenAmount
TweenVertices.tu = vResult2.X
TweenVertices.tv = vResult2.Y
Tween کردن اطلاعات رنگ ‘
D3DXColorLerp TweenVertices.ColorEx, Source.ColorEx, Dest.ColorEx, TweenAmount
With TweenVertices.ColorEx
TweenVertices.color = RGB(.B * 255, .G * 255, .R * 255)x
End With
End Function
نکته اي که بايد به آن توجه کنيد اينست که در تابع فوق براي اشاره به vertex ، يک بردار ساخته شده است ( توسط تابع MakeVector ) .
3 – روش سوم پر استفاده ترين روش انيميشن سازي است . اگر شما انيميشن هاي پيچيده با تعداد زيادي شي در آن داشته باشيد و اگر بخواهيد تغييرات اشيا را در هر فريم ذخيره کنيد ، به حجم بالايي از منابع ذخيره سازي نياز است . بجاي آن ما با استفاده از يکسري فريم کليدي ، فريمهاي مياني را پيش بيني مي کنيم .
براي انجام درون يابي فريم کليدي ، بايستي مقدار vertex را در هر فريم کليدي بدانيم و نيز بدانيم هر فريم کليدي در چه زماني ظاهر مي شود . بنابراين بايد براي هر انيميشن چند فايل را بعنوان فريم کليدي ذخيره کنيم .
در اين درس ما داده هاي کليدي انيميشن را از يکسري فايل load مي کنيم بنابراين تمام ثابتهاي زمان keyframe درون برنامه قرار داده مي شود ( شما مي توانيد خودتان يک ماژول بنويسيد که انيميشن هاي عمومي تر را نيز مديريت کند . اين ماژول بايد قادر باشد که يک فرمت استاندارد فايل را import کند ، اشيا و texture هاي مربوطه را load نمايد و سپس خودش ساخت انيميشن را بطور اتوماتيک انجام دهد و برنامه اصلي فقط روتين render و يا update را فراخواني کند ) . پس از جمع آوري اطلاعات فريم هاي کليدي ، بايد در هر زمان محاسبه کنيم که چه مدتي از شروع انيميشن گذشته است و بنابراين انيميشن در چه موقعيتي قرار دارد . سپس محاسبه مي کنيم که فريم کليدي قبلي و فريم کليدي بعدي چيست همچنين حساب مي کنيم در چه فاصله زماني از ايندو قرار داريم . سرانجام يک درون يابي نرمال را انجام مي دهيم تا اطلاعات فريم جاري بدست آيد و اين اطلاعات را درون يک شي Mesh مي گذاريم و آنرا رندر مي کنيم .
در درسهاي قبلي در مورد load کردن اشيا از يک فايل X صحبت کردم اما در مورد چگونگي گرفتن اطلاعات vertex از يک شي Mesh صحبت نشد . کتابخانه D3DX براي اينکار دو تابع دارد :
- تابع D3DXMeshVertexBuffer8GetData : اطلاعات يک شي D3DXMesh را گرفته و در يک آرايه از D3DVERTEX ذخيره مي کند :
D3DXMeshVertexBuffer8GetData( D3DXMeshobj As Unknown, Offset As Long, Size As Long, Flags As Long, Data As Any) As Long
- D3DXMeshobj As Unknown = A D3DXMESH object that you want to extract the data from.
- Offset As Long = How far into the vertex buffer we want to start reading, 0 is the beginning
- Size As Long = Size of the vertex buffer, this will be Len(D3DVERTEX) * Mesh.GetNumVertices
- Flags As Long = A combination of the CONST_D3DLOCKFLAGS, leave as 0.
- Data As Any = The first element in the array that you want the data to be read into, should be an array of D3DVERTEX vertices
- Return Code As Long = Returns D3D_OK for success, or either of D3DERR_INVALIDCALL or E_INVALIDARG for an error
- تابع D3DXMeshVertexBuffer8SetData : اطلاعات يک بافر vertex را در يک شي D3DXMesh قرار مي دهد :
D3DXMeshVertexBuffer8SetData( D3DXMeshobj As Unknown, Offset As Long, Size As Long, Flags As Long, Data As Any) As Long
- D3DXMeshobj As Unknown = The D3DXMESH object that defines where you want the data to be placed
- Offset As Long = How far into the Destination vertex buffer you want to place the data
- Size As Long = The Size of the buffer in bytes, this will be Len(D3DVERTEX) * Mesh.GetNumVertices
- Flags As Long = A Combination of the CONST_D3DLOCKFLAGS, leave as 0
- Data As Any = The first element in the array of data you want placed in the mesh's vertex buffer
- Return Code As Long = D3D_OK for success or D3DERR_INVALIDCALL or E_INVALIDARG for failure
عمليات انجام انيميشن فريم کليدي بصورت زير است :
- load کردن اشيا از فايلهاي X به درون شي D3DXMesh
- استخراج اطلاعات vertex از اين شي
- انجام درون يابي بين فريمهاي کليدي
- قرار دادن اطلاعات vertex هاي درون يابي در يک شي D3DXMesh
فرض مي کنيم که انيميشن ما هميشه از زمان صفر تا زمان n باشد – برحيب ميلي ثانيه – بنابراين مي توانيم از GetTickCount براي توابع زماني خود استفاده کنيم . همچنين يک ساختار را براي هر فريم کليدي بصورت زير تعريف مي کنيم :
Private Type KeyFrame
شي load شده از يک فايل’ Mesh As D3DXMesh
آرايه material براي هر شي’ MatList() As D3DMATERIAL8
آرايه Texture’ TexList() As Direct3DTexture8
تعداد material ها و texture هايي که استفاده مي کنيم’ nMaterials As Long
داده هاي vertex براي اين فريم کليدي’ VertexList() As D3DVERTEX
موقعيت اين فريم کليدي در انيميشن’ TimeIndex As Long
End Type
حال بايد تابعي بنويسيم که اطلاعات را از يک فايل X استخراج کرده و درون فريم کليدي قرار دهد :
Private Function CreateKeyFrameFromFile(Filename As String, TexturePrefix As String, Time As Long) As KeyFrame
نام فايل X براي شي سه بعدي: Filename ’
پوشه اي که اطلاعات texture اين شي در آن قرار دارد : TexturePrefix ’
انديس زمان براي اين فريم کليدي : Time '
Dim I As Long
Dim XBuffer As D3DXBuffer
Dim TextureFile As String
Dim hResult As Long
'خواندن اطلاعات از فايل ورودي به حافظه
Set CreateKeyFrameFromFile.Mesh = D3DX.LoadMeshFromX(Filename, D3DXMESH_MANAGED, D3DDevice, Nothing, XBuffer, CreateKeyFrameFromFile.nMaterials)x
توليد material ها و texture ها ‘
ReDim CreateKeyFrameFromFile.MatList(CreateKeyFrameFromFile.nMaterials) As D3DMATERIAL8
ReDim CreateKeyFrameFromFile.TexList(CreateKeyFrameFromFile.nMaterials) As Direct3DTexture8
For I = 0 To CreateKeyFrameFromFile.nMaterials - 1
D3DX.BufferGetMaterial XBuffer, I, CreateKeyFrameFromFile.MatList(I)x
CreateKeyFrameFromFile.MatList(I).Ambient = CreateKeyFrameFromFile.MatList (I).diffuse
TextureFile = D3DX.BufferGetTextureName(XBuffer, I)x
If TextureFile <> "" Then
Set CreateKeyFrameFromFile.TexList(I) = D3DX.CreateTextureFromFileEx(D3DDevice, TexturePrefix & TextureFile, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED,
D3DX_FILTER_LINEAR, D3DX_FILTER_LINEAR, 0, ByVal 0, ByVal 0)x
End If
Next I
استخراج داده هاي vertex’
ReDim CreateKeyFrameFromFile.VertexList(CreateKeyFrameFromFile.Mesh.GetNumVertices) As D3DVERTEX
hResult = D3DXMeshVertexBuffer8GetData(CreateKeyFrameFromFile.Mesh, 0, Len(CreateKeyFrameFromFile.VertexList(0)) * reateKeyFrameFromFile.Mesh.GetNumVertices, 0, CreateKeyFrameFromFile.VertexList(0))
CreateKeyFrameFromFile.TimeIndex = Time
End Function
در تابع Initialize خطوط زير را براي ساخت فريم هاي کليدي اضافه مي کنيم :
nKeyFrames = 4
kfAnimLength = 2500
AnimLastStartAt = GetTickCount()x
ReDim kfAnim(nKeyFrames - 1) As KeyFrame
kfAnim(0) = CreateKeyFrameFromFile(App.Path & "\frame0.x", App.Path & "\", 0)x
kfAnim(1) = CreateKeyFrameFromFile(App.Path & "\frame1.x", App.Path & "\", kfAnimLength * (1 / 3))x
kfAnim(2) = CreateKeyFrameFromFile(App.Path & "\frame2.x", App.Path & "\", kfAnimLength * (2 / 3))x
kfAnim(3) = CreateKeyFrameFromFile(App.Path & "\frame3.x", App.Path & "\", kfAnimLength)x
kfCurrent = CreateKeyFrameFromFile(App.Path & "\frame0.x", App.Path & "\", 0)
دقت کنيد که از يک انديس زمان براي ساخت فريم هاي کليدي استفاده شده است .
حال بايد کدي براي نمايش دادن انيميشن بنويسيم . ابتدا بايد به روشي تغييرات فريمها را کنترل کنيم :
For I = 0 To nKeyFrames - 2
If CurrentTimeIndex >= kfAnim(I).TimeIndex Then
PrevFrame = I
NextFrame = I + 1
End If
Next I
سپس بايد با توجه به زمان index دو فريم کليدي و زمان جاري ، پارامتر درون يابي را محاسبه کنيم :
sTime = kfAnim(PrevFrame).TimeIndex
eTime = kfAnim(NextFrame).TimeIndex
cTime = CurrentTimeIndex
eTime = eTime - sTime
cTime = cTime - sTime
sTime = sTime - sTime
InterpolateAmount = cTime / eTime
سپس بايد بر اساس اين پارامتر عمل درون يابي را روي داده هاي vertex انجام دهيم :
For I = 0 To kfCurrent.Mesh.GetNumVertices
'درون يابي مختصاتها
D3DXVec3Lerp vTemp3D, MakeVector(kfAnim(PrevFrame).VertexList(I).X, kfAnim(PrevFrame).VertexList(I).Y, _
kfAnim(PrevFrame).VertexList(I).Z), MakeVector(kfAnim(NextFrame).VertexList(I).X, kfAnim(NextFrame).VertexList(I).Y, _
kfAnim(NextFrame).VertexList(I).Z), InterpolateAmount
kfCurrent.VertexList(I).X = vTemp3D.X
kfCurrent.VertexList(I).Y = vTemp3D.Y
kfCurrent.VertexList(I).Z = vTemp3D.Z
'درون يابي نرمالها
D3DXVec3Lerp vTemp3D, MakeVector(kfAnim(PrevFrame).VertexList(I).nx, kfAnim(PrevFrame).VertexList(I).ny, _
kfAnim(PrevFrame).VertexList(I).nz), MakeVector(kfAnim(NextFrame).VertexList(I).nx, kfAnim(NextFrame).VertexList(I).ny, _
kfAnim(NextFrame).VertexList(I).nz), InterpolateAmount
kfCurrent.VertexList(I).nx = vTemp3D.X
kfCurrent.VertexList(I).ny = vTemp3D.Y
kfCurrent.VertexList(I).nz = vTemp3D.Z
'درون يابي اطلاعات بافت
D3DXVec2Lerp vTemp2D, MakeVector2D(kfAnim(PrevFrame).VertexList(I).tu, kfAnim(PrevFrame).VertexList(I).tv), _
MakeVector2D(kfAnim(NextFrame).VertexList(I).tu, kfAnim(NextFrame).VertexList(I).tv), InterpolateAmount
kfCurrent.VertexList(I).tu = vTemp2D.X
kfCurrent.VertexList(I).tv = vTemp2D.Y
Next I
حال بايد داده توليد شده را به فرمت Mesh برگردانيم :
hResult = D3DXMeshVertexBuffer8SetData(kfCurrent.Mesh, 0, Len(kfCurrent.VertexList(0)) * kfCurrent.Mesh.GetNumVertices, 0, kfCurrent.VertexList(0))x
با استفاده از روش فوق مي توانيد هر تعداد فريم کليدي را به انيميشنتان اضافه کنيد . اشکالي که روش فوق دارد اينست که اطلاعات texture براي تمام فريمهاي کليدي جداگانه ذخيره شده است در حاليکه texture در تمام فريمها ثابت است . در درسهاي بعدي از روشي بنام texture pooling استفاده مي کنيم تا تنها يک کپي از texture ها نگهداري کنيم
مقدمه :
کنترل WinSock نسبت به تمام کنترلهاي اينترنت در سطح پايينتري قرار دارد . اين کنترل امکان ايجاد سرويسهاي شبکه اي مبتني بر پروتکلهاي TCP و UDP را مهيا مي کند . بعبارت ديگر توسط اين کنترل مي توان برنامه هاي کاربردي Client/Server ( سرويس گيرنده / سرويس دهنده ) ايجاد و با استفاده از پروتکل TCP و يا UDP بين آنها ارتباط برقرار نمود .
با تنظيم خصوصيات و فراخواني متدهاي اين کنترل مي توانيد به راحتي به يک کامپيوتر راه دور متصل شويد و داده ها را در هر دو جهت جابجا نمائيد . نمونه کاربرهايي که مي توان با اين کنترل ايجاد نمود :
Client-server chat ، Mail client ، Mail server ، Proxy Server ، Network Game ، Port Scanner ، پياده سازي الگوريتم هاي موازي و …
مباني TCP :
پروتکل کنترل اينترنت ( Transfer Control Protocol ) اجازه مي دهد يک اتصال ( Connection ) را از طريق سوکت ( socket ) به يک کامپيوتر راه دور ( Remote Computer ) ساخته و استفاده کنيد . با استفاده از اين اتصال ، هر دو کامپيوتر مي توانند داده ها را بين خودشان انتقال دهند . برقراري ارتباط از طريق TCP همانند صحبت کردن با تلفن است که بايد حتماً اتصالي بين دو کامپيوتر صورت گيرد تا بتوانند با هم ارتباط برقرار کنند .
اگر يک برنامه Client مي سازيد بايستي بدانيد که نام يا آدرس IP کامپيوتر Server چيست ( Remote Host IP ) و همچنين از طريق چه پورتي مي توانيد به آن متصل شويد ( Remote Port ) . حال بايستي به آن پورت Connect کنيد .
همچنين اگر يک برنامه Server مي سازيد بايستي پورتي را که روي آن به درخواستها گوش مي دهيد مشخص کنيد ( LocalPort ) و سپس به پورت گوش دهيد ( Listen ) .
زمانيکه يک کامپيوتر Client تقاضاي يک اتصال را مي دهد Server اين درخواست را Accept مي کند .
زمانيکه يک اتصال ساخته مي شود ، هر دو کامپيوتر مي توانند داده را فرستاده و دريافت کنند .
مباني UDP :
پروتکل ديتاگرام کاربر ( User Datagram Protocol ) پروتکلي بدون اتصال ( Connectionless ) است . برخلاف TCP ، کامپيوترها نياز به برپا کردن يک اتصال ندارند بنابراين يک برنامه مي تواند يک client و يا يک server باشد . برقراري ارتباط در UDP شبيه ارسال نامه از طريق پست است .
براي انتقال داده توسط UDP ابتدا بايد Local Port کامپيوتر Client تنظيم گردد . کامپيوتر Server تنها بايستي RemoteHost را برابر آدرس کامپيوتر Client قرار دهد و همچنين Remote Port را همان Local Port کامپيوتر Client قرار دهد . سپس دو کامپيوتر مي توانند داده ها را بين خود جابجا کنند .
استفاده از کنترل WinSock :
1 – انتخاب پروتکل: در زمان استفاده از کنترل WinSock اولين کاري که بايد انجام دهيد انتخاب يکي از پروتکلهاي TCP يا UDP است . طبيعت برنامه اي که شما مي سازيد نوع پروتکلي را که بايد استفاده کنيد مشخص مي کند . چند سوال زير به شما کمک مي کند که پروتکل مورد نيازتان را انتخاب کنيد :
- آيا برنامه شما در زمانيکه داده فرستاده مي شود يا دريافت مي شود نياز به اطلاعاتي از طرف Server يا Client دارد ؟ اگر چنين است بايستي يک اتصال TCP قبل از ارسال يا دريافت داده ايجاد شود .
- آيا داده بسيار بزرگ است ( مثل تصوير يا فايلهاي صوتي ) ؟ زمانيکه يک اتصال TCP ساخته مي شود پروتکل TCP اتصال را باقي نگه مي دارد و درستي ارسال داده تضمين شده است . اين اتصال در هر حال به منابع محاسباتي بيشتري نياز دارد و بنابراين پرهزينه تر است .
- آيا داده متناوب ارسال مي شود يا در يک نشست ( Session ) ارسال خواهد شد ؟ براي مثال اگر شما يک برنامه مي سازيد که کامپترهاي مشخصي را در يک زمان خاص از انجام شدن عملياتي مطلع مي کند پروتکل UDP مناسب تر است . پروتکل UDP همچنين براي ارسال مقادير کوچک داده اي مناست تر مي باشد .
2 – تنظيم پروتکل : براي تنظيم پروتکلي که مي خواهيد در برنامه تان از آن استفاده کنيد در زمان طراحي برنامه خاصيت Protocol کنترل WinSock را برابر sckTCPProtocol و يا sckUDPProtocol قرار دهيد . همچنين مي توانيد پروتکل خود را توسط کد زير تنظيم کنيد :
WinSock.Protocol=sckTCPProtocol
3 – مشخص کردن نام کامپيوتان : براي اتصال به کامپيوتر راه دور بايستي آدرس IP و يا نام کامپوتر را بدانيد .
نام کامپيوتر در Control Panel/Network/Identification موجود است . در صورتيکه مي خواهيد دو برنامه Client و Server خود را روي يک کامپيوتر تست کنيد از آدرس IP 127.0.0.1 براي هر دو استفاده کنيد اما اگر دو برنامه را روي دو کامپيوتر مجزا در شبکه قرار داده ايد با اجراي دستور ipconfig در DOS Prompt مي توانيد آدرس IP کامپيوتر ها را بدست آوريد .
4 – ايجاد اتصال TCP : در زمان ساخت برنامه اي که از پروتکل TCP استفاده مي کند ابتدا بايد تصميم بگيريد که اين برنامه Client است يا Server . براي ساخت يک برنامه Server بايستي روي يک پورت خاص Listen کنيد . زمانيکه Client تقاضاي يک اتصال را مي دهد ، برنامه Server مي تواند آنرا Accept کند و بنابراين اتصال کامل شده است . حال Client و Server مي توانند با هم ارتباط داشته باشند .
مراحل زير ساخت يک سرور چت ساده بر مبناي TCP را نشان مي دهد :
- از منوي Project گزينه Components را انتخاب کنيد و در ليست Component ها مورد Microsoft WinSock 6.0 را انتخاب کنيد .
- يک کنترل WinSock در فرم خود قرار دهيد و نام آنرا tcpserver بگذاريد
- دو textbox با نامهاي txtSendData و txtReceiveData و نيز يک دکمه در فرم قرار دهيد .
- کد زير را در رويداد Form_Load بنويسيد :
Tcpserver.LocalPort=1000
tcpserver.Listen
- زمانيکه درخواستي از طرف Client مي آيد رويداد ConnectionRequest اجرا مي شود . در اين رويداد ابتدا بايد چک کنيد که حالت کنترل بسته باشد . اگر چنين نيست اتصال را قبل از پذيرفتن اتصال جديد ببنديد . سپس تقاضا را بر اساس پارامتر requestID مي پذيريم :
Private Sub tcpserver_ConnectionRequest(ByVal requestID As Long)
If tcpserver.State <> sckClosed Then tcpserver.Close
tcpserver.Accept requestID
End Sub
- حال اتصال بين Client و Server برقرار شده است . کد زير را براي event مربوط به کليک دکمه Send بنويسيد :
Tcpserver.SendData txtSendData.text
- اگر داده اي از طرف Client بيايد رويداد DataArrival اجرا مي شود . کد زير را براي اين رويداد بنويسيد :
Private Sub tcpserver_DataArrival(ByVal bytesTotal As Long)
Dim strData As String
tcpserver.GetData strData
txtReceiveData.Text = strData
End Sub
- کد زير را براي رويداد Form_Unload بنويسيد :
Tcpserver.Close
مراحل ساخت يک TCP Client بصورت زير است :
- يک کنترل WinSock در فرم قرار دهيد و نام آنرا tcpclient بگذاريد .
- دو textbox با نامهاي txtsend و txtreceive و نيز يک دکمه با نام sendدر فرم قرار دهيد .
- يک دکمه با نام connect در فرم قرار دهيد .
- کد زير را براي متد Form_Load بنويسيد :
tcpclient.RemoteHost=”yourservername”x
tcpclient.RemotePort=1000
- کد زير را براي رويداد کليک شدن دکمه connect بنويسيد :
tcpclient.Connect
- کد زير را براي رويداد کليک شدن دکمه send بنويسيد :
tctclient.SendData txtsend.Text
- کد زير را براي رويداد DataArrival بنويسيد :
Private Sub tcpclient_DataArrival(ByVal bytesTotal As Long)
Dim strData As String
tcpclient.GetData strData
txtreceive.Text = strData
End Sub
- کد زير را باري رويداد Form_Unload بنويسيد :
Tcpclient.Close
کدهاي فوق يک سيستم Client-Server ساده را نشان مي دهد . فايل exe هر دو برنامه را بسازيد و آنها را اجرا کنيد تا بتوانيد سيستم خود را تست کنيد .
5 – پذيرفتن بيش از يک تقاضاي اتصال : Server اي که در بالا ساخته شد تنها مي تواند تقاضاي يک اتصال را بپذيرد . با استفاده از ايجاد يک آرايه از کنترل WinSock مي توان چندين تقاضاي اتصال را پذيرفت . براي اينکار کافي است يک کپي ( instance ) از کنترل بسازيم ( با تنظيم خاصيت Index ) و متد Accept را براي instance جديد بکار ببريم . فرض کنيد يک کنترل WinSock با نام sckServer در فرم داريم که خاصيت Index آنرا صفر قرار داده ايم . همچنين يک متغير intMax از نوع Long تعريف مي کنيم که تعداد اتصالات همزمان به Server را نگه مي دارد . در event مربوط به Form_Load کد زير را بنويسيد :
intMax=0
sckServer(0).LocalPort=1000
sckServer(0).Listen
هر بار که تقاضاي يک اتصال مي رسد کد ابتدا تست مي کند که مقدار Index چقدر است . اگر مقدار Index صفر باشد متغير intMax يکي افزايش مي يابد و از intMax براي ساخت يک instance جديد از کنترل استفاده مي شود . حال از اين instance براي پذيرفتن تقاضاي اتصال استفاده مي گردد . براي اينکار کد زير را براي رويداد ConnectionRequest بنويسيد :
Private Sub sckServer_ConnectionRequest(Index As Integer, ByVal requestID As Long)
If Index = 0 Then
intmax = intmax + 1
Load sckServer(intmax)x
sckServer(intmax).LocalPort = 0
sckServer(Index).Accept requestID
End If
End Sub
6 – ايجاد اتصال UDP : ساخت يک برنامه UDP ساده تر از برنامه هاي TCP است زيرا پروتکل UDP به اتصال نياز ندارد . در برنامه TCP بالا يک کنترل WinSock بايستي حتماً Listen مي کرد و يک کنترل ديگر يک اتصال را توسط متد Connect ايجاد نمود . در عوض پروتکل UDP نيازي به اتصال ندارد . براي ارسال داده بين دو کنترل WinSock سه مرحله بايستي انجام شود :
- پارامتر RemoteHost برابر نام کامپيوتر مقابل است .
- پارامتر RemotePort برابر پارامتر LocalPort کامپيوتر مقابل
- استفاده از متد Bind براي مشخص کردن LocalPort
چون هر دو کامپيوتر از نظر ارتباط مساوي هستند ، اين نوع برنامه ها را Peer-to-Peer گويند . براي نمونه از کد زير براي ساخت يک برنامه chat استفاده مي کنيم :
- يک کنترل WinSock در فرم قرار دهيد و نام آنرا udppeerA بگذاريد .
- خاصيت Protocol آنرا UDPProtocol قرار دهيد .
- دو textbox با نامهاي txtsend و txtreceive و نيز يک دکمه در فرم قرار دهيد .
- کد زير را براي متد Form_Load بنويسيد :
udppeerA.RemoteHost=”nameofpeerB”x
udppeerA.RemotePort=1001
udppeerA.Bind 1002
- کد زير را براي event مربوط به کليک دکمه بنويسيد :
udppeerA.SendData txtsend.text
- کد زير را براي رويداد DataArrival بنويسيد :
Dim strData as String
udppeerA.GetData strData
txtreceive.Text=strData
براي ساخت UDP peerB مشابه مراحل بالا عمل کنيد فقط خاصيت RemoteHost آنرا نام کامپيوتر PeerA و خاصيت RemotePort آنرا 1002 و خاصيت Bind آنرا 1001 قرار دهيد .
بررسی خواص کنترل WinSock :
ByteReceived : مقدار داده دريافت شده ( موجود در بافر receive ) را نشان مي دهد . توسط متد GetData مي توان اين داده را دريافت نمود .
LocalHostName : نام ماشين محلي را نشان مي دهد . اين پارامتر فقط خواندني است .
LocalIP : آدرس IP ماشين محلي را بصورت يک string برمي گرداند . اين پارامتر فقط خواندني است .
LocalPort : براي خواندن و يا تنظيم شماره پورت محلي بکار مي رود .
Protocol : براي خواندن و يا تنظيم پروتوکل مورد استفاده توسط کنترل WinSock بکار مي رود .
RemoteHost : براي خواندن و يا تنظيم نام يا آدرس IP ماشين راه دور بکار مي رود .
RemoteHostIP : آدرس IP ماشين راه دور را برمي گرداند :
۱- براي برنامه هاي Client بعد از زمانيکه يک اتصال توسط متد Connect پذيرفته شد ، اين خاصيت حاوي آدرس IP ماشين راه دور است .
۲ - براي برنامه Server ، بعد از آمدن يک Connection Request اين خاصيت شامل آدرس IP ماشين راه دور است .
۳ - در زمان استفاده از پروتکل UDP بعد از اينکه رويداد Data Arrival رخ داد اين خاصيت حاوي آدرس IP ماشيني است که داده را فرستاده .
RemotePort : براي خواندن و يا تنظيم شماره پورت ماشين راه دوري که مي خواهيد به آن متصل شويد بکار مي رود .
SocketHandle : مقداري را برمي گرداند که مرتبط با سوکتي است که کنترل WinSock را مديريت مي کند و براي ارتباط با لايه WinSock بکار مي رود . اين پارامتر فقط خواندني است و تنها براي ارسال به API هاي WinSock طراحي شده است .
State : وضعيت کنترل WinSock را نشان مي دهد . وضعيتهاي ممکن براي State عبارتند از :
۱ - sckClosed : اتصال بسته است .
۲ - sckOpen : اتصال باز است .
۳ - sckListening : حالت گوش دادن به پورت
4 - sckConnectionPending : معلق شدن اتصال
۵ - sckResolvingHost : تصميم گيري در مورد ميزبان
۶ - sckHostResolved : در مورد ميزبان تصميم گيري شد .
۷ - sckConnecting : حالت برقراري ارتباط
۸ - sckConnected : ارتباط برقرار شد .
۹ - sckClosing : حالت قطع اتصال
۱۰ - sckError : حالت خطا
بررسی متدهای کنترل WinSock :
متد Accept : تنها براي برنامه هاي TCP Server بکار مي رود . اين متد براي پذيرفتن يک اتصال در زمان مديريت رويداد ConnectionRequest استفاده مي شود .
متد Bind : اين پارامتر LocalPort و LocalIP يک اتصال را مشخص مي کند .
متد Close : براي بستن يک اتصال TCP و يا بستن يک listening socket بکار مي رود .
متد GetData : بلوک جاري داده دريافت شده را گرفته و آنرا در متغيري از نوع Variant ذخيره مي کند . شکل کلي اين متد بصورت زير است :
WinSock.GetData data[,type][,maxlen]x
که data داده دريافتي است . اگر داده کافي موجود نباشد data برابر empty خواهد بود .
type نوع داده دريافتي است که مي تواند مقادير زير باشد :
vbByte - vbInteger - vbLong - vbSingle - vbDouble - vbDate - vbBoolean - vbError - vbString - vbArray+vbByte
maxlen حداکثر سايز را در زمان دريافت يک byte Array و يا يک string مشخص مي کند .
متد Getdata در رويداد Data Arrival استفاده مي شود که اين رويداد يک پارامتر با نام TotalBytes دارد . اگر maxlen اي که شما تعيين کرده ايد کمتر از TotalBytes باشد پيغام هشدار شماره ۱۰۰۴۰ دريافت مي کنيد بدين معني که بايتهاي باقيمانده گم خواهند شد .
متد Listen : يک سوکت مي سازد و آنرا در حالت Listen قرار مي دهد . اين متد تنها در اتصالات TCP بکار ميرود .
متد PeekData : مشابه GetData است با اين تفاوت که داده را از صف ورودي حذف نمي کند . اين متد تنها براي اتصالات TCP بکار مي رود .
متد SendData : براي ارسال داده به کامپيوتر راه دور بکار مي رود .
بررسي event هاي کنترل WinSock :
رويداد Close : زماني رخ مي دهد که کامپيوتر راه دور اتصال را ببندد .
رويداد Connect : بعد از اينکه يک اتصال به Server ايجاد شد روي مي دهد . شکل کلي آن بصورت زير است :
Private Sub WinSock_Connect(ErrorOccurred As Boolean)x
که پارامتر ErrorOccurred دو مقدار دارد : اگر True باشد يعني اتصال Fail شده است و اگر False باشد يعني اتصال با موفقيت انجام شده است .
با رويداد Connect مي توانيد error هايي که در زمان فرايند باز کردن اتصال برگردانده شده را چک کنيد .
رويداد ConnectionRequest : زماني رخ مي دهد که يک کامپيوتر راه دور تقاضاي يک اتصال را بدهد . اين رويداد فقط براي برنامه هاي TCP Server بکار مي رود .
رويداد DataArrival : زماني رخ مي دهد که داده جديدي بيايد .
رويداد Error : زماني رخ مي دهد که يک خطا در فرايند ارتباط رخ دهد ( مثلاً Failed to Connect و يا Failed to Send ) . شکل کلي آن بصورت زير است :
Private WinSock_Error(number as Integer,description as String,scode as Long,source as String,helpfile as String,helpcontext as Long,canceldisplay as Boolean)x
number شماره کد خطا است .
description توضيحي در مورد خطا است .
source توصيف منبع خطا
canceldisplay : مشخص مي کند آيا پيغام خطاي پيش فرض نشان داده شود يا نه
رويداد SendComplete : زماني رخ مي دهد که يک عمل Send تکميل شده باشد .
رويداد SendProgress : زماني رخ مي دهد که کنترل شروع به ارسال داده نمايد . شکل کلي آن بصورت زير است :
WinSock_SendProgress (bytesSent As Long, bytesRemaining As Long)x
که bytesSent تعداد بايتهاي ارسال شده و bytesRemaining تعداد بايتهاي باقيمانده است .
نکته ۱ : براي دريافت جدول خطاهاي WinSock با من تماس بگيريد .
نکته ۲ : موضوع بعدي : آشنايي با الگوريتم Collision Detection در ساخت انيميشن هاي دوبعدي
موضوعات مرتبط: آموزش ، ،
برچسبها:
یه سوال عجیب؟
چطور ميشه کنترلي نوشت که اگه چند تا از انها رو در فرم گذاشتیم بتونن همديگرو پيدا کنن مثله Raido Button
Dim c As Control
For Each c In UserControl.Parent.Controls
If TypeOf c Is UserControl1 Then
MsgBox c.Name
' Put your code here
End If
Next
آشنايي با RAS API و WinInet API – کامل
مقدمه
ويندوز برای برقراری ارتباط با Internet Service Provide- ISP- شما از طريق مودم و خط تلفن در اتصالات dial-up networking ، از سرويسی خاص به اسم RAS (Remote Access Service) استفاده می کند . اين سرويس دارای يک واسط برنامه نويسی است که RAS API نام دارد . اين واسط شامل مجموعه ای از توابع است که شما می توانيد آنها را در برنامه خود صدا بزنيد . RAS API ابزاری بسيار قدرتمند و قابل انعطاف است همچنين بسيار پيچيده می باشد .
خوشبختانه برای استفاده راحتتر ، مايکروسافت تعدادی تابع را در مجموعه ای به اسم WinInet API قرار داده تا بتوان از آنها برای برقراری ارتباط و کنترل اتصال استفاده کرد .
آشنايي با WinInet API :
WinInet API مجموعه ای از توابع است که امکان ايجاد و توسعه برنامه های اينترنتی را بصورتی ساده ، سريع و کارآمد برای برنامه نويسان مهيا می کند . با استفاده از اين مجموعه توابع شما می توانيد برنامه هايي بنويسيد که از منابع اينترنتی با استفاده از پروتکلهايي چون HTTP و FTP استفاده کنند . همچنين WinInet به شما اجازه می دهد تا بتوانيد ارتباطی dial-up با يک ISP ايجاد نموده و آنرا کنترل کنيد .
مزيت اصلی توابع WinInet آينست که شما نيازی به دانستن ساختار پروتکلهای ارتباطی و نيز برنامه نويسی Socket نخواهيد داشت . بعبارت ديگر WinInet يک واسط سطح بالا را برای کار با منابع اينترنتی ارائه می دهد .
امکانات Dial-Up موجود در WinInet :
تا قبل از ارائه اينترنت اکسپلورر ورژن 4 ، WinInet تنها دارای دو تابع dial-up بود :
تابع InternetAttemptConnect : برای بررسی اينکه آيا يک ارتباط به اينترنت وجود دارد يا نه استفاده می شد . اگر هيچ اتصالی به اينترنت وجود نداشت اين برنامه کادر تبادلی dial-up networking را نمايش می داد و کاربر اجازه داشت تا يک اتصال را برای وصل شدن به اينترنت انتخاب کند .
تابع InternetCheckConnection : تابع با استفاده از انجام يک دستور ping به url ای که به تابع داده شده ، بررسی می کرد که آيا ارتباطی به اينترنت وجود دارد يا نه .
اين دو تابع دارای محدوديتهای فراوانی بودند . برای مثال تابع اول نمی تواند بطور اتوماتيک اتصال به اينترنت را برقرار کند و تابع دوم نيز نمی تواند هيچ اطلاعاتی در مورد نوع ارتباط به ما بدهد .
IE نسخه 4 ، تعدادی تابع جديد برای WinInet معرفی کرد که برخی از آنها عبارتند از :
تابع InternetGetConnectedState : اطلاعاتی در مورد نوع ارتباط استفاده شده را بيان می کند . برای مثال اين تابع اطلاع می دهد که نوع ارتباط به اينترنت از طريق مودم است يا شبکه LAN و يا از طريق پروکسی .
تابع InternetAutodial : اين امکان را فراهم می سازد تا يک ارتباط اينترنتی اتوماتيک از طريق مودم را با استفاده از مدخل اتصال پيش فرض که کاربر آنرا در dial-up networking مشخص کرده ايجاد کنيد .
تابع InternetDial : اين تابع کارآمدتر از تابع InternetAutodial است و کادری را نمايش می دهد که کاربر می تواند نوع مدخل مورد نظر خود برای ارتباط تلفنی با اينترنت را انتخاب کند
تابع InternetAutodialHangup : برای قطع کردن اتصالی مودمی که از طريق تابع InternetAutodial برقرار شده استفاده می شود
تابع InternetHangUp : برای قطع کردن اتصالی مودمی که از طريق تابع InternetDialبرقرار شده استفاده می شود
تابع InternetSetDialState : برای تنظيم کردن وضعيت جاری ارتباط اينترنتی استفاده می شود
در قسمت بعدی اين سلسه مباحث جزئيات اين توابع را بررسی کرده و نهايتاً برنامه ای کاربردی برای کار با اين توابع در ويژوال بيسيک ارائه خواهم داد .
اطلاعات بيشتری در مورد WinInet :
در اين بخش ما تنها توابع dial-up موجود در WinInet API را بررسی کرديم اما همانطور که در ابتدا گفته شد WinInet دارای امکانات فراوانی در زمينه کار با اينترنت است . برای آشنايي بيشتر با اين امکانات در زير جداولی ارائه شده که به اختصار امکانات مختلف اين مجموعه تابع را نشان می دهد :
توابع Dial-Up :
Name Description
InternetGetConnectedState
Retrieves the current state of the Internet connection
InternetAutodial
Initiates an unattended dial-up connection
InternetAutodialHangup
Disconnects a modem connection initiated by
InternetDial
Initiates a dial-up connection
InternetHangUp
Disconnects a modem connection initiated by InternetDial
InternetGoOnline
Prompts the user for permission to initiate a dial-up connection to the given URL
InternetSetDialState
Sets the current state of the Internet connection
توابع عمومی اينترنت :
Name Description
InternetOpen
Initializes the Win32 Internet functions
InternetConnect
Opens an FTP, Gopher, or HTTP session for a given site
InternetCloseHandle
Closes a single Internet handle or a subtree of Internet handles
InternetErrorDlg
Displays a dialog box for the error that is passed to InternetErrorDlg
InternetFindNextFile
Continues a file search started as a result of a previous call to FtpFindFirstFile or GopherFindFirstFile
InternetGetLastResponseInfo
Retrieves the last Win32 Internet function error description or server response on the thread calling this function
InternetLockRequestFile
Allows the user to place a lock on the file being used
InternetQueryDataAvailable
Queries the amount of data available
InternetQueryOption
Queries an Internet option on the specified handle
InternetReadFile
Reads data from a handle opened by the InternetOpenURL, FtpOpenFile, GopherOpenFile, or HttpOpenRequest function
InternetReadFileEx
Reads data from a handle opened by the InternetOpenURL, FtpOpenFile, GopherOpenFile, or HttpOpenRequest function
InternetSetFilePointer
Sets a file position for InternetReadFile
InternetSetOption
Sets an Internet option
InternetSetStatusCallback
Sets up a callback function that Win32 Internet functions can call as progress is made during an operation
InternetStatusCallback
Placeholder for the application-defined status callback function
InternetTimeFromSystemTime
Formats a date and time according to the specified RFC format (as specified in the HTTP version 1.0 specification)
InternetTimeToSystemTime
Takes an HTTP time/date string and converts it to a SYSTEMTIME structure
InternetUnlockRequestFile
Unlocks a file that was locked using InternetLockRequestFile
InternetWriteFile
Writes data to an open Internet file
InternetConfirmZoneCrossing
Checks for changes between secure and nonsecure URLs
توابع URL :
Name Description
InternetCanonicalizeUrl
Canonicalizes a URL, which includes converting unsafe characters and spaces into escape sequences.
InternetCombineUrl
Combines a base and relative URL into a single URL. The resultant URL will be canonicalized.
InternetCrackUrl
Cracks a URL into its component parts.
InternetCreateUrl
Creates a URL from its component parts.
InternetOpenUrl
Begins reading a complete FTP, Gopher, or HTTP URL.
توابع FTP :
Name Description
FtpCreateDirectory
Creates a new directory on the FTP server
FtpDeleteFile
Deletes a file stored on the FTP server
FtpFindFirstFile
Searches the specified directory of the given FTP session
FtpGetCurrentDirectory
Retrieves the current directory for the given FTP session
FtpGetFile
Retrieves a file from the FTP server and stores it under the specified file name, creating a new local file in the process
FtpPutFile
Stores a file on the FTP server
FtpRemoveDirectory
Removes the specified directory on the FTP server
FtpRenameFile
Renames a file stored on the FTP server
FtpSetCurrentDirectory
Changes to a different working directory on the FTP server
توابع HTTP :
Name Description
HttpAddRequestHeaders
Adds one or more HTTP request headers to the HTTP request handle
HttpEndRequest
Ends an HTTP request
HttpOpenRequest
Opens an HTTP request handle
HttpQueryInfo
Queries for information about an HTTP request
HttpSendRequest
Sends the specified request to the HTTP server
HttpSendRequestEx
Sends the specified request to the HTTP server
بررسی جزئيات توابع Dial-Up موجود در WinInet :
1 – تابع InternetAutodial : بطور اتوماتيک باعث شماره گيری اتصال پيش فرض اينترنت توسط مودم می شود . اگر اتصال با موفقيت انجام شود تابع مقدار true و در غير اينصورت false بر می گرداند .
پارامترهای ورودی تابع :
dwFlags : فلگ کنترل کننده عمليات اتصال می باشد و يکی از مقادير زير را می تواند داشته باشد :
- INTERNET_AUTODIAL_FORCE_ONLINE
- INTERNET_AUTODIAL_FORCE_UNATTENDED
dwReserved : پارامتری رزرو شده است و بايستی صفر باشد .
چگونگی declare کردن تابع :
Public Declare Function InternetAutodial Lib "wininet.dll" (ByVal dwFlags As Long, ByVal dwReserved As Long) As Long
2 – تابع InternetAutodialHangup : باعث قطع کردن يک اتصال dial-up اتوماتيک می شود . اگر قطع اتصال با موفقيت انجام شود تابع مقدار true و در غير اينصورت false برمی گرداند . تابع دارای يک پارامتر ورودی به اسم dwReserved است که رزرو شده بود و بايستی صفر باشد .
چگونگی declare کردن تابع :
Public Declare Function InternetAutodialHangup Lib "wininet.dll" (ByVal dwReserved As Long) As Long
3 – تابع InternetDial : يک اتصال به اينترنت را با استفاده از يک ارتباط مودم مقداردهی اوليه می کند . پارامترهای ورودی آن عبارتند از :
hwndParent : هندل مربوط به پنجره parent
lpszConnectoid : نام ارتباط dial-up مورد استفاده
dwFlags : فلگ کنترل اتصال که يکی از مقادير زير را می تواند داشته باشد :
- INTERNET_AUTODIAL_FORCE_ONLINE
- INTERNET_AUTODIAL_FORCE_UNATTENDED
- INTERNET_DIAL_UNATTENDED : اتصال به اينترنت از طريق مودم بدون نمايش واسط کاربر
lpdwConnection : آدرس داده ای که شامل عدد متناظر با اتصال است .
dwReserved : پارامتری رزرو شده است و بايستی صفر باشد .
چگونگی declare کردن تابع :
Public Declare Function InternetDial Lib "wininet.dll" (ByVal hwndParent As Long, ByVal lpszConnectoid As String, ByVal dwFlags As Long, lpdwConnection As Long, ByVal dwReserved As Long) As Long
4 – تابع InternetGetConnectedState : اين تابع وضعيت اتصال جاری به اينترنت را بر می گرداند . اگر اتصال برقرار باشد تابع مقدار true و در غير اينصورت false برمی گرداند .
پارامترهای ورودی تابع عبارتند از :
lpdwFlags : توصيف وضعيت اتصال . اين پارامتر يکی از مقادير زير را می تواند داشته باشد :
- INTERNET_CONNECTION_MODEM
- INTERNET_CONNECTION_LAN
- INTERNET_CONNECTION_PROXY
- INTERNET_CONNECTION_MODEM_BUSY
dwReserved : پارامتری رزرو شده است و بايستی صفر باشد .
چگونگی declare کردن تابع :
Public Declare Function InternetGetConnectedState Lib "wininet.dll" (ByRef lpdwFlags As Long, ByVal dwReserved As Long) As Long
5 – تابع InternetGoOnline : پيغامی به کاربر برای دادن مجوز برای مقداردهی اوليه اتصال به يک URL را می دهد . اگر اينکار موفقيت آميز باشد مقدار true و در غير اينصورت false برمی گرداند . پارامترهای ورودی تابع عبارتند از :
lpszURL : URL وب سايت مورد نظر برای اتصال
hwndParent : هندل پنجره parent
dwReserved : پارامتری رزرو شده است و بايستی صفر باشد .
چگونگی declare کردن تابع :
Public Declare Function InternetGoOnline Lib "wininet.dll" (ByVal lpszURL As String, ByVal hwndParent As Long, ByVal dwReserved As Long) As Long
6 – تابع InyernetHangUp : به مودم می گويد که اتصال به اينترنت را قطع کند . پارامترهای اين تابع عبارتند از :
dwConnection : شماره مربوط به اتصالی که می خواهيم آنرا قطع کنيم .
dwReserved : پارامتری رزرو شده است و بايستی صفر باشد .
چگونگی declare کردن تابع :
Public Declare Function InternetHangUp Lib "wininet.dll" (ByVal dwConnection As Long, ByVal dwReserved As Long) As Long
7 – تابع InternetSetDialState : تنظيم نمودن وضعيت شماره گيری مودم . اگر تنظيم با موفقيت انجام شود تابع true و در غيراينصورت false برمی گرداند . پارامترهای ورودی تابع عبارتند از :
lpszConnectoid : نام اتصال dial-up
dwState : وضعيت مربوط به اتصال dial-up . در حال حاضر اين پارامتر تنها مقدار INTERNET_DIALSTATE_DISCONNECTED را می تواند داشته باشد .
dwReserved : پارامتری رزرو شده است و بايستی صفر باشد .
چگونگی declare کردن تابع :
Public Declare Function InternetSetDialState Lib "wininet.dll" (ByVal lpszConnectoid As String, ByVal dwState As Long, ByVal dwReserved As Long) As Long
بررسی فلگهای مورد استفاده در توابع dial-up :
1 – فلگهای تابع InternetDial :
Public Const INTERNET_DIAL_UNATTENDED = &H8000& '0x8000
Public Const INTERENT_GOONLINE_REFRESH = &H1 '0x00000001
Public Const INTERENT_GOONLINE_MASK = &H1 '0x00000001
2 – فلگهای تابع InternetAutoDial :
Public Const INTERNET_AUTODIAL_FORCE_ONLINE = 1
Public Const INTERNET_AUTODIAL_FORCE_UNATTENDED = 2
Public Const INTERNET_AUTODIAL_FAILIFSECURITYCHECK = 4
3 – فلگهای تابع InternetGetConnectedState :
Public Const INTERNET_CONNECTION_MODEM = 1
Public Const INTERNET_CONNECTION_LAN = 2
Public Const INTERNET_CONNECTION_PROXY = 4
Public Const INTERNET_CONNECTION_MODEM_BUSY = 8
4 - فلگهای مربوط به dial handler اختصاصی :
Public Const INTERNET_CUSTOMDIAL_CONNECT = 0
Public Const INTERNET_CUSTOMDIAL_UNATTENDED = 1
Public Const INTERNET_CUSTOMDIAL_DISCONNECT = 2
5 – فلگهای عملياتی پشتيبانی شده برای dial handler اختصاصی :
Public Const INTERNET_CUSTOMDIAL_SAFE_FOR_UNATTENDED = 1
Public Const INTERNET_CUSTOMDIAL_WILL_SUPPLY_STATE = 2
Public Const INTERNET_CUSTOMDIAL_CAN_HANGUP = 4
6 - وضعيتهای مربوط به InternetSetDialState :
Public Const INTERNET_DIALSTATE_DISCONNECTED = 1
در اين بخش که آخرين بخش از مباحث WinInet API است برنامه ای نمونه برای کار با توابع مودمی اين کتابخانه ارائه خواهيم داد :
برای نوشتن برنامه ای که بتوان از طريق آن با استفاده از مودم به اينترنت متصل شد بصورت زير عمل می کنيم :
در ابتدا بايستی تابع InternetDial را Declare کنيم :
Private Declare Function InternetDial Lib "wininet.dll" Alias "InternetDialA" (ByVal hwndParent As Long, ByVal lpszConnectoid As String, ByVal dwFlags As Long, lpdwConnection As Long, ByVal dwReserved As Long) As Long
سپس وضعيت شماره گيری را در متغيری به اسم lOption قرار می دهيم . اين متغير می تواند مقادير زير را داشته باشد :
- DF_FORCE_ONLINE
- DF_FORCE_UNATTENDED
- DF_DIAL_FORCE_PROMPT
- DF_DIAL_UNATTENDED
حال نام اتصالی را که می خواهيم از آن استفاده شود در متغيری به اسم ConnectionName قرار می دهيم .
همچنين دو متغير به اسم ConnectionID و RetVal را از نوع long تعريف می کنيم .
حال تابع InternetDial را بصورت زير صدا می کنيم :
RetVal = InternetDial(Me.hwnd, ConnectionName, lOption, ConnectionID, 0)
اگر RetVal مخالف صفر باشد عمل Dial بدرستی انجام شده است .
برای قطع اتصال فوق بايستی از تابع InternetHangUp استفاده کنيم . برای اينکار ابتدا تابع فوق را Declare می کنيم :
Private Declare Function InternetHangUp Lib "wininet.dll" (ByVal dwConnection As Long, ByVal dwReserved As Long) As Long
سپس اين تابع را بصورت زير فراخوانی می کنيم :
RetVal = InternetHangUp(ConnectionID, 0)
برای اينکه مودم را مجبور کنيم تا بطور اتوماتيک از اتصال پيش فرض سيستم برای شماره گيری استفاده کند از تابع InternetAutodial استفاده می کنيم .
برای اينکار ابتدا تابع را Declare می کنيم :
Private Declare Function InternetAutodial Lib "wininet.dll" (ByVal dwFlags As Long, ByVal hwndParent As Long) As Long
سپس تابع را بصورت زير فراخوانی می کنيم :
RetVal = InternetAutodial(ADF_FORCE_UNATTENDED, Me.hwnd)
اگر RetVal مخالف صفر باشد عمل AutoDial بدرستی انجام شده است .
برای قطع اتصالی که توسط AutoDial ايجاد شده از تابع InternetAutodialHangup استفاده می کنيم . ابتدا اين تابع را Declare می کنيم :
Private Declare Function InternetAutodialHangup Lib "wininet.dll" (ByVal dwReserved As Long) As Long
فراخوانی اين تابع بصورت زير است :
Call InternetAutodialHangup(0)
برای اينکه بفهيم آيا اتصال به اينترنت وجود دارد يا نه از تابع InternetGetConnectedStateEx استفاده می کنيم . برای اينکار ابتدا تابع را Declare می کنيم :
Private Declare Function InternetGetConnectedStateEx Lib "wininet.dll" Alias "InternetGetConnectedStateExA" (lpdwFlags As Long, lpszConnectionName As Long, dwNameLen As Long, ByVal dwReserved As Long) As Long
سپس تابع را بصورت زير فراخوانی می کنيم :
strConnectionName = Space(256)
lNameLen = 256
lPtr = StrPtr(strConnectionName)
lNameLenPtr = VarPtr(lNameLen)
RetVal = InternetGetConnectedStateEx(lConnectionFlags, ByVal lPtr, ByVal lNameLen, 0)
که strConnectionName از نوع String و بقيه متغيرها از نوع Long هستند .
اگر RetVal مخالف صفر باشد اتصال برقرار است .
ثابتهايی که در کدهای فوق استفاده شده عبارتند از :
Private Const INTERNET_AUTODIAL_FORCE_ONLINE = 1&
Private Const INTERNET_AUTODIAL_FORCE_UNATTENDED = 2&
Private Const INTERNET_AUTODIAL_FAILIFSECURITYCHECK = 4&
Private Const INTERNET_DIAL_FORCE_PROMPT = &H2000
Private Const INTERNET_DIAL_SHOW_OFFLINE = &H4000
Private Const INTERNET_DIAL_UNATTENDED = &H8000
TAPI چيست ؟
TAPI يا Telephony API يک کتابخانه استاندارد برای کار با مودم و نوشتن برنامه های تلفنی می باشد . برای نمونه می توان از برنامه های Phone Dialer ( شماره گير تلفن ) ، برنامه شبکه سازی تلفنی ( Dialup Networking ) ، برنامه تشخيص پالس مودم برای ضبط اطلاعات وارد شده از طرف کاربران و کاربردهای ديگر در اين زمينه نام برد . اين کتابخانه به شما کمک کمک می کند تا بدون درگير شدن با برنامه نويسی سخت افزار مودم و درايور آن بطور مستقيم بتوانيد برنامه های کاربردیي در اين زمينه بنويسيد .
مروری بر Microsoft Telephony :
Telephony امکان مجتمع سازی کامپيوترها با دستگاههای ارتباطی و شبکه ها را فراهم نموده است . معمولاً دستگاه ارتباطی يک مودم و خط ارتباطی نيز شبکه PSTN ( شبکه عمومی تلفن سوئيچينگ ) می باشد . برخی از کاربردهای Telephony عبارتند از :
۱ - کنفرانسهای مالتی مديا بصورت Multicast
۲ - VoIP
۳ - مرکز پاسخ گويي اتوماتيک
۴ - تماس تلفنی از طريق کامپيوتر روی شبکه PSTN
دياگرام زير معماری Microsoft Telephony را نشان می دهد :
برنامه های TAPI :
برای نوشتن برنامه های کاربردی با استفاده از TAPI بايستی ابتدا در مورد سطح سرويسی که می خواهيم ارائه دهيم تصميم گيری کنيم . برای مثال برای نوشتن يک برنامه شماره گير تلفن نياز به استفاده کامل از TAPI نيست و می توان از قابليتهای خود ويندوز در اين زمينه استفاده کرد ( Assisted Telephony ) . در بخشهای بعدی در مورد سطوح مختلف سرويس در TAPI بيشتر صحبت خواهم کرد .
دومين مطلبی که بايد مورد توجه قرار داد اينست که می خواهيم از TAPI 2.x استفاده کنيم يا از TAPI 3.x . تفاوت ايندو آنست که TAPI ورژن ۲ يک API برمبنای C است در حاليکه ورژن ۳ آن بر مبنای تکنولوژی COM می باشد . در بخشهای بعدی مطالب بيشتری در مورد تفاوتهای اين دو نسخه بيان خواهم کرد .
بخشهای اصلی يک برنامه کامل TAPI عبارتند از :
۱ - TAPI Initialization : شامل load کردن TAPI dll ، اتصال به TAPI Server ، مذاکره در مورد ورژن TAPI و برپاسازی سيستم اطلاع رسانی event می باشد .
۲ - Session Control : مقداردهی اوليه ، دريافت و کنترل تماسها
۳ - Device Control : دريافت و تنظيم اطلاعات دستگاه
۴ - Media Control : تشخيص و يا توليد تونها و ارقام ، کنترل stream
۵ - TAPI Shutdown : آزاد سازی منابع
مقداردهی اوليه TAPI :
عملکرد درست اجزای TAPI نياز به برپاسازی محيط ارتباطی روی کامپيوتر مورد نظر دارد . مراحل اين امر عبارتند از :
۱ - نصب TAPI : زمانيکه سخت افزار و يا نرم افزار برای اولين بار به کامپيوتر اضافه می شود انجام می گيرد . جزئيات کار به سيستم عامل و نرم افزار بستگی دارد .
۲ - مقداردهی ابتدائی : ساخت اشيا و مسيرهای ارتباطی
۳ - مذاکره در مورد ورژن TAPI : برای اطمينان از اينکه اجزای TAPI قادر به تبادل داده ها باشند .
۴ - استخراج اطلاعات منابع : بدست آوردن اطلاعاتی در مورد دستگاهی که می توان از آن در برنامه TAPI مورد نظرمان استفاده نمود .
۵ - Event notification : برپاسازی سيستم اطلاع رسانی event
مقداردهی اوليه TAPI در ويژوال بيسيک :
از منوی Project گزينه References را انتخاب کرده و از ليست مربوطه مورد Microsoft TAPI 3.0 Type Library را انتخاب کنيد .
حال وارد بخش کد نويسی فرمتان شويد و متغير objTAPI را بصورت زير تعريف کنيد :
Dim objTapi As TAPI
سپس در بخش مربوط به Form Load شی objTAPI را بصورت زير ايجاد می کنيم :
Set objTapi = New TAPI
همانطور که در بخشهای قبلی گفته شد ، قبل از فراخوانی هر تابع TAPI ابتدا بايستی آنرا مقداردهی اوليه کنيم . برای مقداردهی اوليه کردن شی TAPI عبارت زير را بنويسيد :
Call objTapi.Initialize
انتخاب يک آدرس :
کد زير نشان می دهد که چگونه می توان با استفاده از شی TAPI در ويژوال بيسيک منابع تلفنی در دسترس را برای يک آدرس که بتواند يک مجموعه مشخص از نيازها را مديريت کند ، بررسی کرد .
توجه داشته باشيد که قبل از انجام اين کار بايستی عمل مقداردهی اوليه TAPI را که در بخش قبل ررسی شد ، انجام دهيد .
نکته : در کد زير عمل error checking انجام نگرفته است و برای استفاده از کد زير در برنامه های واقعی بايستی بخش بررسی خطا را به آن اضافه کنيد .
۱ - تعريف يک شی آدرس و يک شی مجموعه آدرس :
Dim gobjAddress As ITAddress
Dim objCollAddresses As ITCollection
۲ - تنظيم شی objCollAddress بعنوان يک مجموعه آدرس از شی objTapi :
Set objCollAddresses = objTapi.Addresses
۳ - پيدا کردن آدرسی که بتواند از واسط مورد نظر ما پشتيبانی کند :
bFound = False
For indexAddr = 1 To objCollAddresses.Count
Set objCrtAddress = objCollAddresses.Item(indexAddr)x
Set objMediaSupport = objCrtAddress
Set objAddressCapabilities = objCrtAddress
If objMediaSupport.QueryMediaType( nSelectedType ) x
bFound = True
End If
Set objAddressCapabilities = Nothing
Set objMediaSupport = Nothing
Set objCrtAddress = Nothing
If bFound = True Then Exit For
Next indexAddr
در صورتيکه آدرس مورد نظزر پيدا شود برنامه از حلقه خارج شده و gobjAddress يک آدرس قابل استفاده خواهد بود :
Set gobjAddress = objcollAddresses.Item(indexAddr)x
انجام Event Handling در TAPI :
کد زير شامل يک event handler ساده برای TAPI ، رجيستر کردن واسط event ، تنظيم فيلتر event و رجيستر کردن تمام فراخوانيهای دادن اخطار است . هدف اصلی از اين کد اينست که مطمئن شويم بخشی از TAPI که event ها را دريافت می کند پردازشی را قبل از انتقال به بخشهای ديگر انجام دهد .
تعاريفها :
Dim WithEvents gobjTapiWithEvents As TAPI
Attribute gobjTapiWithEvents.VB_VarHelpID = -1
Dim glRegistrationToken As Long
Const TAPI3_CALL_EVENTS =TE_CALLMEDIA Or
TE_CALLNOTIFICATION Or TE_CALLSTATE
تنظيم eventfilter بصورتيکه تمام event های تعريف شده برای TAPI را بپذيرد :
objTapi.EventFilter = TAPI3_CALL_EVENTS
رجيستر کردن event ها :
Set gobjTapiWithEvents = objTapi
Dim fOwner As Boolean, fMonitor As Boolean
Dim lMediaTypes As Long, lCallbackInstance As Long
fOwner = True
fOwner = True
fMonitor = False
lMediaTypes = TAPIMEDIATYPE_AUDIO
lCallbackInstance = 1
glRegistrationToken = gobjTapi.RegisterCallNotifications(gobjAddress,fMonitor,
fOwner,lMediaTypes,lCallbackInstance)x
انتخاب يک ترمينال :
+ قبل از اينکه يک ترمينال را برای برقراری ارتباط انتخاب کنيد بايستی TAPI Initialization و عمل انتخاب آدرس را انجام داده باشيد .
ابتدا يک متغير از نوع ITBasicCallControl ( واسط کنترل تماس ) تعريف می کنيم :
Dim objCallControl As ITBasicCallControl
Set objCallControl = gobjReceivedCallInfo
سپس يک متغير از نوع ITTerminalSupport ( کوئری از شی آدرس ) تعريف می کنيم :
Dim objTerminalSupport As ITTerminalSupport
Set objTerminalSupport = gobjAddress
سپس متغير ترمينال را تعريف کرده و توسط شی objTerminalSupport يک ترمينال را برای آن استخراج می کنيم :
Dim objTerminal As ITTerminal
Set objTerminal = objTerminalSupport.GetDefaultStaticTerminal(lMediaType, dir)x
در اينجا ديگر نيازی به شی objTerminalSupport نيست بنابراين آنرا آزاد می کنيم :
Set objTerminalSupport = Nothing
سپس نياز به تعريف شی objStreamControl برای کنترل ترمينال است :
Dim objStreamControl As ITStreamControl
Set objStreamControl = objCallControl
در صورتيکه اين شی ايجاد شود ، به ازای استريم های موجود در ITCollection امکان ايجاد ترمينال در يک حلقه for بررسی می شود و ترمينال مناسب انتخاب می گردد :
If Not (objStreamControl Is Nothing) Then
Dim objITCollStreams As ITCollection
Set objITCollStreams = objStreamControl.Streams
Dim nIndex As Long, objCrtStream As ITStream
For nIndex = 1 To objITCollStreams.Count
Set objCrtStream = objITCollStreams.Item(nIndex)x
If objCrtStream.MediaType = lMediaType Then
If objCrtStream.Direction = dir Then
Call objCrtStream.SelectTerminal(objTerminal)x
End If
End If
Set objCrtStream = Nothing
Next nIndex
Set objITCollStreams = Nothing
Set objStreamControl = Nothing
End If
ايجاد يک تماس ( Make a Call ) :
+ قبل از اين بخش بايستی مراحل TAPI Initialization و عمل انتخاب آدرس انجام شده باشد .
اين بخش برای ايجاد يک شی تماس ، بررسی و مشخص کردن استريمی که با اين تماس در ارتباط است ، انتخاب و ايجاد ترمينالهای مناسب و کامل کردن ارتباط استفاده می شود .
قبل TAPI Initialization و عمل انتخاب آدرس و انتخاب ترمينال انجام شده باشد .
در ابتدا با استفاده از متد CreateCall يک شی تماس ساخته می شود :
Set gobjCall = gobjOrigAddress.CreateCall(strDestAddress, nSelectedType,lMediaTypes)x
سپس در اينجا بايستی کدی که در بخش اول اين درس برای انتخاب ترمينال نوشته شد آورده شود :
}
Select Terminal Code
{
سپس بايستی دستور Connect اجرا شود :
gobjCall.Connect (False)x
False بدين معناست که ارتباط بصورت آسنکرون برقرار می شود .
دريافت يک تماس :
کد زير برای يافتن و يا ايجاد يک ترمينال مناسب برای دريافت يک تماس بکار می رود . بايستی توجه داشته باشيد که قبل از اجرای کد زير بايستی مراحل مقداردهی اوليه ، انتخاب يک آدرس و رجيسر کردن event ها را انجام دهيد . همچنين در کد زير بايستی مرحله انتخاب ترمينال را نيز انجام دهيد . توجه داشته باشيد که در کد زير متغير pEvent يک اشاره گر برای واسط ITCallNotificationEvent است که توسط TAPI به event Handler داده می شود :
If TapiEvent = TE_CALLNOTIFICATION Then
Dim objCallNotificationEvent As ITCallNotificationEvent
Set objCallNotificationEvent = pEvent
Dim gobjReceivedCallInfo As ITCallInfo
Set gobjReceivedCallInfo = objCallNotificationEvent.Call
Dim objCallControl As ITBasicCallControl
Set objCallControl = gobjReceivedCallInfo
objCallControl.Answer
End If
گرفتن اطلاعات ورودی از کيبرد
مقدمه
Direct Input 8 همانطور که از نامش مشخص است به شما اجازه می دهد که بتوانيد برنامه هايي بنويسيد که توسط هر نوع دستگاه ورودی کنترل شود .
Direct Input 8 دارای چندين مزيت نسبت به استفاده از کنترلهای ورودی خود ويژوال بيسيک دارد – کنترلهايي مثل Form_KeyUp, Form_KeyDown, Form_MouseMove - و همچنين قابليت کنترل بيشتری نسبت به توابع استاندارد Win32 از قبيل GetCursorPos, GetKeyState دارد .
Direct Input 8 سريعتر ، کاراتر و قدرتمند تر بوده و برای ساخت بازيها طراحی شده بنابراين باعث کندی برنامه ها نخواهد شد .
چگونگی کار با Direct Input 8 برای گرفتن ورودی از کيبرد
دو روش برای استفاده از کيبرد در DirectX8 وجود دارد : روش polling و روش event-based که هر دو دارای مزايا و معايبی هستند .
بطور کلی در اغلب طراحيها از روش event-based استفاده می شود زيرا کار با آن راحت تر اسن . در اين روش هر پيغام فرستاده شده ازطرف دستگاه ورودی log می شود و برنامه نيازی به هيچگونه پردازشی بمنظور منتظر ماندن برای يک پيغام از طرف ورودی ندارد ، بنابر اين کاراتر است . در روش polling کنترل کمی دقيقتر و راحتر است .
اگر در مورد برنامه نويسی بر مبنای polling و بر مبنای event اطلاعات کافی نداريد می توانيد از منابع موجود در سايتهايي چون Gamasutra و GameDev استفاده کنيد .
روش Polling
مراحل اين روش عبارتند از :
1 – تعريفات Declerations : يک فرم ايجاد کرده و يک TextBox به نام txtOutput با خصوصيات Multiline ، Locked و Vertical Scroll Bar در آن قرار دهيد . کدهای زير را در بخش کدنويسی اين فرم بنويسيد :
Private Const UsePollingMethod As Boolean = True
Private Const UseEventMethod As Boolean = False
نکته مهم اينست که تنها يکی از دو ثابت فوق بايستی True باشد .
Private bRunning As Boolean
اين متغير برای polling استفاده می شود
Private DX As DirectX8
Private DI As DirectInput8
تعريف شی اصلی DirectX و شی DirectInput
Private DIDevice As DirectInputDevice8
Private DIState As DIKEYBOARDSTATE
اين دو شی برای دسترسی به دستگاه ورودی ( کيبرد ) استفاده می شوند
Private KeyState(0 To 255) As Boolean
آرايه ای برای تشخيص فشرده شدن کليد
Private Const BufferSize As Long = 10
سايز بافر نگهدارنده event ها . در روش event-based اين مقدار برابر يک و در روش polling برابر 10 تا 20 است ( بسته به سرعت حلقه بازی )
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)x
تابع Sleep برای متوقف کردن حلقه polling در صورت بالا بودن نرخ ورودی
2- مقدار دهی اوليه Initialisation : اين بخش سه مرحله دارد :
در مرحله اول اشيا و Device ها ساخته می شوند .
در مرحله دوم تنظيمات مربوط به Device انجام می شود .
در مرحله سوم به Device می گوئيم که می خواهيم شروع به استفاده از آن کنيم .
در Form_Load کدهای زير را بنويسيد :
Me.Show
Dim I As Long
Dim DevProp As DIPROPLONG
Dim DevInfo As DirectInputDeviceInstance8
Dim pBuffer(0 To BufferSize) As DIDEVICEOBJECTDATA
If UsePollingMethod And UseEventMethod Then
MsgBox "You must select only one of the constants before running"x
Unload Me
End
End If
If UsePollingMethod Then txtOutput.Text = "Using Polling Method" & vbCrLf
If UseEventMethod Then txtOutput.Text = "Using Event Based Method" & vbCrLf
مقداردهی اوليه روش انتخاب شده
Set DX = New DirectX8
Set DI = DX.DirectInputCreate
Set DIDevice = DI.CreateDevice("GUID_SysKeyboard")x
DIDevice.SetCommonDataFormat DIFORMAT_KEYBOARD
DIDevice.SetCooperativeLevel frmMain.hWnd, DISCL_BACKGROUND Or ISCL_NONEXCLUSIVE
برپاسازی بافر
DevProp.lHow = DIPH_DEVICE
DevProp.lData = BufferSize
DIDevice.SetProperty DIPROP_BUFFERSIZE, DevProp
به دايرکت ايکس می گوئيم که می خواهيم از دستگاه ورودی استفاده کنيم
DIDevice.Acquire
استخراج اطلاعاتی در مورد دستگاه ورودی
Set DevInfo = DIDevice.GetDeviceInfo()x
txtOutput.Text = txtOutput.Text & "Product Name: " & DevInfo.GetProductName & vbCrLf
txtOutput.Text = txtOutput.Text & "Device Type: " & DevInfo.GetDevType & vbCrLf
txtOutput.Text = txtOutput.Text & "GUID: " & DevInfo.GetGuidInstance & vbCrLf
در صورتی که بخواهيم به برنامه خاتمه بدهيم کدهای زير را می نويسيم
DIDevice.Unacquire
Set DIDevice = Nothing
Set DI = Nothing
Set DX = Nothing
Unload Me
End
3 – گرفتن ورودی از کيبرد : در اين بخش فرض کنيد بخواهيم يک بازی را در يک حلقه Do-Loop شبيه سازی کنيم . در اين حلقه هر بار فشرده شدن کليدهای کيبرد را چک می کنيم :
If Not Err.Number Then bRunning = True
Do While bRunning
دريافت اطلاعات شامل خواندن وضعيت کيبرد ، خواندن اطلاعات بافر و سپس خطا
DIDevice.GetDeviceStateKeyboard DIState
DIDevice.GetDeviceData pBuffer, DIGDD_DEFAULT
If Err.Number = DI_BUFFEROVERFLOW Then
Msgbox(“BUFFER OVERFLOW (Compensating)...")x
GoTo ENDOFLOOP:
End If
‘بررسی فشرده شدن کليدها
For I = 0 To 255
If DIState.Key(I) = 128 And (Not KeyState(I) = True) Then
txtOutput.Text = txtOutput.Text & "{ DOWN } " & KeyNames(CInt(I))& vbCrLf
txtOutput.SelStart = Len(txtOutput.Text)x
KeyState(I) = True
End If
Next I
‘بررسی رها شدن کليد
For I = 0 To BufferSize
If KeyState(pBuffer(I).lOfs) = True And pBuffer(I).lData = 0 Then
KeyState(pBuffer(I).lOfs) = False
txtOutput.Text = txtOutput.Text & "{ UP } " & KeyNames(CInt(pBuffer(I).lOfs)) & vbCrLf
txtOutput.SelStart = Len(txtOutput.Text)x
End If
Next I
Sleep (50)x
DoEvents
ENDOFLOOP:
Loop
در کد فوق يک تابع KeyName وجود دارد که نام کليد فشارداده شده را بر می گرداند . بخشی از اين تابع را در زير می بينيد :
Function KeyNames(iNum As Integer) As String
Dim aKeys(0 To 255) As String
aKeys(1) = "DIK_ESCAPE"
aKeys(2) = "DIK_1 On main keyboard"x
aKeys(3) = "DIK_2 On main keyboard"x
aKeys(4) = "DIK_3 On main keyboard"x
aKeys(5) = "DIK_4 On main keyboard"x
aKeys(6) = "DIK_5 On main keyboard"x
aKeys(7) = "DIK_6 On main keyboard"x
aKeys(8) = "DIK_7 On main keyboard"x
aKeys(9) = "DIK_8 On main keyboard"x
aKeys(10) = "DIK_9 On main keyboard"x
aKeys(11) = "DIK_0 On main keyboard"x
aKeys(12) = "DIK_MINUS On main keyboard"x
aKeys(13) = "DIK_EQUALS On main keyboard"x
aKeys(14) = "DIK_BACK BACKSPACE"x
aKeys(15) = "DIK_TAB"x
aKeys(16) = "DIK_Q"x
aKeys(17) = "DIK_W"x
aKeys(18) = "DIK_E"x
aKeys(19) = "DIK_R"x
aKeys(20) = "DIK_T"x
.
.
.
KeyNames = aKeys(iNum)x
End Function
موضوع : کنترل کيبرد با روش Event-Based
مقداردهی اوليه و مفاهيم اصلی در روش Event-Based مشابه روش Polling است و تنها بايستی ساختار بخش جمع آوری داده و حلقه پردازشی را تغيير دهيم . مراحل کار با روش Event-Based بصورت زير می باشد :
۱ - تعاريف و مقداردهی اوليه : در بخش تعاريف دو تعريف جديد بصورت زير داريم :
Dim hEvent As Long
Implements DirectXEvent8
hEvent يک پارامتر هندل برای يک می باشد .
نکته : زمانی که کليدی فشرده يا رها می شود ، DirectX اين امر با فراخوانی تابعی به اسم DirectXEvent8_DXCallback به برنامه شما اطلاع می دهد . ( اين نوع توابع را Call Back Function گويند ) . اين تابع به برنامه شما می گويد که يک رويداد اتفق افتاده است و بايستی بافرها را چک کند .
تنها تغييری که در بخش مقداردهی اوليه نياز است ، برپاسازی يک event می باشد :
If UseEventMethod Then
hEvent = DX.CreateEvent(frmMain)x
DIDevice.SetEventNotification hEvent
End If
در انتهای برنامه نيز کد زير را برای از بين بردن event اضافه کنيد :
If hEvent <> 0 Then DX.DestroyEvent hEvent
۲ - استفاده از event : برای اين بخش کدهايي را در داخل تابع DirectXEvent8_DXCallback می نويسيم :
Private Sub DirectXEvent8_DXCallback(ByVal eventid As Long)x
'متغيرهای موردنياز
Dim I As Long
Dim pBuffer(0 To BufferSize) As DIDEVICEOBJECTDATA
If eventid = hEvent Then
If DIDevice Is Nothing Then Exit Sub
'درصورت رخ دادن event داده را از کيبرد می گيريم
DIDevice.GetDeviceStateKeyboard DIState
DIDevice.GetDeviceData pBuffer, DIGDD_DEFAULT
'چک کردن تمام کليدها برای اينکه متوجه شويم چه اتفاقی افتاده است
For I = 0 To 255
'عدد ۱۲۸ نشان دهنده key_down event است .
If DIState.Key(I) = 128 Then
If pBuffer(0).lData = 128 Then
txtOutput.Text = txtOutput.Text & "{ DOWN } " & KeyNames(CInt(I)) & vbCrLf
End If
End If
'کد فوق برای بررسی فشرده شدن يک کليد بود . کد زير رها شدن کليد را بررسی می کند
If (pBuffer(0).lData = 0 And pBuffer(0).lOfs = I) Then
txtOutput.Text = txtOutput.Text & "{ UP }" & KeyNames(CInt(I)) & vbCrLf
End If
txtOutput.SelStart = Len(txtOutput.Text)x
Next I
End If
End Sub
موضوع : کنترل ماوس با DirectX Input
مقدمه :
برای استفاده از ماوس در برنامه های مالتی مديا و بازيها همانند کی برد می توانيم از امکانات دايرکت ايکس استفاده کنيم . روش کنترل ماوس توسط DirectX Input بسيار ساده بوده و مشابه کنترل کيبرد می باشد بنابراين درصورتی که دو درس گذشته را نخوانده اين پيشنهاد می کنم ابتدا آنها را مطالعه کنيد .
برپاسازی Device :
علاوه بر متغيرهايي که در بخش کنترل کيبرد تعريف شد بايستی متغيرهای جديد زير را نيز در ابتدای برنامه تان تعريف کنيد :
Private Const mSpeed As Single = 2
Private Const BufferSize As Long = 10
Private mPosition As Point
mSpeed مقدار سرعت حرکت کرسر ماوس را مشخص می کند .
BufferSize سايز بافر DI می باشد .
mPosition موقعيت جاری کرسر ماوس را نشان می دهد .
در مرحله بعدی بايستی مقداردهي های اوليه لازم را انجام دهيد :
Set DIDevice = DI.CreateDevice("guid_SysMouse")x
Call DIDevice.SetCommonDataFormat(DIFORMAT_MOUSE)x
Call DIDevice.SetCooperativeLevel(frmMain.hWnd, DISCL_FOREGROUND Or DISCL_EXCLUSIVE)x
تفاوت عمده کدهای فوق با کدهای مقداردهی اوليه در بخش کی برد آنست که cooperativelevel تغيير کرده است . در اينجا گفته شده که ما می خواهيم از ماوس بصورت انحصاری در برنامه استفاده کنيم . اين حالت برای برنامه های window-base مناسب نيست و بهترست از آن در بازيهايي که بصورت full screan هستند استفاده کنيد .
خواندن ورودی از ماوس :
در اين بخش می توانيد هم از روش polling و هم event-based استفاده کنيد . نکته مهمی که در اينجا وجود دارد آنست که Direct Input فقط حرکت داده شدن ماوس و کليک شدن يک دکمه را به شما اطلاع می دهد و برای تشخيص حالتهای double click و single click خودتان بايستی کد بنويسيد برای مثال اگر فاصله زمانی بين دو کليک کمتر از ۴۰ ميلی ثانيه باشد آنگاه اين يک double click بوده است .
کد زير حرکت داده شدن ماوس و کليک يکی از سه دکمه آنرا اطلاع می دهد :
Dim DevData(1 To BufferSize) As DIDEVICEOBJECTDATA
Dim nEvents As Long
Dim I As Long
nEvents = DIDevice.GetDeviceData(DevData, DIGDD_DEFAULT)x
For I = 1 To nEvents
Select Case DevData(I).lOfs
Case DIMOFS_X
mPosition.x = mPosition.x + (DevData(I).lData * mSpeed)x
If mPosition.x < 0 Then mPosition.x = 0
If mPosition.x > frmMain.ScaleWidth Then mPosition.x = frmMain.ScaleWidth
imgCursor.Top = mPosition.y
imgCursor.Left = mPosition.x
lablel(1).Caption = "Mouse Coordinates: [" & mPosition.x & ", " & mPosition.y & "]"x
Case DIMOFS_Y
mPosition.y = mPosition.y + (DevData(I).lData * mSpeed)x
If mPosition.y < 0 Then mPosition.y = 0
If mPosition.y > frmMain.ScaleHeight Then mPosition.y = frmMain.ScaleHeight
imgCursor.Top = mPosition.y
imgCursor.Left = mPosition.x
lablel(1).Caption = "Mouse Coordinates: [" & mPosition.x & ", " & mPosition.y & "]"x
Case DIMOFS_BUTTON0
label(2).Caption = "Button 0 State: " & IIf(DevData(I).lData = 0, "Up", "Down")x
Case DIMOFS_BUTTON1
label(3).Caption = "Button 1 State: " & IIf(DevData(I).lData = 0, "Up", "Down")x
Case DIMOFS_BUTTON2
label(4).Caption = "Button 2 State: " & IIf(DevData(I).lData = 0, "Up", "Down")x
Case DIMOFS_BUTTON3
label(5).Caption = "Button 3 State: " & IIf(DevData(I).lData = 0, "Up", "Down")x
End Select
Next I
برای استفاده از کد فوق در روش Polling ، بايستی آنرا در يک حلقه Do while-Loop قرار دهيد .
برای استفاده از کد فوق در روش Event-Based ، بايستی آنرا درون روتين DirectXEvent8_DXCallback قرار دهيد
آشنايي با کتابخانه Windows Packet Capture
معرفی :
کتابخانه WinPcap يک معماری برای استخراج Packet های TCP/IP و آناليز شبکه در محيطهای ۳۲ بيتی ويندوز می باشد . اين کتابخانه شامل سه بخش است :
۱ - يک فيلتر Packet در سطح هسته سيستم عامل ( Kernel )
۲ - يک کتابخانه dll سطح پايين ( low-level ) با نام packet.dll
۳ - يک کتابخانه مستقل از سيستم عامل و سطح بالا ( high-level ) با نام wpcap.dll
فيلتر packet يک درايور دستگاه ( device driver ) است که به ويندوزهای ۹۵ ، ۹۸ ، ME ، NT و ۲۰۰۰ قابليت استخراج و capture کردن و نيز ارسال داده خام ( raw data ) از يک کارت شبکه را می دهد . همچنين اين امکان را دارد که packet های capture شده را در يک بافر ذخيره کند و يا آنها را فيلتر نمايد .
packet.dll يک API است که بمنظور دسترسی مستقيم به عملکرد درايور packet استفاده می شود . بنابراين packet.dll يک واسط برنامه نويسی مستقل از سیستم عامل های مايکروسافت را مهيا می کند .
Wpcap.dll مجموعه ای از ابزارهای سطح بالای اصلی برای capture را مهيا می کند که اين توابع با کتابخانه libpcap ( کتابخانه capture در سيستم عامل UNIX ) سازگار می باشند . اين توابع اجازه capture کردن packet ها را با روشی مستقل از سخت افزار شبکه و مستقل از سيستم عامل مهيا می کنند
موضوع : پخش افکتهاي صوتی در برنامه هاي مالتي مديا
مقدمه : در سلسله مباحث DirectXAudio شما تکنيکهاي لازم براي اضافه کردن موزيک و افکتهاي صوتي سريع و ديناميک را به بازيها و برنامه هاي مالتي مديا خواهيد آموخت . DirectXAudio جايگزيني براي بخشهاي DirectSound ، DirectSound3D و DirectMusic موجود در DirectX 7 مي باشد و داراي امکانات بهتر و سريعتری بوده و برنامه نويسي آن نيز ساده تر است .
در اولين درس از DirectXAudio چگونگي پخش افکتهاي صوتي را در برنامه هايتان خواهيد آموخت .
Initial کردن DirectSound :
DirectSound اولين مبحثي است که آنرا توضيح خواهم داد . گرچه DirectXAudio يک نام عمومي براي امکانات صوتي DirectX8 مي باشد اما بين Sound و Music تفاوت وجود دارد .
DirectSound با پخش افکتهاي صوتي ارتباط دارد . DirectSound همانند Direct3D از يکسري device سخت افزاري و نرم افزاري استفاده مي کند و افکتهاي صوتي در يکسري بافر ذخيره مي شوند .
اولين قدم براي برپاسازي DirectSound ، اضافه کردن کتابخانه DirectX8 به پروژه تان مي باشد . قدم بعدي تعريف متغيرها و object هاي موردنياز است . براي استفاده از DirectSound به متغيرهاي زير نياز داريم :
Private DX As DirectX8
Private DS As DirectSound8
Private DSBuffer As DirectSoundSecondaryBuffer8
Private DSEnum As DirectSoundEnum8
Private bLoaded As Boolean
DirectX شي کنترل کننده مرکزي است . DirectSound8 واسط مراقب براي تمام interface هاي پخش صدا است . DirectSoundSecondaryBuffer8 داده audio واقعي را براي پخش ذخيره مي کند . DirectSoundEnum8 اجازه مي دهد که اطلاعاتي را در مورد device هاي سخت افزاري/نرم افزاري استخراج کنيد و متغير bLoaded يک flag وضعيت مي باشد .
حال در برنامه بايد ليست تمام device هاي در دسترس را مشخص کنيم . ( اين امر کاملاً امکان پذير است که يک کامپيوتر بيش از يک device براي DirectSound داشته باشد ) :
Private Sub Form_Load()x
bLoaded = False
Dim I As Long
Set DX = New DirectX8
Set DSEnum = DX.GetDSEnum
For I = 1 To DSEnum.GetCount
MsgBox(DSEnum.GetDescription(I))x
Next I
End Sub
فرض کنيم که يکي از device هاي شناخته شده را انتخاب کرديم . حال بايستي device را واقعاً برپا کنيم :
If bLoaded Then
Set DSBuffer = Nothing
Set DS = Nothing
Set DX = Nothing
End If
Dim DSBDesc As DSBUFFERDESC
Set DX = New DirectX8
Set DS = DX.DirectSoundCreate(DSEnum.GetGuid(devicenumber))x
DS.SetCooperativeLevel frmMain.hWnd, DSSCL_NORMAL
متغير devicenumber شماره device اي است که شما مي خواهيد با آن کار کنيد . DSBDesc فايل صوتي شما را توصيف مي کند .
موضوعات مرتبط: آموزش ، ،
برچسبها:
قرار دادن متن به صورت عمودی در یک کنترل Text Box
ابتدا یک کنترل Picture Box به فرم اضافه کنید. که به طور پیش فرض Picture1 ایجاد می شود. خصوصیت AuotRedraw کنترل مذبور را به True تنظیم کنید. بعد یک کنترل Text Box به فرم روی کنترل Picture Box اضافه کنید. Text1 به وجود می اید و سپس خصوصیت MultiLine این را به True تنظیم کنید. بعد این کدها را در فرمتون کپی کنید Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long Const WM_USER = &H400 Const EM_GETLINECOUNT = &HBA Dim numlines As Long Private Sub Form_Load() Dim ht As Integer Text1.Left = 0 Text1.Height = Picture1.Width - 400 Text1.Width = Picture1.TextHeight("A") Text1.Top = (Picture1.Height - Text1.Height) / 2 + 170 Text1.Visible = True numlines = 1 End Sub Private Sub Text1_Change() Dim ret As Long Dim ht As Long ret = SendMessage(Text1.hwnd, EM_GETLINECOUNT, 0, ByVal 0&) If ret <> numlines Then ht = Picture1.TextHeight("A") Text1.Top = (Picture1.Height - Text1.Height) / 2 + 170 numlines = ret SendKeys "{PGUP}", True Text1.SelStart = Len(Text1) End If End Sub پاسخ اقا محمد اسماعیل حسنی که خواسته بودند نحوه ذخیره متن داخل یک TextBox رو توضیح بدم یک فرم خالی درست کنید و فقط یک TextBox روی ان قرار دهید و سپس این کدها رو داخل فرم کپی کنید Private Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName As String) As Long Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long Private Sub Form_Load() Dim buf As String buf = Space(255) GetPrivateProfileString "form1", "textbox1", "10", buf, 255, "c:\testini.ini" Text1.Text = buf End Sub Private Sub Form_Unload(Cancel As Integer) WritePrivateProfileString "form1", "textbox1", Text1.Text, "c:\testini.ini" End Sub و ضمنأ من ویژوال بیسیک فارسی رو با خود ویژوال بیسیک 6 و C++6 نوشتم. توجه : یاهو مسنجر من خرابه و تا اطلاع ثانوی نمیتوانم با هیچ کس چت کنم.ببخشید. اقا کمال برنامه ای رو که خواسته بودید براتون میل کردم. برنامه درخواستی اقای نفوذگر تنها(Hacker alone) فقط یک فرم درست کن و این کدها رو توش کپی کن البته شاید... فعلا چون نمی خواستم منتظرت نگذارم اینو گذاشتم قبلا یه همچین برنامه ای داشتم ولی باورت میشه شاید بیش از نیم ساعت تو هاردم گشتم ولی پیداش نکردم اگه پیداش کردم حتما خبرت میکنم و میزارمش یا شاید هم اگه وقت کردم نوشتمش Private Const SHFD_CAPACITY_DEFAULT = 0 Private Const SHFD_CAPACITY_360 = 3 Private Const SHFD_CAPACITY_720 = 5 Private Const SHFD_FORMAT_QUICK = 0 Private Const SHFD_FORMAT_FULL = 1 Private Declare Function SHFormatDrive Lib "shell32" (ByVal hwndOwner As Long, ByVal iDrive As Long, ByVal iCapacity As Long, ByVal iFormatType As Long) As Long Private Sub Form_Load() SHFormatDrive Me.hWnd, 2, SHFD_CAPACITY_DEFAULT, SHFD_FORMAT_QUICK End Sub اقا حامد گفته بودن من چه کتابهایی برای یادگیری VB میخونم یاید بگم: اموزش ویژوال بیسیک در 21 روز اموزش C++ در 21 روز ( که تا حدودی مرتبط است ) اموزش برنامه نویسی تحت اینترنت با ویژوال بیسیک 211 نکته برای برنامه نویسان ویژوال بیسیک (چکیده MSDN ) 500 تابع کاربردی API و ویژوال بیسیک توسط داییم که استاد کامپیوتر به من فقط معرفی شد که من خودم با پشتکارم یاد گرفتمش و هیچ کلاس اموزشی هم نرفتم آشنایی با تابع Shell Function Shell(PathName, [WindowStyle]) As Double تابع shell مثل run ویندوز برای برنامه نویسی است PathName : آدرس محل برنامه ای که قرار است اجرا شود WindowStyle : حالت باز شدن پنجره برنامه می باشد کاربرد: مثلا برنامه P.exe را در شاخه "c:\Mahdi\spy" دارید. فراخوانی برنامه به صورت زیر می باشد . Call shell "c:\Mahdi\spy\p.exe" در صورتی که حالات نمایش پنجره را مشخص نکنید . مثل مثال بالا . نمایش پنجره همان نمایش پیش فرض ویندوز می باشد. استفاده از تایع sendkeys خوب اینجا می خوام یک کد کاربردی دیگه رو بهتون بگم . این کد باعث می شه که وقتی شما رویداد خاصی رو اجرا مکنید , کلید خاصی از کیبرد اجرا شود یعنی مثلاً اگر شما روی یک Textbox هستید و کلید Enter را فشردید عملی معادل فشردن کلید ..... , Tab ,Delete,Pagedown , F1 ,F2 روی دهد : Private Sub TextBox_KeyPress(KeyAscii As Integer) If KeyAscii = 13 Then SendKeys "{tab}" End If End Sub |
||
![]() |
![]() |
|
بخش اول : تعاریف مربوط به نمایش درختی گره ( Node ) : بهترین ایده این است که یک گره را مانند یک شاخه منشعب در یک درخت فرض کنیم. هرگره نشان دهنده یک عضو است. یک گره میتواند هر تعدتد زیر گره را نیز داشته باشد. برادر ( Subline ) : یک برادر گره دیگری است که در همان انشعاب گره مذکور قرار دارد فرزندان ( Childern ) : فرزندان به زیر گره های گره جاری گفته میشود. بعضی مواقع به انها فرزند هم گفته میشود. پدر ( Parent ) : پدر گره ای است که در بالای گره جاری قرار دارد. همه گره ها به جزء گره ریشه دارای پدر می باشند.
بخش دوم : اضافه کردن گره به درخت
در این بخش شما به کنترل های زیر نیاز خواهید داشت: یک کنترل Tree View که در قسمت کامپوننت و بخش Microsoft Common control 6.0 است و ان را به فرمتان بیافزایید یک کنترل ImageList که سه تصویر یا 16*16 یا 32*32 باشد که تصاویر را به صورت زیر نام گذاری کنید ////////////////////// تصویر فولدر بسته با Key >>> Closed تصویر یک فولدر باز شده با Key >> Open تصویر یک فایل با key >> Leaf //////////////////////// کنترل Tree View تعدادی گره را در خود نگه می دارد بنابراین برای اضافه کردن گره به درخت باید یک گره جدید را ابتدا تعریف کنید و سپس ان گره را به مجموعه Tree View اضافه کنید مانند مثال زیر : Dim nodx As Node Set nodx = TreeView1.Nodes.Add(, , "Root", "Root Node") ان را اجرا کنید. شما باید فقط یک عضو را در درخت بنام " Root Node " ببینید. دقت کنید که خاصیتParent در مثال فوق خالی بود. این مساله برای این است که میخواستیم گره مذکور بالاترین گره یا ریشه باشدو بنابراین نباید هیچ پدری داشته باشد. اجازه بدید تعدادی گره به درختمان اضافه کنیم اما این دفعه تصاویری را نیز به گره هایمان اضافه خواهیم کرد: Dim nodx As Node Set TreeView1.ImageList = ImageList1 'Add root Node Set nodx = TreeView1.Nodes.Add(, , "Root", "Root Node","Closed") 'Expand root node so we can see what's under it nodx.ExpandedImage = "Open" nodx.Expanded = True 'Create a child node under the root node Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child1", "Child node 1", "Closed") 'Expand this node so we can see what's under it nodx.ExpandedImage = "Open" nodx.Expanded = True 'Create several more children Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child2", _ "Child node 2", "Leaf") Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child3", _ "Child node 3", "Leaf") Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child4", _ "Child node 4", "Leaf") Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child5", _ "Child node 5", "Leaf") 'Create two child nodes under the first child node of root Set nodx = TreeView1.Nodes.Add("Child1", tvwChild, "Child1A", _ "Child node 1 A", "Leaf") Set nodx = TreeView1.Nodes.Add("Child1", tvwChild, "Child1B", _ "Child node 1 B", "Leaf") حالا اگر به کد دقت کنید می بینید ما یک ارجاع ئاریم که گره را در خودش ذخیره میکند و Nodx نام دارد. شما میتوتنید این ارجاع را برای اصلاح خواص ان گره استفاده کنید. مانند خطوط که به شکل زیر امده اند: nodex.expanded=True کاری که این خط انجام میدهد باز کردن یک گره است بنابراین ما میتوانیم گره های فرزند ان را مشاهده کنیم. ان مانند وقتی است که کاربر روی یک گره کلیک می کند تا خودشان را باز کنند. این کار بوسیله این کد قایل انجام است.ادامه دهید و ان را اجرا کنید. شما خواهید دید که گره ریشه اصلی حالا 5 فرزند در زیر دارد و اولین انها دو فرزند در زیر دارد.
بخش سوم : پیمایش درختی در اینجا نمونه های برای پیمایش درخت برای پیدا کردن همه گرههای زیر یک گره خاص را اوردهایم. مثال زیر مثال ساده ای است که به شما نشان میدهد چگونه همه گره های زیر یک گره خاص را بدست اورید و متن برچسب انها را نمایش دهید: Dim nodx As Node Set TreeView1.ImageList = ImageList1 'Add root Node Set nodx = TreeView1.Nodes.Add(, , "Root", "Root Node", "Closed") 'Expand root node so we can see what's under it nodx.ExpandedImage = "Open" nodx.Expanded = True 'Create a child node under the root node Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child1", _ "Child node 1", "Closed") 'Expand this node so we can see what's under it nodx.ExpandedImage = "Open" nodx.Expanded = True 'Create several more children Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child2", _ "Child node 2", "Leaf") Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child3", _ "Child node 3", "Leaf") Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child4", _ "Child node 4", "Leaf") Set nodx = TreeView1.Nodes.Add("Root", tvwChild, "Child5", _ "Child node 5", "Leaf") 'Create two child nodes under the first child node of root Set nodx = TreeView1.Nodes.Add("Child1", tvwChild, "Child1A", _ "Child node 1 A", "Leaf") Set nodx = TreeView1.Nodes.Add("Child1", tvwChild, "Child1B", _ "Child node 1 B", "Leaf") 'Loop though each child of the root node Dim i As Long 'Set nodx to the first child node of root. Set nodx = TreeView1.Nodes("Root").Child 'Loop though each child nod assigning it to nodx For i = 1 To TreeView1.Nodes("Root").Children MsgBox nodx.Text Set nodx = nodx.Next Next فقط بلوک اخر کد در این مثال جدید است. شما میتوانید خاصیت Childern را برای فهمیدن تعداد گره های فرزند یک گره خاص بکار ببرید. خاصیت Children به اولین گره فرزند اشاره میکند و خاصیت Nextبه گره بعدی نسبت به گره جاری اشاره میکند بخش چهارم : رویدادها الان باید درباره چیزهای جدیدی صحبت کنیم. برای مثال ما میخواهیم فقط دو گره را که مانند دایرکتورب در سیستم شما هستند به ان اضافه کنیم. وقتی شما روی گره کلیک کنید ان وقت مسیر واقعی فایل برای ان فولدر را به شما میدهد: Private Sub Form_Load() Dim nodx As Node Set TreeView1.ImageList = ImageList1 'Add Drive Set nodx = TreeView1.Nodes.Add(, , , "c:", "Closed") nodx.ExpandedImage = "Open" nodx.Expanded = True 'Add Folder Set nodx = TreeView1.Nodes.Add(nodx, tvwChild, , "Windows", "Closed") nodx.ExpandedImage = "Open" nodx.Expanded = True 'Add Another Folder Set nodx = TreeView1.Nodes.Add(nodx, tvwChild, , "System", "Closed") nodx.ExpandedImage = "Open" nodx.Expanded = True End Sub Private Sub TreeView1_NodeClick(ByVal Node As MSComctlLib.Node) MsgBox Node.FullPath End Sub وقتی ما روی یک گره در درخت کلیک میکنیم رویداد Node Click روی میدهد و ان گره ای را که کلیک شده بود را به ما بر میگرداند بنابراین دستکاری ان بسیار اسان است. به نظر شما یز خطرناکی در مثال فوق وجود دارد؟ این صحیح است در اینجا هیچ کلیدی برای هر کدام از گره ها وجود ندارد. کلید اختیاری است. دقت کنید که چون من کلیدی ندارم به جای فقط کلید گره که در تابع add برای اضافه کردن یک گره به درخت استفاده میشود. خود گره را بدست میاورم. این علت ارسال شی Nodx به تابع add می باشد. چون ان واقعا فقط یک اشاره گر به گره قبلی است. شما باید قادر باشید که ذر مثال فوق ببینید که کنترل Tree View بخوبی برای نمایش سیستم فایل کارذمیکند. مانند اکسپلورر در ویندوز خاصیت FiullPath هر چیزی را که ما درباره محل فایل نیاز داریم را به خواهد داد. در اینجا یک مثال از رویدادهای Node Click و collapse و Expand است: Private Sub Form_Load() Dim nodx As Node Dim nodr As Node 'Show Root Lines TreeView1.LineStyle = tvwRootLines 'Display Checkboxes TreeView1.Checkboxes = True 'Add Items Set nodx = TreeView1.Nodes.Add(, , , "Item 1") Set nodx = TreeView1.Nodes.Add(, , , "Item 2") Set nodx = TreeView1.Nodes.Add(, , , "Item 3") Set nodx = TreeView1.Nodes.Add(, , , "Item 4") Set nodx = TreeView1.Nodes.Add(, , , "Item 5") nodx.Expanded = True Set nodr = TreeView1.Nodes.Add(nodx, tvwChild, , "Item 6") nodr.Expanded = True Set nodx = TreeView1.Nodes.Add(nodr, tvwChild, , "Item 7") Set nodx = TreeView1.Nodes.Add(nodr, tvwChild, , "Item 8") Set nodx = TreeView1.Nodes.Add(nodr, tvwChild, , "Item 9") Set nodx = TreeView1.Nodes.Add(nodr, tvwChild, , "Item 10") Set nodx = TreeView1.Nodes.Add(nodr, tvwChild, , "Item 11") Set nodx = TreeView1.Nodes.Add(, , , "Item 12") End Sub Private Sub TreeView1_Collapse(ByVal Node As MSComctlLib.Node) MsgBox "Colapsing node: " & Node.Text End Sub Private Sub TreeView1_Expand(ByVal Node As MSComctlLib.Node) MsgBox "Expanding node: " & Node.Text End Sub Private Sub TreeView1_NodeCheck(ByVal Node As MSComctlLib.Node) If Node.Checked Then MsgBox "Node " & Node.Text & " was checked" Else MsgBox "Node " & Node.Text & " was Unchecked" End If End Sub ان را اجرا کنید و روی چک باکسها کلیک کنید. شما باید پیغامی را در یافت کنید که به شما میگوید که این گره شما فقط چک خورده است. حالا سعی کنید بعضی از گره ها را جمع کنید و یا باز کنید و در این حالتها شما پیغامی را از رویدادهای Collapse و Expand در یافت خواهید کرد |
موضوعات مرتبط: آموزش ، ،
برچسبها:
طبیعت بصری ویژوال بیسیک
دیدید که ویژال بیسیک چیزی بیشتر از یک زبان برنامه نویسی است. از ویژوال بیسیک در نام آنvisual به معنای بصری یا محیط نمایشی است.
کار با ویژال بیسیک
در اولین بار که برنامه را باز می کنید با پنجره new project روبه رو می شوید در این قسمت نوع فرم خود را انتخاب کرده ماننده activex|standard و....
این پنجره شامل 3 قسمت بوده
New:در این پنجره امکان انتخاب فورم مورد نظر شما امکان پذیر می باشد.
Existing:در این پنجره امکان انتخاب project های مختلف که در مکانهای مختلف ذخیره یا... امکان انتخاب می باشد.
Recent:در این قسمت هر projectرا که ذخیره می کنید به صورت دسته ای جمع می شود حالا یک فایلی
در درایو Dباشد حالا چه در درایو c.
DON.T SHOW THIS DIALOG IN THE FUTURE
این قسمت جلو گیری از باز شدن پینجره NEW PRIJECT می باشد.
HELP:از این قسمت وقتی امکان استفاده می باشد که نرم افزار MSDN را نصب کرده باشید.
معرفی قسمت های بیسیک.
نوار ابزار:TOOLBAR:نوار ابزار VB زیر منو قرار دارد. ویژال بیسیک کلا چهار نوار ابزار دارد:
STANDARD:این نوار ابزار زیر منو ظارهر است و پیش فرض است.
DEBUG:وقتی از ابزارهای رفع اشکال برای ردیابی و اصلاح اشکالات استفاده می کنید. این نوار ابزار ظاهر می شود.
EDIT:این نوار ابزار برای تنظیم کردن اشیاء بر روی فرم می باشد
FORM EDITOR:این نوار ابزار برای تنظیم کردن اشیاء بر روی فرم می باشد.
جعبه ابزار:TOOLBOX:
در این پنجره تمامی شی های مختلف برای کار بر روی فرم هستند و حتی امکان اضافه کردن به این پنجره ها می باشد.
پنجرهPROJECT:در این پنجره فرم های انتخابی شما با هر گروه و هر فرم مشخص شده است.
پنجرهPROPERTISE: این پنجره امکان تنضیمات لازم برای هر شیئی را مشخص می کنید.
توابع ریاضی در ویژال بیسیک
برای نوشتن برنامه های مهندسی ، محاسباتی ، گرافيکی و آماری نياز داريد تا از برخی توابع رياضی استفاده نمائيد . ويژوال بيسيک ۶ دارای مجموعه ای از توابع است که برای انجام محاسبات عددی پيش بينی شده اند . در اين مقاله ابتدا با اين توابع آشنا شده و سپس چگونگی ايجاد ساير توابع رياضی را که در ميان اين مجموعه وجود ندارند خواهيد ديد . در پايان نيز با توابع رياضی موجود در دات نت آشنا می شويد .
توابع رياضی موجود در ويژوال بيسيک
1 تابع Abs (قدرمطلق) : مقدار بدون علامت يک عدد را برمی گرداند .
2 تابع Atn (آرک تانژانت) : خروجی تابع عددی از نوع double است که برابر زاويه ای است که تانژانت آن عدد ورودی تابع است .
3 تابع Cos ( کسينوس ) : خروجی تابع عددی از نوع double است که برابر کسينوس زاويه ورودی است .
4 تابع Exp (توان نمانی) : خروجی تابع عددی از نوع double است که برابر e به توان ورودی تابع است .
5 تابع Int (تابع کف يا تابع جزء صحيح) : نزديکترين عدد صحيح مساوی يا کوچکتر نسبت به عدد ورودی را برمی گرداند .
6 تابع Log (لگاريتم ) : خروجی تابع عددی از نوع double است که برابر لگاريم طبيعی عدد ورودی است ( لگاريتم بر مبنای عددe يا همان Ln )
7 تابع Round ( گرد کردن ) : خروجی تابع عددی از نوع double است که برابر نزديکترين عدد صحيح به مقدار عدد ورودی است .
8 تابع Sgn (علامت) : خروجی تابع عددی از نوع صحيح است که نشان دهنده علامت عدد ورودی است .
9 تابع Sin (سينوس ) : خروجی تابع عددی از نوع double است که برابر سينوس زاويه ورودی است .
10 تابع Sqr (جذر) : خروجی تابع عددی از نوع double است که برابر ريشه دوم يا جذر عدد ورودی است .
11 تابع Tan (تانژانت) : خروجی تابع عددی از نوع double است که برابر با تانژانت زاويه ورودی ( برحسب راديان ) می باشد .
نکته : برای محاسبه توان n ام يک عدد ( n می توان صحيح يا اعشاری باشد ) از اپراتور ^ استفاده نمائيد . برای مثال :
2 ^ 5 = 32
9 ^ 0.5 = 3
4.2 ^ 3.7 = 202.31
چگونگی ايجاد ساير توابع رياضی که در ويژوال بيسيک 6 وجود ندارند
جدول زير چگونگی محاسبه ساير توابع رياضی که در ويژوال بيسيک وجود ندارند را نشان می دهد
سکانت
Sec(X) = 1 / Cos(X)
کسکانت
Cosec(X) = 1 / Sin(X)
کتانژانت
Cotan(X) = 1 / Tan(X)
آرک سينوس
Arcsin(X) = Atn(X / Sqr(1-X * X ))
آرک کسينوس
Arccos(X) = Atn(-X / Sqr(1-X * X)) + 2 * Atn(1)
آرک سکانت
Arcsec(X) = Atn(X / Sqr(X * X - 1)) + Sgn((X) -1) * (2 * Atn(1))
آرک کسکانت
Arccosec(X) = Atn(X / Sqr(X * X - 1)) + (Sgn(X) - 1) * (2 * Atn(1))
آرک کتانژانت
Arccotan(X) = Atn(X) + 2 * Atn(1)
سيونس هيپربوليک
HSin(X) = (Exp(X) - Exp(-X)) / 2
کسينوس هيپربوليک
HCos(X) = (Exp(X) + Exp(-X)) / 2
تانژانت هيپربوليک
HTan(X) = (Exp(X) - Exp(-X)) / (Exp(X) + Exp(-X))
سکانت هيپربوليک
HSec(X) = 2 / (Exp(X) + Exp(-X))
کسکانت هيپربوليک
HCosec(X) = 2 / (Exp(X) - Exp(-X))
کتانژانت هيپربوليک
HCotan(X) = (Exp(X) + Exp(-X)) / (Exp(X) - Exp(-X))
آرک سينوس هيپربوليک
HArcsin(X) = Log(X + Sqr(X * X + 1))
آرک کسينوس هيپربوليک
HArccos(X) = Log(X + Sqr(X * X - 1))
آرک تانژانت هيپربوليک
HArctan(X) = Log((1 + X) / (1 - X)) / 2
آرک سکانت هيپربوليک
HArcsec(X) = Log((Sqr(1-X * X) + 1) / X)
آرک کسکانت هيپربوليک
HArccosec(X) = Log((Sgn(X) * Sqr(X * X + 1) +1) / X)
آرک کتانژانت هيپربوليک
HArccotan(X) = Log((X + 1) / (X - 1)) / 2
لگاريتم بر مبنای N
LogN(X) = Log(X) / Log(N)
اعداد π و e در ويژوال بيسيک
برای استفاده از عدد پی و عدد e در برنامه های خود ثوابت زير را تعريف نمائيد :
Const Pi = 3.14159265358979
Const e = 2.71828182845904
همچنين عدد پی را می توان به صورت زير تعريف کرد :
Pi = 4*Atn(1)
تبديل راديان / درجه
چون اکثر توابع مثلثاتی بر حسب راديان کار می کنند گاهی اوقات نياز داريم تا زاويا را از در جه به راديان و بالعکس تبديل کنيم . برای تبديل يک زاويه که بر حسب راديان می باشد به درجه آنرا در 180 ضرب کرده و سپس بر عدد پی تقسيم می کنيم :
Degree(x) =x*180/Pi
برای تبديل يک زاويه که بر حسب درجه بيان شده به راديان آنرا در عدد پی ضرب کرده و سپس بر 180 تقسيم می کنيم :
Rad(x) =x*Pi/180
توابع رياضی و VB.Net
مجموعه توابع رياضی در در ويژوال بيسيک دات نت وجود دارند بسيار قويتر و کاملتر هستند . اين مجموعه توابع در کلاس System.Math موجود می باشند :
** در کلاس Math دو ثابت به اسم E و PI برای نشان دادن پايه لگاريتم طبيعی و عدد پی وجود دارند .
** توابع مثلثاتی : Acos ( آرک کسينوس ) ، Asin ( آرک سينوس) ، Atan ( آرک تانژانت) ، Atan2 ( آرک تانژانت خارج قسمت تقسيم ورودی ها ) ، Cos ( کسينوس ) ، Sin ( سينوس ) ، Tan ( تانژانت )
** توابع عمومی : Abs ( قدرمطلق ) ، BigMul ( حاصلضرب کامل دو عدد 32 بيتی ) ، Ceiling ( تابع سقف ) ، DivRem ( خارج قسمت نقسيم دو عدد ) ، Floor ( تابع کف ) ، IEEERemainder ( باقيمانده نقسيم دو عدد ) ، Max ( ماکزيمم بين دو عدد ) ، Min ( مينيمم بين دو عدد ) ، Round ( تابع گرد کردن ) ، Sign ( تابع علامت ) ، Sqrt ( تابع جذر )
** توابع هيپربوليک : Cosh ( کسينوس هيپربوليک ) ، Sinh ( سينوس هيپربوليک ) ، Tanh ( تانژانت هيپربوليک )
** توابع نمايي و لگاريتمی : Exp ( عدد e به توان مقدار ورودی ) ، Log ( لگاريتم ) ، Log10 ( لگاريتم بر پايه 10 ) ، Pow ( تابع توان )
آشنايي با تابع BitBlt
هدف از اين مبحث آموزشي ، آشنايي با تابع BitBlt و برخي ديگر از توابع کتابخانه Win32 GDI براي انجام برخي عمليات گرافيکي مثل double buffering و خواندن sprite از فايل است .
نکته : sprite به کاراکترهاي متحرکي گفته مي شود که در بازيها وجود دارد .
اولين چيزي که به آن نياز داريد ايجاد يک فرم است . خاصيت ScaleMode آنرا برابر 3-Pixel قرار دهيد . پيشنهاد مي کنم که هميشه در هنگام استفاده از فرم بهمراه API از pixel براي scalemode استفاده کنيد .
سپس سايز فرم را به اندازه اي افزايش دهيد تا ScaleWidth برابر 320 و ScaleHeight برابر 256 شود . توجه کنيد که خاصيت HasDC فرم را True قرار دهيد . همچنين از خاصيت AutoRedraw براي فرم استفاده نمي کنيم زيرا مي خواهيم از Double Buffering استفاده کنيم که بسيار سريعتر و کارامدتر مي باشد .
مرحله بعدي declare کردن API هايي است که به آنها نياز داريم :
'blitting
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
'code timer
Private Declare Function GetTickCount Lib "kernel32" () As Long
'creating buffers / loading sprites
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
'loading sprites
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
'cleanup
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
سوال : DC چيست ؟ DC و يا بعبارت ديگر Device Context ، hDC يک عدد است که به يک آدرس در حافظه اشاره مي کند که داده اي در آن ذخيره شده است . در هنگام استفاده از BitBlt براي اشاره کردن به آدرسي که داده گرافيکي در آنجا ذخيره شده ، استفاده مي شود .
در مرحله بعدي نياز به ذخيره آدرسهاي DC داريم که مي سازيم . آدرسهاي DC مقادير Long هستند همچنين آنها را بصورت Public تعريف مي کنيم :
'our Buffer's DC
Public myBackBuffer As Long
Public myBufferBMP As Long
'The DC of our sprite/graphic
Public mySprite As Long
'coordinates of our sprite/graphic on the screen
Public SpriteX As Long
Public SpriteY As Long
حال بايد تابعي بسازيم که تصاوير گرافيکي درون حافظه load کند . نکته مهمي که بايد به آن توجه کنيد اينست که يک device context خودش به تنهايي هيچ داده گرافيکي ندارد و بايستي يکbitmap موجود باشد تا درون آن load شود براي مثال يک فايل bmp يا يک bitmap خالي که از آن بعنوان back buffer استفاده مي کنيد .
تابعي که خواهيم نوشت يک device context منطبق با صفحه مي سازد سپس فايلهاي گرافيکي مورد نظر را درون device context قرار مي دهد
Public Function LoadGraphicDC(sFileName As String) As Long
'temp variable to hold our DC address
Dim LoadGraphicDCTEMP As Long
'create the DC address compatible with
'the DC of the screen
LoadGraphicDCTEMP = CreateCompatibleDC(GetDC(0))
'load the graphic file into the DC...
SelectObject LoadGraphicDCTEMP, LoadPicture(sFileName)
'return the address of the file
LoadGraphicDC = LoadGraphicDCTEMP
End Function
سوال : double-buffering چيست ؟ زمانيکه يک محيط گرافيکي مي سازيد تا درون آن چيزي را ترسيم کنيد ، شما sprite ها / گرافيکها / متن را درون حافظه blit مي کنيد ( offscrean ) سپس نتيجه نهايي را روي صفحه blit مي کنيد . اين عمل از لرزش تصوير يا flickering جلوگيري مي کند ( زماني رخ مي دهد که چندين sprite مستقيماً روي صفحه blit شوند ) و بسيار سريعتر ازAutoRedraw است .
قبل از اينکه مثالي براي اين تابع ذکر کنم تابع BitBlt را توضيح خواهم داد :
BitBlt تابعي از کتابخانه dll “gdi32” است . اين تابع يک انتقال bit-block از داده هاي مرتبط به يک مستطيل از پيکسلها به يک device context مقصد انجام مي دهد . بعبارت ديگر داده هاي گرافيکي را از محيط گرافيکي ( يک bitmap ) به محيط گرافيکي ديگري ( screen يا يک form ) کپي مي کند . فرم کلي اين تابع بصورت زير است :
Declare Function BitBlt Lib "gdi32" Alias "BitBlt" _
(ByVal hDestDC As Long, _
ByVal x As Long, _
ByVal y As Long, _
ByVal nWidth As Long, _
ByVal nHeight As Long, _
ByVal hSrcDC As Long, _
ByVal xSrc As Long, _
ByVal ySrc As Long, _
ByVal dwRop As Long) As Long
اولين خط بيان مي کند که ما بوسيله gdi32 DLL به تابع BitBlt دسترسي خواهيم داشت . خطوط ديگر پارامترهايي هستند که اين تابع مي گيرد :
hDestDC : hDC مربوط به محيط مقصد ( اگر مي خواهيد مقصد يک فرم باشد از form.hDC استفاده کنيد و يا اينکه آدرس يک backbuffer را که ساخته ايد بدهيد )
x : مختصات افقي محلي که مي خواهيد گرافيک شما ظاهر شود .
y : مختصات عمدي محلي که مي خواهيد گرافيک شما ظاهر شود .
nWidth : عرض گرافيک شما
nHeight : ارتفاع گرافيک شما
hSrcDC : hDC مربوط به محيط مبدا
xSrc : افست x . 0 زماني استفاده مي شود که بخواهيد از سمت چپترين گوشه گرافيک مبدا عملblit را انجام دهيد .
ySrc : افست y
dwRop : مد draw اي که در زمان blitting گرافيکتان مي خواهيد استفاده کنيد ( Raster Operationsيا ROP ) . اين پارامتر مقادير زير را مي تواند بگيرد :
- vbSrcCopy : داده تصوير مبدا را مستقيماً در مقصد کپي مي کند .
- vbSrcPaint : داده هاي تصاوير مبدا و مقصد را با هم OR مي کند ( pseudo-alphablending effect )
- vbSrcAnd : داده هاي تصاوير مبدا و مقصد را با هم AND مي کند ( pseudo-gamma effect )
- vbSrcInvert : داده هاي تصاوير مبدا و مقصد را با هم XOR مي کند
- vbSrcErase : ابتدا داده تصوير مقصد را invert مي کند سپس آنرا با داده تصوير مبدا AND مي کند .
- vbDstInvert : داده تصوير مقصد را invert مي کند و داده تصوير مبدا را در نظر نمي گيرد .
- vbNotSrcCopy : داده تصوير مبدا را invert مي کند و آنرا مستقيماً در مقصد کپي مي کند .
- vbNotSrcErase : داده تصاوير مبدا و مقصد را OR کرده و نتيجه را invert مي کند .
مثالي از کاربرد BitBlt :
BitBlt Form1.hDC, PlayerX, PlayerY, 48, 48, picPlayer.hDC, 0, 0, vbSrcCopy
حال مي خواهيم از BitBlt در يک حلقه استفاده کنيم تا يک image را در فرم حرکت دهيم :
1 – يک فايل bmp با ابعاد 32x32 بسازيد و با نام sprite1.bmp در دايرکتوري پروژه ذخيره کنيد .
2 – يک دکمه در فرم قرار دهيد و نام آنرا cmdTest بگذاريد .
3 – دکمه را در گوشه بالايي فرم و در سمت راست قرار دهيد .
4 – کد زير را براي event مربوط به کليک شدن دکمه بنويسيد :
'Timer variables...
Dim T1 As Long, T2 As Long
ساخت DC براي backbuffer’
myBackBuffer = CreateCompatibleDC(GetDC(0))
ساخت يک سطح bitmap براي DC’
myBufferBMP = CreateCompatibleBitmap(GetDC(0), 320, 256)
load کردن سطح bitmap خالي درون buffer’
SelectObject myBackBuffer, myBufferBMP
قبل از blit کردن درون بافر بايد آنرا با black پر کنيم’
BitBlt myBackBuffer, 0, 0, 320, 256, 0, 0, 0, vbWhiteness
load کردن split توسط تابعي که در بالا نوشتيم’
mySprite = LoadGraphicDC(App.Path & "\sprite1.bmp")
cmdTest.Enabled = False
== شروع حلقه اصلي ==’
خواندن tickcount جاري’
T2 = GetTickCount
Do
DoEvents
T1 = GetTickCount
اگر 15 ميلي ثانيه گذشته بود فريم بعدي شروع شود
If (T1 - T2) >= 15 Then
پاک کردن محل قبلي sprite بوسيله پر کردن آنجا با black
BitBlt myBackBuffer, SpriteX - 1, SpriteY - 1,32, 32, 0, 0, 0, vbBlackness
Blit کردن sprite درون back buffer’
BitBlt myBackBuffer, SpriteX, SpriteY, 32, 32,mySprite, 0, 0, vbSrcPaint
Blit کردن backbuffer روي فرم’
BitBlt Me.hdc, 0, 0, 320, 256, myBackBuffer,0, 0, vbSrcCopy
حرکت دادن sprite روي صفحه’
SpriteX = SpriteX + 1
SpriteY = SpriteY + 1
'update timer
T2 = GetTickCount
End If
Loop Until SpriteX = 320
سپس بايد يک cleanup code بنويسيد تا حافظه هاي را که براي نگهداري تصاوير گرافيکي و bufferها استفاده کرده ايد آزاد کنيد :
Private Sub Form_Unload(Cancel As Integer)
DeleteObject myBufferBMP
DeleteDC myBackBuffer
DeleteDC mySprite
End
End Sub
اگه نظرات بیشتر از ۱۵ نشه دیگه ناراحت میشم ها
موضوعات مرتبط: آموزش ، ،
برچسبها:
قبل از اینکه اموزش ListView رو بدم شما به این اشیا نیاز دارید
توضیح : ListView در قسمت component وی بی و در بخش Microsoft common control 6.0 است
یک عدد ListView که اونو فقط روی فرم قرار بدید
یک عدد imageList1 که فقط یک ایکون 16*16 درونش قرار بدید
یک عدد دیگر ImageList2 که فقط یک ایکون 32*32 درونش قرار بدید
بخش اول : اضافه کردن ایتم ها به ListView
برای اینکه بتوانیم با ListView هر کاری را انجام دهیم ابتدا باید تعدادی ایتم به ان اضافه کنیم. این کار اسان است.همه چیزی که شما احتیاج دارید این است که یک شی از ListItem بسازید و سپس به یک خط برای ساختن ایتم احتیاج خواهیم داشت. به مثال زیر توجه کنید
Dim itmx As ListItem
Set itmx = ListView1.ListItems.Add(, , "Item1")
این همه ان چیزی است که برای یک ایتم ساده به ان احتیاج داریم. کد بالا را به رویداد لود فرم بیافزاید و اجرا کنید. شما میتوانید ان ایتم را در ListView ببینید. اما تا اندازه ای ساده به نظر میرسد. در اینجا هیچ تصویری نمایش داده نشده است. برای نمایش تصاویر در ListView شما باید ان را به یک کنترل Image List (یا چند کنترل Image List) الحاق کنید. برای انجام این کار فقط روی خاصیتCustom کلیک کنید و زمانی که صفحه خواص برای ListView نمایش داده شد روی برگه Image Listکلیک کنید. برای گزینه Normal Image List > Image List1 را انتخاب کنید و برای گزینه Small Image List > Image List2 را انتخاب کنیدو سپس خارج شوید. حالا شما تصاویری را خواهید داشت که با اجرای برنامه برای هرکدام از ایتم ها خواهید داشت. بزودی تصاویر بسیاری را خواهیم داشت. برای اینکه هنگام اجرا تصویر شماره 1 از Image List1 و تصویر شماره 1 از Image List2 را نمایش دهیم فقط کافی است کد زیر را بنویسیم :
Dim itmx As ListItem
Set itmx = ListView1.ListItems.Add(, , "Item1",1,1)
حالا برنامه رو اجرا کنید
بخش دوم >> تغییر نما
چندین راه برای نمایش اطلاعات در ListView وجود دارد. مثلا میتوانید به مرورگر ویندوز نگاه کنید که میتوانید نمایش اطلاعات را در ان تغییر دهید . مثلا به شکا نمایش ایکون به شکا بزرگ یا نمایش ایکون به شکل کوچک یا ... برای دیدن هر کدام از حالات مختلف ان را در رویداد لود فرم قرار داده و اجرا کنید
Dim itmx As ListItem
Dim colx As ColumnHeader
'Add a bunch of items
Set itmx = ListView1.ListItems.Add(, , "Item1", 1, 1)
Set itmx = ListView1.ListItems.Add(, , "Item2", 1, 1)
Set itmx = ListView1.ListItems.Add(, , "Item3", 1, 1)
Set itmx = ListView1.ListItems.Add(, , "Item4", 1, 1)
Set itmx = ListView1.ListItems.Add(, , "Item5", 1, 1)
Set itmx = ListView1.ListItems.Add(, , "Item6", 1, 1)
Set itmx = ListView1.ListItems.Add(, , "Item7", 1, 1)
'Force window to be shown.
'This is just so you can see what is happening or else the
'messages would come up and the window would not have
'appeared yet
Me.Show
'Set the listview to icon
ListView1.View = lvwIcon
MsgBox ("You are now viewing the list in Icon View")
'Set the listview to small icon
ListView1.View = lvwSmallIcon
MsgBox ("You are now viewing the list in Small Icon View")
'Set the listview to list
ListView1.View = lvwList
MsgBox ("You are now viewing the list in List View")
'Add a column at runtime
'You can do this ahead of time in the controls
'property pages
Set colx = ListView1.ColumnHeaders.Add(, , "Name")
'Set the listview to report
ListView1.View = lvwReport
MsgBox ("You are now viewing the list in Report View")
در اینجا شما فقط باید خواص ListView را تنظیم کنید. ببینید که شما میتوانید نمای هر کدام انها را تغییر دهید
بخش سوم >> کار کردن با ستون ها
ایا بخاطر دارید نمای Report را در مرورگر ویندوز به شکل چی دیدید؟ دران حالت بصورت چند ستونی است و شامل : نام فایل و اندازه و .. است ممکن است مشکل به نظر برسد من به شما اطمینان میدهم که خیلی ساده است
این کدها را اجرا و بررسی کنید که هر کدام بعد از این کد دارای توضیحاتی هستند
Dim itmx As ListItem
Dim colx As ColumnHeader
'Add some columns
Set colx = ListView1.ColumnHeaders.Add(, , "Filename")
Set colx = ListView1.ColumnHeaders.Add(, , "Type")
Set colx = ListView1.ColumnHeaders.Add(, , "Size")
Set colx = ListView1.ColumnHeaders.Add(, , "Date")
'Add an item. The text here is always the
'First Column (Index 0)
Set itmx = ListView1.ListItems.Add(, , "Abstract.exe", 1, 1)
'Here is how we access each of the columns
itmx.SubItems(1) = "Program File"
itmx.SubItems(2) = "15 KB"
itmx.SubItems(3) = "10/10/1999"
'Set the listview to report
ListView1.View = lvwReport
شما می توانید ببینید که ما فقط به اضافه کردن تعدادی از ستونها احتیاج داشتیم مانند مثال قبلی. تفاوتی که در اینجا وجود دارد این است که ما به SubItem ها در یک ListItem دسترسی داریم. وقتی شما ابتدا یک ایتم متن را اضافه کنید ان را در Column(0) دریافت خواهید کرد.سپس برای قرار دادن متن در ستونهای دیگر ما لازم است که متن را به هر کدام از زیر ایتمها انتقال دهیم.برای مثال بالا من خودم را برای اضافه کردن تصاویر به header ستونها دردسر ندادم امل اگر شما از VB6 استفاده میکنید میتوانید این کار را نیز انجام دهید.
بخش چهارم >> رویدادهای ListView
در اینجا دو تا از رویدادهای اصلی که شما هنگام کار با ListView واقعا به انها احتیاج داردید توضیح میدهم. رویدادهای itemClick و ItemCheck.
رویداد ItemClick وقتی روی میدهد که روی لیست ایتمها کلیک شده باشد. این رویداد شی ListItemرا که روی ان کلیک شده است را بر میگرداند و بنابراین شما میتوانید با ان هر کاری را که لازم است انجام دهید.پس این کد را به مثال قبلی اضافه کنید
Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem)
MsgBox Item.Text
End Sub
وقتی شما برنامه را اجرا کنید و روی هر کدام از ایتمها در ListView کلیک کنید یک پیغام مبنی حاوی متن ایتمی که روی ان کلیک شده نمایش میدهد
رویداد دیگر برای وقتی است که شما حالتی را استفده کرده باشید که کنار هر کدام از ایتمهایListView یک جعبهچک نمایش داده شده باشد. این کد را به رویداد لود فرم اضافه کنید:
ListView1.Checkboxes = True
وقتی شما برنامه را اجرا کنید متوجه خواهید شد که یک جهبه چک در کنار هر کدام از ایتم ها داده شده است. ممکن است تا زمانی که روی ListView کلیک نشود ان را به روز نکند ( به نظر میرسد این یکی از باگهای VB باشد). اما اگر شما مقادیر جعبه چکها را در زمان طراحی تغییر دهید ان را بدرستی نمایش خواهد داد. برای انجام دادن کاری در زمان که کاربر جعبه چکها را تیک میزند و یا تیک را برمیدارد شما باید کد زیر را در رویداد ItemCheck قرار دهید.
Private Sub ListView1_ItemCheck(ByVal Item As MSComctlLib.ListItem)
If Item.Checked Then
MsgBox "Box is being checked"
Else
MsgBox "Box is being unchecked"
End If
End Sub
بخش پنجم >> مرتب کردن ListView بوسیله ستون ها:
در این بخش ما میخواهیم ایتم های را در حالت Report به ListView اضافه کنیم و وقتی که کاربر روی عنوان یکی از ستونها کلیک کرد میخواهیم ایتم های موجود در ListView را بر اساس همان ستون مرتب کنیم. اگر کاربر روی همان ستون دوباره کلیک کرد این مرتب سازی بین حالت صعودی و نزولی تغییر کند. ابتدا نگاهی به کد زیر میاندازیمکه تا اندازه ای واضح است:
Private Sub Form_Load()
Dim itmx As ListItem
Dim colx As ColumnHeader
'Add Some Columns
Set colx = ListView1.ColumnHeaders.Add(, , "Col1")
Set colx = ListView1.ColumnHeaders.Add(, , "Col2")
'Add two items
Set itmx = ListView1.ListItems.Add(, , "ABC")
itmx.SubItems(1) = "XYZ"
Set itmx = ListView1.ListItems.Add(, , "XYZ")
itmx.SubItems(1) = "ABC"
'Set the view to report
ListView1.View = lvwReport
End Sub
Private Sub ListView1_ColumnClick(ByVal ColumnHeader As
MSComctlLib.ColumnHeader)
'Check if the Sortkey is the same a the current one
If ListView1.SortKey <> ColumnHeader.Index - 1 Then
'When a column is clicked set the sortkey
'to the columnheader index -1
ListView1.SortKey = ColumnHeader.Index - 1
ListView1.SortOrder = lvwAscending
Else
'If the column is already selected then change the
'sortorder to be the opposite of what is currently
'being used
ListView1.SortOrder = IIf(ListView1.SortOrder = lvwAscending, _
lvwDescending, lvwAscending)
End If
'Set the sorted property to use the new sortkey
'and sort the contents
ListView1.Sorted = True
End Sub
بنابراین وقتی کاربر روی یک ایتم کلیک میکند رویداد Column_Click رخ میدهد که مرجعی به شیColumnHeader که کلیک شده است برمیگرداند. در این کد ما این را برای دسترسی به خاصیتindex برای مقدار دهی SortKey از کنترل ListView بکار برده ایم ( SortKey فقط میگوید ListViewچگونه مرتب شود ) وقتی که این کامل شد ما مقدار خاصیت Sorted کنترل ListView را به Trueمقدار میدهی.این اطمینان حاصل میکند که کنترل مرتب شده است و بوسیله این تغییرات درSortKey منعکس شده و نمایش داده شود. اگر همان ستون دوباره کلیک شد سپس ما نوع مرتب سازی را تغییر میدهیم و ان را برابر عکس روش فعلی قرار میدهیم
بخش ششم >> ذخیره کردن عناوین ستونهای مرتب شده :
ایا تاکنون شما برنامه های را دیده اید که چیزهایی را در ان مطابق میلتان تنظیم کرده اید و سپس از برنامه خارج شده و وقتی که دوباره به برنامه برگشته اید همه تنظیمات ان به حالت پیش فرض برگشته است و هیچ کدام از تنظیمات مورد نیاز شما را ذخیره نکرده باشد؟ این مساله بی نهایت ازار دهنده است و نشان دهنده یک برنامه نویسی ضعیف است. برای کنترل ListView این مساله در رابطه با ColumnHeader صدق میکند. اگر شما اجازه دهید که کاربر اندازه یا نوع مرتب سازی ستونها را تغییر دهد باید این تنظیمات را ذخیره کنید بنابراین دفعه بعد که کاربر از برنامه شما استفاده کند این تنظیمات را در اختیار خواهد داشت. در اینجا کدی را که شما برای انجام دادن این کار نیاز دارید قرار دادم البته بیشتر کدها رو قبلا گفتم و فقط یه مقدارش جدید است
Private Sub Form_Load()
Dim colx As ColumnHeader
'Let the user reorder the columns
ListView1.AllowColumnReorder = True
'Set view to report
ListView1.View = lvwReport
'Add some columns
Set colx = ListView1.ColumnHeaders.Add(, , "Col1")
Set colx = ListView1.ColumnHeaders.Add(, , "Col2")
Set colx = ListView1.ColumnHeaders.Add(, , "Col3")
Set colx = ListView1.ColumnHeaders.Add(, , "Col4")
Set colx = ListView1.ColumnHeaders.Add(, , "Col5")
Set colx = ListView1.ColumnHeaders.Add(, , "Col6")
'Loop though each ColumnHeader object and set the
'position of it dependent on what the user did
'the last time
For Each colx In ListView1.ColumnHeaders
colx.Position = GetSetting(App.Title, "Settings", "Col" & colx.Index,colx.Index)
colx.Width = GetSetting(App.Title, "Settings", "ColWidth" & colx.Index, colx.Width)
Next
End Sub
Private Sub Form_Unload(Cancel As Integer)
Dim colx As ColumnHeader
'Save the Position of each of the ColumnHeader
'Objects so we can load them the next time
'the user starts the program
For Each colx In ListView1.ColumnHeaders
SaveSetting App.Title, "Settings", "Col" & colx.Index,ListView1.ColumnHeaders(colx.Index).Position
SaveSetting App.Title, "Settings", "ColWidth" & colx.Index,ListView1.ColumnHeaders(colx.Index).Width
Next
End Sub
رویداد Unload فرم تا زمانی که همه ColumnHeader ها در کنترل ListView را بدست اورده و مکان و اندازه (عرض) انها را ذخیره کند ادامه پیدا میکند.سپس دفعه بعد که برنامه اجرا شود در رویداد لود فرم این خواص اشاره شده و مقادیر انها را به اشیای ColumnHeader خاص برمی گردانیم.
چند روز دیگه اموزش تری ویو رو میزارم سر بزنی
موضوعات مرتبط: آموزش ، ،
برچسبها:
فرستادن پیام کوتاه با ویژوال بیسیک
توجه : یه کتاب گرفتم : 211 نکته برای برنامه نویسان ویژوال بیسیک
ابتدا یک پروژه استاندارد باز کنید و سپس یک ماژول هم به اون اضافه کنید
و 4 تا TextBox و 4 تا Label و 2 تا Command به فرم بیافزایید
و بعدش نام TextBox ها رو مثل شکل قرار بدید و نام command1 رو btnSend بزارید
label ها رو هم دلخواه قرار بدید حالا این کدها رو هم تو فرمتون کپی کنید
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Sub btnSend_Click()
On Error Resume Next
btnSend.Enabled = False
MsgBox (SendMessage(txtusername.Text, txtpassword.Text, txtNumber.Text, txtMessage.Text))
btnSend.Enabled = True
End Sub
Private Sub Form_Load()
txtusername.Text = "User@host.com"
End Sub
حالا این کدها رو هم تو ماژولتون کپی کنید
Public Function SendMessage(username As String, password As String, _
destination As String, message As String)
Dim xmlstring As String
xmlstring = ") & "?> " & _
"<Request xmlns:xsi=" & Chr(34) & "http://www.w3.org/2001/XMLSchema-instance" & Chr(34) & " xsi:noNamespaceSchemaLocation=" & Chr(34) & "http://schema.2sms.com/1.0/0410_RequestSendMessage.xsd" & Chr(34) & " Version = " & Chr(34) & "1.0" & Chr(34) & ">" & _
"<Identification>" & _
"<UserID>>" & _
"<Password>" & password & ">" & _
"Identification>" & _
"<Service>" & _
"<ServiceName>SendMessage>" & _
"<ServiceDetail>" & _
"<SingleMessage>" & _
"<Destination>" & destination & ">" & _
"<Text>>" & _
"SingleMessage>" & _
"ServiceDetail>" & _
"Service>" & _
"Request>"
'open connection to server and send
Dim xmlrequest As MSXML2.XMLHTTP
Set xmlrequest = New MSXML2.XMLHTTP
xmlrequest.Open "POST", "http://www.2sms.com/xml/xml.jsp", False
xmlrequest.setRequestHeader "content-type", "text/xml"
xmlrequest.send xmlstring
'get the response back
response = xmlrequest.responseText
'set up DOM to parse
Dim xmlresponse As MSXML2.DOMDocument30
Set xmlresponse = New MSXML2.DOMDocument
xmlresponse.async = False
xmlresponse.resolveExternals = False
xmlresponse.validateOnParse = False
On Error Resume Next
xmlresponse.loadXML response
'pull out relevant variables from response
javaresult = (xmlresponse.getElementsByTagName("Result").Item(0).Text)
errorCode = (xmlresponse.getElementsByTagName("ErrorCode").Item(0).Text)
errorreason = (xmlresponse.getElementsByTagName("ErrorReason").Item(0).Text)
messageid = (xmlresponse.getElementsByTagName("MessageID").Item(0).Text)
If errorCode = "00" Then
SendMessage = javaresult
Else
If errorreason = "" Then
SendMessage = "Message Failed - Unknown Error"
Else
SendMessage = "Message FAILED. (Reason: " & errorreason & ")"
End If
End If
End Function
بچه ها من خودم تست نکردم ولی فکر کنم باید تو یه سایتی ثبت نام کنید
حالا بریم سراغ ولتمتر برای بچه های الکترونیک
یه پروژه استاندارد درست کنید و سپس 2 تا Radio button و 1 دونه Textbox و از قسمت کامپوننت ها
با زدن Ctrl + T ابزار Microsoft Comm Control 6.0 رو انتخاب و در اخر هم یک Command به فرمتون اضافه کنید
Radio 1 = optEnable
Radio 2 = optstop
text = txtVolts
cmd = cmdexit
MSComm = MSComm1
حالا این کدها رو تو فرمتون کپی کنید
Option Explicit
Private fEnable As Boolean
Private Sub cmdExit_Click()
If MSComm1.PortOpen = True Then
MSComm1.PortOpen = False
End If
End
End Sub
Private Sub Form_Load()
MSComm1.InputLen = 0
MSComm1.CommPort = 1
MSComm1.Settings = "9600,N,8,1"
End Sub
Private Sub optEnable_Click()
fEnable = True
Do Until fEnable = False
DoEvents
Dim BytesToRead As Integer
Dim DataIn As Variant
MSComm1.PortOpen = True
BytesToRead = 1
Do
DoEvents
Loop Until MSComm1.InBufferCount = BytesToRead
DataIn = MSComm1.Input
txtVolts.Text = Asc(DataIn) * 0.0196 & " Volts DC"
MSComm1.PortOpen = False
Loop
End Sub
Private Sub optStop_Click()
fEnable = False
End Sub
حالا برنامه رو اجرا کنید و باید از طریق درگاه کام یه ولتاژ دلخواه رو اعمال کنید
اموزش برنامه تبدیل عدد به زمان برای کارهای مولتی مدیا فوق العاده کاربردی
ابتدا یک فرم درست کنید و بعد دوتا TextBox قرار بدید و این کدها رو تو فرمتون کپی کنید
نکته : اینکه TextBox 1 برای وارد کردن عدد مورد نظر شماست
Private Sub Text1_LostFocus()
Dim isec As Integer
isec = Val(Text1.Text)
Dim breaksec
breaksec = Str$(Int(isec / 60)) & " ÏÞíÞå æ " & Str$(isec Mod 60) & " ËÇäíå"
Text2.Text = breaksec
موضوعات مرتبط: آموزش ، ،
برچسبها:
Dynamic Data Exchange پروتکلی است که انتقال داده بین برنامه های کاربردی تحت ویندوز را ممکن میسازد . با استفاده از این ارتباط میتونید اطلاعات مورد نظر خود را از سایر برنامه ها بدست آورید یا به آنها ارسال کنید ... البته این ارتباط باید از طرف برنامه ای که میخواهید ارتباط DDE رو با اون برقرار کنید مورد تایید باشه و اون برنامه هم با استفاده از روشهایی که در ادامه متوجه خواهید شد این اجازه رو به برنامه های دیگه بده که از اطلاعات اون استفاده کنند .
مثلا" Explorer ویندوز یا برنامه Excel یه همچین امکانی رو به برنامه نویسان میدن یا برنامه های دیگه ویندوز … علاوه بر این شما هم میتونید به برنامه های دیگه این جازه رو بدید که از اطلاعات برنامه شما استفاده کنن ... این که میگم اطلاعات منظورم فقط تصویر به صورت عکس و Text هست . چون فقط کنترلهای Label و Textbox و PictureBox از متدها و خواص مربوط به DDE پشتیبانی میکنند .
نکته ای که باید توجه کنید اینه که همیشه اول باید برنامه منبع ( Source ) اجرا شده باشه تا برنامه مقصد ( Distinition )
بتونه با روشهایی که اعلام میکنم ارتباط DDE رو برقرار کنه ..در غیر این صورت برنامه با خطا مواجه میشود .خوب فکر کنم قبل از اینکه متد ها و رویدادها و خواص مختلف مربوط به این ارتباط رو بررسی کنیم بهتر باشه یه مثال رو که ایجاد یک ارتباط ساده رو نشون میده با هم انجام بدیم بعد یکی یکی موارد رو بررسی کنیم .
ابتدا یک پروژه جدید تعریف کنید .... اول میخواهیم فرم و برنامه Server برنامه منبع رو طراحی کنیم
فقط کافیه روی فرم یک textbox مثلا" با نام intxt قرار بدهید ... بعد نام پروژه و نام فیل اجرایی که ساخته میشه رو به مثلا invblog تغییر بدید حالا فقط یک کار مونده که برای برنامه Server انجام بدید .... اون اینکه از قسمت خواص فرم در حالت design خاصیت LinkMode فرم رو به 1-Source تغییر بدهید
نکته : بهتره نام های کنترلها رو همینی که من تعیین کردم بگذارید چون بعدا در طراحی برنامه مقصد به این اسمها نیاز دارید و اینجوری اسمها رو اشتباه نمیکنید . چون کوچیکترین اشتباه برنامه رو در همون مرحله اول متوقف میکنه و اصلا" ارتباطی برقرار نمیشه .
حالا با هم برنامه مقصد یا Client رو طراحی می کنیم .
اینبار برای برنامه Client تعیین نام پروژه و فایل اجرایی مهم نیست . فقط کافیه که یک textbox روی فرم قرار بدهید و خاصیت LinkTopic عنصر textbox رو به invblog|Form1 تغییر بدهید ... invblog بر اساس نام پروژه یا نام فایل اجرایی است که textbox برنامه مقصد قرار است از آن فرمان بگیرد تعیین میشود و Form1 بر اساس مقدار تعیین شده در خاصیت LinkTopic فرم برنامه منبع ( Server ) تعیین میشود مقدار پیش فرض این خاصیت نام اولیه و پیش فرض فرم برنامه Server است که اگر این مقدار رو در برنامه اول تغییر دادید باید بجای پارامتر دوم خاصیت LinkTopic عنصر textbox در برنامه دوم (Client) قرار دهید.
لازم به تذکر است که این دو پارامتر با علامت | بدون فاصله از هم جدا میشوند
در آخر خاصیت LinkItem عنصر textbox رو با نام textbox برنامه Server که در اینجا intxt است مقدار دهی میکنیم و فقط یک خط Text1.LinkMode = 1 رو برای فرم لود برنامه مینویسیم .
توجه داشته باشید که موقع اجرا حتما" باید اول برنامه Server رو اجرا کنید بعد برنامه Client رو اجرا کنید وگرنه اجرا با مشکل مواجه میشه
حالا با احرای دو تا برنامه میبینید که هرچی تو textbox برنامه invblog تایپ میکنید تو textbox برنامه دوم هم تایپ میشهفقط اگه برنامه رو از روی فایل اجرایی اجرا میکنید نام فایل برنامه Server رو باید حتما ( در این مثال ) invblog قرار بدید وگرنه ارتباطی در کار نخواهد بود .
تو این مثال برای فایل منبع که اصلا کد نویسی نداشتیم و برای فایل مقصد هم که فقط یک خط Text1.LinkMode = 1 رو نوشتیم .... ولی تمامی این خواص که در حالت طراحی فرم تقییر دادیم مثل LinkTopic و .... از طریق کد نویسی هم میتونید انجام بدید .
خوب و اما بررسی دقیقتر خواص و متدهای لازم برای این کار همون طور که در مثال دیدید اولین کاری که باید بکنیم اینه که خاصیت Linkmode فرم برنامه منبع رو 1 قرار بدهیم .کار بعدی که باید انجام بدیم اینه که یه نام مشخص برای برای خاصیت LinkTopic فرم برنامه منبع تعیین کنیم اسم کنترلی که میخوایم با اون ارتباط برقرار کنیم رو هم در خاطر داشته باشیم که بعدا در برنامه مقصد لازمش داریم در مرحله آخر هم پروژه و فایل اجرایی برنامه رو با نام مشخصی ذخیره کنیم و اون نام رو هم به یاد داشته باشیم .
حالا برنامه منبع ما آماده هست باید برنامه دوم ( مقصد ) رو طراحی کنیم :
برای یک ارتباط ساده و اولیه سه تا از خصوصیات کنترلی که میخواهید ارتباط را با آن ایجاد کنید در برنامه مقصد تنضیم میکنید
این سه خصوصیت LinkTopic و LinkItem و Linkmode میباشد و به صورت زیر تعریف میشود .
نام کنترل . LinkTopic = “نام فایل اجرایی برنامه | مشخص شده در فرم برنامه منبع Linktopic "
در اینجا اگر برنامه رو در محیط vb اجرا میکنید و نه از طریق فایل اجرایی" نام فایل اجرایی برنامه " باید به نام فایل پروژه تغییر کند...
نام کنترل . LinkItem = " نام کنترلی ( در برنامه منبع ) که ارتباط باید با آن برقرار شود "
توجه کنید که کنترل میتواند Label و Textbox و PictureBox باشد اگر کنترل PictureBox باشد عکس آن انتقال داده میشود و در غیر این صورت متن منتقل میشود .
بعد از تعیین LinkTopic و LinkItem باید Linkmode تعیین شود .
1 یا 2 یا 3 . LinkeMode = نام کنترل
اگر این مقدار 1 باشد ارتباط و انتقال اطلاعات به صورت اتوماتیک میباشد . یعنی به طور مداوم با تغییر محتویات کنترل تعیین شده اطلاعات آن به کنترل لازمه انتقال داده میشود . یک نمونه از این نوع ارتباط را در مثال اول ملاحضه کردید .
اما اگر مقدار LinkeMode برابر 2 تعیین شده باشد انتقال اطلاعات به طور دستی صورت میگیرد … یعنی ارتباط در این حالت در صورت درست بودن برقرار میشود ولی اطلاعاتی بطور اتوماتیک منتقل نمیشود . در این حالت اطلاعات میتواند با فراخوانی متد LinkRequest منتقل شود .
نام کنترل . LinkRequest
اما حالت سوم LinkeMode با مقدار 3 میباشد .... این حالت هم مانند ارتباط قبلی غیر اتوماتیک میباشد که انتقال اطلاعات
باید از طریق LinkRequest صورت گیرد ... فقط در این حالت میتوان از رویداد Linknotify استفاده کرد
رویدادهای DDE
رویداد LinkNotify
این رویداد وقتی فراخوانی میشود که محتویات کنترل تعیین شده در LinkItem تغییر کرده باشد .یادآوری میشود که برای استفاده از این رویداد باید LinkeMode با مقدار 3 تعیین شده باشد . در واقع این رویداد تغییر محتویات کنترل مورد نظر در برنامه منبع را به برنامه مقصد اعلام میکند !
رویداد LinkClose : این رویداد وقتی فراخوانی میشود که ارتباط وصل شده DDE به هر دلیلی قطع شود ." مثلا یکی از برنامه های منبع یا مقصد بسته شود "
رویداد LinkError
اگر در طی ارتباط DDE Conversation خطایی رخ دهد این رویداد فراخوانی میگردد و شماره خطا به عنوان پارامتر و آرگومان این رویداد ارسال میشود .
رویداد LinkOpen
وقتی که ارتباط DDE مقدار دهی شود و ارتباط برقرار شود این رویداد فراخوانی میشود .
خاصیت LinkTimeOut
مدت زمانی که طول میکشه تا اطلاعات از یک برنامه به یک برنامه دیگه منتقل بشه در کامپیوتر های مختلف متفاوت میباشد
این زمان به حجم داده اطلاعاتی و سرعت کامپیوتر و حافظه آزاد کامپوتر و .... بستگی داره اگر برنامه ای در مدت معین شده در این خاصیت اطلاعات خود را نتواند انتقال دهد برنامه با خطا مواجه میشود .
مقدار پیشفرض این خاصیت 50 است که بع اندازه تقریبا" 5 ثانیه میباشد و مقدار مناسبی برای این خاصیت میباشد
متد LinkPoke:
این متد عکس عمل LinkRequest میباشد و انتقال اطلاعات از کنترل برنامه مقصد به کنترل برنامه منبع را انجام میدهد .
با استفاده از این دستور میتوانید مثلا" متنی را به برنامه دیگر بفرستید و نتیجه را در TextBox برنامه منبع مشاهدا کنید .
متد LinkSend
این دستور اطلاعات کنترل PictureBox را به برنامه مقصد میفرستد ... این دستور همان کار
LinkRequest را انجام میدهد با این تفاوت که در LinkRequest درخواست انتقال اطلاعات از کنترل منبع به مقصد از طرف برنامه مقصد صورت میگیرد ولی در LinkSend درخواست انتقال اطلاعات باز هم از کنترل منبع به مقصد از طرف برنامه منبع صورت میگیرد
متد LinkExecute
این دستور رشته ای را از یک برنامه ( منبع یا مقصد ) به برنامه دیگر میفرستد .... اما چگونه این رشته رو در برنامه دیگر دریافت کنیم ؟ با استفاده ار رویداد LinkExecute در برنامه دیگر
این دستور یک پارامتر ورودی از نوع String دارد که رشته مورد نظر را تعیین میکند
رویداد LinkExecute
این رویداد به محض ارسال رشته ای از برنامه دیگر در ارتباط DDE اتفاق میافتد و این رویداد دو پارامتر دارد که اولی رشته فرستاده شده و دومی Cancel میباشد که نشان میدهد آیا برنامه رشته را پذیرفته و دریافت میکند یا نه یه مثال میزنم فرض کنید برنامه مقصد به وسیله کد زیر دستوری را به برنامه دیگه میفرسته
Private Sub Command2_Click()
Text1.LinkExecute "www.vblog.blogfa.com"
End Sub
و در برنامه منبع هم کد زیر رو داریم :
Private Sub Form_LinkExecute(CmdStr As String, Cancel As Integer)
Me.Caption = CmdStr
Cancel = False
End Sub
همینطور که میبینید CmdStr رشته ای را در Caption فرم نمایش میدهد که با LinkExecute فرستاده شده بود
در اینجا اگر پارامتر Cancel رو بعد از اینکه مقدار فرستاده شده را دریافت کردیم False نکنیم با خطا 285 مواجه میشویم ...
مشخصه Causes Validation
شايد زمانی که داشتيد روی مشخصات (Properties) مربوط به Text Box نظر می کرديد، مشخصه causes Validation را نيز ديده باشيد که ما در بخش قبل از آن نام نبرديم. اين مشخصه در بسياری از VCLها نيز وجود دارد و دارای کاربرد بسيار زيبايی است که شايد اگر وجود نداشت، برای شبيه سازی آن بوسيله کدنويسی، زمان زيادی را صرف خود می کرد.
فرض کنيد بر روی فرم يک Text Box به نام txtNumber وجود دارد که برای دريافت نمره يک درس دانش آموزی از کاربر، استفاده می شود. ترجيح می دهيم کاربر نتواند اين فرم را save کند (نتواند به سراغ کليد cmdSave برود) مگر اينکه نمره صحيح (که در فاصله صفر تا بيست است،) را وارد نموده باشد. اما مثلاً بتواند روی دکمه cmdCancel برود و فرم را Cancel نمايدو يعنی می خواهيم اگر نمره در دامنه صحيح خود نبود روی برخی ديگر از آنها نرود.
اگر برای رويداد(Event) Validate از txtNumber ، چنين داشته باشيم:
Private Sub txtNumber_Validate (Cancel As Boolean)
IF Val (txtNumber) > 20 then
Cancel = True
End IF
End Sub
Event Procedure فوق می گويد هر وقت که txtNumber ، Validate شد، بررسی کن که آيا نمره کمتر از 20 هست يا خير که اگر نبود، پارامتر Cancel را True نمايد. حال اگر مشخصه Causes Validation از هر کنترلی را True نماييم، در صورتی که Val(txtNumber)>20 باشد، کاربر نمی تواند، آن کنترل را فوکوس دهد. لذا اگر مشخصه مذکور را برای کليد cmd Save برابر True و برای cmdCancel برابر False تغير دهيم، درصورت نبودن نمره در فاصله مناسب، کاربر می تواند عمليات دکمه Cancel را انجام دهد اما عمليات دکمه Save را خير مگر اينکه دوباره Text Box مذکور را با عددی مناسب Validate نمايد.
توجه کنيد که می توانيد برای زيبايی برنامه مذکور برای رويداد Key up از txtNumber نيز چنين داشته باشيم.
Private Sub txtNumber_KeyUp (KeyCode As Integer, shift As Integer)
IF Value (txtNumber)>20 then
txtNumber. Forecolore = VBRed
Else
txtNumber. Forecolore = VBBlack
Evd IF
End Sub
که برای نمايش اينکه با وارد نودن نمره ای به کاربر خطايی رخ داده است، رنگ متن آن را به رنگ قرمز (Red) در می آوريم و در غير اينصورت (يعنی بدون خطا) رنگ متن را به رنگ سياه (Black) در خواهيم آورد.
ذکر چند مشخصه ديگر:
1- اگر برای يک کليد (Command Button)، مشخصه Cancel را به True قرار دهيم، در صورت زدن کليد (Escape) ESC توسط کاربر، رويداد Click از اين کليد روی می دهد.
2- اگر برای يک کليد، مشخصه Default را به True تغيير دهيم، در صورت زدن کليد Enter توسط کاربر، رويداد Click از آن روی خواهد داد.
3- اگر برای يک فرم، رويداد Key Preview را به True تغيير دهيم، هر کليدی را کاربر از صفحه کليد فشار دهد، ابتدا اين کليد به رويداد KeyPress از فرم فرستاده می شود و آنگاه به رويداد Key Press از کنترلی که Focus در اختيار آن است، فرستاده می شود
*********************************
نکاتی در باره توابع ، متغيرها و نحوه استفاده آنها
ارسال متغير بصورت ByRef و ByVal
اگر موقع تعريف يک تابع قبل از نام متغير از عبارت ByRef استفاده نماييم هنگام فراخوانی تابع با قراردادن متغيری در تابع که دارای مقدارميباشد ، پس از محاسبات ، مقدار متغير نيز تغيير ميکند. در ضمن مقدار تابع با آخرين مقدار متغير محاسبه ميگردد:
Function Use_ByRef(ByRef intVar As Integer)
intVar = intVar + 1
Return intVar
End Function
Dim intMyVar As Integer
intMyVar = 1
Response.Write(intMyVar & "-----" & Use_ByRef(intMyVar))
پاسخ : 2-----3
ولی اگر در مثال فوق ازByVal استفاده کنيم ، پس از محاسبات مقدار متغير تغيير نميکند و نيز مقدار تابع با مقدار اوليه متغير محاسبه ميشود:
Function Use_ByVal(ByVal intVar As Integer)
intVar = intVar + 1
Return intVar
End Function
Dim intMyVar As Integer
intMyVar = 1
Response.Write(intMyVar & "-----" & Use_ByVal(intMyVar))
پاسخ : 1-----2
متغير Static
اگر درون تابعی متغيری را بصورت Static تعريف نماييم و مقدار اين متغير طي عمليات تابع تغيير نمايد در هرفراخوانی متغير ياد شده با آخرين مقدارخود در محاسبات شرکت ميکند.
اين مورد بر خلاف تعريف متغيير بوسيله دستور Dim است . چون در Dim متغيير بمحض تعريف شدن دوباره ، مقدار قبلي خود را از دست ميدهد.
Function Use_Static()
Static intCount As Integer
intCount = intCount+1
Return intCount
End Function
Response.Write( "
" & Use_Static)
Response.Write( "
" & Use_Static)
Response.Write( "
" & Use_Static)
1 پاسخ :
2
3
تعريف توابع بصورت OverLoads
در نظر بگيريد كه در جايي از برنامه خود احتياج داشته باشيد كه يك فانكشن را فراخواني و پارامترهاي ورودي آنرا كه استرينك است به آن پاس نماييد. حال اگر شما مجبور باشيد در مواقعي خاص به اين فانكشن بجاي استرينگ ، عدد يا يك متغيير از نوع ديگري پاس كنيد ، چكار بايد كرد؟ آيا بايد دو تا فانكشن با نامهاي متفاوت و نوع عملكرد متفاوت تعريف نمود ؟ آيا راه حل ديگري وجود ندارد؟
در اينجا راه حل ديگري نيز وجود دارد كه اگر دو يا چند تابع را با يک نام ثابت ولی تعداد يا نوع متغير متفاوت در يک کلاس تعريف نماييم ميتوانيم از هر کدام برحسب نياز استفاده کنيم:
Function Use_OverLoads(ByVal strVar1 As String, ByVal strVar2 As String)As String
Return strVar1 & strVar2
End Function
Function Use_OverLoads (ByVal intVar As Integer) As Integer
Return intVar + intVar
End Function
Response.Write(Use_OverLoads (5))
Response.Write("
" & Use_OverLoads("Over", "Loads"))
10 پاسخ :
OverLoads
*************************
4.برای رایت تو لفت کردن فرمها و هر کنترلی از این تابع استفاده کنیدو برای کنترلی خاص فقط به جای اسم فرم در پایین نام کنترل رو بنویسید
Option explicit
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Const WS_EX_LAYOUTRTL = &H400000
Private Const GWL_EXSTYLE = -20
Private sub form_Load()
SetWindowLong form1.hWnd, GWL_EXSTYLE, GetWindowLong(form1.hWnd, GWL_EXSTYLE) Or WS_EX_LAYOUTRTL
End sub
نکته !!!؟
1.برای بسته نشدن یک فرم میتوانید در رویداد UnLoad فرمتون عبارت Cancel = True رو تایپ کنید
2.برای دیدن عبارات فارسی در محیط کد میتوانید در قسمت تنظیمات برنامه در تب Editor Format فونت رو به Courier New (Arabic) تغییر بدهید
*************************
برای من و شما بعنوان برنامهنویس، صحت ورود اطلاعات از سمت کاربر در بیشتر حالات از اهمیت ویژهای برخوردار هست. یه نمونه ساده ورود آدرس پستالکترونیکی هست که باید بررسیهای ویژهای در اون گنجانده بشه. بعنوان مثال آدرسهای زیر همه صحیح هستند:
power@yahoo.com
power@ia.un.ir
power.station@yahoo.com
واقعا اگر قرار باشه حالات مختلف رو بررسی کنیم، راهش این نیست که به ازای هر حالت یه دستور شرطی رو بنویسیم. در این موارد میتونیم از عبارات باقاعده (Regular Expression) برای صحت یک ورودی استفاده کنیم.
ابزاری که در اینجا معرفی میکنم با عنوان MTrace از شرکت RegExLab هست که یه محیط ساده و خیلی کارآمد رو در اختیار ما قرار میده تا عبارت با قاعده خودمون رو در اون تعریف کنیم و سپس تست کنیم که چه رشتههایی در این عبارت قابل پذیرش خواهد بود.
و جالب تر اینکه سورس همین عبارت رو با زبانهای چون جاوا، ویبی، سی++ و سیشارپ ارائه میده که فقط باید کپی کنید و در برنامه خودتون قرار بدید.
برای نمونه یک عبارت باقاعده که صحت آدرس ایمیل رو نشون میده به این قرار هست:
^[a-zA-Z][w.-]*[a-zA-Z0-9]@[a-zA-Z0-9][w.-]*[a-zA-Z0-9].[a-zA-Z][a-zA-Z.]*[a-zA-Z]$
در این ابزار بعد از ورود عبارت، کلیه اجزای آنرا با رنگهای مختلف و بخشهای قابل تفکیک در ساختاری درختی به نمایش میذاره. برای درک درستی از عملکرد این ابزار به تصویر زیر نگاه کنید.
سورس زیر نمونه کد تولید شده به زبان C# هست.
using System.Text.RegularExpressions;
// regular expression object
Regex re = new Regex(@"^[a-zA-Z][w.-]*[a-zA-Z0-9]@[a-zA-Z0-9][w.-]*[a-zA-Z0-9].[a-zA-Z][a-zA-Z.]*[a-zA-Z]$");
// Match object
Match m = re.Match("your string");
// found or not
if( m.Success )
{
// found
}
else
{
// not found
}
موضوعات مرتبط: آموزش ، ،
برچسبها: