Help converting a girder plugin to python

Do you have questions about writing plugins or scripts in Python? Meet the coders here.
Post Reply
srs
Posts: 9
Joined: Thu Dec 18, 2008 7:53 pm

Help converting a girder plugin to python

Post by srs » Thu Dec 18, 2008 7:59 pm

hello,

I'm trying to convert a girder plugin I wrote for using the usb ir receiver that comes with my asus p5w dh motherboard.

the girder plugin is as follows:

Code: Select all

// AsusP5W_Girder.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

ATOM registerClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= NULL;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= L"AsusP5W_Girder";
	wcex.hIconSm		= NULL;

	return RegisterClassEx(&wcex);
}

BYTE button;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {

	switch (message) {

		case WM_CREATE: {
			RAWINPUTDEVICE Rid[1];
			Rid[0].usUsagePage = 65280; 
			Rid[0].usUsage = 0;
			Rid[0].dwFlags = RIDEV_PAGEONLY | RIDEV_INPUTSINK;
			Rid[0].hwndTarget = hWnd;
			RegisterRawInputDevices(Rid, 1, sizeof(RAWINPUTDEVICE));
			break;
		}

		case WM_DESTROY:
			PostQuitMessage(0);
			break;

		case WM_INPUT: {
			UINT dwSize;

			GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
			LPBYTE lpb = new BYTE[dwSize];
			GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize,  sizeof(RAWINPUTHEADER));
			RAWINPUT* raw = (RAWINPUT*)lpb;
			if (raw->header.dwType==RIM_TYPEKEYBOARD) {
				dwSize = 0;
			} else if (raw->header.dwType==RIM_TYPEHID) {
				char txt[100];
				if (raw->data.hid.bRawData[2]==0) {
					KillTimer(hWnd,1);
				} else {
					sprintf(txt,"%i",raw->data.hid.bRawData[2]);
					button = raw->data.hid.bRawData[2];
					SendEventEx(txt, NULL, 0, 2000, EVENT_MOD_NONE);
					SetTimer(hWnd,1,400,NULL);
				}

			}
			delete[] lpb; 
			return 0;
		}

		case WM_TIMER:
			char txt[100];
			sprintf(txt,"%i",button);
			SendEventEx(txt, NULL, 0, 2000, EVENT_MOD_NONE);
			SetTimer(hWnd,1,100,NULL);
			break;

			
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

#define PLUGINNUM  2000
#define PLUGINNAME "Asus P5W DH Deluxe Remote"
#define PLUGINDESC "Receive events from the USB receiver included with the Asus P5W DH Deluxe"
#define PLUGINVER  "1.0"
#define APIVER     3

pCoreVars CoreVars = NULL;
HINSTANCE g_hInst;
HWND wnd;

int WINAPI gir_open(int gir_major_ver, int gir_minor_ver, int gir_micro_ver, pFunctions3 p) {
    CoreVars = p->CoreVars;
    // TODO: Extra start-up code goes here.
    GirderLogMessageEx(PLUGINNAME, "Open", GLM_INFORMATION_ICON);
    return GIR_TRUE;
}

int WINAPI gir_close() {
    // TODO: Extra shut-down code goes here.
    return GIR_TRUE;
}

int WINAPI gir_requested_api(int maxapi) {
    return APIVER;
}

int WINAPI gir_devicenum() {
    return PLUGINNUM;
}

void WINAPI gir_description(PCHAR Buffer, BYTE Length) {
    strncpy(Buffer, PLUGINDESC, Length);
}

void WINAPI gir_name(PCHAR Buffer, BYTE Length) {
    strncpy(Buffer, PLUGINNAME, Length);
}

void WINAPI gir_version(PCHAR Buffer, BYTE Length) {
    strncpy(Buffer, PLUGINVER, Length);
}

BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD  dwReason, LPVOID lpReserved) {
	g_hInst = hModule;
    return TRUE;
}

int WINAPI gir_start() {
    //TODO: Start the event generation mechanism,
	registerClass(g_hInst);
	wnd = CreateWindow(L"AsusP5W_Girder",L"AsusP5W_Girder",0,0,0,0,0,NULL,NULL,g_hInst,NULL);
    return GIR_TRUE;
}

