VB(A)
Exontrol.COM Software - Frequently Asked Questions
VB.0:
In VB6 you can use any of the following versions:
  • /COM indicates the 32-bit edition of the ActiveX version

The application built using /COM version runs on any Windows 32 or 64-bit machine.

VB.1:
The ExPropertiesList control exports a property called Interfaces, that helps you to get the list of implemented interfaces. The MsgBox PropertiesList1.Interfaces(Me) displays a list of interfaces that form implements.
VB.2:
The MultiPage control is implemented in the FM20.DLL file. The control loses it's content because the window that hosts the control is destroyed every time when the user changes the active page. If you would like to check this issue you need to add a MultiPage control to your project references, to insert a instance of MultiPage control to your main form, and to use the following code:
Private Declare Function GetWindow _
    Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Private Declare Function GetClassName Lib "user32" Alias _
    "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, _
    ByVal nMaxCount As Long) As Long
Private Const GW_CHILD = 5
Private Const GW_HWNDNEXT = 2

Private Sub MultiPage1_Change()
    ' The MultiPage control has no hWnd property so we need to look for a window which
    ' class name is 'F3 Server 60000000'
    Dim h As Long
    h = GetWindow(Me.hwnd, GW_CHILD)
    ' Looks for the window that hosts ActiveX controls in a MultiPage control
    While (h <> 0)
        ' Gets the window's class name
        Dim strClass As String * 255
        GetClassName h, strClass, 255
        If (strClass Like "F3 Server 60000000*") Then
            ' We have found the handle of the MultiPage's window
                    ' Displays the window that hosts any ActiveX control
                    Debug.Print GetWindow(h, GW_CHILD)
        Exit Sub
    End If
    ' Gets the next window in the form
    h = GetWindow(h, GW_HWNDNEXT)
    Wend
End Sub
Now, run the project and select pages into your MultiPage control. You will see that the VB's immediate window displays different numbers, each time when you select a new page. Each number represents the handle of the window that hosts ActiveX controls in the MultiPage control. The MultiPage control destroys it's child window every time when a new page is activated. That means that any ActiveX control that is contained by a page in the MultiPage control will be destroyed each time when a new page is activated. How do I fix that? You can do one of the followings: use the Microsoft Tabbed Dialog Control ( tabctl.ocx )  instead MultiPage control, use Visible property of your control to hide or show the control when a page is activated,  load the control's content during changing the page.
VB.3:
The VB provides the statement 'withevents' that can be used in order to notify you application when a runtime control fires an event. For instance, the following sample shows how to create and handle events for ExComboBox control. The project references needs to include a reference to 'ExComboBox 1.0 Control Library'.
Option Explicit
Dim WithEvents combobox As EXCOMBOBOXLib.combobox

Private Sub combobox_SelectionChanged()
    If (combobox.Items.SelectCount > 0) Then
        Debug.Print "Selection changed to '" & _
        combobox.Items.CellCaption(combobox.Items.SelectedItem(0), 0) & "'."
    End If
End Sub

Private Sub Form_Load()
    Dim cb As Object
    
    ' USE this code only if you are creating the control at runtime on the client's machine.
    ' The following code is NOT required if you are adding the control on your development machine.
    ' The control requires a runtime license key, if you are creating the control at runtime
    ' Each product has an unique runtime license key. The runtime license key is FREE of charge.
    ' Use the runtime license key you have received from us, instead of XXXXXXXX
    
    Licenses.Add "Exontrol.ComboBox", "XXXXXXXX"
  
    ' Adds an ExComboBox control at runtime
    Set cb = Controls.Add("Exontrol.ComboBox", "combobox")
    ' Sets the "combobox" variable being the control events handler
    Set combobox = cb.object
    
    ' Updates the control
    combobox.BeginUpdate
    
        ' The Height should be called, to set the control's drop down height.
        cb.Height = 128 * Screen.TwipsPerPixelY
        cb.Left = (ScaleWidth - cb.Width) / 2
        
        ' Sets few control properties
        combobox.ColumnAutoResize = True
        combobox.HeaderVisible = False
        combobox.Columns.Add "1"
            
        ' Adds few items
        Dim i As Long
        With combobox.Items
            For i = 0 To 15
                .AddItem i
            Next
        End With
        
        ' Makes the control visible
        cb.Visible = True
    combobox.EndUpdate
    
