-
[RESOLVED] Problem with Public UDT: 'Only public user defined types......
Hi Guys, Having a problem with a UDT. I have the following :
Code:
Public Type mUdtObjInfo
A as string
B as string
C as string
D as string
End Type
Public Type myInfo
ObjInfo as mUdtObjInfo
PosX As Integer
PosY As Integer
End Type
Ok these work fine and are declared in a Module. However, when I try and access/Pass them through a Custom User Control, such as:
Code:
Public Function EditCurrent() As myInfo
EditCurrent = theOne(m_Selected)
End Function
I get an error:
"Only public user defined types defined in public object modules can be used as parameters or return types for public procedures of class modules or as fields of public user defined types"
I've searched around and have tried Declaring it as a Friend instead of Public, but still no result. I've tried putting all of this in a Class and still same.
I read that you could get away if you add these in a .TLB or ActiveX/Dll:
Check this link:
Any ideas?
-
Re: Problem with Public UDT: 'Only public user defined types......
The easiest "fix" is to use Classes instead of UDTs.
What makes you think you want UDTs for this?
-
Re: Problem with Public UDT: 'Only public user defined types......
Quote:
Originally Posted by
dilettante
The easiest "fix" is to use Classes instead of UDTs.
What makes you think you want UDTs for this?
I guess UDT's are easier/faster to deal with.
-
Re: Problem with Public UDT: 'Only public user defined types......
Oh, ok. I thought maybe you were using them to mimic structs for API calls or something.
The only way to use UDTs for this is to create and register an ActiveX DLL or TLB defining them. In VB6 it is possible to create such a DLL, but to make the necessary TLB requires the use of other tools.
Using Classes instead saves all of this extra work. Simple value Classes are trivial to create, though each one needs its own module:
Code:
Public A As String
Public B As String
Public C As String
Public D As String
-
Re: Problem with Public UDT: 'Only public user defined types......
I would see it working with Classes but would require a few Classes, which would kind of enlarge needlessly the project. I just need a few UDT's.
Any ideas on creating a TLB?
-
Re: Problem with Public UDT: 'Only public user defined types......
You might start here: How To Pass Array of UDTs with Variable Length Strings to C/C++.
I haven't seen anything in the way of a cookbook for creating TLBs just to define public UDTs though.
-
1 Attachment(s)
Re: Problem with Public UDT: 'Only public user defined types......
I would also recommend musing classes however if its too much to change then try using ordinary array instead - each item will have all values delimited by some character (pipe perhaps).
You can also use collections and/or dictionary object (this one is collection on steroids).
Here is a quick sample project that uses dictionary object (ms scripting runtime library is required).
-
Re: Problem with Public UDT: 'Only public user defined types......
I think I've gone too much to change everything again now and I'm also trying to avoid 3rd party .dll's etc.. so the MS Scripting Runtime library, I'm not too keen on.
It seems i need C++ in order to generate/create this .TLB having looked at that link, but I don't have or understand C++.
Could anyone with knowledge of C++ give me a hand? All i need is a few declared UDT's and that's it. No functions, subs etc... only UDT's.
Thanks
-
Re: Problem with Public UDT: 'Only public user defined types......
Quote:
Originally Posted by
some1uk03
I think I've gone too much to change everything again now and I'm also trying to avoid 3rd party .dll's etc.. so the MS Scripting Runtime library, I'm not too keen on.
It's a major Windows component since Windows 98 (late 90's) so it's always available without addition effort meaning that you can use late bindings (CreateObject) practically on any Windows OS.
Sometimes we takes approach that has no way out so it's necessary to think it through before hitting the keyboard... But I understand your concern...
-
Re: Problem with Public UDT: 'Only public user defined types......
You don't need C++, you should be able to "compile" a typelib definition using tools like MkTypLib. This means manually generating UUIDs of course.
Another drawback of this is that using a TLB in this way usually means it must be deployed and registered with the program. See the ancient (2003) Declaring UDT in .TLB: is it really a dead end?
Seriously, just use Classes and be done with it.
Generic objects like Collection or Dictionary are just going to be slow and bulky if you need to beat on a lot of these. Internal Classes are lighter by quite a large margin.
-
Re: Problem with Public UDT: 'Only public user defined types......
To avoid painting yourself into such a corner in the future always remember that UDTs are a last resort. They are only left as a vestige in VB6 so they can be used for the rare random I/O (also obsolete) or as surrogates for structs when calling APIs.
-
Re: Problem with Public UDT: 'Only public user defined types......
I wouldn't go the TLB route either but if you want to play with it here is an easy way to create a TLB right from VB.
1. Create a new class and add your UDTs (use Public Type).
2. In Project/Properties/Component Tab check the box for Remote Server Files.
3. Compile the project and you will get both a DLL and TLB.
4. Open the TLB in OleView.exe and you will see the UDTs.
http://www.mediafire.com/i/?84u2457wwpswqsr
-
Re: Problem with Public UDT: 'Only public user defined types......
That makes sense. You'd need the TLB for marshalling data to and from the remote server.
-
Re: Problem with Public UDT: 'Only public user defined types......
Hi doc,
Ok I did it that way and generated the .DLL and with it came the .TLB too.
I can view the contents with OleView.exe and seem ok. But when I try and add a reference to it in VB, it wants the .DLL too.
How do i get rid of the .dll?
-
Re: Problem with Public UDT: 'Only public user defined types......
Maybe it's better to convert your UDT array to binary array, pass the array (or pointer to array), then convert back to UDT at destination.
-
Re: Problem with Public UDT: 'Only public user defined types......
You'll probably want to unregister the DLL after creation (clean up registry pollution) though this isn't necessary if you reinstall Windows a lot anyway.
To use the typelib you set a reference to the TLB, not the DLL.
-
Re: Problem with Public UDT: 'Only public user defined types......
Quote:
Originally Posted by
dilettante
.... To use the typelib you set a reference to the TLB, not the DLL.
Yes, but the problem is, only the .DLL is referenced in the LIST and If i try and BROWSE for the .TLB and Add it, nothing seems to happen or get added to the list !?
-
Re: Problem with Public UDT: 'Only public user defined types......
I see what you mean.
When you set a reference to TLB the IDE seems to register the DLL. I compiled the DLL project, unregistered the DLL, then created a new project and set a reference to the TLB. After compiling the new project my DLL was registered again.
I do just see the TLB reference if I list the VBP file, and it shows in the References dialog as well. But without that DLL registered the program won't run.
-
Re: Problem with Public UDT: 'Only public user defined types......
-
Re: Problem with Public UDT: 'Only public user defined types......
I need more informations about your problem....
try this:
Create a module and put this declarations
Public Enum SELECTION: A = 0: B = 1: End Enum
Public Type mUdtObjInfo: A As String: B As String: End Type
Public Type myInfo
ObjInfo As mUdtObjInfo
PosX As Integer
PosY As Integer
End Type
Public m_Selected As SELECTION
Public OneUDT As myInfo
------------------------------------------------------------
Now create a form by default and past this code:
02 CommandButton = Caption: cmdSelection (index 0 and 1)
Dim OnemUdtObjInfo(1) As mUdtObjInfo
Public Function EditCurrent() As myInfo '<< MSG = Only public user defined types defined in public ... first msg
EditCurrent = theOne(m_Selected)
End Function
Public Function theOne(ByVal SelectedItem As SELECTION) As myInfo '<< MSG = Only public user defined types defined in public ... secound msg
theOne.ObjInfo = OnemUdtObjInfo(SelectedItem) 'pass the UDT to UDT
End Function
Private Sub cmdSelection_Click(Index As Integer) 'Two commandbutton index 0 and 1
Me.Cls
m_Selected = Index
OneUDT = EditCurrent
MsgBox "String A: " & OneUDT.ObjInfo.A & vbCr & "String B: " & OneUDT.ObjInfo.B
End Sub
Private Sub Form_Load()
OnemUdtObjInfo(0).A = "Selected Command 01"
OnemUdtObjInfo(0).B = "XXXXXXXXXXXXXXXXXXX"
OnemUdtObjInfo(1).A = "XXXXXXXXXXXXXXXXXXX"
OnemUdtObjInfo(1).B = "Selected Command 02"
End Sub
press F5 and ....
change Public Function EditCurrent() As myInfo to Private and try again ....
change Public Function theOne(ByVal SelectedItem As SELECTION) As myInfo to private...
modules (BAS) are defined to public declarations and Class or forms are private
A UDT delared same public in a module can't be declared public in private
I'm sorry... my English is not good
-
Re: Problem with Public UDT: 'Only public user defined types......
Hi MarMo,
Ok I've tried your suggestion. Changing them from Public to Private doesn't return any errors, BUT
I need them to be Public, because I'm actually trying to pass info from a UserControl:
i.e myUC.EditCurrent
-
Re: Problem with Public UDT: 'Only public user defined types......
Hello! excuse the delay in responding, I was traveling ... If you still need help with the problem of UDT let me know. I developed an OCX that shares UDT and I can post here.
-
Re: Problem with Public UDT: 'Only public user defined types......
Hi Marmo,
Yes still have same prob.
-
1 Attachment(s)
Re: Problem with Public UDT: 'Only public user defined types......
Here is the project which contains a OCX and a EXE for demostrar methods,
properties and events. Is not fully completed because the little time to
create but demonstrates how to work with UDT. Open the project OcxPassUDT and ative with F5 before use. Save and complile after open FormPassUDT and insert the OCX created.
I hope that will help
-
Re: Problem with Public UDT: 'Only public user defined types......
Hi Marmo,
Thanks, I appreciate your contribution, but this would require me to deploy the .OCX too.
My .EXE is currently deployed as a portable .exe without an installation, which is why I'm trying to avoid any extra files / .dll / ocx files.
-
Re: Problem with Public UDT: 'Only public user defined types......
I know I'm years late with a response but I just stumbled across this thread and didn't see the obvious answer. VB6 class modules allow a "Friend" declaration of variables and procedures (in addition to "Public" and "Private"). When a procedure is declared as "Friend", you can use UDTs as arguments. No need for all the above rigmarole. It's really that easy. Again, sorry it took me almost 4 years to give you the answer. In a standard .EXE project, this is the only distinction between a "Public" and "Friend" declaration. These "Friend" declarations are still exposed to the entire project. Good luck, if you happen to get this.
-
Re: Problem with Public UDT: 'Only public user defined types......
Quote:
Originally Posted by
Elroy
I know I'm years late with a response but I just stumbled across this thread and didn't see the obvious answer. VB6 class modules allow a "Friend" declaration of variables and procedures (in addition to "Public" and "Private"). When a procedure is declared as "Friend", you can use UDTs as arguments. No need for all the above rigmarole. It's really that easy. Again, sorry it took me almost 4 years to give you the answer. In a standard .EXE project, this is the only distinction between a "Public" and "Friend" declaration. These "Friend" declarations are still exposed to the entire project. Good luck, if you happen to get this.
Hi Elroy,
Only 4 yrs late on a reply but thanks for your input. :D
I think i ended up using classes not sure, can't even remember :)
Although havent now tested but you're saying it was as easy as changing a Public scope to a Friend :confused:
Will definitely remember & try it next time.
-
Re: Problem with Public UDT: 'Only public user defined types......
Since you are replying to your own thread so many years later, here's another one:
Declare them as UDTs, no biggie. The catch if you want to call it that:
In the usercontrol project.
1. Create a class and name it whatever you want, maybe something like GizmoGlobals
2. Change the class Instancing property to: GlobalMultiUse
3. Declare your UDTs in that class as Public
Now they can be declared within any project that hosts the usercontrol
Tip: This global class is also a good place to add custom public functions that apply to any of the usercontrol instances. Whatever is in the global is public to the customer and they do not have to declare that public class. If you were to look at your user control in the object browser (F2), you would see those UDTs and other stuff on the left side when viewing the usercontrol
-
Re: Problem with Public UDT: 'Only public user defined types......
*laughs* Yep, sorry I didn't see your question years ago. I still do a substantial amount of coding in VB6. Probably know about every nook and cranny there is to know about it. I just love that you can create an EXE file that needs absolutely no installation. My primary application runs on 100s of networked computers. They just find the EXE on the network and double-click it from there. I love it. And updates are a total snap too (other than getting everyone out of the application).
For new installations, I even use the .RES (resource file) feature to wrap .manifest and the core .ocx files into the compiled EXE. The first time it runs, it unloads these out of its internal resource file (in the EXE) and sets them out into the EXE's folder and then re-executes itself. I even put a blank database and other core startup files in the .RES file that gets wrapped into the EXE upon compilation.
I've even got all the Unicode worked out as well, with full unicode controls. That one was a bit tricky because the actual FRM files (that ultimately contain all the properties) are ASCII, but, if you put the unicode in the property bag as a byte array, it'll go into the FRX file instead. *smiles and shakes head* Just some of the old VB6 tricks of the trade.
But back to "Friend vs Public" for a sec. Yeah, the "Friend" procedure is really better than "Public". It can be used in both forms and classes (but not BAS modules). These will then be seen by the entire project, but NOT outside of the project. VB6 has the ability to "expose" entry points (primarily for when you are creating DLLs), but the "Friend" declaration does not expose these entry points. Therefore, you circumvent any need to register your UDTs (for "automation" purposes), and you can pass them around anywhere you like. The actual UDT declaration (Type statement) will still need to be in a BAS module, but I suspect you already knew that.
Take care,
Elroy
-
Re: Problem with Public UDT: 'Only public user defined types......
Ok, let me respond to LaVolpe as well. He/she brings up a good point. User controls can get a bit tricky. I just love them, but I've gotten totally away from anything but the core set of controls and controls I've written myself. Let me list the standard control sets I use: comdlg32.ocx, mscomctl.ocx, mscomct2.ocx richtx32.ocx, & tabctl32.ocx. I've also used the grid control at times. I found that all those third-party controls that were all the rage a decade or so were all crap.
I have written a handful of controls on my own. For instance, the unicode controls I mentioned above. I also have a degree slider control, a multi-select combobox control, and a few others for specific uses. However, because I wrote these (and have the source code), I just throw them right into my project, and don't compile them as separate OCX files. This sort of circumvents the issues you were discussing.
However, there is still an issue regarding the "Friend" vs "Public" declaration when dealing with user controls. Specifically, I have two user control that have external forms that's sort of part of the controls. These forms come up when you "Edit" the controls while in design mode. They're sophisticated controls for putting rich text (and even pictures) on buttons and labels. A little word processor with image insert pops up in the IDE when you edit these controls. However, since these controls are compiled actually at design time (and not when you actually say to compile), any calls that are made from these user controls into other forms (or classes) must be made to "Public" procedures and not "Friend" procedures. So this is one (but rather esoteric) exception to "Friend" being better than "Public".
Hey, most of my stuff is open source these days, so if you think I've mentioned something useful, let me know and I'll shoot it to you. My email is [email protected]. (that's elroy sullivan @ gmail dot com (with no spaces), in case it gets deleted by the parser).
Take Care,
Elroy
-
Re: Problem with Public UDT: 'Only public user defined types......
Ok, one last point, and I'm done for the day. There is another spot where "Friend" won't work (even though it will compile). If you make an API call that has a call-back into one of your BAS modules, that call-back procedure can NOT make calls into "Friend" class (or form) procedures. You'll get a GPF if you try it. Again, this is rather esoteric, and also a case where you're not (strictly speaking) staying confined within your program. 99% of the time, "Friend" is the way to go though, and you get your UDTs passed in and out of class modules (and forms, which are just class modules with interfaces).
-
Re: Problem with Public UDT: 'Only public user defined types......
Quote:
Originally Posted by
Elroy
When a procedure is declared as "Friend", you can use UDTs as arguments. No need for all the above rigmarole. It's really that easy.
Quote:
Originally Posted by
some1uk03
... have tried Declaring it as a Friend instead of Public, but still no result.
It appears that merely changing the scope of a procedure in a UserControl from Public to Friend isn't enough (at least in a Standard EXE). The following example shows one way of making it work correctly:
Code:
Option Explicit 'In Form1.frm
Private Sub Form_Load()
Dim MI As MyInfo
'MI = UserControl11.EditCurrent '<-- Compile error: Method or data member not found
Dim UC1 As UserControl1 'Declare an object variable
Set UC1 = UserControl11 'and set it to the target UserControl
MI = UC1.EditCurrent 'The UserControl's Friend procedures/
End Sub 'properties will now become accessible
Code:
Option Explicit 'In Module1.bas
Public Type mUdtObjInfo
A As String
B As String
C As String
D As String
End Type
Public Type MyInfo
ObjInfo As mUdtObjInfo
PosX As Integer
PosY As Integer
End Type
Code:
Option Explicit 'In UserControl1.ctl
Private m_Selected As Long
Private theOne(0) As MyInfo
Friend Function EditCurrent() As MyInfo
EditCurrent = theOne(m_Selected)
End Function
Quote:
Originally Posted by
Elroy
I've even got all the Unicode worked out as well, with full unicode controls.
You might be interested in checking this out. ;)
Quote:
Originally Posted by
Elroy
There is another spot where "Friend" won't work (even though it will compile). If you make an API call that has a call-back into one of your BAS modules, that call-back procedure can NOT make calls into "Friend" class (or form) procedures. You'll get a GPF if you try it.
Maybe you were doing it wrong? :confused: The attached project here demonstrates that it's possible to have Friend callback procedures.
-
Re: Problem with Public UDT: 'Only public user defined types......
Hmmm, yeah, I took a look at the project you recommended, VBForms team. The CTL user control and the callback were two separate issues for me. For the callback, I was hooking the mouse system-wide, and then calling a procedure in a loaded form. Here's a bit of the callback code (which is obviously in a BAS module):
Code:
Private Function SystemWideMouseMove(ByVal ncode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
If ncode = HC_ACTION Then
Call CopyMemory(SystemMouseStruct, ByVal lParam, Len(SystemMouseStruct))
SystemMouseWParam = wParam
frmMouseHookingForm.SystemMouseEvent
End If
SystemWideMouseMove = CallNextHookEx(pLastMouseHook, ncode, wParam, lParam)
End Function
It's interesting that the actual callback function can be private, but the callback was set in the same module so I guess VB could "see" the procedure address to tell Windows about. But it's that "frmMouseHookingForm.SystemMouseEvent" piece that I'm talking about. The "SystemMouseEvent" MUST be Public (not Friend) in the form, or I get GPFs. It's sort of interesting though. It works on speedy computers but not on slower ones, so maybe it's some kind of timing issue. I do my development on an i7 gamers computer, but the software has to run on older WindowsXP computers. The frmMouseHookingForm was actually a small form that you would drag around on the desktop and drop onto another form or control. That's why it had to be system wide. On the slower computers, it would start to work, and then crash. I changed the SystemMouseEvent procedure in the form to Public, and all problems go away. I'm not sure why. I just assumed that the callback procedure (shown above) couldn't correctly "see" the Friend address.
I'm certainly willing to listen to a better explanation of what's happening though. I just have a big comment on the procedure to not make it Friend.
Elroy
-
Re: Problem with Public UDT: 'Only public user defined types......
Quote:
Originally Posted by
Elroy
It's interesting that the actual callback function can be private, ...
Yes, the scope of a callback function (or any other kind of procedure for that matter) isn't really important because in compiled code, there is no such distinction. What matters (to external codes) is the address of a function.
Quote:
Originally Posted by
Elroy
I changed the SystemMouseEvent procedure in the form to Public, and all problems go away. I'm not sure why. I just assumed that the callback procedure (shown above) couldn't correctly "see" the Friend address.
Sorry, but I fail to see why too. Maybe you could provide a sample project that exhibits the issue(s) you're having so we can take a closer look.
Quote:
Originally Posted by
Elroy
I'm certainly willing to listen to a better explanation of what's happening though. I just have a big comment on the procedure to not make it Friend.
I believe we are getting a bit off-topic :p so if you don't mind, could you please start a new thread that discusses your issue(s) in more detail? As I've mentioned above, it would really be helpful if you could attach a small project demonstrating the problem(s). Thank you.
BTW, you shouldn't publish your email address in a forum such as this, unless you like receiving spam. ;) It isn't too late yet to edit your post #30.
-
1 Attachment(s)
Re: Problem with Public UDT: 'Only public user defined types......
Hi Bonnie,
If it's okay, I'll just pull it back on topic and post here. In the above, I do an API CopyMemory into a UDT. There's really no way to avoid the CopyMemory, but it would be nice if the UDT could have been declared in the procedure and then passed in the call to frmMouseHookingForm.SystemMouseEvent. However, being required to declare frmMouseHookingForm.SystemMouseEvent as Public, this wasn't possible. Upon further reflection, I bet I know why the Friend declaration won't work. Some of the machines the program runs on are a bit short on memory. My development machine has hoards. I bet VB6 feels free to move the program code of Friend procedures, whereas Public procedures must maintain a fairly consistent memory address. This would explain the relative intermittent performance when the frmMouseHookingForm.SystemMouseEvent procedure is declared as Friend.
I've cut out the smallest project I can to illustrate the problem, as you've requested. I'll post all the necessary code segments here, and also try and attach it as a ZIP file.
This first bit of code is a FRM. I've included all of it, so just save it as FRM in notepad:
Code:
VERSION 5.00
Begin VB.Form frmDragText
BackColor = &H00C0FFFF&
BorderStyle = 1 'Fixed Single
ClientHeight = 885
ClientLeft = 12045
ClientTop = 4140
ClientWidth = 2790
ClipControls = 0 'False
ControlBox = 0 'False
HasDC = 0 'False
LinkTopic = "Form2"
MaxButton = 0 'False
MinButton = 0 'False
ScaleHeight = 885
ScaleWidth = 2790
ShowInTaskbar = 0 'False
Begin VB.Timer tmr
Enabled = 0 'False
Interval = 50
Left = 1080
Top = 60
End
Begin VB.Label lblDragText
Alignment = 2 'Center
BackColor = &H00C0FFFF&
Caption = "Drag Text"
Height = 195
Left = 60
TabIndex = 0
Top = 0
Width = 900
End
End
Attribute VB_Name = "frmDragText"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
'
Private Type POINTAPI
X As Long
Y As Long
End Type
'
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
Private Declare Function WindowFromPointXY Lib "user32" Alias "WindowFromPoint" (ByVal xPoint As Long, ByVal yPoint As Long) As Long
'
Private Const VK_LBUTTON = &H1
Private Const VK_RBUTTON = &H2
'
Dim pt32 As POINTAPI
Dim frm As Form
'
Friend Sub Drag(frmIn As Form, sText As String)
Set frm = frmIn
lblDragText.Caption = sText
lblDragText.Left = -30
lblDragText.Top = 0
lblDragText.Height = 240
lblDragText.Width = Me.TextWidth(lblDragText.Caption) + 180
Me.Width = lblDragText.Width
Me.Height = 240
'
GetCursorPos pt32
Me.Left = (pt32.X + 5) * Screen.TwipsPerPixelX - (Me.Width \ 2) - 90
Me.Top = (pt32.Y + 5) * Screen.TwipsPerPixelY - Me.Height - 60
Me.Show
End Sub
Private Sub Form_Load()
SetSystemWideMouseHook Me
End Sub
Private Sub Form_Unload(Cancel As Integer)
UnSetSystemWideMouseHook
End Sub
Public Sub SystemMouseEvent() ' THIS MUST STAY PUBLIC!!!!!!!!!!!!!!!! (NOT FRIEND)
' Now see if we are ready to drop the text.
If Not ((GetKeyState(VK_LBUTTON) >= 0) And (GetKeyState(VK_RBUTTON) >= 0)) Then
Me.Left = (SystemMouseStruct.X + 5) * Screen.TwipsPerPixelX - (Me.Width \ 2) - 90
Me.Top = (SystemMouseStruct.Y + 5) * Screen.TwipsPerPixelY - Me.Height - 60
Else
Unload Me
End If
End Sub
This next segment is just a form to test it:
Code:
VERSION 5.00
Begin VB.Form Form1
Caption = "Form1"
ClientHeight = 5475
ClientLeft = 780
ClientTop = 2340
ClientWidth = 6585
LinkTopic = "Form1"
ScaleHeight = 5475
ScaleWidth = 6585
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
frmDragText.Drag Me, "Dragging"
End Sub
The next segment is the hooking and callback BAS module:
Code:
Attribute VB_Name = "Module1"
Option Explicit
'
Private Type MSLLHOOKSTRUCT
X As Long
Y As Long
MouseData As Long
flags As Long
Time As Long
dwExtraInfo As Long
End Type
'
Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
Private Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal cbLength As Long)
Private Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal ncode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'
Public SystemMouseStruct As MSLLHOOKSTRUCT
Public SystemMouseWParam As Long
'
Private pLastMouseHook As Long
Private frmMouseHookingForm As Form
'
Private Const WH_MOUSE_LL = 14& ' Hook Flag
Private Const HC_ACTION = 0& ' Mouse Process Message
'
Public Sub SetSystemWideMouseHook(frm As Form)
If pLastMouseHook = 0 Then
pLastMouseHook = SetWindowsHookEx(WH_MOUSE_LL, AddressOf SystemWideMouseMove, App.hInstance, 0&)
Set frmMouseHookingForm = frm
End If
End Sub
Public Sub UnSetSystemWideMouseHook()
If pLastMouseHook <> 0 Then
UnhookWindowsHookEx pLastMouseHook
pLastMouseHook = 0
Set frmMouseHookingForm = Nothing
End If
End Sub
Private Function SystemWideMouseMove(ByVal ncode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
If ncode = HC_ACTION Then
Call CopyMemory(SystemMouseStruct, ByVal lParam, Len(SystemMouseStruct))
SystemMouseWParam = wParam
frmMouseHookingForm.SystemMouseEvent
End If
SystemWideMouseMove = CallNextHookEx(pLastMouseHook, ncode, wParam, lParam)
End Function
And lastly, here's the VBP project to test it all. Just run it, and drag anywhere on the test form, and you'll see how it's suppose to work.
Code:
Type=Exe
Form=Form1.frm
Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#..\..\..\..\Windows\SysWow64\stdole2.tlb#OLE Automation
Form=DragText.frm
Module=Module1; Module1.bas
IconForm="Form1"
Startup="Form1"
Command32=""
Name="Project1"
HelpContextID="0"
CompatibleMode="0"
MajorVer=1
MinorVer=0
RevisionVer=0
AutoIncrementVer=0
ServerSupportFiles=0
VersionCompanyName="Test"
CompilationType=0
OptimizationType=0
FavorPentiumPro(tm)=0
CodeViewDebugInfo=0
NoAliasing=0
BoundsCheck=0
OverflowCheck=0
FlPointCheck=0
FDIVCheck=0
UnroundedFP=0
StartMode=0
Unattended=0
Retained=0
ThreadPerObject=0
MaxNumberOfThreads=1
To make it intermittently fail, with GPF faults and/or other bizarre errors, just declare the SystemMouseEvent in the frmDragText as Friend, and you'll get errors on slower machines with limited memory.
Attachment 117839
-
Re: Problem with Public UDT: 'Only public user defined types......
Serialising UDT's is a little more tricky than a straight CopyMemory.
Here's my code from 8 years ago (!!) that will serialise most UDT's: some restrictions, however ...
http://www.vbforums.com/showthread.p...t=yrwyddfa+udt
-
Re: Problem with Public UDT: 'Only public user defined types......
Quote:
Originally Posted by
Elroy
I bet VB6 feels free to move the program code of Friend procedures, whereas Public procedures must maintain a fairly consistent memory address.
I doubt VB6 does something like that. I don't know the low-level details though, so I could be wrong.
Quote:
Originally Posted by
Elroy
I've cut out the smallest project I can to illustrate the problem, as you've requested. I'll post all the necessary code segments here, and also try and attach it as a ZIP file.
Thanks a lot! :thumb:
Quote:
Originally Posted by
Elroy
To make it intermittently fail, with GPF faults and/or other bizarre errors, just declare the SystemMouseEvent in the frmDragText as Friend, and you'll get errors on slower machines with limited memory.
After changing SystemMouseEvent's scope to Friend, the EXE crashed immediately when I tried dragging the Form's client area.
I think the problem is because the public variable frmMouseHookingForm was dimensioned as the generic Form type. If you'll notice in the demo project I've linked to above, the object variables that point to an instance of their respective object modules were dimensioned as that specific object type. By doing it that way, the SystemMouseEvent procedure now appears in the IntelliSense list, which means that calls to that procedure won't be late bound (remember, Friend procedures can't be late bound). Sure enough, when I amended the declaration of frmMouseHookingForm to frmDragText, it worked! The program didn't self-destruct anymore.
-
Re: Problem with Public UDT: 'Only public user defined types......
Oh WOW, good information Bonnie. :))) That hooking code is used in a couple of different places, but I don't mind making copies of it for each form that uses it. Also, it's just good to know how things work. :)) You take care, and thanks for your diligence on this. Maybe some others will get some benefit from all of this as well.
-
Re: Problem with Public UDT: 'Only public user defined types......
Hi Bonnie, and all:
I've done further testing and you are exactly right. All the problems I mentioned above have to do with "Friend" procedures being in "Late Bound" objects. So long as they are "Early Bound", all works fine. It turns out the MSDN Library actually states this. I had just never completely recorded it in my head because you quickly get a run-time error in most cases. However, apparently, in certain callback and User-Defined-Control situations, the error checking doesn't catch the late binding, and the program just crashes badly. What's even most interesting is that I believe you actually answered Frenzied's original question. He mentioned that he tried dimensioning as "Friend" and it didn't work. The only reason I can think of that it wouldn't work is late binding.
Ok, I'm officially out'a here. This has been fun, and I actually learned something.
Regards,
Elroy
-
Re: Problem with Public UDT: 'Only public user defined types......
Quote:
Originally Posted by
LaVolpe
In the usercontrol project.
1. Create a class and name it whatever you want, maybe something like GizmoGlobals
2. Change the class Instancing property to: GlobalMultiUse
3. Declare your UDTs in that class as Public
How do you do do #2? I see no class instancing property for a class. Did you mean usercontrol not class?
I just want to make 2 functions for adding and removing items for a UDT array, so I don't have to remake the 2 functions for every UDT array. Passing UDTs into a collection would work too