int WINAPI gir_stop() {
    //TODO: Stop the event generation mechanism.
	DestroyWindow(wnd);
    return GIR_TRUE;
}

I have no knowledge of python, but took a look at a few examples and this is what I have so far:

Code: Select all

eg.RegisterPlugin(
    name = "Asus P5W DH remote",
    author = "",
    version = "1.0." + "$LastChangedRevision: 314 $".split()[1],
    kind = "remote",
    description = (
        'Plugin for USB IR Reciever of Asus P5W DH Motherboard'
    ),
)

import os
from threading import Timer
from msvcrt import get_osfhandle
import _winreg
import ctypes

from ctypes import Structure, Union, c_byte, c_char, c_int, c_long, c_ulong, c_ushort, c_wchar
from ctypes import pointer, byref, sizeof, POINTER, cast
from ctypes.wintypes import ULONG, BOOLEAN, BYTE

from eg.WinApi.Dynamic import (
    WinDLL,
    windll,
    WINFUNCTYPE,
    POINTER,
    HWND ,
    HMODULE ,
    c_uint,
    c_void_p,
    c_char_p,
    WNDCLASS,
    GetDesktopWindow,
    WNDPROC,
    CreateWindow,
    WinError,
    RegisterClass,
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,
    WM_TIMER,
    pointer,
    DestroyWindow,
    UnregisterClass,
    GetTickCount,
    DWORD,
    HANDLE,
    WPARAM
)

class RAWINPUTDEVICE(Structure):
    _fields_ = [
		("usUsagePage", c_ushort),
        ("usUsage", c_ushort),
        ("dwFlags", DWORD),
        ("hwndTarget", HWND),
    ]
       
class RAWHID(Structure):
	_fields_ = [
		("dwSizeHid", DWORD),
		("dwCount", DWORD),
		("bRawData", BYTE),
	]
    
class RAWINPUTHEADER(Structure):
    _fields_ = [
        ("dwType", DWORD),
        ("dwSize", DWORD),
        ("hDevice", HANDLE),
        ("wParam", WPARAM),
    ]
            
class RAWINPUT(Structure):
	class RAWINPUT_DATA_VALUE(Union):
		_fields_ = [
			("mouse", DWORD),
			("keyboard", DWORD),
			("hid",RAWHID),
		]
	_fields_ = [
		("header", RAWINPUTHEADER),
		("data", RAWINPUT_DATA_VALUE),
	]
		
WM_INPUT              = 0x00FF    
RIDEV_PAGEONLY        = 0x00000020
RIDEV_INPUTSINK       = 0x00000100
RID_INPUT 			  = 0x10000003
RIM_TYPEMOUSE         = 0
RIM_TYPEKEYBOARD      = 1
RIM_TYPEHID           = 2


