ورود

View Full Version : سوال: مشکل در مورد پردازش پیام WM_COMMAND در روال دیالوگ



feri88
شنبه 02 خرداد 1388, 00:55 صبح
سلام
من یه دیالوگ دارم که به این صورته:
یک رشته رو تجزیه می کنه و اونو به چند تا زیر رشته تقسیم می کنه و توی یه ComboBox نشون می ده و کاربر باید یکی از اینا رو انتخاب کنه و و بعد از زدن دکمه ی ok ، زیر رشته ی انتخابی اش، اول به روال دیالوگ و بعد به تابع فراخوانی کننده ی دیالوگ انتقال پیدا کنه و ...
در ضمن اون رشته اولیه که به روال دیالوگ فرستاده می شه، به صورت زیره:

TCHAR* buffer=”substring1 \0 substring2 \0 substring3\0\0”

روالش به صورت زیره:

BOOL CALLBACK DlgProc(HWND hwndDlg,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
HWND hwndComboBox ;
hwndComboBox = GetDlgItem(hwndDlg, IDC_COMBO1);
TCHAR* =new param TCHAR[30];
TCHAR * pparamName;
int nPtr=0;
//counter
int n=0,i=0;
int a=0,iIndex, iLength;
static TCHAR* buff;

switch (message)
{
case WM_INITDIALOG:
buff = (TCHAR *) lParam ;
SendMessage (hwndComboBox, LB_RESETCONTENT, 0, 0) ; while(buff[nPtr] != '\0')
{
while(buff[nPtr] != '\0')
{
param [nPtr-n]= buff[nPtr];
nPtr++;
i++;
} // while do
nPtr++;
n=++nPtr;
TCHAR* param1;
param1=new TCHAR[i+1];
for(int k=0;k<i;k++)
param1[k]= param[k];
param1[i]='\0';
i=0;
static int p=0;
SendMessage (hwndComboBox, CB_INSERTSTRING, p, (LPARAM) param1) ;
delete[]param1;
p++;
}// while do

case WM_COMMAND:
if (LOWORD (wParam) == IDC_COMBO1 && HIWORD (wParam) == LBN_SELCHANGE)
{

// Get current selection.
iIndex = SendMessage (hwndComboBox, CB_GETCURSEL, 0, 0) ;
iLength = SendMessage (hwndComboBox, CB_GETLBTEXTLEN, iIndex, 0) + 1 ;
pparamName =(TCHAR*) calloc (iLength, sizeof (TCHAR)) ;
SendMessage (hwndComboBox, CB_GETLBTEXT, iIndex, (LPARAM) pparamName) ;
return TRUE;
}
if(LOWORD (wParam) == IDOK){
while(pparamName [a]!='\0')
{
buff[a]= pparamName[a] ;
a++;
}
buff[a]='\0';
EndDialog (hwndDlg, TRUE) ;
return TRUE ;
}
if(LOWORD (wParam) == IDCANCEL){
EndDialog (hwndDlg, FALSE) ;
return TRUE ;
}
break;
}
return FALSE;
}


در صورتی که پیغام ، WM_INITDIALOG باشه،میاد رشته ورودی رو از lParam میگیره و بعد اون رو parse (تجزیه) می کنه و به ComboBox اضافه می کنه،یعنی الآن محتویات CobmoBox هست:
1- substring1
2- substring2
3- substring3

در صورتیکه پیغام WM_COMMAND باشه، مقدار LOWORD (wParam) رو چک می کنه، در if اول، اون زیررشته ای رو که کاربر انتخاب می کنه میگیره و در متغیر pparamName می ریزه.
حالا من می خوام اگه LOWORD (wParam) ، مقدارش IDOK شد، محتوای pparamName رو توی buff بریزه تا این زیر رشته رو برگردونم به همون تابع فراخوان دیالوگ!

سوالم اینه که چه جوری pparamName رو به بخش IDOK انتقال بدم؟
من که این متغیر رو توی بخش IDC_COMBO1 مقدار دادم، چه جوری می تونم از این مقدار توی بخش IDOK استفاده کنم؟

