You are on page 1of 7

To begin with, I will assume that you have a working knowledge of at least the b

asics of the Win32 API and of C++*. Also, I assume that you are using Microsoft
Visual C++ 6 (7 I don't have, so I can't teach that right now). Also, I do not t
hink I'm "the best" at making plugins, I've only been making them for about 1 1/
2 months now, but I believe the basics are behind me and I think that other peop
le trying should at least have something to go by. Now then, let's understand pr
etty much what Aston plugins really are... Aston plugins are simply Windows DLLs
renamed with a PLG or TBP extension. The PLG extension is for plugins that resi
de on the Desktop, Taskbar, or System tray. The TBP extension is for the left an
d right toolbars. In this tutorial, I will teach about the PLG type plugin, as t
he TBP type is a little different.
*Note: As of now, I am doing only C++ examples and code. Later on I will try my
best to work in a Delphi version.
Let's start with a complete and totally basic plugin that will place itself on
the desktop. Start up Microsoft Visual C++ and start a new project. Select the W
in32 Dynamic-Link Library project. Type in your project name, and make sure the
Win32 checkbox is checked under the Platforms list. Click OK. Select An empty DL
L project. and click Finish. Click OK to the dialog that pops up. What we have t
o do now, is tell the compiler how we want the project to compile. Follow these
steps carefully as if you mess up, chances are it might be something small and y
ou won't know what to do.
1.Click on Project, then select Settings.... Click on the Link tab. Change the O
utput file name to have the PLG extension, i.e. replace "dll" with "plg".
2.Under the Object/library modules section, add to the end: starter.lib
3.Under the Project Options you will need to go to the very end of the text, and
add this to it: /libpath:"../". This is if you're under a folder in the SDK. Th
e libpath should really point to starter.lib.
Now then, we've just created the project workspace to compile correctly for an A
ston plugin. Now we need to add files to it. Click on the New Text File toolbar
button (if you have that toolbar up), otherwise click on File, go to New, select
the Files tab, and select C/C++ Source File. Make sure the Add to Project check
box is checked, and the project it's going to add to is the correct one. If you
want, you can specify a filename, or you can do that later. For now, let's call
it DeskWnd.c.
I'm going to give you a simple, yet complete plugin source. This is a modified
version of the DeskWnd plugin found in the Aston Plugin SDK. I have removed comm
ents as I'm going to explain everything afterwards.
File: DeskWnd.c
#pragma comment(linker,"/DEF:DeskWnd.def")
#pragma comment(linker,"/entry:DllMain")
/* Note for VC7 users:
You should use project options "Module Definition file" and "Entry point",
instead of this "#pragma" directives.
Thanks to Oleg for telling me this. */
#include <windows.h>
#include "../rtlmem.h"
#define NODEFAULTLIB
#include "../astonsdk.h"
#include "../st_api.h"
HMODULE hInstance;

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID


