PDA

View Full Version : سوال: گرفتن آی.دی منوی سیستمی یک فرم با WndProc



ROSTAM2
جمعه 11 آذر 1401, 21:51 عصر
سلام.

من با تابع API با نام GetMenuItemID آی.دی آیتم منوی کلیک شده رو به لیست باکس با این دستور توسط WndProc به ی لیست باکس اضافه می کنم ولی آی دی رو اشتباه برمی گردونه؟!

154320


Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = &H11F And m.LParam <> HMainMenu Then
If m.LParam <> 0 Then
MenuID = GetMenuItemID(m.LParam, 0)
Me.ListBox1.Items.Add("Selected.")
Else
Me.ListBox1.Items.Add("Clicked. " & MenuID.ToString)
End If
End If
MyBase.WndProc(m)
End Sub


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

ROSTAM2
شنبه 12 آذر 1401, 13:02 عصر
سلام.

من با تابع API با نام GetMenuItemID آی.دی آیتم منوی کلیک شده رو به لیست باکس با این دستور توسط WndProc به ی لیست باکس اضافه می کنم ولی آی دی رو اشتباه برمی گردونه؟!

154320


Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = &H11F And m.LParam <> HMainMenu Then
If m.LParam <> 0 Then
MenuID = GetMenuItemID(m.LParam, 0)
Me.ListBox1.Items.Add("Selected.")
Else
Me.ListBox1.Items.Add("Clicked. " & MenuID.ToString)
End If
End If
MyBase.WndProc(m)
End Sub


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

تابع GetMenuItemID یک ورودی دومی همداره که شماره ایندکس از subMenu رو می گیره و در اصل اون Handle برای ورودی اول منوی اصلی باید باشه و باید Position رو از آیتمهای منو داشته باشیم تا مقدار آی.دی صحیح رو بر گردونه پس این تابع منتفیه.

ROSTAM2
شنبه 12 آذر 1401, 17:04 عصر
سلام مجدد.
مقداری که wParam برمی گردونه حتی نزدیک به Handle از آیتم هم نیست و خیلی بیشتره عددش،


چیزی که مشخصه خود منوی انتخاب شده با wParam یک عدد متفاوت برمی گردونه منتها Handle از اون آیتم انتخاب شده نیست!!!،

سوال: توسط چه تابعی می تونم Handle آیتم انتخاب شده رو بدست بیارم؟! (20 نمره)

mazoolagh
شنبه 12 آذر 1401, 19:11 عصر
سلام و روز خوش

براتون امکانش هست که پروژه ساده شده (فقط در حد بازسازی وضعیت) رو پیوست کنین؟
یا کدهاش رو بگذارین؟
همه کد منظورم هست: شامل dllimport و تعریف توابع و مقداردهی به پارامترها و ....
و اینکه handle منو رو چجوری گرفتین و ....

ROSTAM2
شنبه 12 آذر 1401, 19:57 عصر
سلام و روز خوش

براتون امکانش هست که پروژه ساده شده (فقط در حد بازسازی وضعیت) رو پیوست کنین؟
یا کدهاش رو بگذارین؟
همه کد منظورم هست: شامل dllimport و تعریف توابع و مقداردهی به پارامترها و ....
و اینکه handle منو رو چجوری گرفتین و ....

این هم فایل های سورس برنامه که باید به یک پروژه جدید اضافه بشن:

کلاسی که توابع API رو برای فراخوانی منو از ریسورس External Resource DLL استفاده می کنه.
فرم اصلی برنامه


و فایلی که منوی ریسورس از اون فراخوانی می شه و باید در پوشه برنامه باشه(Debug) - به پروژه اضافه نمی شه و با متود New از کلاس فراخوانی می شه.

ROSTAM2
شنبه 12 آذر 1401, 20:07 عصر
در مورد کلاس ExternalResources

متود New آدرس فایل dll رو میگیره

Dim APIs As New ExternalResources("Resources")

و توسط تابع ایجاد شده PresetMenu منو رو به کمک توابع LoadMenu و SetMenu از توابع API منوی ریسورس رو به فرمی که Handle اون رو به تابع دادیم اضافه می کنه و خروجیش هم Handle از منوی ست شده است(MainMenu)


Imports System.ComponentModel
Public Class ExternalResources
#Region "API Functions..."
Private Declare Function LoadLibrary Lib "kernel32" _
Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function FreeLibrary Lib "kernel32.dll" (
ByVal hLibModule As IntPtr
) As Boolean
Private Declare Function LoadMenu Lib "user32" Alias "LoadMenuA" (
ByVal hInstance As Long,
ByVal lpString As String) As Long
Public Declare Function SetMenu Lib "user32" (
ByVal hwnd As Long,
ByVal hMenu As Long) As Boolean
Public Declare Function DrawMenuBar Lib "user32" (
ByVal hwnd As Long) As Long
#End Region


