Get selected value from Script Combo box of Common Dialog control in VB6 - vb6

I am using Common Dialog Control of VB6 to select Font by calling ShowFont method. Here I can select desired font, font size, bold, italic, strike thru etc. I also select Arabic from script combo box. The problem is not able to get the value which I selected from the Script combo box. Any one please help.
Code:
With CommonDialog1.ShowFont
FontObject.Name = .FontName
FontObject.Bold = .FontBold
FontObject.Italic = .FontItalic
FontObject.Size = .FontSize
FontObject.Strikethrough = .FontStrikethru
FontObject.Underline = .FontUnderline
End With

You have two options:
Subclass the Common Dialog Window -
Here is an example from VBForum
Use the Windows API to call the ChooseFont Common Dialog by your self
Here is a snippet using the second approach:
Option Explicit
Private FontObject As New StdFont
Const FW_REGULAR As Integer = 400
Const FW_BOLD As Integer = 700
Const CF_BOTH = &H3
Const CF_EFFECTS = &H100
Const CF_INITTOLOGFONTSTRUCT = &H40
Const LF_FACESIZE = 32
Const LOGPIXELSY As Long = 90
Private Type LOGFONT
lfHeight As Long
lfWidth As Long
lfEscapement As Long
lfOrientation As Long
lfWeight As Long
lfItalic As Byte
lfUnderline As Byte
lfStrikeOut As Byte
lfCharSet As Byte
lfOutPrecision As Byte
lfClipPrecision As Byte
lfQuality As Byte
lfPitchAndFamily As Byte
lfFaceName(LF_FACESIZE) As Byte
End Type
Private Type CHOOSEFONT
lStructSize As Long
hwndOwner As Long
hDC As Long
lpLogFont As Long
iPointSize As Long
flags As Long
rgbColors As Long
lCustData As Long
lpfnHook As Long
lpTemplateName As String
hInstance As Long
lpszStyle As String
nFontType As Integer
MISSING_ALIGNMENT As Integer
nSizeMin As Long
nSizeMax As Long
End Type
Private Declare Function GetDesktopWindow Lib "USER32" () As Long
Private Declare Function GetDC Lib "USER32" (ByVal hWnd As Long) As Long
Private Declare Function ReleaseDC Lib "USER32" (ByVal hWnd As Long, ByVal hDC As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hDC As Long, ByVal nIndex As Long) As Long
Private Declare Function ChooseFontA Lib "comdlg32.dll" (pChoosefont As CHOOSEFONT) As Long
Private Sub String2ByteArr(ByVal str As String, ByRef arr)
Dim b() As Byte, i As Long, l As Long
b = StrConv(str & Chr(0), vbFromUnicode)
l = UBound(b)
For i = 0 To l
arr(i) = b(i)
Next
End Sub
Private Function ByteArr2String(ByRef arr) As String
Dim b() As Byte
b = StrConv(arr, vbUnicode)
bytearray2string = Left$(b, InStr(b, Chr$(0)) - 1)
End Function
Private Sub FontDialog()
Dim cf As CHOOSEFONT, lf As LOGFONT, hWnd As Long, hDC As Long, ppi As Long
hWnd = GetDesktopWindow
hDC = GetDC(hWnd)
ppi = GetDeviceCaps(hDC, LOGPIXELSY)
With lf
String2ByteArr FontObject.Name, lf.lfFaceName
.lfHeight = -(FontObject.Size * ppi) / 72
.lfWeight = IIf(FontObject.Bold, FW_BOLD, FW_REGULAR)
.lfItalic = FontObject.Italic
.lfUnderline = FontObject.Underline
.lfStrikeOut = FontObject.Strikethrough
.lfCharSet = FontObject.Charset
End With
With cf
.lStructSize = Len(cf)
.hDC = hDC
.flags = CF_BOTH Or CF_EFFECTS Or CF_INITTOLOGFONTSTRUCT
.hwndOwner = Me.hWnd
.lpLogFont = VarPtr(lf)
.lpTemplateName = vbNullString
End With
If ChooseFontA(cf) Then
With FontObject
.Name = ByteArr2String(lf.lfFaceName)
.Size = (-72 * lf.lfHeight) / ppi
.Bold = lf.lfWeight >= FW_BOLD
.Italic = lf.lfItalic
.Underline = lf.lfUnderline
.Strikethrough = lf.lfStrikeOut
.Charset = lf.lfCharSet
End With
' If you choose Arabic charset, this will print 178
Debug.Print "CharSet:", FontObject.Charset
End If
Call ReleaseDC(hWnd, hDC)
End Sub
Please note: as this topic is quite old, you will find many other examples by googling on the net (ChooseFont: Using the ChooseFont Common Dialog API, Karl E. Peterson and so on).

