I am trying to call the GetConsoleScreenBufferInfoEx function from a console application. If it matters, the application is a 32 bit application running on 64 bit Windows 7. The language is RealBasic.
I believe I have defined all the structures correctly, and the buffer output handle works for every other API function that is being called:
Declare Function GetConsoleScreenBufferInfoEx Lib "Kernel32" (cHandle As Integer, ByRef info As CONSOLE_SCREEN_BUFFER_INFOEX) As Boolean
Declare Function GetLastError Lib "Kernel32" () As Integer
Declare Function GetStdHandle Lib "Kernel32" (hIOStreamType As Integer) As Integer
Const STD_OUTPUT_HANDLE = -11
Dim stdHandle As Integer = GetStdHandle(STD_OUTPUT_HANDLE)
Dim err As Integer
Dim info As CONSOLE_SCREEN_BUFFER_INFOEX
If GetConsoleScreenBufferInfoEx(stdHandle, info) Then
Break
Else
err = GetLastError //Always 87, Invalid parameter
Break
End If
Structures:
Structure CONSOLE_SCREEN_BUFFER_INFOEX
cbSize As Integer
dwSize As COORD
CursorPosition As COORD
Attribute As UInt16
srWindow As SMALL_RECT
MaxWindowSize As COORD
PopupAttributes As UInt16
FullScreenSupported As Boolean
ColorTable(15) As UInt32
Structure COORD
X As UInt16
Y As UInt16
Structure SMALL_RECT
Left As UInt16
Top As UInt16
Right As UInt16
Bottom As UInt16
I've gone over this 20 times and nothing looks wrong to me. I've used the COORD and SMALL_RECT structures many times before, so I don't think I made any translation errors on them. The CONSOLE_SCREEN_BUFFER_INFOEX structure, however, is seeing its first use by me here, and I sense that the error lies somewhere in my translation of it.
You need to set the cbSize parameter of the CONSOLE_SCREEN_BUFFER_INFOEX before you send it in. GetConsoleScreenBufferInfoEx will check that it is the correct size and that's why it's returning an invalid parameter.
So before the call to GetConsoleScreenBufferInfoEx add:
info.cbSize = 96
Or better yet Real Basic does allow you to access the size of the structure:
info.cbSize = GetConsoleScreenBufferInfoEx.Size
Which should handle the calculation for you.
Related
I'm developing a daemon with no UI apart from a simple icon in the Windows systray.
I would like to have no dependencies on any other package(s), so I'm trying to use the syscall package and implement the necessary call(s) by myself.
Documentation
After some research, I think that I must use the Shell_NotifyIcon function in shell32.dll.
The Golang WIKI gives three ways to call a Windows DLL. The third solution is to be excluded. For many reasons, I want to build sources with the Golang compiler only.
I found an interesting article on "Developing for Multiple Platforms With Go" which explained how to use Shell_NotifyIconW (Unicode declination), but the implementation is partial.
I have found a few Golang libraries which implement Windows System Tray. They are useful to help understand the structure and calls involved in dealing with it.
Libraries
getlantern/systray
cratonica/trayhost
xilp/systray
lxn/walk
Implementation
Structures
Built with xilp/systray documentation.
type HANDLE uintptr
type HICON HANDLE
type HWND HANDLE
type GUID struct {
Data1 uint32
Data2 uint16
Data3 uint16
Data4 [8]byte
}
type NOTIFYICONDATA struct {
CbSize uint32
HWnd HWND
UID uint32
UFlags uint32
UCallbackMessage uint32
HIcon HICON
SzTip [128]uint16
DwState uint32
DwStateMask uint32
SzInfo [256]uint16
UVersion uint32
SzInfoTitle [64]uint16
DwInfoFlags uint32
GuidItem GUID
}
Variables
const (
NIM_ADD = 0x00000000
NIM_MODIFY = 0x00000001
NIM_DELETE = 0x00000002
NIM_SETVERSION = 0x00000004
NIF_MESSAGE = 0x00000001
NIF_ICON = 0x00000002
NIF_TIP = 0x00000004
NIF_STATE = 0x00000008
NIF_HIDDEN = 0x00000001
)
Source
package main
import (
"log"
"syscall"
"unsafe"
)
func main() {
shell32 := syscall.MustLoadDLL("shell32.dll")
Shell_NotifyIcon := shell32.MustFindProc("Shell_NotifyIconW")
iconData := NOTIFYICONDATA{
HWnd: 0,
UFlags: NIF_MESSAGE | NIF_STATE,
DwState: NIF_HIDDEN,
DwStateMask: NIS_HIDDEN,
}
iconData.CbSize = uint32(unsafe.Sizeof(iconData))
ret, _, _ := Shell_NotifyIcon.Call(
NIM_ADD,
uintptr(unsafe.Pointer(&iconData)),
)
if ret == 0 {
log.Println("Failed")
return
}
// Do anything, like open a HTTP server to keep the program running
http.ListenAndServe(":8080", nil)
}
Details
I have no idea what information to give in HWnd, but without it, the executable crashes.
UFlags, DwState and DwStateMask have values that I have found in different projects.
I know that it is possible; the Golang WIKI gives an implementation to call a message box.
Fields of NOTIFYICONDATA
hWnd
hWnd field of NOTIFYICONDATA holds a window handle that is associated with notifyicon itself, as mentioned in MSDN:
hWnd
A handle to the window that receives notifications associated with an icon in the notification area.
I found that it's necessary to associate a window handle, even if the window is not visible.
uFlags tells which fields of NOTIFYICONDATA are valid in single command.
As you see there are lots of fields in NOTIFYICONDATA, and if you are going to change just the icon of the notifyicon, you can leave other fields unchanged and set only hIcon field then pass the whole NOTIFYICONDATA to Shell_NotifyIcon.
If you want to change both icon and message, just set it to NIF_MESSAGE|NIF_ICON.
dwState
dwState can be used to control icon's visibility. If you specify NIF_STATE for uFlags, and NIS_HIDDEN for dwState and dwStateMask, it'll make notifyicon hidden.
dwStateMask
And in most case, just set dwStateMask as same as dwState. It just tells which bit of dwState is valid for the command:
The possible values are the same as those for dwState.
Example
You can find full example I've wrote at here: https://github.com/hallazzang/go-windows-programming/tree/master/example/gui/notifyicon
I'm trying to create a thin wrapper of Windows MMDevice API for Go, and I faced the problem about Windows data types for strings.
According to the documentation of IMMDevice::GetId method, it takes the parameter below:
HRESULT GetId(
[out] LPWSTR *ppstrId
);
And here is my Go code that corresponds to the method above. (github.com/moutend/ywca/immdevice_windows.go:13)
func getId(mmd *IMMDevice, strId *uint16) (err error) {
hr, _, _ := syscall.Syscall(
mmd.VTable().GetId,
2,
uintptr(unsafe.Pointer(mmd)),
uintptr(unsafe.Pointer(strId)),
0)
// ...
}
My understand is that the LPWSTR is the pointer to the array of uint16 values, but it causes invalid pointer error.
What type should I use in this case? Thanks.
It is a pointer to a pointer. The LPWSTR type is a wchar_t* and therefor the parameter in that method is a wchar_t**.
You are not passing in a string buffer for the method to fill. The method will allocate memory with CoTaskMemAlloc and return this memory address back to you after it has been filled. You are responsible for freeing this memory with CoTaskMemAlloc.
The first thing to do is read the documentation for the Windows function.
IMMDevice::GetId
method,
HRESULT GetId(
[out] LPWSTR *ppstrId
);
Parameters
ppstrId [out]
Pointer to a pointer variable into which the method writes the address
of a null-terminated, wide-character string containing the endpoint
device ID. The method allocates the storage for the string. The caller
is responsible for freeing the storage, when it is no longer needed,
by calling the CoTaskMemFree function. If the GetId call fails,
*ppstrId is NULL. For information about CoTaskMemFree, see the Windows SDK documentation.
Return value
If the method succeeds, it returns S_OK. If it fails, possible return
codes include, but are not limited to, the values shown in the
following table.
In particular, "ppstrId [out] Pointer to a pointer variable ..." You have strId *uint16 or *pstrId when I would expect you to have strId **uint16 or *ppstrId.
(Talking about Visual Basic 6)
I was able to find how to convert Double into 8-bytes array, but not the viceversa.
Before I start to try to code it, is there some routine to do it (like the "CopyMemory" described in the linked question)? Can the "CopyMemory" be used in this case?
Use the same code as the answer you linked to but swap the source and destination around:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
ByRef Destination As Any, _
ByRef Source As Any, _
ByVal Length As Long)
Function BytesToDbl(ByRef Bytes() As byte) As Double
Dim D As Double
CopyMemory D, Bytes(0), LenB(D)
BytesToDbl = D
End Function
I've skipped any error checking for this example but you'll want to make sure that your byte array is actually 8 bytes long otherwise you'll get an access violation.
Note that this assumes the byte array was created using the linked to question. Floating point values from other sources may well be using a different binary representation which means this will not work.
If a dll exports some functions and the functions have only ordinal numbers, how can I call the functions?
Give me a short example please.
The documentation for GetProcAddress explains that you pass the integer ordinal in the low-order word of the lpProcName parameter. The MAKEINTRESOURCE macro can actually be used to make this a little easier:
int ordinal = 123;
HANDLE dll = LoadLibrary("MyDLL.dll");
FARPROC fn = GetProcAddress(dll, MAKEINTRESOURCE(ordinal));
I have a simple DLL written with VC6 with one function:
__declspec(dllexport) int myfunc(long a, unsigned char *b, unsigned char *c, unsigned char *d, unsigned char *e)
And im calling it from vb6 using:
Declare Function myfunc Lib "mylib.dll" (ByVal a As Long, ByVal b As String, ByVal c As String, ByVal d As String, ByVal e As String) As Long
....
dim a as long
dim b as string
dim c as string
dim d as string
dim e as string
dim r as long
r=myfunc(a,b,c,d,e)
Im getting "bad dll calling convention" error but I cant figure out why. Any ideas?
Generally speaking, 'bad DLL...' means what it says. VB6 requires the _stdcall convention (like the Win API) for any external functions it calls.
Try adding __stdcall to the C function prototype and see what happens.
Check out the Universal DLL function caller, by Paul Caton:
http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=70195&lngWId=1
It will allow you to call pretty much any type of function from VB6.