PDA

View Full Version : سوال: تابع های ای پی آی TextOut و CopyMemory



IranVB
پنج شنبه 20 مهر 1391, 18:05 عصر
با سلام به همه اساتید برنامه نویس
من دو تابع api دارم که نامشون رو در عنوان تاپیک مشاهده میکنید، دوستان میتوانید به من کمک کنید که چه کاربردی دارند مخصوصا دومیش (اولین توضیح ندین هم مشکلی نیست)، اموز داشتم داخل تاپیک های انجمن را میدیدم دیدم یکی از دوستان فکر کنم جناب M.safa بودن درمورد تابع CopyMemory توضیح داده بودن که تازه یادم اومد چقدر یادگیری این تابع منو پیر کرد و آخرش هم نتونستم درست یادش بگیرم،
چندتا سوال داشتم:
1- این تابع چکار میکنه؟
2- درکجا لازم هست استفادش بکنیم؟ یکجایی خوندم که نوشته بود تکه ای از مموری رو به جای دیگه منتقل میکنه خب این چکاریه :لبخند:! با دوتا متغیر هم میتونیم یک مقداری رو از یکی از آدرس ها که همون متغیرباشه به آدرس بعدی که متغیردوم باشه منتقل کنیم

درکل هرتوضیحی که فکر میکنین من مبتدی در api رو در درک این تابع کمک بهتری میکنه
پیشاپیش خیلی ممنونم از کمک اساتید

مهرداد صفا
پنج شنبه 20 مهر 1391, 22:50 عصر
با سلام
البته من از شاگردها هستم.
اول ببخشید اگر موضوع را خوب منتقل نکردم:ناراحت: یا اگر توضیحات اضافه دادم.
تابع TextOut برای نوشتن یا رسم کردن متن روی یک پنجره یا کنترل استفاده میشود که تقریبا کارآیی شبیه به متود print را دارد.
نحوه تعریف:

Public Declare Function TextOut Lib "gdi32" Alias "TextOutA" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal lpString As String, ByVal nCount As Long) As Long

آرگومنتها:
hdc هندل dc پنجره مقصد است که متن روی آن نوشته میشود.
x,y مختصات نقطه ای است که متن آنجا نوشته میشود.
lpString متنی است که باید نوشته شود.
nCount طول رشته lpString است. متن از ابتدای lpString به تعداد nCount کاراکتر نوشته میشود.
مثال:

'in the name of god
'صل الله علی محمد و آله
Private Declare Function TextOut Lib "gdi32" Alias "TextOutA" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal lpString As String, ByVal nCount As Long) As Long

Private Sub Form_Paint()
TextOut Me.hdc, 20, 20, "ali", 3
End Sub


بیشتر زمانی کارآیی دارد که میخواهید روی یک پنجره بیرون از محیط نرم افزار خود متنی را بنویسید.

تابع CopyMemory برای انتقال مقداری از حافظه از یک نقطه به نقطه دیگر استفاده میشود.
نحوه تعریف:

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

آرگومنتها:

Destination اشاره گر به نقطه مقصد است.
Source اشاره گر به نقطه مبدا است.
length تعداد بایتهای حافضه است که باید کپی شوند.

در vb وقتی آرگومنتی به صورت ByRef تعریف میشود به جای متغیر آدرس آن به تابع فرستاده میشود که به آن اشاره گر میگویند. تابع CopyMemory در حقیقت فقط به آدرس مبدا و مقصد نیاز دارد تا تعداد بایتها را کپی کند حالا نوع متغیرها هر چیزی باشد مهم مکان آنهاست. به همین دلیل آرگومنتهای مبدا و مقصد به عنوان Any تعریف شدهاند.
به این کد دقتی کنید:

Dim a As Integer
Dim b As Integer
a = 5
CopyMemory b, a, 2
MsgBox b

