Search This Blog

Showing posts with label VB6. Show all posts
Showing posts with label VB6. Show all posts

Wednesday, January 18, 2012

Using ShowHelp instead of HtmlHelp

My ongoing project is rewriting a database application I first wrote in MS Access 95 to VB6 and now into C#.
97% of the conversion is finally done except for the help file.

I am using HelpNDoc to author a chm help file and I was using the following VB6 code to open topics by topicID


Option Explicit

Public Declare Function HtmlHelp Lib "hhctrl.ocx" Alias "HtmlHelpA" _
        (ByVal hwndCaller As Long, ByVal pszFile As String, _
        ByVal uCommand As Long, ByVal dwData As Long) As Long

Global Const HH_DISPLAY_TOPIC = &H0
Global Const HH_SET_WIN_TYPE = &H4
Global Const HH_GET_WIN_TYPE = &H5
Global Const HH_GET_WIN_HANDLE = &H6

' Display string resource ID or text in a popupwin.
Global Const HH_DISPLAY_TEXT_POPUP = &HE
' Display mapped numeric value in dwdata
Global Const HH_HELP_CONTEXT = &HF
' Text pop-up help, similar to WinHelp's HELP_CONTEXTMENU
Global Const HH_TP_HELP_CONTEXTMENU = &H10
' Text pop-up help, similar to WinHelp's HELP_WM_HELP
Global Const HH_TP_HELP_WM_HELP = &H11

Calling HtmlHelp:

Private Sub cmdHelp_Click()
    Call HtmlHelp(0&, gHelpFile, HH_HELP_CONTEXT, HELP_BookWindow)
End Sub

So to keep my helpfile structure I needed to be able to open help topics by topicID in C#
Here is how to call ShowHelp using topicIDs.
(Needed to invoke ToString() on the int variable to get the value to pass as an object).


public static void OpenHelp(Form currentForm, Int32 helpTopicID)
{
            try           
            {
                System.Windows.Forms.Help.ShowHelp(currentForm, _helpFile, HelpNavigator.TopicId, helpTopicID.ToString());
            }
            catch(Exception ex)
            {
                DRCCommon.Common.ErrorLog(ex);
            }
}

Tuesday, July 5, 2011

How can I lock an application after period of user inactivity?

My post on StackOverFlow.com

I have a fat Windows application written in VB6. User must log into the application to use it. I need to log the user out after a period of inactivity. There are over 100 separate forms with one Main form that is always open after the user logs in, so I am looking for an application solution not a form level solution.

Here is the solution I decided upon. I wanted to document it properly. As this is the approach I had envisioned, it is not my code. Someone smarter than I did awhile ago.
I simply implemented the solution into my application.

The app is an multiple-document interface app.

Private Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) 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
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal cb As Long)
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer

Private m_hDllKbdHook As Long

Private Type POINTAPI
x As Long
y As Long
End Type
Private Declare Function GetCursorPos Lib "user32.dll" (ByRef lpPoint As POINTAPI) As Long


Global variables to hold DateTime last user activity and if mouse and keyboard activity has occurred

Public KeysHaveBeenPressed As Boolean
Public HasMouseMoved As Boolean
Public gLastUserActivity As Date

Code to detect keyboard activity

Public Function HookKeyboard() As Long
On Error GoTo ErrorHookKeyboard
m_hDllKbdHook = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf LowLevelKeyboardProc, App.hInstance, 0&)
HookKeyboard = m_hDllKbdHook
Exit Function
ErrorHookKeyboard:
MsgBox Err & ":Error in call to HookKeyboard()." _
& vbCrLf & vbCrLf & "Error Description: " & Err.Description, vbCritical, "Warning"
Exit Function
End Function
Public Sub UnHookKeyboard()
On Error GoTo ErrorUnHookKeyboard
UnhookWindowsHookEx (m_hDllKbdHook)
Exit Sub
ErrorUnHookKeyboard:
MsgBox Err & ":Error in call to UnHookKeyboard()." _
& vbCrLf & vbCrLf & "Error Description: " & Err.Description, vbCritical, "Warning"
Exit Sub
End Sub
Public Function LowLevelKeyboardProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Static kbdllhs As KBDLLHOOKSTRUCT
If nCode = HC_ACTION Then
'keys have been pressed
KeysHaveBeenPressed = True
End If
LowLevelKeyboardProc = CallNextHookEx(m_hDllKbdHook, nCode, wParam, lParam)
End Function

