Sunday, June 04, 2006

GUI alternatives to Win32

If you're looking for alternatives to Win32 for writing GUI applications, but still want to deal with windows at a low level, GTK is a pretty good choice. I've been using it to write simple GUI applications in C targeted at the Linux platform. GTK applications can also be run on Windows using the GTK+ Runtime Environment for Windows available at http://gimp-win.sourceforge.net/stable.html

Tuesday, May 02, 2006

Microsoft: You're a member - I don't want to deal with you

I tried to use a member function as a ThreadProc method in the CreateThread call, but it didn't work. The following are all of my attempts at doing it...

- "&listeners::tossToServer" and "&(listeners::tossToServer)" result in:
error C2664: 'CreateThread': cannot convert parameter 3 frpm 'DWORD (__stdcall listeners::*)(LPVOID)' to 'LPTHREAD_START_ROUTINE'.
I tried casting it to 'LPTHREAD_START_ROUTINE', but that would work either.

- "tossToServer" results in:
error C3867: 'listeners::tossToServer': function call missing argument list; use '&listeners::tossToServer' to create a pointer to member

- "&tossToServer" results in:
error C2276: '&'; illegal operation on bound member function expression

In all of the above, my statements were a little like so:
DWORD dwThreadId;
HANDLE hThread = CreateThread (NULL, 0, Thread_proc_here, Ptr_to_param, 0, &dwThreadId);


After all that testing and turning, I decided to search the WWW, or more specifically, Google. An article from Microsoft turned up on why we can't use member functions in Win32 API calls. It's available at http://support.microsoft.com/kb/q102352/ but I have an extract of the part you need to read right here:


--- BEGIN QUOTE ---

The problem is that the function expects a C-style callback, not a pointer to a member function. A major difference is that member functions are called with a hidden argument called the "this" pointer. In addition, the format of the pointer isn't simply the address of the first machine instruction, as a C pointer is. This is particularly true for virtual functions.

If you want to use a member function as a callback, you can use a static member function. Static member functions do not receive the "this" pointer and their addresses correspond to an instruction to execute.

Static member functions can only access static data, and therefore to access nonstatic class members, the function needs an object or a pointer to an object. One solution is to pass in the "this" pointer as an argument to the member function.

--- END QUOTE ---

Friday, April 28, 2006

No more Win32 for a while

I won't be coding any Win32 for a while as my computer just ended it's life and I'm not too keen on rejuvenating it 'cos the repairs cost a lot - I can get a new desktop for the price of repairing my notebook PC.

Anyway, hope you have a good time coding with the Win32 API.

Monday, April 24, 2006

It's been a while

I haven't posted since my blog was blocked after it was detected as spam. It was whitelisted after about 2 days, but then there were server problems and so I stepped away from blogging and from Win32 programming.

Now, I'm back and am hoping to get into Win32 for User Interfaces, Sockets, and Thread to develop a tiny chat application. I have a chat server/client that runs on Linux; this will be a complete re-write targeted at Windows (Win32 platform).

The first speed-bump I came across was using the 'how' constant for shutdown - the Linux equivalent wasn't supported and SD_BOTH worked only with Winsock2.h. It took me a while to figure out that I have to include Winsock2.h before Windows.h because Windows.h automatically includes Winsock.h.

Socket Programming in Win32

//Socket programming in Win32
//Author: Nitin Reddy Katkam



//---- chatty_ui.h ----

#ifndef _CHATTY_UI__H

#define _CHATTY_UI__H


#undef UNICODE
#undef _UNICODE
#define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */

#include
#include


LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void ExitWithError(char *ac_operation);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);

#endif




//---- chatty_sock.h ----

#ifndef _CHATTY_SOCK__H

#define _CHATTY_SOCK__H


#undef UNICODE
#undef _UNICODE

#include
#include

int initChattySock (char *ac_ipaddr);
void listenChatty ();
void ShutEar();
DWORD WINAPI ServerThreadProc( LPVOID lpParam );
#endif




//---- chatty_ui.cpp ----

#include "chatty_ui.h"
#include "chatty_sock.h"

HWND hwnd_edit_ip;
HWND hwnd_button_connect;
HINSTANCE hInstance_g;

char c_currStatus=0;

LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_COMMAND:
switch (wParam) {
case 2: //connect button
//MessageBox(NULL, "button pressed", "info", MB_OK);

c_currStatus=!c_currStatus;
if (c_currStatus) {
char buf[256];
GetWindowText(hwnd_edit_ip, buf, 256);
initChattySock(buf);
listenChatty();

SetWindowText(hwnd_button_connect, "Disconnect");
} else {
ShutEar();

SetWindowText(hwnd_button_connect, "Connect");
}

break;
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
exit(0);
break;
case WM_CREATE:
hwnd_edit_ip = CreateWindowEx(0, "edit", "0.0.0.0", WS_CHILD|WS_VISIBLE|WS_BORDER, 70, 45, 140, 25, hwnd, (HMENU) 1, hInstance_g, 0);
hwnd_button_connect = CreateWindowEx(0, "button", "Connect", WS_CHILD|WS_VISIBLE, 220, 45, 75, 25, hwnd, (HMENU) 2, hInstance_g, 0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}


void ExitWithError(char *ac_operation) {
printf("Error in %s\r\n", ac_operation);
getchar();
exit(1);
}


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
hInstance_g=hInstance;
int callRetVal=0;

WNDCLASSEX wc;
PWNDCLASSEX p_wc = &wc;
int i_wcSize = sizeof(WNDCLASSEX);
memset(p_wc, 0, i_wcSize);

wc.cbSize=i_wcSize;
wc.lpszClassName="katkam_chatwin";
wc.lpfnWndProc=WndProc;
wc.hInstance=hInstance;
wc.hbrBackground=(HBRUSH) (COLOR_WINDOW+1);

callRetVal = RegisterClassEx(p_wc); //if the return value is zero, an error occurred
if (!callRetVal) {
ExitWithError("Window Registration");
}


HWND hwnd = CreateWindowEx(0, "katkam_chatwin", "K-Chat", WS_OVERLAPPEDWINDOW, 0, 0, 320, 200, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);


MSG msg;
MSG *p_msg=&msg;
while (GetMessage(p_msg, hwnd, 0, 0)) {
TranslateMessage(p_msg);
DispatchMessage(p_msg);
}

return (int) msg.wParam;
}




//---- chatty_sock.cpp ----

#include "chatty_ui.h"
#include "chatty_sock.h"


SOCKET sock1;
SOCKET sock2;

struct sockaddr_in saddr_in;
struct sockaddr *p_saddr = (struct sockaddr *) &saddr_in;
int sockaddr_len = sizeof(struct sockaddr_in);
struct sockaddr_in saddr_in2;
struct sockaddr *p_saddr2 = (struct sockaddr *) &saddr_in2;

HANDLE hThread;


int initChattySock (char *ac_ipaddr) {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) {
ExitWithError("WSAStartup");
//fprintf(stderr, "WSAStartup failed.\n");
//getchar();
//exit(1);
}

saddr_in.sin_addr.S_un.S_addr = inet_addr(ac_ipaddr);
saddr_in.sin_family=AF_INET;
saddr_in.sin_port=htons(1210);
memset(saddr_in.sin_zero, 0, 8);

sock1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock1 == INVALID_SOCKET) {
ExitWithError("socket");
}

return 0;
}

void listenChatty () {
bind(sock1, p_saddr, sockaddr_len);
listen(sock1, 0);

DWORD dwThreadId;
hThread = CreateThread(NULL, 0, ServerThreadProc, NULL, 0, &dwThreadId);

}


void ShutEar() {
shutdown(sock1, SD_BOTH); //0x02 is for both read & write
closesocket(sock1);
shutdown(sock2, SD_BOTH); //0x02 is for both read & write
closesocket(sock2);
CloseHandle(hThread);
}


DWORD WINAPI ServerThreadProc( LPVOID lpParam ) {
sock2 = accept(sock1, p_saddr2, &sockaddr_len);
char buf[256];

strcpy(buf, "Hello\r\n"); // '\r' is carriage return; '\n' is line feed
send(sock2, buf, strlen(buf), 0);

return 0;
}

Wednesday, March 29, 2006

Java

I know, you're probably wondering why an article on Java appears on a blog on Win32. Well, it's because Java can call native Win32 code using the JNI interface and I'm posting about my little experiment.

I first created the Java source code like so:

public class jennie {
private native void showMessage(String msg);
static {
System.loadLibrary("genie");
System.out.println("Library loaded");
}
public static void main(String[] args) {
jennie j = new jennie();
j.showMessage("Hola! These are 2 feet.");
}
}


I then compiled the Java source code and used a utility called javah (provided with Sun JDK 1.5) and it generated the following code:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class jennie */

#ifndef _Included_jennie
#define _Included_jennie
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: jennie
* Method: showMessage
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_jennie_showMessage
(JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif


Now, I looked up the function prototype and created a method with the exact same name, and included a couple of header files (the jni.h header provided with JDK 1.5 and the javah generated header file, along with others that I call from my function). The resulting source code is like so:

#include
#include
#include

JNIEXPORT void JNICALL Java_jennie_showMessage(JNIEnv* env, jobject, jstring jMsg) {
const char* msg=env->GetStringUTFChars(jMsg, 0);
printf("%s\n", msg);
env->ReleaseStringUTFChars(jMsg, msg);
}

Now, although it's pretty cool that you can call Win32 code from Java, it does look pretty ugly - just take a look at the function prototype and the string conversion!
When it comes to communicating between Win32 and another development platforms, .NET should be your first choice since it's much cleaner (which is because they're both from Microsoft so they want you to stick to their products). There are a few restrictions on communicating between Unmanaged code (Win32) and Managed code so you'd probably want to get more details on it before you go exploring.

Wednesday, March 15, 2006

Who Am I?

Who Am I?

#include <windows.h>

//
// Identify Windows Version
//

int __stdcall WinMain(HINSTANCE hPrevIntance, HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow) {
OSVERSIONINFO vi = { sizeof(OSVERSIONINFO) };

// Versions
// VER_PLATFORM_WIN32_NT (PlatformId)
// (Major, Minor = Version)
// 5, 0 = Windows 2000
// 5, 1 = Windows XP
// 5, 2 = Windows 2003
// VER_PLATFORM_WIN32_WINDOWS
// 4, 0 = Windows 95
// 4, 10 = Windows 98
// 4, 90 = Windows ME
// VER_PLATFORM_WIN32s
// ???
//
// Additional Info is available using:
// ProductType: VER_NT_WORKSTATION, VER_NT_SERVER, VER_NT_DOMAIN_CONTROLLER
// wSuiteMask (Use '&' with the constants and check the boolean result; Standard, if none match):
// VER_SUITE_PERSONAL, VER_SUITE_ENTERPRISE, VER_SUITE_BLADE, VER_SUITE_DATACENTER

if (GetVersionEx(&vi)) {
if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
if (vi.dwMajorVersion==5 && vi.dwMinorVersion==0)
MessageBoxA(NULL, "Running Windows 2000", "Info", MB_OK);
else if (vi.dwMajorVersion==5 && vi.dwMinorVersion==1)
MessageBoxA(NULL, "Running Windows XP", "Info", MB_OK);
else if (vi.dwMajorVersion==5 && vi.dwMinorVersion==2)
MessageBoxA(NULL, "Running Windows 2003", "Info", MB_OK);
else
MessageBoxA(NULL, "Unidentified Win32 NT class OS", "Info", MB_OK);
} else if (vi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
if (vi.dwMajorVersion==4 && vi.dwMinorVersion==0)
MessageBoxA(NULL, "Running Windows 95", "Info", MB_OK);
else if (vi.dwMajorVersion==4 && vi.dwMinorVersion==10)
MessageBoxA(NULL, "Running Windows 98", "Info", MB_OK);
else if (vi.dwMajorVersion==4 && vi.dwMinorVersion==90)
MessageBoxA(NULL, "Running Windows Millennium Edition", "Info", MB_OK);
else
MessageBoxA(NULL, "Unidentified Win32 non-NT class OS", "Info", MB_OK);
} else if (vi.dwPlatformId == VER_PLATFORM_WIN32s) {
MessageBoxA(NULL, "Unidentified Win32 class OS", "Info", MB_OK);
} else {
MessageBoxA(NULL, "Unidentified OS", "Info", MB_OK);
}
} else {
MessageBoxA(NULL, "OS info not available!", "Error", MB_OK);
}

return 0;
}

Saturday, March 04, 2006

Minimize to Tray for Win32 64-bit

//I just got SetWindowLongPtr working, so here's the new code for 'Minimize to tray'.

//
// MINIMIZE TO TRAY
//

NOTIFYICONDATA nid;
bool isTrayIcon=false;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case 10101:
if (lParam == WM_LBUTTONDBLCLK) {
OpenIcon(hwnd);

// SendMessage(hwnd, WM_SIZE, SIZE_RESTORED, NULL);
// SendMessage(hwnd, WM_SIZE, SIZE_RESTORED, MAKELPARAM(320, 200));
// SetForegroundWindow(hwnd);
// SetActiveWindow(hwnd);
// ShowWindow(hwnd, SW_SHOW);
// UpdateWindow(hwnd);
// SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOREPOSITION | SWP_SHOWWINDOW);
}
break;
case WM_SIZE:

if (wParam == SIZE_MINIMIZED) {
//Set tray icon here

nid.cbSize=sizeof(NOTIFYICONDATA);
nid.hWnd = hwnd;
nid.hIcon = LoadIcon(NULL, IDI_APPLICATION);
nid.szTip[0] = 'T'; nid.szTip[1] = 'V'; nid.szTip[2] = '\0';
nid.uID = 1;
nid.uCallbackMessage = 10101;
nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;

Shell_NotifyIcon(NIM_ADD, &nid);

ShowWindow(hwnd, SW_HIDE);
// SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
SetWindowLongPtr(hwnd, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
ShowWindow(hwnd, SW_SHOW);

isTrayIcon=true;
} else {
//Remove tray icon here
if (isTrayIcon) {
Shell_NotifyIcon(NIM_DELETE, &nid);

ShowWindow(hwnd, SW_HIDE);
SetWindowLongPtr(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW);
// SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW);
ShowWindow(hwnd, SW_SHOW);

isTrayIcon=false;
}
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}


int __stdcall WinMain(HINSTANCE hPrevInstance, HINSTANCE hInstance, LPSTR CmdLine, int nCmdShow) {

WNDCLASSEX wc;
int ret=0;

wc.cbClsExtra=sizeof(LONG);
wc.cbSize=sizeof(WNDCLASSEX);
wc.cbWndExtra=sizeof(LONG);
wc.hbrBackground=(HBRUSH) (COLOR_WINDOW+1);
wc.hCursor=LoadCursor(NULL, IDC_ARROW);
wc.hIcon=LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);
wc.hInstance=hInstance;
wc.lpfnWndProc=WndProc;
wc.lpszClassName="Marshal";
wc.lpszMenuName=NULL;
wc.style=0;

ret = RegisterClassExA(&wc);
if (ret==0) {
MessageBoxA(NULL, "Could not register window class", "Error", MB_OK);
ExitProcess(-1);
}


HWND hwnd;
hwnd = CreateWindowEx(0, "Marshal", "Video Game", WS_OVERLAPPEDWINDOW, 10, 10, 320, 200, NULL, NULL, hInstance, NULL);

if (hwnd==NULL) {
MessageBoxA(NULL, "Could not instantiate window class", "Error", MB_OK);
ExitProcess(-1);
}


ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);


MSG msg;
while (GetMessage(&msg, NULL, 0, 0)>0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return (int) msg.wParam;

}