Related

VB.net SendMessage with CB_ADDSTRING '0' not valid for 'index'

I'm trying to fasten up the loading of one form, which populates several combobox with a big amount of data. I did my best stopping the UI while loading ecc shredding the loading time from 20s to 13s, but still the only bottleneck remaining is loading data into ComboBox which takes about 3-4s each. I did research on the internet and found that using Windows API you can fasten it up a lot more. So I did the code:
Private Const CB_ERR As Integer = -1
Private Const CB_ADDSTRING As Integer = &H143
Private Const CB_SETITEMDATA As Integer = &H151
<DllImport("user32.dll", CharSet:=CharSet.Auto)>
Private Function SendMessage(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, lParam As String) As Integer
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)>
Private Function SendMessage(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Object) As Integer
End Function
Public Sub AddItem(cmb As ComboBox, Item As Object)
Dim l As Integer
l = SendMessage(cmb.Handle, CB_ADDSTRING, 0&, Item.ToString())
SendMessage(cmb.Handle, CB_SETITEMDATA, l, Item)
End Sub
But on CB_ADDSTRING the program crashes with this error (that can be catched):
InvalidArgument=Value of '0' not valid for 'index'. Parameter Name: index
Since I'm using Krypton Controls, I had to shuffle trough Parents until I found KryptonComboBox, and then pass it's Handle value.

How to get the Browser UserAgent String in Visual Basic 6?