lpReserved)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hInstance = hModule);
}
return TRUE;
}
#define szClassName "DeskWnd"
TAstonData AData;
HWND ChildWindow = 0;
RECT ClientRC;
LONG WINAPI WindowProc(HWND Window, UINT Message, WPARAM WParam, LPARAM
LParam)
{
HDC DC;
PAINTSTRUCT ps;
RECT r;
RECT r2;
UINT uFlags;
switch (Message)
{
case WM_PAINT:
{
DC = BeginPaint(Window, &ps);
GetClientRect(Window, &ClientRC);
GetWindowRect(Window, &r);
SetRect(&r2, 0, 0, GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN));
uFlags = DPEx_CORRECTBRUSH;
if (AData.WallPaperInfo.TileWallpaper != 0)
uFlags = uFlags | DPEx_TILED;
if (AData.WallPaperInfo.WallpaperStyle == 0)
uFlags = uFlags | DPEx_CENTERED;
DrawSkin(DC, DC, &ClientRC, &r, &r2, AData.WallPaperInfo.WallPaper,
AData.WallPaperInfo.DesktopBrush, uFlags);
DrawEdge(DC, &ClientRC, BDR_SUNKENOUTER, BF_RECT);
EndPaint(Window, &ps);
return FALSE;
}
break;
case WM_ERASEBKGND:
{
return TRUE;
}
break;
case WM_ENTERSIZEMOVE:
{
SetWindowLong(Window, GWL_EXSTYLE, GetWindowLong(Window, GWL_EXSTYLE)
& ~WS_EX_TRANSPARENT);
}
break;
case WM_MOVING:
{

InvalidateRect(Window, NULL, FALSE);


}
break;
case WM_EXITSIZEMOVE:
{
SetWindowLong(Window, GWL_EXSTYLE, GetWindowLong(Window, GWL_EXSTYLE)
| WS_EX_TRANSPARENT);
InvalidateRect(Window, NULL, FALSE);
}
break;
case WM_NCHITTEST:
{
DefWindowProc(Window, Message, WParam, LParam);
return HTCAPTION;
}
}
return DefWindowProc(Window, Message, WParam, LParam);
}
void WINAPI InitGlobalModule(const PAstonData AstonData)
{
WNDCLASS WindowClass;
if (AstonData->cbsize < sizeof(TAstonData))
return;
RtlMoveMemory(&AData, AstonData, sizeof(TAstonData));
RtlZeroMemory(&WindowClass, sizeof(WNDCLASS));
WindowClass.style = CS_HREDRAW | CS_VREDRAW;
WindowClass.hCursor = LoadCursor(0, IDC_ARROW);
WindowClass.hInstance = hInstance;
WindowClass.lpfnWndProc = WindowProc;
WindowClass.lpszClassName = szClassName;
if (RegisterClass(&WindowClass) == 0)
return;
ChildWindow = CreateWindowEx(WS_EX_TRANSPARENT, szClassName,
NULL,
WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
0,
0, 64,
48, AData.Desktop,
0,
hInstance,
NULL);
AData.Node->Wnd = ChildWindow;
GetClientRect(ChildWindow, &ClientRC);
}
void WINAPI DoneModule(void)
{
if (ChildWindow != 0)
{
DestroyWindow(ChildWindow);
ChildWindow = 0;
}
UnregisterClass(szClassName, hInstance);
}

File: DeskWnd.def
LIBRARY DeskWnd.plg
EXPORTS
InitGlobalModule=_InitGlobalModule@4
DoneModule=_DoneModule@0

Now, if you want to be brave, you can try compiling the plugin now. You will no
tice that everything will go perfect as long as everything is set correctly in t
he compiler, which is a given if you think about it! :)

Part Two - The Explanation of Everything (well not everything)


What shall we start with, eh? The short DEF file or the long C file... Let's go
with the long C file. ;)
Starting at the top:
#pragma
comment(linker,"/DEF:DeskWnd.def")
#pragma comment(linker,"/entry:DllMain")

These two lines are simple. The pragma preprocessor directive tells the compile
r before it starts to compile to do... well whatever you tell it to. These two l
ines add two sections to the linker. The first tells the linker to use a DEF fil
e, which just gives some extra information on how to do things. The second tells
the linker where to find the entry point, or rather what function you tell is t
he entry point. My suggestion is to keep these there. I haven't tried to compile
and build without them, so I don't know what would happen if you did not. An id
ea I have if you leave out the DEF file, that Aston will not be able to properly
read your plugin. As for the other, I think that the plugin itself would not in
itialize correctly, but I may be mistaken.
Next section:
#include <windows.h>
#include "../rtlmem.h"
#define NODEFAULTLIB
#include "../astonsdk.h"
#include "../st_api.h"

These are the basic includes you will have for all plugins that you make. The f
irst line is the Windows header file, which will include all information on func
tion and constants and all that good stuff that makes your DLL about to work wit
h Windows. The next include, the rtlmem.h file is just redefinitions of Windows
functions. RtlZeroMemory, RtlFillMemory, and RtlMoveMemory are the only function
s redefined. The next line is a define preprocessor directive. I will explain th
is after the next include. The next include is astonsdk.h. This is all the struc
tures and messages and other goodies that make your plugin work with Aston. Now
for the NODEFAULTLIB line. NODEFAULTLIB combined with the /entry: option, will r
educe the size of your plugin, but will prohibit the use of libc.lib and CRT fun
ctions. Remove or comment both #pragma comment(linker,"/entry:DllMain") and #def
ine NODEFAULTLIB if you need CRT functions. In the astonsdk.h file, there is a s
ection that checks to see if there is a define named NODEFAULTLIB. If it is defi
ned, it will include another pragma statement, which is just this: #pragma comme
nt(linker,"/nodefaultlib"). Don't really worry about this much, if you really wa
nt to know more about it, either look it up at MSDN, or ask someone else. :P The
next header, st_api.h, is just a bunch of function prototypes that enabled you
to access things that Aston let's you.
DllMain, the beginning of your plugin:
HMODULE hInstance;
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID
lpReserved)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hInstance = hModule);
}
return TRUE;
}