در یک مثال ساده دو integer دو بایتی داریم که از a دو بایت را به b کپی کردیم.
در واقع در اینجا فقط b=a شده است اما گاهی (بیشتر در توابع api) پیش می آید که شما مثلا به جای یک type که دارای چند فیلد است یک متغیر از نوع long را به صورت ByRef تابع استفاده کردید و حالا متغیر ما فقط آدرس type مورد نظر را دارد. در اینجا باید از CopyMemory استفاده کنیم و فضایی را که متغیر به آن اشاره میکند در یک متغیر از نوع ساز گار با type مورد نظر کپی کنیم. این مورد را ان شالله بعدا با یک مثال بررسی میکنیم تا مطلب را درک کنیم.
فرض کنید که یک آرایه از 100000 بایت داریم که میخواهیم مقادیر آن را در یک آرایه دیگر کپی کنیم، در اینجا یک حلقه طولانی با 100000 تکرار سرعت برنامه را کاهش میدهد درحالی که میتوانیم با CopyMemory به راحتی و با سرعت بیشتری این کار را انجام دهیم:

Dim buffer1(100000) As Byte
Dim buffer2(100000) As Byte
'...
'...
CopyMemory buffer2(1), buffer1(1), 100000

از خانه buffer1(1) 100000 بایت خوانده میشود که در واقع چون اعضای آرایه در حافضه پشت سر هم قرار میگیرند شامل 100000 عضو آرایه میشوند و از اولین خانه buffer2 قرار میگیرند که در اینجا هم این 100000 خانه برای buffer2 رزرف شده بوده و حالا تمام اعضای آن مقدار دهی شده اند.
میتوانید برای اینکه بیشتر متوجه موضوع شوید یک آرایه شامل 10 integer را در یک آرایه شامل 20 byte کپی کرده و در یک فایل به صورت binary ذخیره کنید و دوباره 20 بایت را از فایل خوانده در آرایه integer بریزید و نتیجه را ببینید.

IranVB
جمعه 21 مهر 1391, 10:08 صبح
سلام
خیلی ممنونم عالی بود خیلی بهم کمک کرد نحوه استفادش رو فهمیدم، اما هنوز درک نکرده ام کلا در کجاها به این تابع نیاز هست
آیا فقط در زمانی که تابع اشاره گری رو بجای نتیجه اصلی برگردونه به این تابع نیاز هست؟ و یا دوتا نوع شبیه هم نباشن مثلا همون Type که دربالا توضیح دادین یا نه جاهای دیگه ای هم هست که وجود این تابع نیاز میشه؟
چون مثلا اگه به من بگن Replace چکار بردی داره براحتی میتونم بگم در زمانی که لازم باشه یه رشته در مجموعه رشته ای تغییر کنه بجای اینکه اینکار رو با حلقه انجام بدم و زمان برباشه یکباره با Replace انجامش میدیم، یا اگه بگن listbox چکاربردی داره میگم لیست از آیتم ها رو نگهداری میکنه در زمانی لازم هست مجموعه ای از آیتمها رو لیست کنیم. من میخوام اگه ازم پرسیدن CopyMemory چکاربردی داره کاملا مثل شما بهش اشراف داشته باشم و مثل دستورات بالایی راحت معرفیش کنم و بتونم در برنامه هام تشخیص بدم که مثلا الآن لازم هست از این API استفاده کنم

بازم خیلی ممنونم از وقتی که برای این تاپیک گذاشتین

مهرداد صفا
سه شنبه 25 مهر 1391, 03:48 صبح
سلام خواهش میکنم.
این تابع دسترسی مستقیم به حافظه و خواندن و نوشتن و انتقال مقادیر آن را در vb ممکن میکند. این یک تابع low level برای دسترسی به حافظه(direct memory access) است و میتواند موارد استفاده گسترده و گوناگونی داشته باشد و این برنامه نویس است که با استفاده به جا می تواند سرعت و کارآیی برنامه را افزایش دهد؛ اما شما گاهی مخصوصا زمانی که با توابع API کار میکنید نیاز به استفاده از اشاره گرها دارید که در vb برای دسترسی به مقداری که اشاره گر، به آن اشاره میکند ملزم به استفاده از این تابع می شوید.
در زیر چند نمونه کاربرد این تابع را میبینیم که امیدوارم موجب درک هر چه بهتر کار شود:

'in the name of god
Option Explicit

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal length As Long)

اولین مورد تبدیل ansi string به آرایه ای از بایت می باشد که ممکن است در مواردی مثل رمزگذاری رشته ها کاربرد داشته باشد.

Function StringToBytes(ByVal text As String) As Byte()
text = StrConv(text, vbFromUnicode)
Dim bytes() As Byte
ReDim bytes(Len(text)) As Byte
CopyMemory bytes(0), ByVal StrPtr(text), Len(text)
StringToBytes = bytes
End Function

همانطور که ملاحظه می فرمایید تابع بالا یک رشته به عنوان ورودی دریافت می کند. در vb معمولا رشته ها به صورت آرایه ای از کاراکتر های unicode ذخیره میشوند. که در این حالت هر کاراکتر دو بایت از حافظه را اشغال میکند. با استفاده از StrConv رشته را به ansi تبدیل کردیم، در این حالت هر کاراتر یک بایت را اشغال میکند. حالا یک آرایه به طول رشته ورودی داریم که با استفاده از CopyMemory به تعداد طول رشته از مکانی که رشته ورودی در آنجا وجود دارد به مکان اولین خانه از آرایه منتقل میکنیم. به عنوان اولین آرگومنت bytes(0) را به صورت ByRef به CopyMemory ارسال میکنیم که در این حالت vb اشاره گری (یک عدد چهار بایتی که حاوی آدرس متغیر است) را به جای مقدار اصلی به تابع ارسال میکند؛ اما در مورد رشته ها قضیه متفاوت است. رشته ها در vb در واقع آرایه ای از کاراکترها هستند که نام رشته فقط یک اشاره گر به آدرس این آرایه است، پس در این صورت اگر رشته را به صورت ByRef ارسال کنیم در واقع یک اشاره گر به یک اشاره گر دیگر ارسال کرده ایم و CopyMemory آدرس یک عدد چهار بایتی (اشاره گر به آرایه ای از کاراکتر ها) را دریافت میکند؛ برای حل این مشکل با استفاده از تابع StrPtr آدرس اولین خانه از رشته را گرفته و به صورت ByVal به تابع ارسال میکنیم. در واقع هر زمانی که ما آدرس متغیر را داشته باشیم میتوانیم به جای اینکه خود متغیر را به صورت ByRef ارسال کنیم( تا vb آدرس آن را ارسال کند)، خودمان آدرس آن را به صورت ByVal ارسال کنیم.
تابع زیر شبیه به left$ عمل میکند:

Function LeftOfString(ByVal text As String, count As Long) As String
LeftOfString = Space(count)
text = StrConv(text, vbFromUnicode)
CopyMemory ByVal LeftOfString, ByVal StrPtr(text), count
End Function

ابتدا به تعداد count که طول رشته بازگشتی تابع است فضای خالی در LeftOfString ایجاد کردیم. انجام این کار به این دلیل است که رشته قبل از ارسال به تابع CopyMemory باید فضای لازم برای دریاف مقادیر را داشته باشد، در غیر این صورت ممکن است برنامه با مشکل رو به رو شود. حالا count تا بایت از اولین کاراکتر در text به LeftOfString کپی میکنیم و تمام.
تابع زیر ب ما این امکان را میدهد تا تقریبا شبیه به یک آرایه به کاراکترهای یک رشته دسترسی داشته باشیم:

Function Char(text As String, index As Long) As String
Char = " "
text = StrConv(text, vbFromUnicode)
CopyMemory ByVal Char, ByVal StrPtr(text) + index, 1
End Function