Private Shared m_hUser32 As IntPtr
Sub New(Path As String)
m_hUser32 = LoadLibrary(Path)
Try
If m_hUser32.Equals(IntPtr.Zero) Then
Throw New Win32Exception
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Public Function PresetMenu(lpString As String, HWND As IntPtr) As IntPtr
Dim hMenu As IntPtr
Dim Expr As Long = 0, hPop As Long = 0
If m_hUser32 <> 0 Then
hMenu = LoadMenu(m_hUser32, lpString)
If hMenu Then
Expr = SetMenu(HWND, hMenu)
End If
Return hMenu
End If
End Function
End Class

ROSTAM2
شنبه 12 آذر 1401, 20:11 عصر
در مورد فرم اصلی برنامه:

در رویداد Load منوی ریسورس فراخوانی و ست می شه:

HMainMenu = APIs.PresetMenu("#115", Me.Handle)


115 آی.دی منو در فایل Resource DLL است.

کد های فرم:


Public Class Form1
#Region "API Members..."
Const WM_NCHITTEST = &H84
Const HTMENU = 5
Const WM_SETCURSOR = &H20
Const WM_LBUTTONDOWN = &H201
Const WM_NCLBUTTONDOWN = &HA1
Const WM_SYSCOMMAND = &H112
Const SC_MOUSEMENU = &HF090&
Const WM_ENTERMENULOOP = &H211
Const HTCAPTION = 2
Const WM_INITMENU = &H116
Const WM_MENUSELECT = &H11F
Const MF_POPUP = &H10&
Const MF_HILITE = &H80&
Const MF_MOUSESELECT = &H8000&
Const WM_INITMENUPOPUP = &H117
Const WM_MOUSEMOVE = &H200
Const MK_LBUTTON = &H1
Const WM_ENTERIDLE = &H121
Const MSGF_MENU = 2


