Ещё раз здравствуйте! Вопрос назрел следующий, если я хочу создать меню динамически, как создавал окна диалоговых панелей, только здесь структуры MENUEX_TEMPLATE_HEADER, MENUEX_TEMPLATE_ITEM, думал делать по аналогии с диалоговыми окнами, не получается, Вызов функции создания меню LoadMenuIndirect() завершается с ошибкой 13. Я думаю, что заполняю структуры не верно, хотелось бы получить дельный совет... Вот как я пытаюсь реализовать создание меню. Code (C++): HMENU CreateMenu() { BYTE* p, * pmenuTempl; p = pmenuTempl = (BYTE*)LocalAlloc(LPTR, 512); if (!p) { MessageBox(NULL, TEXT("Can't memory allocated"), TEXT("Error"), MB_OK); return (HMENU)NULL; } MENUEX_TEMPLATE_HEADER* lpmth; MENUEX_TEMPLATE_ITEM* lpmti; lpmth = (MENUEX_TEMPLATE_HEADER*)p; lpmth->wVersion = 1; lpmth->wOffset = 4; lpmth->dwHelpId = 0; p = (BYTE*)(lpmth + 1); LPWSTR szDst; ULONG_PTR ui = 0; //lpmti = (MENUEX_TEMPLATE_ITEM*)p; //lpmti->dwHelpId = 0; //lpmti->dwType = MFT_STRING; //lpmti->dwState = MFS_ENABLED; //lpmti->menuId = CM_FILE_OPEN; //lpmti->bResInfo = 0; //LPWSTR szDst = (LPWSTR)&lpmti->szText; ////p = (BYTE*)(lpmti + 1); ////LPWSTR szDst = (LPWSTR)p; //wmemcpy_s(szDst, 512, TEXT("Open"), 5); //p = (BYTE*)(szDst + _tcslen(TEXT("Open"))); //ULONG_PTR ui = (ULONG_PTR)p; //ui += 3; //ui >>= 2; //ui <<= 2; //p = (BYTE*)ui; ////2 //lpmti = (MENUEX_TEMPLATE_ITEM*)p; //lpmti->dwHelpId = 0; //lpmti->dwType = MFT_STRING; //lpmti->dwState = MFS_ENABLED; //lpmti->menuId = CM_FILE_CREATE; //lpmti->bResInfo = 0; //szDst = (LPWSTR)&lpmti->szText; //wmemcpy_s(szDst, 512, TEXT("Create"), 7); //p = (BYTE*)(szDst + _tcslen(TEXT("Create"))); //ui = (ULONG_PTR)p; //ui += 3; //ui >>= 2; //ui <<= 2; //p = (BYTE*)ui; ////3 //lpmti = (MENUEX_TEMPLATE_ITEM*)p; //lpmti->dwHelpId = 0; //lpmti->dwType = MFT_STRING; //lpmti->dwState = MFS_ENABLED; //lpmti->menuId = CM_FILE_SAVE; //lpmti->bResInfo = 0; //szDst = (LPWSTR)&lpmti->szText; //wmemcpy_s(szDst, 512, TEXT("Save"), 5); //p = (BYTE*)(szDst + _tcslen(TEXT("Save"))); // //ui = (ULONG_PTR)p; //ui += 3; //ui >>= 2; //ui <<= 2; //p = (BYTE*)ui; //4 lpmti = (MENUEX_TEMPLATE_ITEM*)p; lpmti->dwHelpId = 0; lpmti->dwType = MFT_STRING; lpmti->dwState = MFS_ENABLED; lpmti->menuId = CM_FILE_QUIT; lpmti->bResInfo = 0x80; szDst = (LPWSTR)&lpmti->szText; wmemcpy_s(szDst, 512, TEXT("Exit"), 5); p = (BYTE*)(szDst + _tcslen(TEXT("Exit"))); ui = (ULONG_PTR)p; ui += 3; ui >>= 2; ui <<= 2; p = (BYTE*)ui; HMENU hMenu = LoadMenuIndirect((MENUTEMPLATEW*)(MENUEX_TEMPLATE_HEADER*)pmenuTempl); if (!hMenu) { DWORD er = GetLastError(); } LocalFree(LocalHandle(pmenuTempl)); return hMenu; }
Если никому не интересно, то хотя бы скажите. стоит ли вообще выделять память для построения меню с помощью вышеозначенных структур?
Больше сил потратишь на разыменовывание указателей. Куда проще сделать CreateMenu, а потом по его хэндлу InsertMenu/AppendMenu
Andrey_59, можно либо нарисовать в редакторе ресурсов, либо создавать динамически. 1. Создаете ресурс, верхний элемент (допустим, как "File" делаете пустым или каким угодно, далее его подэлементы и будут элементы меню). Code (Text): hMenu = LoadMenu(NULL, MAKEINTRESOURCE(MAIN_MENU)); hSubMenu = GetSubMenu(hMenu, 0); TrackPopupMenuEx(GetSubMenu....)[ 2. Code (Text): hMenu = CreatePopupMenu(); AppendMenu(hMenu, MF_STRING, 1, L"Open"); AppendMenu(hMenu, MF_STRING, 2, L"Upload"); ... TrackPopupMenuEx(hMenu....)
Вроде бы понятно по вопросу, что именно меня интересует, СОЗДАВАТЬ ДИНАМИЧЕСКИ. Я знаю про CreateMenu, CreatePopupMenu, SetMenu... , значит меня интересует что-то другое, а что именно, ну, вы не поверите, создание меню динамически с помощью MENUEX_TEMPLATE_HEADER, MENUEX_TEMPLATE_ITEM.
Если вам так хочется извращаться на C++, то это ваше дело. Но вот вам на FASM использование этой же функции. Все прекрасно работает. Проверил 4 варианта: статический буфер в секции данных, 3 способа динамического выделения памяти (HeapAlloc, GlobalAlloc, LocalAlloc). Code (ASM): format PE64 GUI 6.0 struc String text& { rb RVA $ and 1 . du text label .Length at (( $ - . ) shr 1 ) dw NULL label .Bytes at ( $ - . ) } include 'WIN64A.INC' section '.idata' import data readable writeable library kernel32, "kernel32.dll",\ user32, "user32.dll" include 'API\KERNEL32.INC' include 'API\USER32.INC' section '.text' code readable executable entry $ push rax mov [wce.cbSize], wce.Length lea rdx, [main] mov [wce.style], CS_HREDRAW or CS_VREDRAW or CS_GLOBALCLASS mov [wce.lpfnWndProc], rdx mov [wce.cbClsExtra], 0 mov [wce.cbWndExtra], 0 invoke GetModuleHandleW, NULL mov [wce.hInstance], rax invoke LoadIconW, NULL, IDI_APPLICATION mov [wce.hIcon], rax invoke LoadCursorW, NULL, IDC_ARROW mov rdx, COLOR_WINDOW + 1 mov [wce.hCursor], rax lea rcx, [main.szClass] mov [wce.hbrBackground], rdx and [wce.lpszMenuName], 0 mov [wce.lpszClassName], rcx invoke LoadIconW, NULL, IDI_APPLICATION mov [wce.hIconSm], rax invoke RegisterClassExW, addr wce mov rdx, rax invoke CreateWindowExW, WS_EX_OVERLAPPEDWINDOW, rdx,\ addr main.szTitle, WS_OVERLAPPEDWINDOW,\ 100, 100, 800, 600, HWND_DESKTOP,\ NULL, [wce.hInstance], NULL pop rax call MessageLoop push rax invoke ExitProcess, rax @@: invoke TranslateMessage, addr msg invoke DispatchMessageW, addr msg MessageLoop: invoke GetMessageW, addr msg, NULL, NULL, NULL inc eax jz @f dec eax jnz @b mov rax, [msg.wParam] retn @@: invoke GetLastError retn CM_FILE_QUIT = 1000 align 64 main: push rdi mov eax, edx mov rdx, rcx mov rcx, .jumps.count lea rdi, [.jumps] repne scas dword [rdi] xchg rax, rcx xchg rdx, rcx pop rdi jne @f neg rax movzx rax, word [.jumps + .jumps.count * 6 + rax * 2 - 2] lea rax, [main + rax] jmp rax @@: jmp [DefWindowProcW] .szClass String "TForm1" .szTitle String "Form1" align 4 .jumps dd WM_CREATE, WM_DESTROY, WM_COMMAND label .jumps.count at (( $ - .jumps ) shr 2 ) dw .Form1Create - main dw .Form1Destroy - main dw .Form1Command - main label .hWnd qword at ( rbp + 16 ) label .uMsg qword at ( rbp + 24 ) label .wPar qword at ( rbp + 32 ) label .lPar qword at ( rbp + 40 ) .Form1Create: enter 0, 0 mov [.hWnd], rcx ; call newMenu0 ; LoadMenuIndirect из структур в секции .data ; call newMenu1 ; LoadMenuIndirect при выделении памяти через HeapAlloc ; call newMenu2 ; LoadMenuIndirect при выделении памяти через GlobalAlloc call newMenu3 ; LoadMenuIndirect при выделении памяти через LocalAlloc invoke SetMenu, [.hWnd], rax invoke IsWindowVisible, [.hWnd] test al, al jnz @f invoke ShowWindow, [.hWnd], SW_SHOWNORMAL invoke UpdateWindow, [.hWnd] @@: xor rax, rax leave retn .Form1Destroy: enter 0, 0 invoke PostQuitMessage, NULL xor rax, rax leave retn .Form1Command: enter 0, 0 cmp r8d, CM_FILE_QUIT jnz .done invoke DestroyWindow, rcx jmp .done .done: xor rax, rax leave retn newMenu0: push rcx invoke LoadMenuIndirect, addr main.mainMenu pop rcx retn newMenu1: push rdi push rdx push rax invoke GetProcessHeap invoke HeapAlloc, rax, HEAP_ZERO_MEMORY, 512 mov rdi, rax call fillMenu invoke LoadMenuIndirect, rdi mov [rsp], rax invoke GetProcessHeap invoke HeapFree, rax, HEAP_NO_SERIALIZE, rdi pop rax pop rdx pop rdi retn newMenu2: push rdi push rdx push rax invoke GlobalAlloc, GPTR, 512 mov rdi, rax call fillMenu invoke LoadMenuIndirect, rdi mov [rsp], rax invoke GlobalFree, rdi pop rax pop rdx pop rdi retn newMenu3: push rdi push rdx push rax invoke LocalAlloc, LPTR, 512 mov rdi, rax call fillMenu invoke LoadMenuIndirect, rdi mov [rsp], rax invoke LocalFree, rdi pop rax pop rdx pop rdi retn fillMenu: virtual at 0 du 'Exit' load .a qword from 0 end virtual push rdx mov rdx, .a mov qword [rdi + 0], 0x0000000000040001 ; MENUEX_TEMPLATE_HEADER(wVersion = 1, wOffset = 4, dwHelpId = 0) mov dword [rdi + 8], MFT_STRING ; MENUEX_TEMPLATE_ITEM mov dword [rdi + 12], MFS_ENABLED mov dword [rdi + 16], CM_FILE_QUIT mov word [rdi + 20], 0x80 mov qword [rdi + 22], rdx mov word [rdi + 30], NULL pop rdx retn section '.data' data readable writeable msg MSG wce WNDCLASSEX wce.Length = $ - wce align 8 main.mainMenu dw 1, 4 ; MENUEX_TEMPLATE_HEADER dd 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_QUIT dw 0x80 du "Exit", 0
Вам же хочется извращаться на ассемблере почему бы этого не делать на другом языке программирования... и в чём здесь извращение? В том, что хочу понять, как это работает?! По теме: У меня ничего не получается, не знаю почему... Возможно ли с помощью данных структур создать не только пункты меню, которые ещё нужно как-то подключить к главному меню, но и главное меню? Что я имею ввиду: CreateMenu(), создаётся главное меню к которому подключаются меню созданные с помощью CreatePopupMenu(), я так понимаю, что с помощью данных структур можно создавать только пункты меню, которые затем подключаются к главному меню. Или я не прав.
Нет. Можно создать полноценное меню. Возьмите мой пример и попробуйте. Его надо просто скормить fasmw. Потом можете скопировать строки со 198 по 203 и изменить данные пунктов меню прибавляя смещение. Так вы создадите и другие элементы меню. А если посмотрите на 106 строку, то увидите как это меню цепляется к окну в качестве MainMenu --- Сообщение объединено, May 28, 2023 --- Вот так у меня выглядит структура в секции данных для создания меню следующего вида (см. скриншот) через LoadMenuIndirect Code (ASM): main.mainMenu dw 1, 4 ; MENUEX_TEMPLATE_HEADER dd 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd 0 dw 0x01 du "File", 0 dd 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_NEW dw 0x00 du "New", 0, 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_QUIT dw 0x80 du "Exit", 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x80 du "Help", 0
Ну у меня тоже вылетала пока я после File не добавил dd 0. Это странно, т.к. File уже выровнена на 4 байта и без dd. Code (ASM): dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd 0 dw 0x01 du "File", 0 dd 0 ; <<< Вот этот ноль тут лишний. Он даже до 8 не выравнивает т.к. это смещение 24 от начала структуры и 32 от начала заголовка. С этим 0 получается 28 и 36 соответственно, но без него вылезает ошибка. Т.е. он не видит следующую структуру и считывает поля не из тех мест. При этом после Exit такой же фигни нету, хотя она по длине эквивалентна File. dd MFT_STRING ; MENUEX_TEMPLATE_ITEM ...
У меня такая вот структура, выравнивать, я выравниваю Code (Text): typedef struct { //DWORD dwHelpId; DWORD dwType; DWORD dwState; DWORD menuId; WORD bResInfo; WCHAR szText[1]; DWORD dwHelpId; } MENUEX_TEMPLATE_ITEM; Куда что добавлять мне, как по мне всё правильно, но всё равно ошибка 13(Недопустимые данные), хоть ты тресни. Code (C++): lpmti = (MENUEX_TEMPLATE_ITEM*)(lpmth+1); //lpmti->dwHelpId = 0; lpmti->dwType = MFT_STRING; lpmti->dwState = MFS_ENABLED; lpmti->menuId = 0; lpmti->bResInfo = 0x01; szDst = (LPWSTR)(lpmti->szText); szSrc = TEXT("FILE"); for (; *szDst++ = *szSrc++;) ; lpmti = (MENUEX_TEMPLATE_ITEM*)szDst; lpmti->dwHelpId = 0; p = (BYTE*)lpmti;
Code (C++): szDst = (LPWSTR)(lpmti->szText); ... lpmti = (MENUEX_TEMPLATE_ITEM*)szDst; lpmti->dwHelpId = 0; Так получается вы обратились к Смещение lpmti->szText = 14 Смещение length(TEXT(L"FILE")) + 1 символ = 10 байт т.к. строка должна быть всегда WideChar Смещение lpmti->dwHelpId = 16 Итого от исходного заголовка вы dwHelpId записали по смещению 40, а надо было записать его по смещению 24 Code (C++): (DWORD*)szDst = 0; Вот так
Тут ещё такая тема, что в одних структурах поле lpmti->dwHelpId есть, а в других нет., нужен ли этот член в структуре MENUEX_TEMPLATE_ITEM? Не знаю, смотрел в отладчике и, вроде бы, со смещениями всё в порядке. Хотя делал я это без поля dwHelpId. --- Сообщение объединено, May 30, 2023 --- В смысле вместо Code (C++): lpmti = (MENUEX_TEMPLATE_ITEM*)szDst; вставить Code (C++): lpmti = (DWORD*)szDst; - НО ЭТО ерунда какая то.
Вот именно. У вас ерунда. Я сказал, что вы присваиваете вот тут Code (C++): lpmti = (MENUEX_TEMPLATE_ITEM*)szDst; адрес конца строки как начало новой структуры MENUEX_TEMPLATE_ITEM и затем заполняете поле Code (C++): lpmti->dwHelpId = 0; Это, по вашему же описанию структуры, поле находится здесь Code (C++): typedef struct { //DWORD dwHelpId; DWORD dwType; // +0 DWORD dwState; // +4 DWORD menuId; // +8 WORD bResInfo; // +12 WCHAR szText[1]; // +14 DWORD dwHelpId; // +16 <<< } MENUEX_TEMPLATE_ITEM; Значит строка Code (C++): lpmti->dwHelpId = 0; эквивалентна записи (DWORD)0 по смещению 16 от начала структуры. Далее рассматриваем значение указателя szDst. По этому указателю в цикле Code (C++): for (; *szDst++ = *szSrc++;); // строка "File", \0 состоит из 5 символов и каждый по 2 байта было сделано 5 инкрементов (по 2 байта). Путём не хитрых математических вычислений получается что к szDst прибавили 10. Но изначально к нему был присвоен адрес со смещением +14 от начала структуры Code (C++): szDst = (LPWSTR)(lpmti->szText); Итого: 14 + 5*2 + 16 = 40, но должно быть так Code (C++): typedef struct { //DWORD dwHelpId; DWORD dwType; // +0 DWORD dwState; // +4 DWORD menuId; // +8 WORD bResInfo; // +12 WCHAR szText[5]; // +14 DWORD dwHelpId; // +24 } MENUEX_TEMPLATE_ITEM; конкретно для строки File из 4 символов.
Я это поле убрал. Только ситуация не меняется. Ну хорошо сделал так как написано, но та же ошибка. Code (C++): lpmti = (MENUEX_TEMPLATE_ITEM*)(lpmth+1); //lpmti->dwHelpId = 0; lpmti->dwType = MFT_STRING; lpmti->dwState = MFS_ENABLED; lpmti->menuId = 0; lpmti->bResInfo = 0x01; szDst = (LPWSTR)(lpmti->szText); szSrc = TEXT("FILE"); for (; *szDst++ = *szSrc++;) ; DWORD* pdwVal = (DWORD*)szDst; *pdwVal++ = 0; //---------------------------------------- lpmti = (MENUEX_TEMPLATE_ITEM*)(BYTE*)pdwVal; lpmti->dwType = MFT_STRING; lpmti->dwState = MFS_ENABLED; lpmti->menuId = CM_FILE_QUIT; lpmti->bResInfo = MF_END; szDst = (LPWSTR)(lpmti->szText); szSrc = TEXT("Exit"); for (; *szDst++ = *szSrc++;) ; *(DWORD*)szDst = 0;
Code (C++): #include <windows.h> #include <stdio.h> #define MM_NEW_ITEM 1000 #define MM_EXIT_ITEM 1001 #define MM_HELP_ITEM 1002 char menuTemplate[1024]; HINSTANCE hInstance = nullptr; struct MENUEX_TEMPLATE_HEADER { WORD wVersion, wOffset; DWORD dwHelpId; }; struct MENUEX_TEMPLATE_ITEM { DWORD dwType, dwState, uId; WORD wFlags; WCHAR szText[1]; }; MENUEX_TEMPLATE_ITEM *FillMenuItemTemplate(MENUEX_TEMPLATE_ITEM *lpmti, DWORD aType, DWORD aState, DWORD aID, WORD aFlags, LPCWSTR szText) { int flag = 0; lpmti->dwType = aType; lpmti->dwState = aState; lpmti->uId = aID; lpmti->wFlags = aFlags; WCHAR *szDst = &lpmti->szText[0]; const WCHAR *szSrc = szText; for (; *szDst++ = *szSrc++; flag ^= 2); return (MENUEX_TEMPLATE_ITEM *)(((BYTE *)szDst) + flag); } void FillMenuBuffer(MENUEX_TEMPLATE_HEADER *lpmth) { lpmth->wVersion = 1; lpmth->wOffset = 4; lpmth->dwHelpId = 0; MENUEX_TEMPLATE_ITEM *lpmti = (MENUEX_TEMPLATE_ITEM *)((void *)(lpmth + 1)); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, 0, 0x01, L"File"); // Эта штука нужна только после пункта File *(DWORD *)lpmti = 0; lpmti = (MENUEX_TEMPLATE_ITEM *)((DWORD *)(lpmti) + 1); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_NEW_ITEM, 0x00, L"New"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_EXIT_ITEM, 0x80, L"Exit"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_HELP_ITEM, 0x80, L"Help"); } HMENU LoadMenuTemplate0() { FillMenuBuffer((MENUEX_TEMPLATE_HEADER *)&menuTemplate); return LoadMenuIndirectA((MENUEX_TEMPLATE_HEADER *)&menuTemplate); } HMENU LoadMenuTemplate1() { MENUEX_TEMPLATE_HEADER *lpmth = (MENUEX_TEMPLATE_HEADER *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024); FillMenuBuffer(lpmth); HMENU hMenu = LoadMenuIndirectA(lpmth); HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, (void *)lpmth); return hMenu; } HMENU LoadMenuTemplate2() { MENUEX_TEMPLATE_HEADER *lpmth = (MENUEX_TEMPLATE_HEADER *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024); FillMenuBuffer(lpmth); HMENU hMenu = LoadMenuIndirectW(lpmth); HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, (void *)lpmth); return hMenu; } ATOM RegisterMainClass(HINSTANCE hInst, LPCWSTR szClass, void *fnProc) { WNDCLASSEXW wce; wce.cbSize = sizeof(wce); wce.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; wce.lpfnWndProc = (WNDPROC)fnProc; wce.cbClsExtra = 0; wce.cbWndExtra = 0; wce.hInstance = hInst; wce.hIcon = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); wce.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); wce.hbrBackground = (HBRUSH)COLOR_WINDOW + 1; wce.lpszMenuName = nullptr; wce.lpszClassName = szClass; wce.hIconSm = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); return RegisterClassExW(&wce); } LRESULT Form1_wndProc(HWND hWnd, UINT uMsg, WPARAM wPar, LPARAM lPar) { switch (uMsg) { case WM_DESTROY: PostQuitMessage(0); return 0; case WM_CREATE: SetMenu(hWnd, LoadMenuTemplate1()); if (!IsWindowVisible(hWnd)) { ShowWindow(hWnd, SW_SHOWNORMAL); UpdateWindow(hWnd); } return 0; case WM_COMMAND: switch (wPar) { case MM_NEW_ITEM: break; case MM_EXIT_ITEM: DestroyWindow(hWnd); return 0; case MM_HELP_ITEM: break; default: break; } break; default: break; } return DefWindowProcW(hWnd, uMsg, wPar, lPar); } int main(int argc, char *argv[]) { hInstance = GetModuleHandleW(NULL); if (!hInstance) { printf("Error! Can't GetModuleHandle. . ."); return 1; } ATOM aClass = RegisterMainClass(hInstance, L"TForm1", (void *)Form1_wndProc); if (!aClass) { printf("Error! Can't RegisterMainClass. . ."); return 1; } HWND aWindow = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, (LPCWSTR)((unsigned)aClass), L"Form1", WS_OVERLAPPEDWINDOW, 100, 100, 800, 600, HWND_DESKTOP, nullptr, hInstance, nullptr); if (!aWindow) { printf("Error! Can't CreateWindowEx. . ."); return 1; } MSG aMsg; while (int state = GetMessageW(&aMsg, 0, 0, 0)) { if (state == -1) { aMsg.wParam = GetLastError(); break; } TranslateMessage(&aMsg); DispatchMessageW(&aMsg); } return aMsg.wParam; } Вот я вам на С++ набросал пример, который ранее показывал на FASM. Там есть меню из 4 пунктов (см. мой предыдущий скриншот).
Это что за ... зачем, для чего... Мне не код нужен я не понимаю почему у меня не работает, я понять хочу, как с этим работать, а код я и сам напишу.
Например вот так выглядит структура для создания меню изображённого ниже на скриншотах Поясняю. После каждого пункта меню первого уровня (File, Edit, ? и Help) есть дополнительное поле 4-батовое (этот поле появляется у каждого вложенного подменю wFlags & 1 != 0). Все остальные (New, Exit, Redo, итд) вложенные пункты меню идут без этого поля. Code (ASM): main.mainMenu dw 1, 4 ; MENUEX_TEMPLATE_HEADER dd 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd 0 dw 0x01 du "File", 0 dd 0 ; <<< Дополнительное поле dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_NEW dw 0x00 du "New", 0, 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_NEW dw 0x00 du "Open...", 0, 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_NEW dw 0x00 du "Save...", 0, 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_NEW dw 0x00 du "Save as...", 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_QUIT dw 0x80 du "Exit", 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x01 du "Edit", 0 dd 0 ; <<< Дополнительное поле dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x00 du "Redo", 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x00 du "Undo", 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x00 du "Cut", 0, 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x00 du "Copy", 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x00 du "Paste", 0, 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x00 du "Remove", 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x80 du "Select all", 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x81 du "?", 0, 0 dd 0 ; <<< Дополнительное поле dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x01 du "Help", 0 dd 0 ; <<< Дополнительное поле dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x00 du "Test1", 0, 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x00 du "Test2", 0, 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x80 du "Test3", 0, 0 dd MFT_STRING ; MENUEX_TEMPLATE_ITEM dd MFS_ENABLED dd CM_FILE_HELP dw 0x80 du "About", 0, 0 --- Сообщение объединено, May 31, 2023 --- А вот и на С++ тоже самое Code (C++): #include <windows.h> #include <stdio.h> #define MM_NEW_ITEM 1000 #define MM_EXIT_ITEM 1001 #define MM_HELP_ITEM 1002 char menuTemplate[1024]; HINSTANCE hInstance = nullptr; struct MENUEX_TEMPLATE_HEADER { WORD wVersion, wOffset; DWORD dwHelpId; }; struct MENUEX_TEMPLATE_ITEM { DWORD dwType, dwState, uId; WORD wFlags; WCHAR szText[1]; }; MENUEX_TEMPLATE_ITEM *FillMenuItemTemplate(MENUEX_TEMPLATE_ITEM *lpmti, DWORD aType, DWORD aState, DWORD aID, WORD aFlags, LPCWSTR szText) { int flag = (aFlags & 1) ? 4 : 0; lpmti->dwType = aType; lpmti->dwState = aState; lpmti->uId = aID; lpmti->wFlags = aFlags; WCHAR *szDst = &lpmti->szText[0]; const WCHAR *szSrc = szText; for (; *szDst++ = *szSrc++; flag ^= 2); if (aFlags & 1) *(DWORD *)szDst = 0; return (MENUEX_TEMPLATE_ITEM *)(((BYTE *)szDst) + flag); } void FillMenuBuffer(MENUEX_TEMPLATE_HEADER *lpmth) { lpmth->wVersion = 1; lpmth->wOffset = 4; lpmth->dwHelpId = 0; MENUEX_TEMPLATE_ITEM *lpmti = (MENUEX_TEMPLATE_ITEM *)((void *)(lpmth + 1)); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, 0, 0x01, L"File"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_NEW_ITEM, 0x00, L"New"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_NEW_ITEM, 0x00, L"Open..."); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_NEW_ITEM, 0x01, L"Reopen"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_NEW_ITEM, 0x00, L"Reopen file 1"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_NEW_ITEM, 0x00, L"Reopen file 2"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_NEW_ITEM, 0x00, L"Reopen file 3"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_NEW_ITEM, 0x00, L"Reopen file 4"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_NEW_ITEM, 0x80, L"Reopen file 5"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_NEW_ITEM, 0x00, L"Save..."); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_NEW_ITEM, 0x00, L"Save as..."); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_EXIT_ITEM, 0x80, L"Exit"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_EXIT_ITEM, 0x01, L"Edit"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_EXIT_ITEM, 0x00, L"Redo"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_EXIT_ITEM, 0x00, L"Undo"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_EXIT_ITEM, 0x00, L"Cut"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_EXIT_ITEM, 0x00, L"Copy"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_EXIT_ITEM, 0x00, L"Paste"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_EXIT_ITEM, 0x00, L"Remove"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_EXIT_ITEM, 0x80, L"Select all"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_HELP_ITEM, 0x81, L"?"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_HELP_ITEM, 0x01, L"Help"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_HELP_ITEM, 0x00, L"Test 1"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_HELP_ITEM, 0x00, L"Test 2"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_HELP_ITEM, 0x00, L"Test 3"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_HELP_ITEM, 0x00, L"Test 4"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_HELP_ITEM, 0x80, L"Test 5"); lpmti = FillMenuItemTemplate(lpmti, MFT_STRING, MFS_ENABLED, MM_HELP_ITEM, 0x80, L"About"); } HMENU LoadMenuTemplate0() { FillMenuBuffer((MENUEX_TEMPLATE_HEADER *)&menuTemplate); return LoadMenuIndirectA((MENUEX_TEMPLATE_HEADER *)&menuTemplate); } HMENU LoadMenuTemplate1() { MENUEX_TEMPLATE_HEADER *lpmth = (MENUEX_TEMPLATE_HEADER *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024); FillMenuBuffer(lpmth); HMENU hMenu = LoadMenuIndirectA(lpmth); HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, (void *)lpmth); return hMenu; } HMENU LoadMenuTemplate2() { MENUEX_TEMPLATE_HEADER *lpmth = (MENUEX_TEMPLATE_HEADER *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024); FillMenuBuffer(lpmth); HMENU hMenu = LoadMenuIndirectW(lpmth); HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, (void *)lpmth); return hMenu; } ATOM RegisterMainClass(HINSTANCE hInst, LPCWSTR szClass, void *fnProc) { WNDCLASSEXW wce; wce.cbSize = sizeof(wce); wce.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; wce.lpfnWndProc = (WNDPROC)fnProc; wce.cbClsExtra = 0; wce.cbWndExtra = 0; wce.hInstance = hInst; wce.hIcon = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); wce.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); wce.hbrBackground = (HBRUSH)COLOR_WINDOW + 1; wce.lpszMenuName = nullptr; wce.lpszClassName = szClass; wce.hIconSm = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); return RegisterClassExW(&wce); } LRESULT Form1_wndProc(HWND hWnd, UINT uMsg, WPARAM wPar, LPARAM lPar) { switch (uMsg) { case WM_DESTROY: PostQuitMessage(0); return 0; case WM_CREATE: SetMenu(hWnd, LoadMenuTemplate1()); if (!IsWindowVisible(hWnd)) { ShowWindow(hWnd, SW_SHOWNORMAL); UpdateWindow(hWnd); } return 0; case WM_COMMAND: switch (wPar) { case MM_NEW_ITEM: break; case MM_EXIT_ITEM: DestroyWindow(hWnd); return 0; case MM_HELP_ITEM: break; default: break; } break; default: break; } return DefWindowProcW(hWnd, uMsg, wPar, lPar); } int main(int argc, char *argv[]) { hInstance = GetModuleHandleW(NULL); if (!hInstance) { printf("Error! Can't GetModuleHandle. . ."); return 1; } ATOM aClass = RegisterMainClass(hInstance, L"TForm1", (void *)Form1_wndProc); if (!aClass) { printf("Error! Can't RegisterMainClass. . ."); return 1; } HWND aWindow = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, (LPCWSTR)((unsigned)aClass), L"Form1", WS_OVERLAPPEDWINDOW, 100, 100, 800, 600, HWND_DESKTOP, nullptr, hInstance, nullptr); if (!aWindow) { printf("Error! Can't CreateWindowEx. . ."); return 1; } MSG aMsg; while (int state = GetMessageW(&aMsg, 0, 0, 0)) { if (state == -1) { aMsg.wParam = GetLastError(); break; } TranslateMessage(&aMsg); DispatchMessageW(&aMsg); } return aMsg.wParam; }