Использование CreateDialog в VBA при попытке создать немодальные диалоговые окна

Я хочу создать немодальный всплывающий диалог в VBA 7.0. На данный момент наиболее многообещающим маршрутом кажется CreateDialog.

Сначала я попробовал CreateDialogW и получил Entry point not found for CreateDialogW in DLL.
После открытия библиотеки DLL я убедился, что этой функции нет в списке. Ссылка MSDN, указанная выше, показывает User32 как DLL для этой функции и перечисляет имена функций CreateDialogW и CreateDialogA (соответственно Unicode/ansi), но они не указаны в этой DLL на моем компьютере (Win 7 Professional, 64-разрядная версия).

Итак, просматривая список функций, которые находятся в DLL, я увидел CreateDialogParam и CreateDialogIndirectParam функции (каждая версия Ansi и Unicode).

Я пытался следовать MSDN и конвертировать примеры C в VB, но я где-то что-то упускаю, и я как бы застрял, потому что не знаю, что я делаю неправильно. Код компилируется и запускается без ошибок, но при вызове API ничего не происходит - он выполняется, но ничего не происходит.

Если бы кто-нибудь мог дать мне несколько указателей в правильном направлении, я был бы очень признателен. Мой текущий обходной путь отстой, и я действительно хотел бы застегнуть этот проект.

Option Explicit

'Reference conversion of C to VB type declarations here
'http://msdn.microsoft.com/en-us/library/aa261773(v=vs.60).aspx

'Declare function to Win API CreateDialog function
'http://msdn.microsoft.com/en-us/library/ms645434(v=vs.85).aspx
Private Declare PtrSafe Function CreateDialog Lib "User32.dll" Alias "CreateDialogParamW" _
                                (ByVal lpTemplateName As LongPtr, _
                                 ByRef lpDialogFunc As DIALOGPROC, _
                                 ByVal dwInitParam As Long, _
                                 Optional ByVal hInstance As Long, _
                                 Optional ByVal hWndParent As Long) _
                                As Long

'Windows Style Constants
'http://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx
Public Const WS_BORDER As Long = &H800000
Public Const WS_CAPTION As Long = &HC00000
Public Const WS_CHILD As Long = &H40000000
Public Const WS_CHILDWINDOW As Long = &H40000000
Public Const WS_CLIPCHILDREN As Long = &H2000000
Public Const WS_CLIPSIBLINGS As Long = &H4000000
Public Const WS_DISABLED As Long = &H8000000
Public Const WS_DLGFRAME As Long = &H400000
Public Const WS_GROUP As Long = &H20000
Public Const WS_HSCROLL As Long = &H100000
Public Const WS_ICONIC As Long = &H20000000
Public Const WS_MAXIMIZE As Long = &H1000000
Public Const WS_MAXIMIZEBOX As Long = &H10000
Public Const WS_MINIMIZE As Long = &H20000000
Public Const WS_MINIMIZEBOX As Long = &H20000
Public Const WS_OVERLAPPED As Long = &H0
Public Const WS_POPUP As Long = &H80000000
Public Const WS_SIZEBOX As Long = &H40000
Public Const WS_SYSMENU As Long = &H80000
Public Const WS_TABSTOP As Long = &H10000
Public Const WS_THICKFRAME As Long = &H40000
Public Const WS_TILED As Long = &H0
Public Const WS_VISIBLE As Long = &H10000000
Public Const WS_VSCROLL As Long = &H200000
Public Const WS_OVERLAPPEDWINDOW As Long = (WS_OVERLAPPED + WS_CAPTION + WS_SYSMENU + WS_THICKFRAME + WS_MINIMIZEBOX + WS_MAXIMIZEBOX)
Public Const WS_TILEDWINDOW As Long = (WS_OVERLAPPED + WS_CAPTION + WS_SYSMENU + WS_THICKFRAME + WS_MINIMIZEBOX + WS_MAXIMIZEBOX)
Public Const WS_POPUPWINDOW As Long = (WS_POPUP + WS_BORDER + WS_SYSMENU)