class AsusMessageReceiver(eg.ThreadWorker):
    """
    A thread with a hidden window to receive win32 messages from the driver
    """
    hwnd = None
    dll = None
    button = None
    
    @eg.LogIt
    def Setup(self, plugin):
        """
        This will be called inside the thread at the beginning.
        """
        
        self.plugin = plugin
 
        wc = WNDCLASS()
        wc.hInstance = GetDesktopWindow()
        wc.lpszClassName = "AsusPluginEventSinkWndClass"
        wc.lpfnWndProc = WNDPROC(self.MyWndProc)
        if not RegisterClass(byref(wc)):
            raise WinError()
        self.hwnd = CreateWindow(wc.lpszClassName, "AsusPlugin Event Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, wc.hInstance, None)
        if not self.hwnd:
            raise WinError()
        self.wc = wc
        self.hinst = wc.hInstance
        
        rid = RAWINPUTDEVICE()
        rid.usUsagePage = 65280
        rid.usUsage = 0
        rid.dwFlags = RIDEV_PAGEONLY | RIDEV_INPUTSINK
        rid.hwndTarget = self.hwnd
        windll.user32.RegisterRawInputDevices(byref(rid), 1, sizeof(rid))
        
    @eg.LogIt
    def Finish(self):
        """
        This will be called inside the thread when it finishes. It will even
        be called if the thread exits through an exception.
        """
        if self.hwnd:
            windll.user32.KillTimer(self.hwnd, 1)

        if self.hwnd:
            DestroyWindow(self.hwnd)
            UnregisterClass(self.wc.lpszClassName, self.hinst)        
        
    #@eg.LogIt
    def MyWndProc(self, hwnd, msg, wParam, lParam):
        if msg == WM_INPUT:
            dwSize = c_uint(0)
            windll.user32.GetRawInputData(lParam, RID_INPUT, 0, byref(dwSize), sizeof(RAWINPUTHEADER))

            bytebuf = c_byte * dwSize.value
            lpb = bytebuf()					
            windll.user32.GetRawInputData(lParam, RID_INPUT, byref(lpb), byref(dwSize), sizeof(RAWINPUTHEADER))
            raw = cast(lpb, POINTER(RAWINPUT))
            if raw.header.dwType == RIM_TYPEHID:
                charbuf = c_char * 100;
                if (raw.data.hid.bRawData[2]==0):
                    windll.user32.KillTimer(self.hwnd, 1)
                else:
                    button = raw.data.hid.bRawData[2]
                    self.TriggerEvent(button)
                    windll.user32.SetTimer(self.hwnd, 1, 400, NULL)
        if msg == WM_TIMER:
            self.TriggerEvent(button)
            windll.user32.SetTimer(self.hwnd, 1, 100, NULL)
					
        return 1
    
    def OnTimeOut(self):
        self.keyStillPressed = False
        self.lastEvent.SetShouldEnd()
        
        

class AsusIR(eg.PluginClass):
    
    class text:
        buttonTimeout = "Button release timeout (seconds):"
        buttonTimeoutDescr = (
            "(If you get unintended double presses of the buttons, "
            "increase this value.)"
        )

    def __start__(self, waitTime=0.15):
        self.msgThread = AsusMessageReceiver(self)
        self.msgThread.Start()


    @eg.LogIt
    def OnComputerSuspend(self, suspendType):
        self.__stop__()
    
    
    @eg.LogIt
    def OnComputerResume(self, suspendType):
        self.__start__()     
          
                        
    def __stop__(self):
        self.msgThread.Stop()
Unfortunately it doesn't work :(

I get a error on line 151 saying: AttributeError: 'LP_RAWINPUT' object has no attribute 'header'.

I'm sure it's not the only error, but any pointers would be appreciated.

CHeitkamp
Plugin Developer
Posts: 49
Joined: Sun Jan 27, 2008 12:42 pm
Location: Münster / Germany

Re: Help converting a girder plugin to python

Post by CHeitkamp » Thu Dec 18, 2008 9:34 pm

Looks the device is an USB HID device - have you already tried the generic HID plugin?

Maybe easier than writing your own...

srs
Posts: 9
Joined: Thu Dec 18, 2008 7:53 pm

Re: Help converting a girder plugin to python

Post by srs » Thu Dec 18, 2008 9:44 pm

yep. Unfortunately it doesn't work.

srs
Posts: 9
Joined: Thu Dec 18, 2008 7:53 pm

Re: Help converting a girder plugin to python

Post by srs » Fri Dec 19, 2008 3:36 am

strange.... even this basic plugin gives me errors:

Code: Select all

eg.RegisterPlugin(
    name = "Asus P5W DH remote",
    author = "",
    version = "1.0.",
    kind = "remote",
    description = (
        'Plugin for USB IR Reciever of Asus P5W DH Motherboard'
    ),
)

import win32con
from ctypes import *
from win32con import *

WNDPROC = WINFUNCTYPE(c_long, c_int, c_uint, c_int, c_int)
NULL = c_int(win32con.NULL)

def ErrorIfZero(handle):
    if handle == 0:
        raise WinError()
    else:
        return handle

CreateWindowEx = windll.user32.CreateWindowExW
CreateWindowEx.argtypes = [c_int, c_wchar_p, c_wchar_p, c_int, c_int, c_int, c_int, c_int, c_int, c_int, c_int, c_int]
CreateWindowEx.restype = ErrorIfZero

class WNDCLASS(Structure):
    _fields_ = [('style', c_uint),
                ('wndProc', WNDPROC),
                ('clsExtra', c_int),
                ('wndExtra', c_int),
                ('hInstance', c_int),
                ('hIcon', c_int),
                ('hCursor', c_int),
                ('hbrBackground', c_int),
                ('lpszMenuName', c_wchar_p),
                ('lpszClassName', c_wchar_p)]

class MsgWindow(object):

    def create(self):
    
        wc = WNDCLASS()
        wc.wndProc = WNDPROC(self.WndProc)
        wc.style = CS_HREDRAW | CS_VREDRAW
        wc.clsExtra = 0
        wc.wndExtra = 0
        wc.hInstance = windll.kernel32.GetModuleHandleW(None)
        wc.hIcon = windll.user32.LoadIconW(c_int(win32con.NULL), c_int(win32con.IDI_APPLICATION))
        wc.hCursor = windll.user32.LoadCursorW(NULL, c_int(win32con.IDC_ARROW))
        wc.hbrBackground = windll.gdi32.GetStockObject(c_int(win32con.WHITE_BRUSH))
        wc.lpszMenuName = None
        wc.lpszClassName = u"AsusMessageClass"
        
        windll.user32.RegisterClassW(byref(wc))
    
        self.hwnd = CreateWindowEx(0, wc.lpszClassName, u"AsusMessageWindow", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 100, NULL, NULL, wc.hInstance, NULL)
        windll.user32.ShowWindow(self.hwnd, SW_SHOW)
        windll.user32.UpdateWindow(self.hwnd)
        
        return self.hwnd

    def WndProc(self, hwnd, msg, wParam, lParam):
        return windll.user32.DefWindowProcW(c_int(hwnd), c_int(msg), c_int(wParam), c_int(lParam))   
        
    def destroy(self):
        windll.user32.DestroyWindow(self.hwnd)


class AsusIR(eg.PluginClass):
    
    def __start__(self):
        self.msgWindow = MsgWindow()
        self.msgWindow.create()
                       
    def __stop__(self):
        self.msgWindow.destroy()

Code: Select all

Traceback (most recent call last) (1476):
  File "C:\Program Files\EventGhost\eg\Classes\ThreadWorker.py", line 191, in __DoOneEvent
  File "C:\Program Files\EventGhost\eg\Classes\ThreadWorker.py", line 39, in PumpWaitingMessages
WindowsError: exception: access violation reading 0x00000020
Either I'm doing something stupid or maybe it's a bug in the beta version of EventGhost?

User avatar
Bitmonster
Site Admin
Posts: 2239
Joined: Mon Feb 06, 2006 10:28 pm

Re: Help converting a girder plugin to python

Post by Bitmonster » Thu Jan 15, 2009 7:39 am

The problem starts here:

Code: Select all

            lpb = bytebuf()               
            windll.user32.GetRawInputData(lParam, RID_INPUT, byref(lpb), byref(dwSize), sizeof(RAWINPUTHEADER))
            raw = cast(lpb, POINTER(RAWINPUT))
            if raw.header.dwType == RIM_TYPEHID:
raw is now a pointer to a structure and not the structure itself. So you would need to write something like:

if raw.contents.header.dwType == RIM_TYPEHID:

Why you get an access violation on your second example, I currently don't know. Might be a wrong parameter. You define:
NULL = c_int(win32con.NULL)
In ctypes you generally use None for NULL. Also it is often problematic to mix something from the win32api-modules and ctypes.
Please post software-related questions in the forum - PMs will only be answered, if really private, thanks!

srs
Posts: 9
Joined: Thu Dec 18, 2008 7:53 pm

Re: Help converting a girder plugin to python

Post by srs » Thu Jan 15, 2009 7:51 am

thanks for the help Bitmonster :)

The problem with my second example seems to have fixed itself when I updated to the latest beta version (at first I mistakenly downloaded the 0.3.6.1476 as it was at the top of the list without paying too much attention to the version number :oops: )

I will go back to my original plugin now and should have it fixed with your help

Post Reply