End Sub
VB.4:
The VB Unload method unloads an object. The "Unload Me" statement closes the form. When "Unload Me" statement is called during in some events of the control, the VB closes the form, and so the control is closed too. So, the control is closed and it's deleted, but the VB still gives the control to the caller, and so an error 361 could occur.

There are several options that you can apply in order to fix this problem.

  • DoCmd: In MS Access, you should call DoCmd.Close command instead Unload Me, such as: DoCmd.Close acForm, Me.Name
  • DoEvents: The Unload Me should be preceded by a DoEvents call, if calling within a UserForm.
  • WM_CLOSE: Call "PostMessage Me.hWnd,  WM_CLOSE, 0, 0" or "SendMessage Me.hWnd,  WM_CLOSE, 0, 0", instead calling the "Unload Me" statement. You need to declare the PostMessage API and the WM_CLOSE constant like follows: 

    Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

    Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long

    Private Const WM_CLOSE = &H10

    With Ribbon1
          SendMessage GetParent(GetParent(.hwnd)), WM_CLOSE, 0, 0
    End With

  • SendKeys: Call the SendKeys "%{F4}", to close the current form.
  • Timer. You need to insert a disabled timer control to your form, and to call "Unload Me" statement in the timer's Timer event. You need to replace the "Unload Me" statement in the control's event, with Timer1.Enabled = True.
See also the Q189812 article in your MSDN, BUG: Unloading a Modal Form's Parent Form Causes VB To Hang
VB.5:
Create a separate class for each interface with the same name, like in the following sample. For instance, let's say that in your form you wrote:
Implements EXCOMBOBOXLibCtl.IOwnerDrawHandler
Implements EXGRIDLibCtl.IOwnerDrawHandler

And you got the error: "Compile error: Ambiguous name detected: IOwnerDrawHandler"

Create and add to the project the Class1 class that implements the EXCOMBOBOXLibCtl.IOwnerDrawHandler like follows:

Implements EXCOMBOBOXLibCtl.IOwnerDrawHandler

Private Sub IOwnerDrawHandler_DrawCell(ByVal hDC As Long, ByVal left As Long, _
        ByVal top As Long, ByVal right As Long, ByVal bottom As Long, _
        ByVal Item As Long, ByVal Column As Long, ByVal Source As Object)
    ' Draws the cell. The Source points to the exComboBox object
End Sub

Create and add to the project the Class2 class that implements the EXGRIDLibCtl.IOwnerDrawHandler like follows:

Implements EXGRIDLibCtl.IOwnerDrawHandler

Private Sub IOwnerDrawHandler_DrawCell(ByVal hDC As Long, ByVal left As Long, _
    ByVal top As Long, ByVal right As Long, ByVal bottom As Long, ByVal Item As Long, _
    ByVal Column As Long, ByVal Source As Object)
    ' Draws the cell. The Source points to the exGrid object
End Sub

Private Sub IOwnerDrawHandler_DrawCellBk(ByVal hDC As Long, Options As Variant, _
    ByVal left As Long, ByVal top As Long, ByVal right As Long, ByVal bottom As Long, _
    ByVal Item As Long, ByVal Column As Long, ByVal Source As Object)
    ' Draws the cell's background. The Source points to the exGrid object
End Sub

So, in the main form we need the following declarations:

Dim c1 As New Class1
Dim c2 As New Class2 

So, when we need to specify an owner draw cell in the exComboBox control we need to something like following:

With ComboBox1
    With .Items
        Set .CellOwnerDraw(...) = c1
    End With
End With

In the exGrid the code should look like:

With Grid1
    With .Items
        Set .CellOwnerDraw(...) = c2
    End With
End With
VB.6:
When the user double-clicks the window's caption, the form is maximized, but the Windows system still sends a WM_LBUTTONUP message to the window from the cursor, and so a Click event may occur. The following VB sample shows how to remove this issue:
Private Type POINTAPI
        x As Long
        y As Long