Code to detect mouse movement:

Public Sub CheckMouse()
On Error GoTo ErrCheckMouse
Dim p As POINTAPI
GetCursorPos p
If p.x <> LastMouse.x Or p.y <> LastMouse.y Then
HasMouseMoved = True
LastMouse.x = p.x
LastMouse.y = p.y
End If
Exit Sub
ErrCheckMouse:
MsgBox Err.Number & ": Error in CheckMouse(). Error Description: " & Err.Description, vbCritical, "Error"
Exit Sub
End Sub



On the Main parent Form:
Added a timer:

Private Sub muTimer_Timer()
CheckMouse
'Debug.Print "MU Timer Fire"
'Debug.Print "Keyboard:" & KeysHaveBeenPressed & " - " & "Mouse:" & HasMouseMoved
If HasMouseMoved = False And KeysHaveBeenPressed = False Then
If DateDiff("m", gLastUserActivity, Now) > gnMUTimeOut Then
muTimer.Interval = 0

Else
'Debug.Print " dT "; DateDiff("s", gLastUserActivity, Now)
End If
Else
HasMouseMoved = False
KeysHaveBeenPressed = False
gLastUserActivity = Now
End If
'Debug.Print " dT "; DateDiff("s", gLastUserActivity, Now)
End Sub

Also on the MainForm load event:

Private Sub MDIForm_Load()
HookKeyboard
end sub

Private Sub MDIForm_QueryUnload(Cancel As Integer, UnloadMode As Integer)
UnHookKeyboard
end sub

Monday, May 16, 2011

How to Implement a wait() in VB6

Here is how I created a Wait() for VB6.
Needed for a project to allow form code to pause while still allowing a 3rd party control to upload a file to a server.

Public Sub Wait(milliseconds As Integer)
Dim dTimer As Double
dTimer = Timer
Do While Timer dTimer + CDbl(milliseconds / 1000)
DoEvents
Loop
End Sub

Monday, April 11, 2011

How To Use the SHGetKnownFolderPath Function from Vb6

I found the answer and posted in StackOverflow.com
How To Use the SHGetKnownFolderPath Function from Vb6

My answer was derived from http://en.kioskea.net/faq/951-vba-vb6-my-documents-environment-variables.

Determine Windows version via WIN32 API in VB6

'Get Windows Version
Public Declare Function GetVersionExA Lib "kernel32" _
(lpVersionInformation As OSVERSIONINFO) As Integer

Public Type OSVERSIONINFO
dwOSVersionInfoSize As Long
dwMajorVersion As Long
dwMinorVersion As Long
dwBuildNumber As Long
dwPlatformId As Long
szCSDVersion As String * 128
End Type

Public Function IsVistaOrHigher() As Boolean
Dim osinfo As OSVERSIONINFO
Dim retvalue As Integer
Dim bVista As Boolean

bVista = False

osinfo.dwOSVersionInfoSize = 148
osinfo.szCSDVersion = Space$(128)
retvalue = GetVersionExA(osinfo)

If osinfo.dwPlatformId = 2 Then
If osinfo.dwMajorVersion = 6 Then
bVista = True
End If
End If
IsVistaOrHigher = bVista
End Function

Thursday, January 27, 2011

My quest for Windows 7 manifest file for a VB6 exe

So, I am currently running a common problem trying to deploy a Vb6 application written and developed for Windows 2000/XP into the Vista/Win2008/Windows 7 world.

Problems
#1 - User Access Control - I get prompted by Windows about I do not know anything about this program are you sure you want to allow this.

#2 - Run as Administrator requirement:  Under compatibility for the Application short-cut, enabling run as Admin.  Turns out my project is creating and storing session and user in Program Files\App Folder\ in sub folders.  Which is being discouraged by Microsoft in Windows 7.

Possible Solutions:
#1 - On workstation after deployment, Run application with Run as Adminstrator turned on.
        This is an okay workaround but requires every workstation to be touched.
#2 - Use on a manifest file to set the execution permission to Administrator
        Alots and alots of discussions on the web and StackoverFlow.
        Found this gem Manifest creation tool - http://www.vbforums.com/showthread.php?t=606736
      
        Downloaded it.  I was able to create an apparently correct manifest file but  it did not work.
        Still trying to find out why.  
        Related post - http://stackoverflow.com/questions/4489165/vb6-manifest-not-working-on-windows-7/4496389#4496389

 My question if any wants to answer it and earn big big karma points....