I am trying to get the UserAgent of the default browser using the ObtainUserAgentString API in Visual Basic 6. I found the documentation on the MSDN and tried to convert it to Visual Basic 6 but it did not work.
C++ (MSDN)
HRESULT ObtainUserAgentString(
_In_ DWORD dwOption = 0,
_Out_ LPCSTR *pcszUAOut,
_Out_ DWORD *cbSize
);
Visual Basic 6 API
Private Declare Function ObtainUserAgentString Lib "Urlmon.dll" (ByVal dwOption As Long, ByRef pcszUAOut As String, ByRef cbSize As Long) As Long
Private Function BrowserUserAgent() As String
Dim httpUseragent As String
Dim szhttpUserAgent As Long
httpUseragent = Space(512)
szhttpUserAgent = Len(httpUseragent)
Call ObtainUserAgentString(0, httpUseragent, szhttpUserAgent)
BrowserUserAgent = httpUseragent
End Function
Private Sub Command1_Click()
MsgBox BrowserUserAgent
End Sub
Aside from the fact this is a cruddy old ANSI entrypoint, everything you need appears to be documented.
Option Explicit
Private Const NOERROR As Long = 0
Private Const E_OUTOFMEMORY As Long = &H8007000E
Private Enum UAS_OPTIONSENUM
[_UAS_EXACTLEGACY] = &H1000&
UAS_DEFAULT = 0
UAS_7 = 7 'Compatible mode.
UAS_7_LEGACY = 7 Or [_UAS_EXACTLEGACY]
UAS_8 = 8
UAS_9 = 9
UAS_10 = 10
UAS_11 = 11
End Enum
Private Declare Function ObtainUserAgentString Lib "urlmon" ( _
ByVal dwOption As Long, _
ByVal pcszUAOut As Long, _
ByRef cbSize As Long) As Long
Private Function BrowserUserAgent( _
Optional ByVal Options As UAS_OPTIONSENUM = UAS_DEFAULT) As String
Const MAX_BUFFER As Long = 2048
Dim Size As Long
Dim Buffer() As Byte
Dim HRESULT As Long
Do
Size = Size + 128
ReDim Buffer(Size - 1)
HRESULT = ObtainUserAgentString(Options, VarPtr(Buffer(0)), Size)
Loop While HRESULT = E_OUTOFMEMORY And Size < MAX_BUFFER
If HRESULT = NOERROR Then
BrowserUserAgent = StrConv(LeftB$(Buffer, Size - 1), vbUnicode)
Else
Err.Raise &H8004D000, _
, _
"ObtainUserAgentString error &H" & Hex$(HRESULT)
End If
End Function
Private Sub Form_Load()
AutoRedraw = True
Print BrowserUserAgent()
Print BrowserUserAgent(UAS_7)
Print BrowserUserAgent(UAS_7_LEGACY)
Print BrowserUserAgent(UAS_8)
Print BrowserUserAgent(UAS_11)
End Sub
HRESULT ObtainUserAgentString(
_In_ DWORD dwOption = 0,
_Out_ LPCSTR *pcszUAOut,
_Out_ DWORD *cbSize
);
Param 2 is LongPointerCString. You always pass C strings ByVal which in reality passes the C string part of the B String ByRef. If it was a IN param you would have to end the string with a Chr(0) which is what real C strings have.
String arguments are a special case. Passing a string by value means you are passing the address of the first data byte in the string; passing a string by reference means you are passing the memory address where another address is stored; the second address actually refers to the first data byte of the string. How you determine which approach to use is explained in the topic "Passing Strings to a DLL Procedure" later in this chapter.
From Visual Basic Concepts in Help.

MS PowerPoint: how to convert a shape's position and size into screen coordinates?