اگر به دومین پارامتری که به CopyMemory ارسال کردیم دقت کنید میبینید که مقدار اشاره گر را با index جمع کرده ایم. دلیل این کار این است که تابع StrPtr مکان اولین خانه از رشته را به ما میدهد، پس اگر ما مثلا سومین خانه را بخواهیم میتوانیم به مقدار اشاره گر دو عدد اضافه کنیم.
تابع زیر شبیه به mid$ عمل میکند:

Function SubString(ByVal text As String, start As Integer, length As Long) As String
SubString = Space(length)
text = StrConv(text, vbFromUnicode)
CopyMemory ByVal SubString, ByVal StrPtr(text) + start, length
End Function

تابع زیر یک بایت از یک عدد را میگیرد که میتوانید مثلا برای به دست آوردن مقدار رنگ قرمز در رنگ یک شی از آن استفاده کنید:

Function GetByte(number As Long, ByteIndex As Integer) As Byte
CopyMemory GetByte, number, 1
End Function

با استفاده از دو تابع زیر میتوانید یک عدد دو بایتی غیر منفی داشته باشید و تا عدد 65535 را در یک integer ذخیره کنید:

Function IntegerToUnsigned(SignedInteger As Integer) As Long
CopyMemory IntegerToUnsigned, SignedInteger, 2
End Function

Function UnsignedToInteger(Unsigned As Long) As Integer
If Unsigned < 0 Or Unsigned > 65535 Then
Beep
Exit Function
End If
CopyMemory UnsignedToInteger, Unsigned, 2
End Function

نتیجه اجرای توابع:

Private Sub Form_Load()
Dim str1 As String
str1 = "Alireza abasi"
MsgBox StringToBytes(str1)(0) '65
MsgBox LeftOfString(str1, 3) 'ali
MsgBox SubString(str1, 3, 4) 'reza
MsgBox Char(str1, 2) 'i
str1 = Hex(GetByte(&HABACADAE, 4))
MsgBox str1 'AE
Dim value As Integer
value = UnsignedToInteger(65535)
MsgBox IntegerToUnsigned(value)
End Sub

یا به عنوان مثال زمانی که به جای یک type یا struct یک اشاره گر از آن را داریم، باید با استفاده از CopyMemory به مقادیر دسترسی پیدا کنیم. در کار با پیام های ویندوز(windows messages) به چنین مواردی برخورد میکنیم. در کد زیر یک hook در ویندوز ایجاد می کنیم:

'in the name of god
'درون یک ماجول
Private Type KBHOOKSTRUCT
vkCode As Long
scanCode As Long
flags As Long
time As Long
dwExtraInfo As Long
End Type

Public Const WH_KEYBOARD_LL = 13
Public Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
Public Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
Public Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal ncode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Sub CopyMemoryLong Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Long, ByVal Length As Long)
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Public HookHW As Long

Public Function myfunc(ByVal code As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Dim kybd As KBHOOKSTRUCT
myfunc = True
If code = HC_ACTION And wParam <> 257 Then
CopyMemory kybd, ByVal lParam, Len(kybd)
Form1.Caption = Form1.Caption + Chr(kybd.vkCode)
myfunc = CallNextHookEx(Hook, code, wParam, lParam)
ElseIf code < 0 Then
myfunc = CallNextHookEx(Hook, code, wParam, lParam)
End If
End Function
'کد درون فرم:
Private Sub Form_Load()
'SetHook
HookHW = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf myfunc, App.hInstance, 0)
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
'Unhook
Call UnhookWindowsHookEx(HookHW)
End Sub

در کد بالا تابعی را به عنوان CallBack به ویندوز معرفی کردیم و ویندوز این تابع را فراخوانی کردهو اشاره گری به یک struct یا type حاوی جزئیات وضعیت کیبورد را به آن ارسال میکند که با CopyMemory اطلاعات آن را به دست می آوریم.
در استفاده از CopyMemory و hook دقت کنید که موجب اختلال در کار برنامه خود و دیگر برنامه ها نشوید!

البته موارد فوق صرفا برای درک بهتر نحوه کار و شیوه های استفاده از تابع CopyMemory می باشند و شاید منطقی ترین کارها نباشند.