What is the best solution to force a VB6 application to run as Administrator premissions
    - Is it really possible for VB6 and Windows 7?
    - Is the problem with session data in ..\Program Files\My App\  really the whole issue?
  
Any advice will be welcomed.  Email: gary.Kindel@gmail.com

Thanks

Monday, November 22, 2010

Read Windows Registry settings for VB and VBA Projects

My continuing exercise in comparative languages VB6 vs C#

Read Windows Registry settings for VB and VBA Projects

VB6
GetSetting("DRC", "Config", "DRC_CD")

C#

using Microsoft.Win32;

RegistryKey DRCKEY = Registry.CurrentUser.OpenSubKey("Software\\VB and VBA Program Settings\\DRC\\Config");
object CDPath = DRCKEY.GetValue("DRC_CD");

Friday, March 12, 2010

VB6 Form_queryUnload Event unloadmode argument

From Microsoft

Visual Basic Reference

QueryUnload Event

See Also    Example    Applies To
Occurs before a form or application closes. When an MDIForm object closes, the QueryUnload event occurs first for the MDI form and then in all MDI child forms. If no form cancels the QueryUnload event, the Unload event occurs first in all other forms and then in an MDI form. When a child form or a Form object closes, the QueryUnload event in that form occurs before the form's Unload event.
Syntax
Private Sub Form_QueryUnload(cancel As Integer, unloadmode As Integer)
Private Sub MDIForm_QueryUnload(cancel As Integer, unloadmode As Integer)
The QueryUnload event syntax has these parts:
Part Description
cancel An integer. Setting this argument to any value other than 0 stops the QueryUnload event in all loaded forms and stops the form and application from closing.
unloadmode A value or constant indicating the cause of the QueryUnload event, as described in Return Values.

Return Values
The unloadmode argument returns the following values:
Constant Value Description
vbFormControlMenu 0 The user chose the Close command from the Control menu on the form.
vbFormCode 1 The Unload statement is invoked from code.
vbAppWindows 2 The current Microsoft Windows operating environment session is ending.
vbAppTaskManager 3 The Microsoft Windows Task Manager is closing the application.
vbFormMDIForm 4 An MDI child form is closing because the MDI form is closing.
vbFormOwner 5 A form is closing because its owner is closing.

Tuesday, October 6, 2009

Deserialization Problem solved!

I have been working on COM interop project to allow access to Google Blogger and Picasa API from a Visual Basic 6.0 App. I know I know, why let go of the past and write a modern app perhaps in WPF..., I have my reasons.

Blogger Solution
COM Interop Project
Google API Project
Test UI Project

VB6 Project
Referencing COM Interop type Library

My solution is written in C#, 2.0 Framework (trying to still support Windows 2000, again I know I know let go of the past...) Added a Test windform project to the solution so I can run UI tests without COM. Worked great!

Created my COM interop project, something I had not done before. It seemed to work fine.

Then I decided I needed to serialize objects to cache downloaded album and images to spend up locating an online image. Worked well in C# except, when deserializing objects kept throwing errors when run from COM from my VB6 project. Two errors, Unable to find file (Assembly DLL) and unable to load type. Puzzled, I googled for a solution, WOW what a fine mess binary deserialization can be!

After several failed attempts, I found the solution:
1) Could not run my VB6 project and get the deserialization to work. Compiled the exe and...
2) Created an App.Config and renamed after my VB6 project exe: MyProject.exe.Config
3) In the App.Config, In the runtime section, I added for each of assemblies.

Now I can run my COM Interop project from VB6 and deserialization does not throw errors. At some point I am going to try to actually understand why this works but right now my goal is simply a working pattern that I can successfully produce.

Key my solution came when I enabled Fusion logging so I could see why was happening as my code loaded or tried to load assemblies needed for deserialization.

Friday, July 17, 2009

VB6 App with C# COM object

VB6 project using C# COM Assembly
-2147024896 (80070002) Automation error The System cannot find file specified

I get this very thing...

from MSDN forum

Question:
i'm having problems running the vb6 app, and the error occurs when i make the form show. i've followed the instructions from the help file and still the problem arises everytime.

here's another weird thing, when i added the interop form to an existing vb6 application, it was ok when run inside the ide. when i compiled this to an .exe file, the error happened. i also tried creating fresh vb6 app and it worked, so any ideas on this issue? thanks.


Answer:
1) Strongly name Assemblies.
   sn -k TestKeyPair.snk
2) Add TestKeyPair.snk to project(s).
3) Edit AssemblyInfo.cs
A) Make assembly is versioned
[assembly: AssemblyVersion("1.0.0.0")]
b) Add [assembly: AssemblyKeyFile("TestKeyPair.snk")]
4) Add Assembly to GAC
gacutil /i MyInterop.dll
- Make sure all Assembly DLLS has been
5) Regasm Assembly and expose COM use /codebase
REGASM MyInterop.dll /tlb:com.MyInterop.tlb /codebase
Better Answer:
1) Strongly name Assemblies.
   sn -k TestKeyPair.snk
2) Add TestKeyPair.snk to project(s).
3) Edit AssemblyInfo.cs
A) Make assembly is versioned
[assembly: AssemblyVersion("1.0.0.0")]
b) Add [assembly: AssemblyKeyFile("TestKeyPair.snk")]
4) Copied all related assemblies local to folder containing VB6 project
5) Run from this folder: tlbexp sampleDLL.dll /out:sampleDLL.tlb
6) run from this folder: regasm sampleDLL.dll /tlb:sampleDLL.tlb
7) Open VB6 project and reference to sampleDLL.tlb is local to project.

Friday, July 10, 2009

Create COM object using C# for VB6 Application

Here is a C# example: Create a COM object with events using C# and implement in a VB6 Application.

Step #1: Define COM interfaces
Need to create two interfaces - 1 class interface and a events interface

Public Class Interface: IMyClass
using System;
using System.Drawing;
using PicasaBloggerUserControls;
using System.Runtime.InteropServices;

namespace MyApp
{
[Guid("xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface
IMyClass
{
void GetDRCUpdate(string url, int majorVer, int minorVer, int RevisionVer, bool GetLatest);
}
}


Public Interface defining the events: IMyClassEvents

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace MyApp
{
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb")]
public interface
IMyClassEvents
{
void DownloadComplete();
void DownloadCancel(bool downloadfailed);
}
}
Step #2
Define Public class using class interface: winformUI

using System.Drawing;
using System.Windows.Forms;
using PicasaBloggerUserControls;
using System.Runtime.InteropServices;

namespace PicasaBloggerUI
{
[Guid(
"xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb")]
[
ComSourceInterfaces("MyApp.PicasaBloggerEventsInterface"),
ClassInterface(ClassInterfaceType.None)
]
[ProgId("winformUI")]


public class winformUI :
IMyClass
{
public event FileDownloadDelegate DRCDownloadComplete;
public delegate void FileDownloadDelegate();

public event FileDownloadCancelDelegate DRCDownloadCancel;
public delegate void FileDownloadCancelDelegate(bool downloadfailed);

private frmDRCUpdate _DRCUpdater;

//ctor
public winformUI()
{
_DRCUpdater = new frmDRCUpdate();
_DRCUpdater.OnFileDownload += new frmDRCUpdate.FileDownloadDelegate(_DRCUpdater_OnFileDownload);
_DRCUpdater.OnFileDownloadCancel += new frmDRCUpdate.FileDownloadCancelDelegate(_DRCUpdater_OnFileDownloadCancel);
}

void _DRCUpdater_OnFileDownloadCancel()
{
if (null != DRCDownloadCancel)
{
DRCDownloadCancel(!_DRCUpdater.Successful);
}
}

void _DRCUpdater_OnFileDownload()
{
try
{
//MessageBox.Show("Download Successful");
DRCDownloadComplete();
}
catch
{
}

}

void IPicasaBlogger.GetDRCUpdate(string url, int majorVer, int minorVer, int RevisionVer, bool GetLatest)
{
_DRCUpdater = new frmDRCUpdate();
_DRCUpdater.OnFileDownload += new frmDRCUpdate.FileDownloadDelegate(_DRCUpdater_OnFileDownload);
_DRCUpdater.OnFileDownloadCancel += new frmDRCUpdate.FileDownloadCancelDelegate(_DRCUpdater_OnFileDownloadCancel);
_DRCUpdater.Show();
_DRCUpdater.GetDRCUpdate(url, majorVer, minorVer, RevisionVer, GetLatest);
}

}
}

Step #3 Compile C# code and register the assembly:
REGASM MyApp.dll /tlb:com.MyApp.tlb
Now you are ready to add the COM object as a reference to a VB project.
1) Open VB6 project, select references and browse to locate of MyApp.tlb and add as a reference.

In a VB6 form Module
Private WithEvents oMyAppUI As MyApp.winformUI

Private Sub Form_Load()
Set oMyAppUI = New MyApp.winformUI
End Sub

Private Sub oMyAppUI_DownloadCancel(ByVal downloadfailed As Boolean)
If downloadfailed Then
MsgBox "Update Failed, please try again. If the failure persists, contact me at Gary.Kindel@gmail.com for assistance.", vbOKOnly
Else
MsgBox "You have the current version, no updates are available.", vbExclamation
End If
End Sub
Private Sub oMyAppUI_DownloadComplete()
If MsgBox("Do you want to exit and install the update?", vbYesNoCancel, "Exit DRC and run Update file") = vbYes Then
mApplyUpdate = True
Unload Me
End If
End Sub





Monday, March 9, 2009

Open folder to location of a file

I cannot seem to remember this even though I wrote code at least twice to do it!

In dlgUpgrade From in DRC

'WIN32 API
Private Declare Function SetActiveWindow Lib "user32.dll" (ByVal hwnd As Long) As Long
Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)


'Add functions
Public Function StartDoc(ByVal fileName As String, Optional CommandLine As String = "") As Long
StartDoc = ShellExecute(0&, "Open", fileName, CommandLine, vbNullString, 1)
End Function
Private Function GetParentDir(ByVal myFile As String, Optional RemoveSeparator As Boolean = True, Optional IgnoreDirValidation As Boolean = False) As String
On Error Resume Next
Dim i As Long, j As Integer
If RemoveSeparator = True Then
j = 1
Else
j = 0
End If
If IgnoreDirValidation = True Then
i = InStr(1, StrReverse(myFile), "\")
If i = 0 Then
GetParentDir = ""
Else
GetParentDir = StrReverse(Mid$(StrReverse(myFile), i + j))
End If
Else
If Dir$(myFile) = "" Then
GetParentDir = ""
Else
i = InStr(1, StrReverse(myFile), "\")
If i = 0 Then
GetParentDir = ""
Else
GetParentDir = StrReverse(Mid$(StrReverse(myFile), i + j))
End If
End If
End If
End Function

Private Function getFileName(filePath As String, Optional separator As String = "\", Optional removeExtension As Boolean = False) As String
Dim tmp As String
tmp = Mid$(filePath, InStrRev(filePath, separator) + 1)
If tmp = filePath Then
getFileName = ""
Else
If removeExtension Then
If InStrRev(tmp, ".") > 0 Then
getFileName = Left$(tmp, InStrRev(tmp, ".") - 1)
Else
getFileName = tmp
End If
Else
getFileName = tmp
End If
End If
End Function


'Call to OpenFileFolder
Private Sub OpenFileFolder(filePath As String)
Dim i As Long, j As Long, fileName As String
If isFileExist(filePath) Then
i = StartDoc("explorer.exe ", GetParentDir(filePath))
j = SetActiveWindow(i)
Sleep 1000 '<< 1 second
DoEvents

fileName = getFileName(filePath)
For i = 1 To Len(fileName)
SendKeys Mid$(fileName, i, 1)
DoEvents
Next i
End If
End Sub

Tuesday, February 24, 2009

Cloning objects in VB6

Add method to object:

Public Function Clone() As Home
Dim m As New MemoryStream()
Dim b As New BinaryFormatter()
b.Serialize(m, Me)
m.Position = 0
Return b.Deserialize(m)
End Function

Showing code
Dim obj As New MyObject()
Dim objNew As MyObject= obj .Clone





Tuesday, December 30, 2008

Tuesday, November 11, 2008

How to Debug Hydrid VB6 and C# project

Use System.Diagnostics.Debugger.Break();
in C# code in class with COM interop that is used in VB6 project.
Allow code to debug using .net 2005/2008 but not .net 2003

Used this approach to debug call center scheduler.

Tuesday, October 21, 2008

VB6 IDE Make/Build menu disabled

Problem:

VB6 IDE can get into a state when the Make XXXX menu option under the File Menu is disabled.

Solution:

Right click on your toolbar, goto Customize and reset toolbars.