I wrote me a little VBA Macro for PowerPoint (2010) that opens a popup with explanations when hovering over some Shape. This works fine. Alas, there is no event that is triggered when leaving the area again and so I now want to extend the code such that it monitors the area of the popup and when the pointer leaves that area it removes the popup again.
But now I ran into some stupid problem: the coordinates of the Shape (.Left, .Top, .Width, and .Height) are given in some "document units" (don't know exactly what unit this is in). The pointer coordinates, however, are obviously in screen pixels. To be able to reasonably compare the two to calculate whether the pointer is inside or outside I need to first convert the Shape's dimensions into screen pixels.
I googled around a lot, but while I found several at first promising code snippets, none of these worked (as most were for Excel and PowerPoint obviously has a different document model).
Could some kind soul give me a hint or some reference how to convert a Shape's dimension into screen pixels (i.e. taking scaling, window position, zoom-factor etc. into account).
M.
In case anyone's interested - here is my solution after LOTS of further googling:
Type POINTAPI
x As Long
y As Long
End Type
Type Rectangle
topLeft As POINTAPI
bottomRight As POINTAPI
End Type
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hDC As Long, ByVal nIndex As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hDC As Long) As Long
Private Declare Function ClientToScreen Lib "user32" (ByVal hwnd As Long, lpPoint As POINTAPI) As Long
Private Function TransformShape(osh As Shape) As Rectangle
Dim zoomFactor As Double
zoomFactor = ActivePresentation.SlideShowWindow.View.zoom / 100
Dim hndDC&
hndDC = GetDC(0)
Dim deviceCapsX As Double
deviceCapsX = GetDeviceCaps(hndDC, 88) / 72 ' pixels per pt horizontal (1 pt = 1/72')
Dim deviceCapsY As Double
deviceCapsY = GetDeviceCaps(hndDC, 90) / 72 ' pixels per pt vertical (1 pt = 1/72')
With TransformShape
' calculate:
.topLeft.x = osh.Left * deviceCapsX * zoomFactor
.topLeft.y = osh.Top * deviceCapsY * zoomFactor
.bottomRight.x = (osh.Left + osh.width) * deviceCapsX * zoomFactor
.bottomRight.y = (osh.Top + osh.height) * deviceCapsY * zoomFactor
' translate:
Dim lngStatus As Long
lngStatus = ClientToScreen(hndDC, .topLeft)
lngStatus = ClientToScreen(hndDC, .bottomRight)
End With
ReleaseDC 0, hndDC
End Function
...
Dim shapeAsRect As Rectangle
shapeAsRect = TransformShape(someSape)
Dim pointerPos As POINTAPI
Dim lngStatus As Long
lngStatus = GetCursorPos(pointerPos)
If ((pointerPos.x <= shapeAsRect.topLeft.x) Or (pointerPos.x >= shapeAsRect.bottomRight.x) Or _
(pointerPos.y <= shapeAsRect.topLeft.y) Or (pointerPos.y >= shapeAsRect.bottomRight.y)) Then
' outside:
...
Else ' inside
...
End If
...
the coordinates of the Shape (.Left, .Top, .Width, and .Height) are given in some "document units" (don't know exactly what unit this is in).
Points. 72 points to the inch.
Sub TryThis()
Dim osh As Shape
Set osh = ActiveWindow.Selection.ShapeRange(1)
With ActiveWindow
Debug.Print .PointsToScreenPixelsX(.Left)
Debug.Print .PointsToScreenPixelsY(.Top)
End With
End Sub

SetBkColor and SetTextColor Don't set the background and text color for DrawText

Using the following code to write a string to the DesktopWindow's device context works, but the background color and text color remain the same (white on blue):
Private Sub writeToScreen(txt As String)
Declare Function GetDesktopWindow Lib "user32" () As Integer
Declare Function DrawTextW Lib "user32" (hdc As Integer, lpStr As WString, nCount As Integer, _
ByRef lpRect As RECT, wFormat As Integer) As Integer
Declare Function CreateDCA Lib "gdi32" (lpDriverName As CString, lpDeviceName As Integer, _
lpOutput As Integer, lpInitData As Integer) As Integer
Declare Function DeleteDC Lib "gdi32" (hdc As Integer) As Integer
Declare Function GetTextColor Lib "gdi32" (hdc As Integer) As Color
Declare Function SetTextColor Lib "gdi32" (hdc As Integer, crColor As Color) As Color
Declare Function GetBkColor Lib "gdi32" (hdc As Integer) As Color
Declare Function SetBkColor Lib "gdi32" (hdc As Integer, crColor As Color) As Color
Const DT_MULTILINE = &H00000001
Const DT_NOCLIP = &H100
Const INVALID_COLOR = &hFFFFFFFF
Dim tFormat As Integer = DT_MULTILINE Or DT_NOCLIP
Dim hdc As Integer = CreateDCA("DISPLAY", 0, 0, 0)
Dim tRect As RECT //The RECT structure is defined elsewhere
Dim textCol, backColor As Color
tR.Left = 200
tR.Top = 250
tR.Right = 600
tR.Bottom = 350
textCol = SetTextColor(hdc, &cFF8040)
backColor = SetBkColor(hdc, &c000000)
If DrawTextW(hdc, txt, Len(txt), tR, tFormat) = 0 Then
System.DebugLog("Text Draw Error")
End If
Call SetTextColor(hdc, textCol)
Call SetBkColor(hdc, backColor)
Call DeleteDC(hdc)
End Sub
What am I doing wrong? The text gets written just fine, but the colors are ugly.
Use SetBkMode() (http://msdn.microsoft.com/en-us/library/dd162965%28v=vs.85%29.aspx) first to set the DC to not draw a background.
SetTextColor() is only used for TextOut(), not DrawText(), IIRC - MSDN is ambiguous on it. Try seleting a different HBRUSH into the DC, that may do what you want.

CheckTokenMembership in VB6

I'm having a hard time converting this C++ code to VB6 code. I've search the net and haven't found anything. PInvoke.net only has reference to VB.NET code. Here's the code from MSDN:
BOOL IsUserAdmin(VOID)
/*++
Routine Description: This routine returns TRUE if the caller's
process is a member of the Administrators local group. Caller is NOT
expected to be impersonating anyone and is expected to be able to
open its own process and process token.
Arguments: None.
Return Value:
TRUE - Caller has Administrators local group.
FALSE - Caller does not have Administrators local group. --
*/
{
BOOL b;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
PSID AdministratorsGroup;
b = AllocateAndInitializeSid(
&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&AdministratorsGroup);
if(b)
{
if (!CheckTokenMembership( NULL, AdministratorsGroup, &b))
{
b = FALSE;
}
FreeSid(AdministratorsGroup);
}
return(b);
}
It would be great if somebody can help out in converting this to VB6 code.
Thanks!
EDIT:
I was originally going to use that function but MSDN says:
This function is a wrapper for CheckTokenMembership. It is recommended to call that function directly to determine Administrator group status rather than calling IsUserAnAdmin.
Try this
Option Explicit
Private Const SECURITY_BUILTIN_DOMAIN_RID As Long = &H20
Private Const DOMAIN_ALIAS_RID_ADMINS As Long = &H220
Private Declare Function AllocateAndInitializeSid Lib "advapi32.dll" (pIdentifierAuthority As Any, ByVal nSubAuthorityCount As Byte, ByVal nSubAuthority0 As Long, ByVal nSubAuthority1 As Long, ByVal nSubAuthority2 As Long, ByVal nSubAuthority3 As Long, ByVal nSubAuthority4 As Long, ByVal nSubAuthority5 As Long, ByVal nSubAuthority6 As Long, ByVal nSubAuthority7 As Long, lpPSid As Long) As Long
Private Declare Sub FreeSid Lib "advapi32.dll" (ByVal pSid As Long)
Private Declare Function CheckTokenMembership Lib "advapi32.dll" (ByVal hToken As Long, ByVal pSidToCheck As Long, pbIsMember As Long) As Long
Private Type SID_IDENTIFIER_AUTHORITY
Value(0 To 5) As Byte
End Type
Private Function pvIsAdmin() As Boolean
Dim uAuthNt As SID_IDENTIFIER_AUTHORITY
Dim pSidAdmins As Long
Dim lResult As Long
uAuthNt.Value(5) = 5
If AllocateAndInitializeSid(uAuthNt, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, pSidAdmins) <> 0 Then
If CheckTokenMembership(0, pSidAdmins, lResult) <> 0 Then
pvIsAdmin = (lResult <> 0)
End If
Call FreeSid(pSidAdmins)
End If
End Function
You've posted the MSDN sample code for CheckTokenMembership - it uses CheckTokenMembership to determine whether the user is an administrator.
In VB6 it's easier to use IsUserAnAdmin, which is a wrapper for CheckTokenMembership. The MSDN docs do say IsUserAnAdmin is deprecated, but it's so much easier to call than CheckTokenMembership.
Private Declare Function IsUserAnAdmin Lib "Shell32" Alias "#680" () As Integer
If IsUserAnAdmin() = 0 Then
MsgBox "Not admin"
Else
MsgBox "Admin"
End If
Unless there is a reason to convert the code, use the API
Private Declare Function IsUserAdmin Lib "Shell32" Alias "#680" () As Boolean
Private Sub Form_Load()
If IsUserAdmin Then MsgBox "User is Admin"
End Sub

Resources