-
[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
-
Re: Problem with Public UDT: 'Only public user defined types......
neotechni,
It's really easy to convert a UDT into a Class. Let's say you have the following UDT:
Code:
Public Type MyUdtType
i1 As Long
i2 As Long
s1 As String
s2 As String
End Type
And then, somewhere in your code, you do this...
Code:
Dim MyUdt As MyUdtType
All you need to do is start a new class. We'll call it MyClass. And then take the elements of your UDT (cutting of the Public Type... and the End Type), and paste the into the top of your class, and declare them as Public, like so:
Code:
Public i1 As Long
Public i2 As Long
Public s1 As Long
Public s2 As Long
And then, where you originally declared a variable with your UDT, just do the following instead...
Code:
Dim MyUdt As New MyClass
Voila, MyUdt is now actually a class and not a true UDT, but it'll work exactly the same, with the added advantage that it'll pass into and out of other objects.
Regards,
Elroy
EDIT1: Another option is to leave it as a UDT, and then just make sure all procedures (in objects [forms and classes]) are declared as "Friend".
-
Re: Problem with Public UDT: 'Only public user defined types......
UDT is not class. For example:
Code:
Private Type tSomeType
a1 As Long
b2 As String
End Type
Dim z As tSomeType
Dim o As tSomeType
z.a1 = 5
o.b2 = "test"
z = o
In this case o will contains copy of all fields. If i change some members in o it doesn't affect to z. If i change UDT to a class then during accessing to o i'll change z members too because both references point to same object. Additionally you need to add Clone method too.
-
Re: Problem with Public UDT: 'Only public user defined types......
Hi Trick,
Yes, you're correct. Your "z = o" statement would not work quite as expected. You'd need to assign each member separately. But the concept is still there. And yes, a clone method might be nice.
Regards,
Elroy
-
Re: Problem with Public UDT: 'Only public user defined types......
hi , maybe it is too late but the answer is only add Friend Keyword before Sub or Function in class module and enjoy it
like this:
Friend Sub Test(ByRef m As MYTYPE)
End Sub
-
Re: Problem with Public UDT: 'Only public user defined types......
It can indeed also be done with a TLB definition, but not only does it have to be a typedef, it has to be a registered typedef with a unique uuid,
Code:
typedef [uuid(guid)] struct MyType
{
long var1,
long var2
} MyType;
And once you do that, you have to be more diligent about registration of the typelib... you can't just overwrite with an updated copy, you have to deregister, then overwrite, then re-register, then launch vb. I've not found it to be worthwhile.
-
Re: Problem with Public UDT: 'Only public user defined types......
Quote:
Originally Posted by
fafalone
I've not found it to be worthwhile.
I agree. At this point, nothing about my primary (very large) program requires registration or installation ... and that's inclusive of several ActiveX DLLs.
It'd sure be nice if we could figure out how to load these TypeLibs without registration, the same way we can load ActiveX DLLs without registration. I'm not even sure that'd help though if we're trying to use them with some third-party DLL to which we don't have the source code, and we're trying to pass one of these UDTs.
If we could load these TypeLibs (without registration), it would solve the problem of getting these UDTs into Variants and Collections, which would be nice. Also, the whole Public vs Friend problem would go away.
-
Re: Problem with Public UDT: 'Only public user defined types......
Quote:
Originally Posted by
Elroy
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.
I had this very issue today, trying to pass a user-defined type array to a function (VB6) and hitting that long error message, but I remembered having used Friend to fix the problem years ago. I went back over my old programs and found it in half a dozen progs. From the preceding thread comments it seems not many people are aware of Friend!
-
Re: Problem with Public UDT: 'Only public user defined types......
Quote:
Originally Posted by
LittleTyke
I had this very issue today, trying to pass a user-defined type array to a function (VB6) and hitting that long error message, but I remembered having used Friend to fix the problem years ago. I went back over my old programs and found it in half a dozen progs. From the preceding thread comments it seems not many people are aware of Friend!
Microsoft really sort of confused things with the "scope" declaration of procedures in classes (FRM, CLS, CTL, PAG files). They did it to maintain some backward compatibility with older versions of VB and BASIC.
But, when used in a class, "Friend" is really analogous to the "Public" declaration when in a BAS module. That is, the scope is project wide, but not "exposed" to any outside process. However, when "Public" is used in a class, it is potentially exposed to other processes, and there's the catch.
When a procedure is potentially "exposed" to other processes (which isn't possible for BAS modules), and a UDT is used (either as an argument or the return), this must be done in such a way that the calling process "understands" that UDT ... and that's typically done with a TypeLib.
But, if we're willing to keep that UDT confined to our project (i.e., Friend declarations for classes, and Public declarations for BAS modules), then only our project needs to know about that UDT, and then the Public declaration of the UDT in a BAS module works perfectly.
Because of all of this, I have used (pretty much) only the "Friend" declaration (or Private) in class modules for years ... as again, it's equivalent to "Public" in a BAS module.
Also, if people don't know this stuff, they haven't been paying attention. Also, this is all well outlined in the help system (both VB6 F1 and online).
-
Re: [RESOLVED] Problem with Public UDT: 'Only public user defined types......
I'm recommending a free utility: Earlier in this thread I remarked that I had gone back over my old programs to find where I had used the Friend keyword. To do this I used AstroGrep, which is a brilliant program to search a folder/subfolder tree on Windows. I used it on Windows XP, but it probably works for all recent versions of Windows, too. The link is https://astrogrep.sourceforge.net/