Const WM_LBUTTONUP = &H202
Const MF_GRAYED = &H1
Const MF_DISABLED = &H2
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (
ByVal lpPrevWndFunc As Long,
ByVal hwnd As Long,
ByVal Msg As Long,
ByVal wParam As Long,
ByVal lParam As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (
ByVal hwnd As Long,
ByVal nIndex As Long,
ByVal dwNewLong As Long) As Long
Declare Function GetMenuItemID Lib "user32" (
ByVal hMenu As Long,
ByVal nPos As Long) As Long
Dim HMainMenu, lPrevWnd, Expr As IntPtr
Public Declare Function GetSubMenu Lib "user32" (
ByVal hMenu As Long,
ByVal nPos As Long) As Long
Private Declare Function GetMenuItemCount Lib "user32" (
ByVal hwnd As IntPtr) As Integer
#End Region
Dim APIs As New ExternalResources("Resources")
Dim MenuID As Long
Protected Overrides Sub WndProc(ByRef m As Message)
Dim str$ = ""
If m.Msg = &H11F And m.LParam <> HMainMenu Then
If m.LParam <> 0 Then
SubMenusID(m.LParam, m.WParam)
MenuID = GetMenuItemID(m.LParam, 0)
str = String.Format("LParam:{0}, Msg:{1}, hWnd:{2}, WParam:{3}",
m.LParam.ToInt64, m.Msg, m.HWnd.ToInt64, m.WParam.ToInt64)
Me.ListBox1.Items.Add("Selected. " & str & "/" & MenuID.ToString)
Else
Me.ListBox1.Items.Add("Clicked. " & MenuID.ToString)
End If
End If
MyBase.WndProc(m)
End Sub
Sub SubMenusID(hMenu As IntPtr, hSubMenu As IntPtr)
Dim Len% = GetMenuItemCount(hMenu)
For i = 0 To Len - 1
Me.ListBox1.Items.Add("GetSubMenu(hMenu, i): " + GetMenuItemID(hMenu, i).ToString)
Next
End Sub

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
HMainMenu = APIs.PresetMenu("#115", Me.Handle)
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.ListBox1.Items.Clear()
End Sub
End Class

ROSTAM2
شنبه 12 آذر 1401, 20:15 عصر
در اصل باید یا شماره Index منوی انتخاب شده و یا Handle از اون رو داشته باشیم تا بتونیم به برنامه بفمونیم که کاربر روی کدوم آیتم کلیک کرده ولی فقط wParam رو داریم که یک عدد متفاوت با انتخاب هر آیتم بر می گردونه که آی دی اصلی ریسورس رو من نتونستم ازش بیرون بکشم.

ROSTAM2
شنبه 12 آذر 1401, 20:47 عصر
یک اشتباه در کد فرم اصلی بود که درستش اینجاست: (متود SubMenusID)


Sub SubMenusID(hMenu As IntPtr, hSubMenu As IntPtr)
Dim Len% = GetMenuItemCount(hMenu)
For i = 0 To Len - 1
Me.ListBox1.Items.Add("GetSubMenu(hMenu, i): " + GetMenuItemID(hMenu, i).ToString)
Next
End Sub

ROSTAM2
شنبه 12 آذر 1401, 21:00 عصر
این هم سورس استفاده از توابع API در VB6:

Binaryworld - How to create MenuBar, Submenu and MenuItem with events at runtime usi ... [ VB -> Menu ] (https://binaryworld.net/Main/CodeDetail.aspx?CodeId=3731)

ROSTAM2
شنبه 12 آذر 1401, 22:16 عصر
طبق کدی که در سورس پست قبلی هست کدهای فرم اصلی رو تغییر دادم ولی هیچ نتیجه ای بیشتر از WndProc نداشت مگر اینکه من کدها رو اشتباه بکار برده باشم:

154325

اون عددی که بر می گردونه آی.دی اصلی آیتم منو نیست.

ROSTAM2
شنبه 12 آذر 1401, 23:39 عصر
در متود WndProc این متغیر به اینصورت مقداردهی بشه تنها آی دی های آیتم های منوهایی که زیر مجموعه دارند نمایش داده می شه؟!! عجیبه ولی اگر می شد مشخص کرد کدوم ایندکس هست خیلی ساده تر می شد آی دی ها رو بدست آورد:

که البته بخاطر اون عدد ورودی دوم از تابع GetMenuItemID این اتفاق می افته:


lLastItemSelected = (m.WParam.ToInt64 And 255) Or GetMenuItemID(m.LParam, 0)


154326

ROSTAM2
یک شنبه 13 آذر 1401, 00:18 صبح
این کد بهتر و باید همه آیدی ها اضافه بشه تا با انتخاب هر آیتم آی.دی اون تشخیص داده بشه: رازش چیه نمی دونم ؟!!!


lLastItemSelected = (m.WParam.ToInt64 And 255) Or (40001 And 40002 And 40003 And 40004 And 40005 And 40006 And 40007)

ROSTAM2
یک شنبه 13 آذر 1401, 20:43 عصر
برای بدست آوردن آی.دی منو باید این کد رو براش بکار گرفت:

MenuID = (m.WParam.ToInt64 And &HFFFF&)

حالا سوال اینجاست این دقیقا کاربردش چیه که باید با اپراتور And آورده بشه؟!!!

ROSTAM2
دوشنبه 14 آذر 1401, 08:19 صبح
سلام.
مقدار &HFFFF& اگر به Long تبدیل بشه می شه 65535

ROSTAM2
دوشنبه 14 آذر 1401, 09:02 صبح
این هم ی سورس کد دیگه که خیلی بکار میاد برای آیتمهای منوی سیستمی:
Visual Basic 6.0 (Source Code)

After reading the other replies, I thought about throwing the following
example into the group.
Please read the comments in the code.
Create a new project, add some menu structure to the form. Make as many
menus and sub-menus as you want. Also put a StatusBar on the form.
Add a module to the project.
Paste the following code to the form's (General)(Declaration) section:
==================================================
Option Explicit
Private Sub Form_Load()
StatusBar1.Style = sbrSimple
gPrevWndProc = SubClass(Me.hWnd)
End Sub
Private Sub Form_Unload(Cancel As Integer)
UnSubClass Me.hWnd, gPrevWndProc
End Sub
==================================================
Paste the following code to the module's (General)(Declaration) section:
==================================================
Option Explicit
Public Const GWL_WNDPROC = (-4)
Public Const WM_MENUSELECT = &H11F
Public Const SC_RESTORE = &HF120&
Public Const SC_MOVE = &HF010&
Public Const SC_SIZE = &HF000&
Public Const SC_MINIMIZE = &HF020&
Public Const SC_MAXIMIZE = &HF030&
Public Const SC_CLOSE = &HF060&
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA"
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA"
(ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal
wParam As Long, ByVal lParam As Long) As Long
Public gPrevWndProc As Long
Public Function SubClass(hWnd As Long) As Long
On Error GoTo SubClass_Error
'Use the Debug object to print some calculation that will rais an error.
'This way the program jumps to the SubClass_Error label,
'and the hWnd's object is NOT subclassed in te IDE
'
'Because the Debug object is ignored in the compiled (.exe) program,
'the hWnd's object IS subclassed in the .exe
'
'Comment-out the next line to subclass also in the IDE
'Debug.Print 1 / 0
'
'NOTE: ------------------------------------------------------+
'Pausing or Stopping a subclassed form can result in |
'unpredictable situations or even let VB crash |
'------------------------------------------------------------+
SubClass = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf WindowProc)
SubClass_Error:
End Function
Public Function UnSubClass(hWnd As Long, lpPrevWndProc As Long) As Long
UnSubClass = SetWindowLong(hWnd, GWL_WNDPROC, lpPrevWndProc)
End Function
Public Function WindowProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal
wParam As Long, ByVal lParam As Long) As Long
Dim hSysMenu As Long
Dim sMenuText As String
Dim uFlags As Long
Dim uItem As Long
Select Case uMsg
Case WM_MENUSELECT
uItem = wParam And &HFFFF&
uFlags = wParam \ &H10000 And &HFFFF&
Select Case uItem
Case 0 ' No menu
sMenuText = ""
'The following values are defined by Visual Basic
'They always start from 1 and increment through each menu
item
'The following prompts correspond to system menu commands
Case SC_RESTORE
sMenuText = "Restore this window to it's previous size"
Case SC_MOVE
sMenuText = "Move this window"
Case SC_SIZE
sMenuText = "Size this window"
Case SC_MINIMIZE
sMenuText = "Minimize this window to the taskbar"
Case SC_MAXIMIZE
sMenuText = "Maximize this window"
Case SC_CLOSE
sMenuText = "Close this window"
Case Else
'This is where I need help also!!
*************************************
'The uItem seems to identify the menu item, but I don't
see any logic *
'The uFlags gives information about grayed, chacked etc.
*
'For this example, just show the values:
*****************************