End Type
Private Type MSG
    hwnd As Long
    message As Long
    wParam As Long
    lParam As Long
    time As Long
    pt As POINTAPI
End Type

Private Declare Function PeekMessage Lib "user32" Alias "PeekMessageA" (lpMsg As MSG, ByVal hwnd As Long, ByVal wMsgFilterMin As Long, ByVal wMsgFilterMax As Long, ByVal wRemoveMsg As Long) As Long
Private Const WM_LBUTTONUP = &H202
Private Const PM_REMOVE = &H1

Private Sub Form_Resize()
    Dim m As MSG
    While PeekMessage(m, 0, WM_LBUTTONUP, WM_LBUTTONUP, PM_REMOVE)
    Wend
End Sub
VB.7:
The idea is to post a WM_LBUTTONDOWN message using the PostMessage API function like in the following sample:

Private Declare Function ScreenToClient Lib "user32" (ByVal hwnd As Long, lpPoint As POINTAPI) As Long
Private Type POINTAPI
        x As Long
        y As Long
End Type
Private Const WM_LBUTTONDOWN = &H201

Private Sub doclick()
    With Grid1
        Dim p As POINTAPI
        p.y = GetMessagePos() / 65536
        p.x = GetMessagePos() Mod 65536
        ScreenToClient .hwnd, p
        Dim l As Long
        l = p.y * 65536 + p.x
        PostMessage Grid1.hwnd, WM_LBUTTONDOWN, 0, l
    End With
End Sub
The sample uses the hWnd property of the exGrid control to retrieve the handle of the window where click should occur.
VB.8:
Use the keybd_event API function to simulate pressing the CTRL key as in the following sample:
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

Private Sub SetCTRLState(ByVal bPressed As Boolean)
    keybd_event &H11, &H1D, 1 Or IIf(bPressed, 0, 2), 0
End Sub

Private Sub Form_Load()
    With List1
        .SingleSel = False
    End With
End Sub

Private Sub List1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    SetCTRLState (True)
End Sub

Private Sub List1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    SetCTRLState (False)
End Sub
The sample uses the exontrol's eXList componet when the SingleSel property is False, so it supports multiple selection. Once the user presses the left mouse button, the sample simulates pressing the CTRL key too, so the item's state selection is toggled ie if the item is already selected another click will unselect it, and if the item is not selected the item gets selected, without unselecting the all items as would it happen without the trick.
VB.9:
Let's say that you need to change the caption of the selected item when a text field loses the focus using a code like follows:
Private Sub Text1_LostFocus()
    With Tree1.Items
        .CellCaption(.SelectedItem, 0) = Text1.Text
    End With
End Sub

Private Sub Tree1_SelectionChanged()
    With Tree1.Items
        Text1.Text = .CellCaption(.SelectedItem, 0)
    End With
End Sub

The VB environment controls the order of firing events. So, the SelectionChanged event is fired first, when the user clicks the control, and after that is fired the LostFocus event. This way, the result is not what we expected, so we need to find a work around so the order of the events should be LostFocus and then SelectionChanged. This way the correct item is updated when the text field loses the focus. The following code changes the order of the events, so the LostFocus event is fired first and after the SelectionChanged event is fired, if case.

Private Sub Tree1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    DoEvents
End Sub
The DoEvents method processes all windows messages currently in the message queue.
VB.10:
Drag-and-drop is one of the fundamental metaphors underlying the Microsoft� Windows� family of operating systems. Users understand that some items can be moved around by holding the mouse down on them, and that they'll get appropriate visual feedback when they're over a spot where the item can be dropped. They expect to be able to move data and images from one spot to another this way. There are two steps required in order to be able to drag and drop items/objects/bars from our /COM components as follows:

Beginning a Drag-and-Drop Operation

To begin a drag-and-drop operation, you have to set the control's OLEDropMode property on 1 and to handle the OLEStartDrag / OLEDragOver event. The AllowedEffects / Effect parameter must be set on 1 or 2, and you need to call the SetData method of the Data parameter so you specify the data to be dragged.