'Declare custom type for lpDialogFunc argument
'http://msdn.microsoft.com/en-us/library/windows/desktop/ms645469(v=vs.85).aspx
Public Type DIALOGPROC
    hwndDlg As Long
    uMsg As LongPtr
    wparam As Long
    lparam As Long
End Type


'MAKEINTRESOURCE Macro emulation
'http://msdn.microsoft.com/en-us/library/windows/desktop/ms648029(v=vs.85).aspx
'Bitwise function example found here: http://support.microsoft.com/kb/112651
'VB conversion found here: https://groups.google.com/forum/#!topic/microsoft.public.vb.winapi/UaK3S-bJaiQ _
 modified with strong typing and to use string pointers for VB7
Private Function MAKEINTRESOURCE(ByVal lID As Long) As LongPtr
     MAKEINTRESOURCE = StrPtr("#" & CStr(MAKELONG(lID, 0)))
End Function

Private Function MAKELONG(ByRef wLow As Long, ByRef wHi As Long)
    'Declare variables
        Dim LoLO            As Long
        Dim HiLO            As Long
        Dim LoHI            As Long
        Dim HiHI            As Long

    'Get the HIGH and LOW order words from the long integer value
        GetHiLoWord wLow, LoLO, HiLO
        GetHiLoWord wHi, LoHI, HiHI

            If (wHi And &H8000&) Then
                MAKELONG = (((wHi And &H7FFF&) * 65536) Or (wLow And &HFFFF&)) Or &H80000000
            Else
                MAKELONG = LoLO Or (&H10000 * LoHI)
                'MAKELONG = ((wHi * 65535) + wLow)
            End If
End Function

Private Function GetHiLoWord(lparam As Long, LOWORD As Long, HIWORD As Long)
    'This is the LOWORD of the lParam:
        LOWORD = lparam And &HFFFF&
    'LOWORD now equals 65,535 or &HFFFF
    'This is the HIWORD of the lParam:
        HIWORD = lparam \ &H10000 And &HFFFF&
    'HIWORD now equals 30,583 or &H7777
        GetHiLoWord = 1
End Function

Public Function TstDialog()
    Dim dpDialog                As DIALOGPROC

    dpDialog.hwndDlg = 0
    dpDialog.uMsg = StrPtr("TEST")
    dpDialog.lparam = 0
    dpDialog.wparam = 0

    CreateDialog hInstance:=0, lpTemplateName:=MAKEINTRESOURCE(WS_POPUPWINDOW + WS_VISIBLE), lpDialogFunc:=dpDialog, dwInitParam:=&H110
End Function

person CBRF23    schedule 07.11.2014    source источник
comment
CreateDialog указывает в своей документации что это макрос, который на самом деле использует CreateDialogParam. Это также указывает на то, что он возвращает значение, и что если это возвращаемое значение равно NULL, вы должны использовать GetLastError, чтобы узнать, почему это не удалось. Вы этого не делаете - почему бы и нет? (В любом случае, не уверен, почему вы прыгаете через все эти обручи; любой продукт Office, поддерживающий VBA, имеет гораздо более простые в использовании встроенные способы создания форм (диалогов).)   -  person Ken White    schedule 18.11.2014
comment
Кен, я использую VBA 7.0 с Solidworks. Насколько мне известно, не существует какой-либо встроенной функции для создания немодального диалога в VBA, кроме создания общей формы и вызова ее экземпляра, установленного в VbModeless. Я изучал вариант Windows API, потому что мне не нравятся ненужные формы в моих проектах, а также как обучающий/сложный опыт.   -  person CBRF23    schedule 18.11.2014
comment
Где бы я использовал функцию GetLastError? В документации указано, что я должен использовать err.LastDllError в VBA. В настоящее время при вызове функции ничего не происходит, поэтому я не верю, что у меня есть возврат для проверки.   -  person CBRF23    schedule 18.11.2014
comment
Вы бы вызвали CreateDialog как функцию и сохранили возвращаемое значение. Если возвращаемое значение равно 0 (NULL), вы вызываете GetLastError и проверяете его результат на наличие определенного кода ошибки. Прямо на странице документации, на которую я ссылался ранее, есть ссылка на GetLastError. Таким образом, логика будет следующей: «Err = CreateDialog(), если Err = 0, то Err = GetLastError(). LastDllError» (как следует из названия) применяется к библиотекам DLL. Создание общей формы и использование экземпляра было бы значительно проще, чем то, что вы пытаетесь сделать сейчас (во всяком случае, из VBA). Вы создаете общий ресурс dlg — примерно то же самое.   -  person Ken White    schedule 18.11.2014
comment
О, подожди. Я просто внимательно посмотрел на ваш код. DIALOGPROC неправильный. DIALOGPROC — это указатель на функцию (процедуру DIALOG), которая принимает параметры hwnd, umsg, wParam, lParam — это не структура данных. Вам нужно передать указатель на метод, что я даже не уверен, что это возможно сделать из VBA. Вам также нужен предопределенный ресурс DIALOG (скрипт ресурсов, скомпилированный с помощью компилятора ресурсов MS и связанный с приложением), чтобы использовать CreateDialog; один из параметров, который он получает, — это имя этого ресурса. Боюсь, вы даже близко не подходите.   -  person Ken White    schedule 18.11.2014
comment
@KenWhite, я согласен, что общая форма была бы проще, но не самая удовлетворительная или ценная с точки зрения опыта. Теперь, когда я зашел так далеко, мне бы очень хотелось, чтобы это сработало. Я уже изучил ТОННУ вещей (о Windows, функциях API, побитовой печати и т. д.), о которых я бы никогда не узнал, если бы я просто использовал пользовательскую форму. На ночь еду домой, а утром поиграюсь с функцией GetLastError.   -  person CBRF23    schedule 18.11.2014
comment
@KenWhite, спасибо за помощь. Я действительно не понимаю, как эмулировать функцию обратного вызова DialogProc в VBA, поскольку я не могу найти никакого объяснения базовой функции. Похоже, я мог бы использовать системный ресурс DIALOGEX, который снова выглядит как тип данных, но, вероятно, таковым не является. Думаю, я рассмотрю использование общей пользовательской формы. Меня расстраивает, что я не могу сделать это так, как хотелось бы.   -  person CBRF23    schedule 18.11.2014
comment
DialogProc [задокументировано]. Это функция обратного вызова (функция, указатель которой вы передаете в WinAPI, и этот API использует ее для передачи информации обратно в ваше приложение через сообщения Windows). Это базовая основа программирования WinAPI.   -  person Ken White    schedule 18.11.2014
comment
К сожалению, я отвлекся, прежде чем смог вставить ссылку. Документы для DialogProc, о котором я упоминал.   -  person Ken White    schedule 18.11.2014
comment
Давайте продолжим обсуждение в чате.   -  person CBRF23    schedule 18.11.2014


Ответы (4)


Это можно заставить работать, хотя если вы попытаетесь заставить это работать, это другой вопрос. У меня есть рабочая версия, которая показывает пустой диалог. У меня больше нет времени сегодня вечером, чтобы закончить с фактическими элементами управления в диалоговом окне, но я публикую в надежде, что это поможет вам начать.

Во-первых, вам нужно забыть о CreateDialog, потому что они требуют, чтобы шаблон диалога был в разделе ресурсов. Вы можете использовать CreateDialogIndirectParam для создания диалога из шаблона диалога в памяти. Вам понадобится это:

Private Type DLGTEMPLATE
    style As Long
    dwExtendedStyle As Long
    cdit As Integer
    x As Integer
    y As Integer
    cx As Integer
    cy As Integer
End Type

Private Type DLGITEMTEMPLATE
    style As Long
    dwExtendedStyle As Long
    x As Integer
    y As Integer
    cx As Integer
    cy As Integer
    id As Integer
End Type

Private Type DLG
    dlgtemp As dlgtemplate
    menu As Long
    classname As String
    title As String
End Type

Private Declare PtrSafe Function CreateDialogIndirectParam Lib "User32.dll" Alias "CreateDialogIndirectParamW" _
  (ByVal hInstance As Long, _
  ByRef lpTemplate As DLGTEMPLATE, _
  ByVal hWndParent As Long, _
  ByVal lpDialogFunc As LongPtr, _
  ByVal lParamInit As Long) _
  As LongPtr

Const WM_INITDIALOG As Long = &H110
Const DS_CENTER As Long = &H800&
Const DS_SETFONT As Long = &H40
Const DS_MODALFRAME As Long = &H80
Const WS_EX_APPWINDOW As Long = &H40000

Затем назовите это так:

Dim d As DLG
d.dlgtemp.style = DS_MODALFRAME + WS_POPUP + WS_VISIBLE + WS_CAPTION + WS_SYSMENU
d.dlgtemp.dwExtendedStyle = WS_EX_APPWINDOW
d.dlgtemp.cdit = 0
d.dlgtemp.x = 100
d.dlgtemp.y = 100
d.dlgtemp.cx = 200
d.dlgtemp.cy = 200
d.menu = 0
d.title = "Test"
d.classname = "Test"

CreateDialogIndirectParam 0, d.dlgtemp, 0, AddressOf DlgFunc, 0

с DlgFunc выглядит примерно так:

Public Function DlgFunc(ByVal hwndDlg As LongPtr, ByVal uMsg As LongPtr, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
    If uMsg = h110 Then  ' = WM_INITDIALOG - you should make a const for the various window messages you'll need...
        DlgFunc = True
    Else
        DlgFunc = False
    End If
End Function

Прошло более десяти лет с тех пор, как я в последний раз делал что-либо из этого. Но если вы решили пойти по этому пути, я думаю, что этот подход является наиболее многообещающим: следующим шагом будет адаптация структуры DLG для добавления некоторых элементов DLGITEMTEMPLATE, установите d.dlgtemp.cdit на количество элементов управления в вашем диалоговом окне, и начните обрабатывать управляющие сообщения в вашем DlgFunc.

person Roel    schedule 18.11.2014
comment
Кроме того, вам, вероятно, следует прочитать эту серию статей: blogs.msdn .com/b/oldnewthing/archive/2005/04/29/412577.aspx, если вы действительно собираетесь это сделать. - person Roel; 19.11.2014
comment
Я могу убедиться, что у меня есть пустые немодальные диалоговые окна, но я не могу закрыть их сейчас. Они уходят при закрытии excel. - person cheezsteak; 19.11.2014
comment
@Roel Это выглядит многообещающе! Мне нужно будет попробовать это, когда я вернусь к работе завтра (завтра должно быть запущено и запущено ПО - ремонт не сработал, поэтому мне пришлось ждать до конца дня, чтобы начать чистый процесс удаления / переустановки). - person CBRF23; 19.11.2014
comment
@cheezsteak Глядя на ответ Роэля, он сказал, что мне нужно будет установить d.dlgtemp.cdit на количество элементов управления в диалоговом окне и начать обработку управляющих сообщений, что согласуется с комментарием Ганса ниже о необходимости писать процедуры для обработки командные события. - person CBRF23; 19.11.2014
comment
Я отметил это как ответ, так как он рассмотрел все в моем первоначальном вопросе - он привел меня к тому, что я могу создать немодальный диалог с помощью вызова API, и это было именно то, что я пытался сделать. Теперь я должен решить, лучший ли это путь, или же пойти по альтернативному пути. С этой целью многие из них были предоставлены здесь, и, похоже, есть много других вариантов, о которых я изначально не знал, поэтому я получил здесь много полезной информации. Спасибо всем!! - person CBRF23; 20.11.2014
comment
Если вы решите пойти по этому пути, я думаю, что проще всего будет повторно реализовать CDlgTempl (из support.microsoft.com/kb/155257/en-us) в VBA, включая использование вызовов HeapAlloc/HeapFree для замены отсутствия malloc() в VBA. Выравнивание памяти по-прежнему будет проблемой, но, думаю, не неразрешимой. Реализация класса/модуля в VBA, который правильно обрабатывает все это, будет сложной, но не намного более сложной, чем то, что сделано в MFC для поддержки диалогов. И это по-прежнему оставит вас с очень громоздким (без дизайнера пользовательского интерфейса) способом реализации диалогов. - person Roel; 20.11.2014
comment
@Roel Мой первоначальный план состоял в том, чтобы создать модуль класса, который я мог бы использовать в нескольких проектах (я все еще работаю над хорошим методом распространения исходного кода в VBA - некоторое время назад я нашел прилично выглядящее решение, но я не могу щас найди ссылку - она ​​у меня в закладках на оф. Я создал довольно много обработчиков вызовов API в качестве объектов класса для таких вещей, как BrowseForFolder, GetOpenFileName и т. д., для которых нет встроенной поддержки в пакете SW VSTA, но которые я использую во многих проектах. Я никогда не ожидал, что эта попытка будет настолько сложной, как это было. Тем не менее БОЛЬШОЙ опыт обучения! - person CBRF23; 21.11.2014

Я не хочу отвлекать внимание от глубокого и хорошо изученного, но существуют возможные обходные пути для динамического создания немодальных диалоговых окон в VBA. Это была первоначальная проблема до того, как спрашивающий храбро нырнул в кроличью нору с CreateDialog. Таким образом, этот ответ предназначен для исходной проблемы динамического создания немодальных диалоговых окон в VBA, а не для использования CreateDialog. Я не могу помочь там.

Как было сказано ранее, немодальные диалоговые окна можно создавать с помощью UserForm, но мы не хотим, чтобы бесполезные формы засоряли проект. Обходной путь, которого я достиг, использует библиотеку расширения Microsoft VBA. Короче говоря, мы создаем класс, который добавляет общую пользовательскую форму в проект при создании и удаляет пользовательскую форму при завершении.

Также обратите внимание, что это проверено с использованием Excel VBA. У меня нет SolidWorks, поэтому я не могу проверить его там.

Грубо сделано как модуль класса.

Option Explicit

Private pUserForm As VBIDE.VBComponent

Private Sub Class_Initialize()
    ' Add the userform when created '
    Set pUserForm = ThisWorkbook.VBProject.VBComponents.Add(VBIDE.vbext_ct_MSForm)
End Sub
Private Sub Class_Terminate()
    ' remove the userform when instance is deleted '
    ThisWorkbook.VBProject.VBComponenets.Remove pUserForm
End Sub
Public Property Get UserForm() As VBIDE.VBComponent
    ' allow crude access to modify the userform '
    ' ideally this will be replaced with more useful methods '
    Set UserForm = pUserForm
End Property
Public Sub Show(ByVal mode As Integer)
    VBA.UserForms.Add(pUserForm.Name).Show mode
End Sub

В идеале этот класс должен быть лучше разработан и облегчит доступ к изменению формы, но пока это решение.

Тесты

Private Sub TestModelessLocal()

    Dim localDialog As New Dialog
    localDialog.UserForm.Properties("Caption") = "Hello World"
    localDialog.Show vbModeless

End Sub

Вы должны увидеть, как окно появляется и исчезает, когда localDialog покидает область видимости. UserForm1 был создан в вашем VBProject и удален.

Этот тест создаст постоянное диалоговое окно. К сожалению, UserForm1 останется в вашем VBProject, так как globalDialog все еще определено. Сброс проекта не удалит пользовательскую форму.

Dim globalDialog As Dialog
Private Sub TestModeless()

    Set globalDialog = New Dialog
    globalDialog.UserForm.Properties("Caption") = "Hello World"
    globalDialog.Show vbModeless
    'Set globalDialog = Nothing  closes window and removes the userform '
    'Set gloablDialog = new Dialog should delete userform1 after added userform2'
End Sub

Поэтому никогда не используйте это в области модуля.

В заключение, это уродливое решение, но оно гораздо менее уродливо, чем то, что пытался сделать Аскер.

person cheezsteak    schedule 18.11.2014
comment
Привет, чизстейк, да, динамическое добавление формы - это вариант. Единственное, что мне не нравится в динамическом изменении проектов VBA, это то, что это делает отладку PITA, потому что, как только проект изменяется программно, больше невозможно приостановить выполнение, и код должен выполняться до завершения. Для меня это означает, что я должен выполнять много проверок ошибок в своем коде и печатать в окне отладки или сохранять результаты и печатать в окне сообщений в конце, что в VBA довольно болезненно, поскольку нет попытки/поймать, как .net :/ Отчасти поэтому я искал другие методы, но это вариант! - person CBRF23; 19.11.2014
comment
Что касается глобального решения, вы, вероятно, могли бы переместить процедуру завершения в общедоступную процедуру, а затем просто вызвать эту процедуру. Таким образом, вы можете уничтожить глобальные/модульные переменные уровня. то есть Public Sub Destroy() ' remove the userform when instance is deleted ThisWorkbook.VBProject.VBComponents.Remove pUserForm End Sub Private Sub Class_Terminate() Destroy End Sub - person CBRF23; 19.11.2014
comment
@ CBRF23 Вы не можете приостановить выполнение? Я могу F8 через приведенный выше код просто отлично. Если вы выйдете раньше, пользовательская форма не будет удалена, но это единственная известная мне ошибка. - person cheezsteak; 19.11.2014
comment
Извините, может быть, я не совсем понимаю вопрос, но не слишком ли этот ответ слишком сложен? Пользователь может создать форму в VBE и запустить простой UserForm1.Show vbModeless, чтобы отобразить форму и продолжить выполнение кода. Зачем делать метапрограммирование, чтобы строить его динамически? Извините, если я невежествен, но я просто не понимаю, почему это так сложно... - person ; 19.11.2014
comment
@ vba4all В комментариях к вопросу задавший вопрос заявил, что мне не нравятся ненужные формы в моих проектах. Насколько я понимаю, он хочет динамически создавать диалоговые окна, как вы можете с Msgbox, но немодальные. Все диалоги, производящие встроенные функции, являются только модальными. - person cheezsteak; 19.11.2014
comment
будет ли работать это? - person ; 19.11.2014
comment
@ vba4all vba4all Это работает и, безусловно, является более чистым решением. Я бы заменил wsh.sleep на DoEvents. - person cheezsteak; 19.11.2014
comment
Хорошо, я думаю, что сон есть, поэтому всплывающее окно закрывается через некоторое время. - person ; 19.11.2014
comment
Давайте продолжим это обсуждение в чате. - person cheezsteak; 19.11.2014
comment
@ CBRF23: это решение работает? Хотя я вижу, что вы приняли другой ответ, потому что я не могу определить «хороший»/«функциональный» код в c++ и vba, я склонен следовать рекомендации сообщества, выбирая ответ, получивший наибольшее количество голосов, но очевидно, что это неверный ответ, если он не работает для вас. - person David says reinstate Monica; 24.11.2014
comment
@DavidThomas Да и нет. Вы можете проверить обсуждение в чате для получения дополнительной информации, но подведем итог: ДА - это работает при программном добавлении UserForm в проект и показе его немодальным. И НЕТ - в том смысле, что это на самом деле не работает для моего приложения, потому что создает слишком много проблем для меня при отладке остальной части проекта. По какой-то причине программное изменение проекта в VSTA 7.0 для Solidworks отключает режим приостановки, поэтому весь проект должен быть завершен до завершения, и я надеюсь, что я ввел достаточно проверок/отчетов об ошибках, чтобы выявить любые проблемы. - person CBRF23; 24.11.2014
comment
@DavidThomas Мой комментарий выше был направлен на этот ответ, используя расширяемость для программного добавления пользовательской формы. Vba4All также предоставил довольно хорошее решение, которое является жизнеспособной альтернативой использованию Windows API. Я определенно смотрю на это решение в своем проекте - оно кажется простым и может быть легко размещено в функции-оболочке. Я отметил ответ, который я сделал, потому что он затрагивал все в моем исходном вопросе, но что касается решений, я думаю, что Vba4All может быть проще всего реализовать. - person CBRF23; 24.11.2014

Вы очень плохо начали этот проект. Вы полностью перепутали порядок аргументов для CreateDialogParam, обратите внимание, что аргумент hInstance является первым, а аргумент dwInitParam последним.

Вы полностью перепутали объявление DIALOGPROC, это указатель на функцию. Для этого требуется LongPtr в объявлении и оператор AddressOf при вызове.

Это был только первый 1% того, как заставить его работать. Следующая проблема заключается в том, что вам придется написать функциональную процедуру диалога (цель AddressOf), которая обрабатывает уведомления, генерируемые диалогом. Основные вещи, такие как распознавание того, что пользователь нажал кнопку «ОК». Очень сложно писать, когда вы недостаточно знаете о программировании WinAPI, маленькие ошибки — это большие недиагностируемые проблемы во время выполнения.

Это мелочи, есть намного проблемы посерьезнее. Аргумент lpTemplateName является очень серьезным препятствием. Это должен быть идентификатор ресурса, который генерируется "rc.exe" и добавляется в исполняемый файл компоновщиком. Вы не можете повторно связать SolidWorks. Немодальный диалог требует помощи от цикла сообщений, он должен вызывать IsDialogMessage(). Вы не можете убедить SolidWorks сделать это за вас. Без этого диалоговое окно ведет себя так, что его трудно диагностировать, например, табуляция не будет работать.

Вы должны знать, когда у вас нет абсолютно никаких шансов заставить это работать. Вы не можете заставить его работать.

person Community    schedule 18.11.2014
comment
Я ценю очень знающий отзыв. Первоначально у меня были аргументы CreateDialogParam в порядке, указанном в документации MSDN, но VBA, к сожалению, требует, чтобы необязательные аргументы были последними, поэтому теперь они упорядочены так, как они есть. Я не знал, повлияет ли это на вызов API. Похоже, это так. Как указал Кен выше, я совершенно неправильно понял, как использовать функцию обратного вызова DialogProc, но я нашел некоторую информацию об использовании AddressOf. Это действительно звучит так, как будто это невыполнимо в VBA или, по крайней мере, не стоит времени/усилий. - person CBRF23; 18.11.2014
comment
[Я не могу решить, идет ли мой комментарий здесь или в вашем вопросе Tumbleweed. - randy] Очевидно, что вы много думали об этой проблеме и вопросе. Мое единственное предложение (если это возможно) состояло бы в том, чтобы сократить код до наименьшего возможного. Это облегчит людям просмотр кода и того, что может быть сломано. И, возможно, самостоятельно проверить правильность функций утилиты, таких как MAKELONG. Удачи! - person Randy Stegbauer; 18.11.2014

Этот ответ, как и Cheezsteak, напрямую не касается проблем, которые у вас возникают с CreateDialog. Он обращается к конечной цели создания немодального диалогового окна.

Я предлагаю использовать UserForm для этого. Это Show Method принимает необязательный параметр, который определяет, отображается ли пользовательская форма как модальная или немодальная форма.

Из документации MSDN:

модальный Необязательный. Логическое значение, определяющее, является ли пользовательская форма модальной или немодальной.

  1. Создайте пользовательскую форму и настройте ее в соответствии со своими потребностями.
  2. В коде, создающем экземпляр UserForm, просто передайте ему константу vbModeless.

    Option Explicit
    
    Private frm As UserForm1
    
    Sub test2()
        Set frm = New UserForm1
        frm.Show vbModeless
    End Sub
    

Если вы беспокоитесь о том, чтобы не загромождать свой проект формами, не беспокойтесь. Просто создайте форму на лету.

person RubberDuck    schedule 19.11.2014
comment
Я ссылался на связанный вопрос для своего ответа. - person cheezsteak; 20.11.2014