_


End Select
Form1.StatusBar1.simpleText = sMenuText
WindowProc = CallWindowProc(gPrevWndProc, hWnd, uMsg, wParam,
lParam)
Case Else
WindowProc = CallWindowProc(gPrevWndProc, hWnd, uMsg, wParam,
lParam)
End Select
End Function

mazoolagh
دوشنبه 14 آذر 1401, 17:48 عصر
این یک مورد رو فکر کنم هدفش این هست نیمه پایینی wparam رو نیاز داره (4 بایت پایین از 8 بایت)

برای بدست آوردن آی.دی منو باید این کد رو براش بکار گرفت:

MenuID = (m.WParam.ToInt64 And &HFFFF&)

حالا سوال اینجاست این دقیقا کاربردش چیه که باید با اپراتور And آورده بشه؟!!!



البته من نتونستم برنامه رو اجرا کنم، راستش پیچیده تر از اندازه ای هست که سر در بیارم!
پرسش: الان شما هدفتون این هست که یک منوی سیستمی به یک windows form دات نت اضافه و بعد message ها رو capture کنین؟

ROSTAM2
دوشنبه 14 آذر 1401, 18:31 عصر
پرسش: الان شما هدفتون این هست که یک منوی سیستمی به یک windows form دات نت اضافه و بعد message ها رو capture کنین؟

با ایجاد یک رویداد و گرفتن آی.دی آیتم ها برای ورودی رویداد می شه دستورات رو بر اساس آی.دی اجرا کرد.
چون منو از ریسورس لود می شه بنابر این هیچ دسترسی به رویدادهای اون نیست و باید براش رویداد ساخته بشه شاید بگین این کار عبس و بیهوده است اما یک فایده ای که داره می شه از منو های اون ریسورس در پنجره های متفاوت بصورت متفاوت یا یکسان داشت و حتی می شه هر کدوم از منوهای اون رو بعنوان ContextMenu استفاده کرد.

در اصل منو از ریسورس dll که با ++VC ساخته شده فراخوانی می شه و اینکه نوشتم آی دی منوی سیستمی چون روش گرفتن آی.دی منوی پنجره های ویندوز هم شبیه به همین هست. و همچنین اگه روی نوار عنوان فرم راست کلیک و آیتم های منوی اون انتخاب بشه آی.دی های اون آیتمها هم با این روش قابل دستیابی هستند.

ROSTAM2
جمعه 18 آذر 1401, 10:38 صبح
دستور صحیح برای گرفتن آی.دی از آیتم کلیک شده توی این پست از این تاپیک (https://barnamenevis.org/showthread.php?572117-%D9%81%D8%B1%D8%A7%D8%AE%D9%88%D8%A7%D9%86%DB%8C-Handle-%D8%A7%D8%B2-%D8%A2%DB%8C%D8%AA%D9%85-%D9%87%D8%A7-%D9%85%D9%86%D9%88%DB%8C-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85%DB%8C&p=2454780&viewfull=1#post2454780) ارسال شد:

154339

ROSTAM2
پنج شنبه 24 آذر 1401, 20:23 عصر
سلام به همه
برای لود منو از ریسورس یک نمونه سورس هست استفاده کنید:(Visual Studio 2015)

برای هر دو پروژه از نسخه 2015 استفاده شده.
154361 154362

حوصله کردم ی تاپیک کامل و مفصل در موردش ایجاد می کنم: