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.

Thursday, March 02, 2006

Changing Window Attributes

When you register a window class or create a window, you get to specify window attributes, however once your window is running, you may wish to change the window's attributes to do something like removing the taskbar button for a window on minimizing (used in the 'Minimize to tray' feature of applications). This can be done using SetWindowLongPtr (SetWindowLong can be used too, but it will not work on 64-bit versions of Windows).

I've read some time back in an MSDN article that the window has to be hidden/disabled (I can't remember which... my guess would be hidden) before the attribute is changed. I'll try it and post the code for it sometime soon.

Wednesday, March 01, 2006

VC++ programs in BC++

To get your Visual C++ programs to run in Borland C++, you'll have to devise some means to call the WinMain method. The following is a method that can do this for you:

int main() {
HINSTANCE hInstance = GetModuleHandle(NULL);
LPSTR CmdLine = GetCommandLine();
int ret = WinMain(hInstance, NULL, CmdLine, SW_SHOWDEFAULT);
ExitProcess(ret);
}