Accepting Data From a Drag Operation

The control fires the OLEDragDrop event, when the user drags data to the control. The OLEDragDrop event occurs when a drag-and-drop operation is in progress (that is, some control has initiated a drag and drop operation) and the cursor enters the control.

The following Access sample shows the requirements in red:

Private Sub Form_Load()
    Grid1.OLEDropMode = exOLEDropManual
End Sub

Private Sub Grid1_OLEDragDrop(ByVal Data As EXGRIDLibCtl.IExDataObject, Effect As Long, ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
    Dim hI As HITEM
    hI = CLng(Data.GetData(exCFText))
    If (hI <> 0) Then
        With Grid1
            Dim c As Long, hit As HitTestInfoEnum
            Dim h As Long
            h = .ItemFromPoint(-1, -1, c, hit)
            If (h <> 0) Then
                .BeginUpdate
                With .Items
                    .SetParent hI, h
                    .ExpandItem(h) = True
                    .SelectItem(hI) = True
                End With
                .EndUpdate
            End If
        End With
    End If
End Sub

Private Sub Grid1_OLEStartDrag(ByVal Data As EXGRIDLibCtl.IExDataObject, AllowedEffects As Long)
    With Grid1
        Dim c As Long, hit As HitTestInfoEnum
        Dim h As Long
        h = .ItemFromPoint(-1, -1, c, hit)
        If (h <> 0) Then
            AllowedEffects = 2
            Data.SetData h, exCFText
        End If
    End With
End Sub

The sample lets user moves an item from another by drag and drop using the SetParent method of Items object. The OLEStartDrag event initiates the OLE Drag-Drop event, by carrying the handle of the item being moved from a parent to the item where the cursor is released.

The following VB6 sample shows how you can list the files being dropped:

Private Sub Form_Load()
    With Grid1
        .OLEDropMode = exOLEDropManual
    End With
End Sub

Private Sub Grid1_OLEDragDrop(ByVal Data As EXGRIDLibCtl.IExDataObject, Effect As Long, ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
    With Data.Files
        If (.Count > 0) Then
            Dim s As String
            For i = 0 To .Count - 1
                s = s + .Item(i) + vbCrLf
            Next
            MsgBox s
        End If
    End With
End Sub

Private Sub Grid1_OLEDragOver(ByVal Data As EXGRIDLibCtl.IExDataObject, Effect As Long, ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single, ByVal State As Integer)
    If (Data.Files.Count > 0) Then
        Effect = 1
    End If
End Sub
Compile and build the EXE. Run the EXE, and drag and drop files from your Windows Explorer. As soon as you drop files from the Windows Explorer, the control displays the list of files you dragged.
VB.11:
An application is considered an isolated application if all of its components are side-by-side assemblies. A side-by-side assembly is a collection of resources?a group of DLLs, windows classes, COM servers, type libraries, or interfaces?available for an application to use at runtime. Typically, a side-by-side assembly is one to several DLLs. 

Isolated COM allows your application to use ActiveX components without having to register them. The original vision of this was to allow copy deployment of the application, but Isolated COM has many benefits. You can have a private copy of the DLL without worrying that another application will install an older or newer copy that breaks your application. Isolated COM also allows you to successfully install and run on non-Administrator accounts.

The solution is to include the control's manifest file to the application's resource under 24( Manifest Resource Type ) with the identifier 1.

  • Generating the RES file:
    • Open Visual Studio, select File\New\File and choose: Native Resource Template
    • Right-click the .rct iem and select the Add Resource ...
    • Add Resource dialog is opened, select Custom, and type 24, click OK
      • Open the eXHelper tool, and select the component whose manifest file you need to generate
      • Right-click the Middle/Template panel, and choose the Generate Assembly Manifest (exg2antt.manifest )
      • Copy the entire generated manifest ( CTRL + A, CTRL + C )
      • Go Back to Visual Studio's Resource Template
    • Paste the content you copied to the clipboard to the Custom resource you created (CTRL + V)
    • Click Properties, to you change the identifier of the resource item from IDR_RT_MANIFEST1 to 1
    • Save the template, by pressing the CTRL + S, so the Save File As dialog is opened
    • Select the 32-bit Resource File (*.res), from Save as type field
    • Change the name and location where the RES file to be saved ( for instance, exg2antt.res ), so it points to your VB6 project folder
    • Click the Save button
    • Finally, you have generated the RES file that includes the Resource Type 24/Manifest with the identifier 1, which contains the assembly manifest file for the component
  • Include the RES file as resource to your project
    • Open the VB6 project,
    • Go to Add-Ins\Add-In Manager...
    • Load the VB6 Resource Editor ( Load Behavior Check )
    • Go to Project \ Add New Resource File ...
    • The Open A Resource File dialog is opened
    • Select the RES file you previously generated ( exg2antt.res )
    • Click Open, and so the Project will show a new item exg2antt.res under the Related Documents
  • Generate the EXE file
    • Select the File \ Make Project1.exe ...
    • Change the name of the EXE to be generated and click OK
    •  Save all, and close the VB6 project
  • Copy the DLL to project folder
    • The "System Error &H8007007E (-2147024770). The specified module could not be found. " is generated if you run the EXE file, and this is happen because the EXE file can not locate the DLL you are using as Isolated COM
    • Copy the exg2antt.dll file ( or the DLL file you generated manifest from ) to the project folder ( same folder as EXE file )
    • Now, the EXE file can be run, and this way the EXE runs the component as Isolated COM

Now, copy the generated EXE and the exg2antt.dll to a client machine, and run the EXE. It will work, as in this case, the application uses the exg2antt as isolated, so it requires no registration ( regsvr32 ).

You can download it here the VB6 project. In the Release folder, you can find the sample.exe that uses the exg2antt.dll as isolated.

See also:

VB.12:
Select the (Custom) item in the properties browser. If the properties browser is not visible, please select the 'Properties Window' in the View menu.
VB.13:
Select the Dialog/Form in design mode, where you need to insert the control and do the following:
  • Click the ActiveX Controls from the Design panel
  • Select the control you need to insert from the "Insert ActiveX Control" list. For instance: "ExComboBox ActiveX Control"
  • Click OK, and the form will create and display your selected control 
VB.14:
First, you need to open the Visual Basic editor by doing any of the following:
  • Click the Database Tools \ Visual Basic or Press Alt + F11

Next, you need to insert the UserForm by doing the following:

  • Right Click the Visual Basic menu, and Select Customize
  • Go to Commands, and look for Insert
  • Click and Drag the UserForm to the Visual Basic menu
  • Close the Customize dialog, and press the newly "Insert UserForm" button

Next, you need to add the control to the newly insert user form:

  • Right Click the Visual Basic menu, and Select Customize
  • Go to Commands, and look for Tools
  • Click and Drag the Additional Controls... to the Visual Basic menu
  • Close the Customize dialog, and press the newly "Additional Controls" button
  • The "Additional Controls" is opened, and check/select the control you are about to add to the your user form. For instance: "ExComboBox ActiveX Control"
  • Click OK, and locate the ToolBox panel
  • The ToolBox panel, should include a new icon for the newly inserted control, click and drag this icon to the user form, and so you have inserted the control to the user-form
VB.15:
Once you generated the EXE to use the side-by-side components, it may happen to get the following error:
The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log or use the command-line sxstrace.exe tool for more detail.

if you run the EXEcutable. This may happen because when generated the EXE, an extra-zero may be added to the end of the manifest as explained bellow.

You can use the Windows' Events Viewer, to check for the error you got as you can see bellow:

So, you got an:

Activation context generation failed for "...". Error in manifest or policy file "..." on line xxx. Invalid Xml syntax.

We need to open the EXE using a resource editor ( using Visual Studio, or any other resource editor ), and to inspect the RT_MANIFEST\1 as seen next:

for some reason, there were added some unnecessary zeros. After we remove the zeros at the end of the RT_MANIFEST\1 resource we get:

Now, the EXE will run just fine, and will use the components as isolated ( no registration required ).