Abraca-Da-Minimo

//
// MINIMIZE TO TRAY
//

NOTIFYICONDATA nid;
bool isTrayIcon=false;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case 10101:
if (lParam == WM_LBUTTONDBLCLK) {
OpenIcon(hwnd);

// SendMessage(hwnd, WM_SIZE, SIZE_RESTORED, NULL);
// SendMessage(hwnd, WM_SIZE, SIZE_RESTORED, MAKELPARAM(320, 200));
// SetForegroundWindow(hwnd);
// SetActiveWindow(hwnd);
// ShowWindow(hwnd, SW_SHOW);
// UpdateWindow(hwnd);
// SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOREPOSITION | SWP_SHOWWINDOW);
}
break;
case WM_SIZE:

if (wParam == SIZE_MINIMIZED) {
//Set tray icon here

nid.cbSize=sizeof(NOTIFYICONDATA);
nid.hWnd = hwnd;
nid.hIcon = LoadIcon(NULL, IDI_APPLICATION);
nid.szTip[0] = 'T'; nid.szTip[1] = 'V'; nid.szTip[2] = '\0';
nid.uID = 1;
nid.uCallbackMessage = 10101;
nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;

Shell_NotifyIcon(NIM_ADD, &nid);

LONG toolWindowExStyle = WS_EX_TOOLWINDOW;
ShowWindow(hwnd, SW_HIDE);
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
// SetWindowLongPtr(hwnd, GWL_EXSTYLE, (LONG_PTR) &toolWindowExStyle);
ShowWindow(hwnd, SW_SHOW);

isTrayIcon=true;
} else {
//Remove tray icon here
if (isTrayIcon) {
Shell_NotifyIcon(NIM_DELETE, &nid);

LONG appWindowExStyle = WS_EX_APPWINDOW;
ShowWindow(hwnd, SW_HIDE);
// SetWindowLongPtr(hwnd, GWL_EXSTYLE, (LONG_PTR) &appWindowExStyle);
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW);
ShowWindow(hwnd, SW_SHOW);

isTrayIcon=false;
}
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}


int __stdcall WinMain(HINSTANCE hPrevInstance, HINSTANCE hInstance, LPSTR CmdLine, int nCmdShow) {

WNDCLASSEX wc;
int ret=0;

wc.cbClsExtra=0;
wc.cbSize=sizeof(WNDCLASSEX);
wc.cbWndExtra=0;
wc.hbrBackground=(HBRUSH) (COLOR_WINDOW+1);
wc.hCursor=LoadCursor(NULL, IDC_ARROW);
wc.hIcon=LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);
wc.hInstance=hInstance;
wc.lpfnWndProc=WndProc;
wc.lpszClassName="Marshal";
wc.lpszMenuName=NULL;
wc.style=0;

ret = RegisterClassExA(&wc);
if (ret==0) {
MessageBoxA(NULL, "Could not register window class", "Error", MB_OK);
ExitProcess(-1);
}


HWND hwnd;
hwnd = CreateWindowEx(0, "Marshal", "Video Game", WS_OVERLAPPEDWINDOW, 10, 10, 320, 200, NULL, NULL, hInstance, NULL);

if (hwnd==NULL) {
MessageBoxA(NULL, "Could not instantiate window class", "Error", MB_OK);
ExitProcess(-1);
}


ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);


MSG msg;
while (GetMessage(&msg, NULL, 0, 0)>0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return (int) msg.wParam;

}

64 is twice 32... that's twice as hard

The MSDN documentation says that SetWindowLong is deprecated (it didn't use that exact word) and should be replaced with SetWindowLongPtr instead to work with 64-bit versions of Windows. I thought I'd try it out and did the following:

LONG appWindowExStyle = WS_EX_APPWINDOW;
SetWindowLongPtr(hwnd, GWL_EXSTYLE, (LONG_PTR) &appWindowExStyle);

It didn't seem to work. However, the old 32-bit way seems to work fine:

SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW);

I guess it's one of those things that I ought to investigate further.