چون الان ارور زمان اجرا میده که متغیر pparamName ، initialize نشده!

ممنون

feri88
چهارشنبه 06 خرداد 1388, 18:28 عصر
سلام
اگه ممکنه حداقل یه منبع واسه حل این مشکل معرفی کنید.
خیلی جوابش واسم مهمه
سایتهای خارجی رو هم گشتم ولی جوابشو پیدا نکردم
ممنون

Nima_NF
پنج شنبه 07 خرداد 1388, 01:19 صبح
من دقیق متوجه مشکل شما نشدم، اما یک اشکال در کار شما دیدم.
وقتی می خواهید از اشاره گر buff استفاده کنید، ابتدا باید به اندازه کارکترهای مورد نظر به آن حافظه اختصاص دهید (با new در ++C ) و سپس آن را پر کنید، یعنی اگر قرار هست محتوای pparamName رو توی buff بریزید، به تعداد کارکتر pparamName ابتدا برای buff حافظه تخصیص دهید و سپس با یک فراخوانی strcpy یا معادل های یونیکد آن، محتویات را کپی کنید. (پس نیاز نیست از while استفاده کنید تا تک تک کارکترها را کپی کنید فقط کافیست strcpy را فراخوانی کنید)

ضمنا توصیه می کنم از new به جای calloc استفاده کنید.

feri88
شنبه 09 خرداد 1388, 11:40 صبح
سلام
خیلی ممنون
دقیقا یک مشکل اینجا بود که من buff رو new نکرده بودم !

و اما منظورم این بود که وقتی WM_COMMAND داره پردازش میشه، در if اول که متغیر pparamName ، حافظه بهش تخصیص داده میشه و مقدار میگیره، چرا این مقدار در if دوم یعنی if مربوط به IDOK ، قابل دسترسی نیست؟
من وقتی این روال رو اجرا می کنم ، اول دیالوگ ظاهر میشه و محتویات ComboBox اون سه تا زیر رشته اس،بعد میام یکی از اینها رو انتخاب می کنم(پس یعنی تو اینجا if اول از بخش WM_COMMAND حتما اجرا میشه!)
خب حالا که pparamName حافظه گرفته و مقدار دهی شده(با زیر رشته ای که من در if اول WM_COMMAND انتخاب کردم) چرا در if دوم (مربوط به IDOK) نمی تونم به این مقدار دسترسی داشته باشم؟
در حالیکه من یکبار if اول (یعنی انتخاب زیر رشته) رو اجرا کردم!
در ضمن return TRUE ی اولین if رو هم برداشتم و برنامه رو دوباره اجرا کردم، ولی بازم مقدار pparamName در if دوم قابل دسترسی نبود.
و اینکه اگه بخوام که در if دوم قابل دسترسی باشه، چی کار باید بکنم؟
در ضمن دلیل اینکه من اصرار داشتم که در if دوم مقدار buff رو آپدیت کنم و به عنوان متغیر خروجی دیالوگ این مقدار جدید رو به تابع فراخوانی کننده ی دیالوگ ارسال کنم، این بود که:

در مورد ارسال یک پارامتر به داخل روال دیالوگ، تو صفحات 474 و 475 از کتاب برنامه نویسی win32 توضیحاتی نوشته شده که با اجازه ی شما اینجا میارم:



Avoiding Global Variables
The use of global variables in ABOUT2 may or may not be disturbing to you. Some programmers (myself included) prefer to keep the use of global variables to a bare minimum. The iCurrentColor and iCurrentFigure variables in ABOUT2 certainly seem to qualify as legitimate candidates for global definitions because they must be used in both the window procedure and the dialog procedure. However, a program that has many dialog boxes, each of which can alter the values of several variables, could easily have a confusing proliferation of global variables.
You might prefer to conceive of each dialog box within a program as being associated with a data structure containing all the variables that can be altered by the dialog box. You would define these structures in typedef statements. For example, in ABOUT2 you might define a structure associated with the About box like so:

typedef struct
{
int iColor, iFigure ;
}
ABOUTBOX_DATA ;


In WndProc, you define and initialize a static variable based on this structure:

static ABOUTBOX_DATA ad = { IDC_BLACK, IDC_RECT } ;

Also in WndProc, replace all occurrences of iCurrentColor and iCurrentFigure with ad.iColor and ad.iFigure.
When you invoke the dialog box, use DialogBoxParam rather than DialogBox. This function has a fifth argument that can be any 32-bit value you'd like. Generally, it is set to a pointer to a structure, in this case the ABOUTBOX_DATA structure in WndProc:


case IDM_ABOUT:
if (DialogBoxParam (hInstance, TEXT ("AboutBox"),
hwnd, AboutDlgProc, &ad))
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;


Here's the key: the last argument to DialogBoxParam is passed to the dialog procedure as lParam in the WM_INITDIALOG message.
The dialog procedure would have two static variables (a structure and a pointer to a structure) based on the ABOUTBOX_DATA structure:


static ABOUTBOX_DATA ad, * pad ;


In AboutDlgProc this definition replaces the definitions of iColor and iFigure. At the outset of the WM_INITDIALOG message, the dialog procedure sets the values of these two variables from lParam:


pad = (ABOUTBOX_DATA *) lParam ;
ad = * pad ;


In the first statement, pad is set to the lParam pointer. That is, pad actually points to the ABOUTBOX_DATA structure defined in WndProc. The second statement performs a field-by-field structure copy from the structure in WndProc to the local structure in DlgProc.
Now, throughout AboutDlgProc, replace iFigure and iColor with ad.iColor and ad.iFigure except in the code for when the user presses the OK button. In that case, copy the contents of the local structure back to the structure in WndProc:


case IDOK:
* pad = ad ;
EndDialog (hDlg, TRUE) ;
return TRUE ;

تو آخر این توضیحات اومده که در IDOK، باید مقدار نهایی متغیر ورودی روال دیالوگ رو تغییر داد،
چون اینجا من می خوام buff که اول حاوی اون سه تا زیر رشته اس، بعد از اتمام دیالوگ محتویاتش بشه اون زیر رشته ی انتخابی من و بعدش این زیر رشته از روال دیالوگ به تابع فراخوان دیالوگ انتقال پیدا کنه که نهایتا بتونم از اون زیررشته هه استفاده کنم، می خواستم که توی IDOK مقدارشو آپدیت کنم!

همین توضیحات منو به اشتباه انداخت،ولی من برای رفع این مشکل اومدم تو همون if اول این مقدار رو به buff انتقال دادم و دیگه توی IDOK فقط دیالوگ رو END کردم و درست هم جواب داد.

پس حتما لزومی نداره که توی IDOK ، مقدار نهایی متغیر ارسالی آپدیت بشه، درسته؟

راستی چون پستم یه خرده طولانی شد، سوالام رو پررنگ کردم.
بازم ممنون

Nima_NF
شنبه 09 خرداد 1388, 14:48 عصر
خب حالا که pparamName حافظه گرفته و مقدار دهی شده(با زیر رشته ای که من در if اول WM_COMMAND انتخاب کردم) چرا در if دوم (مربوط به IDOK) نمی تونم به این مقدار دسترسی داشته باشم؟وقتی یک مرتبه در case شما WM_COMMAND دریافت می کنید و یکدفعه دیگر IDOK ، به این معنی نیست که این دو پشت سر هم حتما ارسال شده اند. ممکن است WM_COMMAND قبل از کار شما ده ها مرتبه فراخوانی شود و اصلا به IDOK هم نرسد، پس بارها این متغیر های داخل این تابع ساخته می شود و حافظه تخصیص داده می شود و باطل می شود. مثلا ارسال WM_COMMAND در هنگام شروع برنامه که کنترل های برنامه پیامشان را می فرستد.