First off, hInstance is a global variable that will allow you to access it from
wherever easily. It's an important variable, and besides, it's needed to create
your plugin. :) I don't think I should really explain this. Just leave it how i
t is, and all will be fine.
Variables:
#define szClassName "DeskWnd"
TAstonData AData;
HWND ChildWindow = 0;
RECT ClientRC;

These are just the basic variables you'll have in a plugin. szClassName is the
classname that will do many things. It will be the classname for your program's
window. Aston keeps track of information for it via the classname. Plugin settin
gs are usually kept under the classname heading. AData is a structure that Aston
passes to the InitGlobalModule function later on. This gives your plugin data a
bout Aston, and the ability to access things within the Aston settings. ChildWin
dow is the handle to the window that you will create for your plugin. ClientRC i

s a structure that keeps the location information of your window. That's all the
re is to it!
The Window Procedure:
LONG WINAPI WindowProc(HWND
Window, UINT Message, WPARAM WParam, LPARAM LParam)
{
HDC DC;
PAINTSTRUCT ps;
RECT r;
RECT r2;
UINT uFlags;
switch (Message)
{
case WM_PAINT:
{
DC = BeginPaint(Window, &ps);
GetClientRect(Window, &ClientRC);
GetWindowRect(Window, &r);
SetRect(&r2, 0, 0, GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN));
uFlags = DPEx_CORRECTBRUSH;
if (AData.WallPaperInfo.TileWallpaper != 0)
uFlags = uFlags | DPEx_TILED;
if (AData.WallPaperInfo.WallpaperStyle == 0)
uFlags = uFlags | DPEx_CENTERED;
DrawSkin(DC, DC, &ClientRC, &r, &r2, AData.WallPaperInfo.WallPaper,
AData.WallPaperInfo.DesktopBrush, uFlags);
DrawEdge(DC, &ClientRC, BDR_SUNKENOUTER, BF_RECT);
EndPaint(Window, &ps);
return FALSE;
}
break;
case WM_ERASEBKGND:
{
return TRUE;
}
break;
case WM_ENTERSIZEMOVE:
{
SetWindowLong(Window, GWL_EXSTYLE, GetWindowLong(Window, GWL_EXSTYLE)
& ~WS_EX_TRANSPARENT);
}
break;
case WM_MOVING:
{
InvalidateRect(Window, NULL, FALSE);
}
break;
case WM_EXITSIZEMOVE:
{
SetWindowLong(Window, GWL_EXSTYLE, GetWindowLong(Window, GWL_EXSTYLE)
| WS_EX_TRANSPARENT);
InvalidateRect(Window, NULL, FALSE);
}
break;
case WM_NCHITTEST:

{
DefWindowProc(Window, Message, WParam, LParam);
return HTCAPTION;
}
}
return DefWindowProc(Window, Message, WParam, LParam);
}

I'm not really going to explain much of this. This is just where all the messag
es that are sent to your plugin are handled. WM_PAINT is probably the most sent
message to your window. It's where you will draw your plugin. You can look up th
e rest of the messages at the MSDN site. Remember I assume that you have a worki
ng knowledge of Win32 programming.
InitGlobalModule, where Aston actually tells your plugin to make itself seen:
void WINAPI
InitGlobalModule(const PAstonData AstonData)
{
WNDCLASS WindowClass;
if (AstonData->cbsize < sizeof(TAstonData))
return;
RtlMoveMemory(&AData, AstonData, sizeof(TAstonData));
RtlZeroMemory(&WindowClass, sizeof(WNDCLASS));
WindowClass.style = CS_HREDRAW | CS_VREDRAW;
WindowClass.hCursor = LoadCursor(0, IDC_ARROW);
WindowClass.hInstance = hInstance;
WindowClass.lpfnWndProc = WindowProc;
WindowClass.lpszClassName = szClassName;
if (RegisterClass(&WindowClass) == 0)
return;
ChildWindow = CreateWindowEx(WS_EX_TRANSPARENT, szClassName,
NULL,
WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
0,
0, 64,
48, AData.Desktop,
0,
hInstance,
NULL);
AData.Node->Wnd = ChildWindow;
GetClientRect(ChildWindow, &ClientRC);
}

You might also like