Using CreateDialog in VBA in an Attempt to Create Modeless Dialog Boxes
This can be made to work, although if you should try to make it work is another question. I have a working version that shows an empty dialog. I don't have any more time tonight to finish with actual controls on the dialog, but I'm posting in the hope it'll get you started.
First you need to forget about CreateDialog because they require the dialog template to be in the resource section. You can use CreateDialogIndirectParam to create a dialog from an in-memory dialog template. You will need this:
Private Type DLGTEMPLATE style As Long dwExtendedStyle As Long cdit As Integer x As Integer y As Integer cx As Integer cy As IntegerEnd TypePrivate Type DLGITEMTEMPLATE style As Long dwExtendedStyle As Long x As Integer y As Integer cx As Integer cy As Integer id As IntegerEnd TypePrivate Type DLG dlgtemp As dlgtemplate menu As Long classname As String title As StringEnd TypePrivate 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 LongPtrConst WM_INITDIALOG As Long = &H110Const DS_CENTER As Long = &H800&Const DS_SETFONT As Long = &H40Const DS_MODALFRAME As Long = &H80Const WS_EX_APPWINDOW As Long = &H40000
Then call it like this:
Dim d As DLGd.dlgtemp.style = DS_MODALFRAME + WS_POPUP + WS_VISIBLE + WS_CAPTION + WS_SYSMENUd.dlgtemp.dwExtendedStyle = WS_EX_APPWINDOWd.dlgtemp.cdit = 0d.dlgtemp.x = 100d.dlgtemp.y = 100d.dlgtemp.cx = 200d.dlgtemp.cy = 200d.menu = 0d.title = "Test"d.classname = "Test"CreateDialogIndirectParam 0, d.dlgtemp, 0, AddressOf DlgFunc, 0
with DlgFunc looking something like this:
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 IfEnd Function
It's been over a decade since I last did any of this stuff. But if you're determined to go this route, I think this approach is the most promising - the next step is to adapt the DLG struct to add some DLGITEMTEMPLATE members, set d.dlgtemp.cdit to the number of controls on your dialog, and start handling control messages in your DlgFunc.
I don't want to detract from the in depth and well researched but there are possible work-arounds to dynamically creating modeless dialog boxes in VBA. That was the original problem before the asker bravely dived down the rabbit hole with CreateDialog
. So this answer is for the original problem of dynamically creating Modeless dialog boxes in VBA not how to use CreateDialog
. I can't help there.
As previously stated, modeless dialog boxes can be created using a UserForm, but we don't want useless forms littering the project. The work-around I have achieved uses the Microsoft VBA Extensibility Library. In short, we create a class that adds a generic userform to the project on construction and removes the userform on termination.
Also note, this is tested using Excel VBA. I don't have SolidWorks so I can't test it there.
Crudely done as a class module.
Option ExplicitPrivate pUserForm As VBIDE.VBComponentPrivate Sub Class_Initialize() ' Add the userform when created ' Set pUserForm = ThisWorkbook.VBProject.VBComponents.Add(VBIDE.vbext_ct_MSForm)End SubPrivate Sub Class_Terminate() ' remove the userform when instance is deleted ' ThisWorkbook.VBProject.VBComponenets.Remove pUserFormEnd SubPublic Property Get UserForm() As VBIDE.VBComponent ' allow crude access to modify the userform ' ' ideally this will be replaced with more useful methods ' Set UserForm = pUserFormEnd PropertyPublic Sub Show(ByVal mode As Integer) VBA.UserForms.Add(pUserForm.Name).Show modeEnd Sub
Ideally, this class would be better developed and would allow easier access to modifying the form, but for now it's a solution.
Tests
Private Sub TestModelessLocal() Dim localDialog As New Dialog localDialog.UserForm.Properties("Caption") = "Hello World" localDialog.Show vbModelessEnd Sub
You should see a window appear and disappear as localDialog
leaves scope. A UserForm1
was created in your VBProject and deleted.
This test will create a persistent dialog box. Unfortunately, UserForm1
will remain in your VBProject as globalDialog
is still defined. Resetting the project will not remove the userform.
Dim globalDialog As DialogPrivate 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
So never use this at module scope.
In conclusion, its an ugly solution but it's far less ugly than what the Asker was trying to do.
You got a very poor start on this project. You completely scrambled the order of the arguments for CreateDialogParam, note how the hInstance
argument is first, the dwInitParam
argument is last.
You completely fumbled the DIALOGPROC declaration, it is a function pointer. That requires LongPtr
in the declaration and the AddressOf
operator when you make the call.
This was just the first 1% of making it work. Next problem is that you'll have to write a functional dialog procedure (the target of AddressOf
) that handles the notifications that the dialog generates. Basic stuff, like recognizing that the user clicked the OK button. Very hard to write when you don't know enough about WinAPI programming, little mistakes are big undiagnosable problems at runtime.
That's just the small stuff, there are much bigger problems. The lpTemplateName
argument is a very serious obstacle. That needs to be a resource identifier, the kind that's generated by "rc.exe" and added to the executable file by the linker. You cannot relink SolidWorks. A modeless dialog requires help from the message loop, it must call IsDialogMessage()
. You cannot convince SolidWorks to make this call for you. Without it, the dialog misbehaves in hard to diagnose ways, like tabbing will not work.
You have to know when you stand absolutely no chance to make it work. You can't make it work.