برای اجری کدهای شما که در case قرار می دهید تابع DlgProc بارها اجرا می شود، پس اگر قرار هست متغیری محتوای خود را نگه داشته باشد باید آن را یا static تعریف کنید یا به صورت عمومی.
پس pparamName هم همین مشکل را دارد که یا static تعریف کنید یا به صورت عمومی. (همین طور سایر متغیر ها که با اجرای مجدد DlgProc برای اجرای case بعدی فراخوانی می شوند باید به همین شکل باشند)

یک نکته، فقط باید یک مرتبه حافظه تخصیص داده شود، یعنی مثلا در حالت static همیشه بررسی کنید اگر حافظه اشاره گر شما NULL نیست حافظه را با new بدهید تا فقط همان دفعه اول تخصیص دهید نه صد مرتبه با فراخوانی صد مرتبه WM_COMMAND


پس حتما لزومی نداره که توی IDOK ، مقدار نهایی متغیر ارسالی آپدیت بشه، درسته؟خیر هر جایی در پروسه برنامه بعد از اجرای WM_INITDIALOG می تواند محتویات ارسال شود، فقط گرفتن اشاره گر اولی هست که حتما باید در WM_INITDIALOG اجرا شود چون فقط در همان پیام داده ها در lParam و قادر به گرفتن هستند.

feri88
یک شنبه 31 خرداد 1388, 16:49 عصر
با سلام
من این برنامه رو که یه تابع، روال مربوط به یه دیالوگ رو فراخونی می کنه، اجرا کردم و به درستی جواب می ده؛
فقط یه مشکلی که دارم اینه که وقتی برنامه رو اجرا می کنم و تابع SecondFunction رو صدا می زنم، بار اول نتیجه درست هست و یکی از اون 3 تا زیر رشته رو انتخاب می کنم و بعد زیر رشته ی انتخابی به تابع SecondFunction برمی گرده و درسته!
ولی وقتی برای بار دوم(در زمان اجرا) تابع SecondFunction رو صدا می زنم، این بار اون 3 تا زیر رشته رو در کنترل ComboBox نشون نمی ده!!
من برنامه رو debug کردم و دیدم که در بار دوم صدا زدن تابع SecondFunction ، دوباره در روال دیالوگ، رشته ی ورودی دیالوگ (از طریق متغیر lparam ) به درستی انتقال پیدا می کنه و به درستی هم به 3 تا زیر رشته تجزیه(parse ) می شه، ولی نمی دونم چرا این زیر رشته ها به کنترل ComboBox ، اضافه نمی شه؟!
خودم فکر می کنم مشکل تو این خط از برنامه پیش می یاد:




SendMessage (hwndComboBox, CB_INSERTSTRING, p, (LPARAM) param1);


ولی تو دیباگر نمی شه دقیقا تست کرد که مشکلش چیه؟!!


من کد تابع فراخوان دیالوگ(SecondFunction ) و روال مربوط به دیالوگ رو اینجا می یارم:
روال دیالوگ:



BOOL CALLBACK DlgProc (HWND hwndDlg,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
HWND hwndComboBox ;
hwndComboBox = GetDlgItem(hwndDlg, IDC_COMBO1);
TCHAR* param=new TCHAR[30];
static TCHAR * pparamName;
int nPtr=0;
//counter
int n=0,i=0;
int a=0,iIndex;
static int iLength;
static buffstruct* pad=new buffstruct ;


switch (message)
{
case WM_INITDIALOG:
pad = (buffstruct *) lParam ;


SendMessage (hwndComboBox, LB_RESETCONTENT, 0, 0) ;// clear contents of the combobox;


while(pad->m_BuffStr[nPtr] != '\0')
{
param [0] = '\0';


while(pad-> m_BuffStr [nPtr] != '\0')
{
param [nPtr-n]=pad-> m_BuffStr [nPtr];
nPtr++;
i++;
} // while do
nPtr++;
n=++nPtr;
TCHAR* param1;
param1=new TCHAR[i+1];


for(int k=0;k<i;k++)
param1 [k]= param [k];
param1 [i]='\0';
i=0;
static int p=0;


SendMessage (hwndComboBox, CB_INSERTSTRING, p, (LPARAM) param1) ;


delete[]param1;
p++;
}// while do


case WM_COMMAND:
if (LOWORD (wParam) == IDC_COMBO1 && HIWORD (wParam) == LBN_SELCHANGE)
{


// Get current selection.
iIndex = SendMessage (hwndComboBox, CB_GETCURSEL, 0, 0) ;


iLength = SendMessage (hwndComboBox, CB_GETLBTEXTLEN, iIndex, 0) + 1 ;


pparamName =new TCHAR[iLength];


SendMessage (hwndComboBox, CB_GETLBTEXT, iIndex, (LPARAM) pparamName) ;




}


if(LOWORD (wParam) == IDOK){


pad-> m_BuffStr =new TCHAR[pad->size];
pad-> m_BuffStr [0]='\0';


while(pparamName [a]!='\0')
{
pad-> m_BuffStr [a] = pparamName [a] ;
a++;
}
pad-> m_BuffStr [a]='\0';


pad->size=iLength;


delete[] pparamName;


EndDialog (hwndDlg, TRUE) ;
return TRUE ;


}


if(LOWORD (wParam) == IDCANCEL){
EndDialog (hwndDlg, FALSE) ;
return TRUE ;
}


break;
}
return FALSE;
}



تابع فراخوان دیالوگ:



EXPORT void WINAPI SecondFunction(){
typedef struct
{
TCHAR* m_BuffStr;
int size;
}buffstruct ;
buffstruct * paramstruct=new buffstruct;
paramstruct ->size=38;
paramstruct -> m_BuffStr =L”substring1 \0 substring2 \0 substring3\0\0”;


if(DialogBoxParam (g_hModule, MAKEINTRESOURCE(IDD_DIALOG2),
NULL, DlgProc, (LPARAM) paramstruct)){


TCHAR* recievedparam=new TCHAR[paramstruct ->size];


for(int i=0;i< paramstruct ->size;i++)
recievedparam [i]= paramstruct -> m_BuffStr [i];


delete[] recievedparam;
delete paramstruct;


MessageBox(NULL,L"The Second Function succeeded!",NULL,0);
}


else
MessageBox(NULL,L"The Second Function Failed.",NULL,0);
}


بی زحمت بگید مشکلش چیه که تو بار دوم فراخوانی تابع(در زمان اجرا) زیر رشته ها رو به ComboBox اضافه نمی کنه؟
(در ضمن ورودی روال هم یه ساختار به نام paramstruct هست که پارامترهاش اون رشته و طولش هست!)

لطفا کمکم کنید، واقعا هر راهی که به نظرم می رسید امتحان کردم!
با تشکر

feri88
دوشنبه 01 تیر 1388, 15:52 عصر
سلام

کسی به این سوال ما جواب نمی ده؟!!!
بد جوری گیر کردم!

feri88
چهارشنبه 03 تیر 1388, 13:06 عصر
سلام

من مشکل این کد رو پیدا کردم، گفتم اینجا هم بگم که دیگران هم بتونن استفاده کنن!

مشکلش تو این خط بود:

Static int p=0;

اشتباه من این بود که p نباید static تعریف می شد و باید بیرون از WM_INITDIALOG به صورت اتوماتیک تعریف می شد!
از طرفی این کد مشکل memory leak هم داره (یعنی آزاد نکردن حافظه هایی که به صورت پویا تخصیص داده شدن)
من سه جا حافظه هایی رو که new کردم، delete نکردم! که این مورد مخصوصاً وقتی چند تا برنامه از حافظه استفاده می کنن، مشکل ایجاد می کنه.

برای کسب اطلاعات بیشتر در مورد یافتن memory leak ها با استفاده از debugger ، می تونید به این صفحه از msdn مراجعه کنید:

http://msdn.microsoft.com/en-us/library/w2fhc9a3.aspx
در ضمن بحث memory leak ها فقط توی ++C (و کدهای Native ) مطرحه نه کدهای مدیریت شده و ...