|
ANY-maze Help > The ANY-maze reference > ANY-maze plug-ins > The ANY-maze demo plug-in > The DemoPlugIn.cpp source code file The DemoPlugIn.cpp source code file
The DemoPlugIn.cpp file contains the source code of the demo ANY-maze plug-in. Although this is a C++ file, the code is essentially just C. The listing
/*---------------------------------------------------------------------------------
Name : DemoPlugIn
Description : Example of an ANY-maze plug in (also know as an analysis extension DLL). This plug-in implements the following plug-ins:
Animal in apparatus centre -------------------------- This returns an "Animal in centre" ON/OFF result to ANY-maze whenever the animal's centre point is within the centre of the apparatus. The definition of "in the centre of the apparatus" is that the animal is within a certain distance of the point at the centre of a rectangle that exactly encloses the apparatus. This distance defaults to 10cm, but can be edited by the user via the plug-in's settings dialog.
Record animal position ---------------------- This plug in dumps the animal's centre x,y coordinates with a time value, to a csv file named for the animal and trial being performed. Thus this plug-in does not return any result. Nor does this plug-in does not a have a settings dialog.
History ------- 7/2/2008 CPL Created first version of this example code
-----------------------------------------------------------------------------------*/
#include #include #include #include "plugin.h" #include "resource.h"
//GUIDs for our analysis. These can be created using guidgen.exe static const GUID GUID_INCENTREANALYSIS = {0xf5c18c7b,0x5a39,0x47f8,{0x99,0x2b,0x13,0x37,0xfe,0x0f,0x90,0xd5}}; static const GUID GUID_POSNEXPORT = {0xbf44eea9,0x4a72,0x4b1d,{0xb4,0xaf,0x67,0xdd,0x80,0x5a,0x16,0xd2}};
//Type used to hold settings for in centre analysis typedef struct { bool Initialised; int Distance; }INCENTREANALYSISSETTINGS, *LPINCENTREANALYSISSETTINGS;
//Globals static HMODULE hInstance;
/*----------------------------------------------------------------------------------
Name CentreWindow
Desc Centres a top level window on the screen. This routine can't centre child windows
Params hWnd Handle of the window to centre
Return None
-----------------------------------------------------------------------------------*/
void CentreWindow (HWND hWnd) { //Vars RECT Rect, MaxSize; int x, y;
//BEGIN //Get the window's rectangle GetWindowRect (hWnd, &Rect);
//Get screen area not obscured by the task bar SystemParametersInfo (SPI_GETWORKAREA, 0, &MaxSize, 0);
//Adjust the window rectangle to the centre of the screen x = MaxSize.left + (((MaxSize.right-MaxSize.left)-(Rect.right-Rect.left))/2); y = MaxSize.top + (((MaxSize.bottom-MaxSize.top)-(Rect.bottom-Rect.top))/2);
//Centre the window SetWindowPos (hWnd, NULL, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE); }
/******************************* Posn export routines *******************************
Name PosnExport
Desc Part of the example analysis: This routine exports the posn of the animal's centre point to a CSV file
Params Mesg The type of message ANY-maze is sending to the plug in. This code handles the "Test" messages: AME_TESTSTART, AME_POSN, AME_TESTEND and AME_TESTABORT wParam wParam sent with the message - depends on the message lParam lParam sent with the message - depends on the message
Return And AME_xxx return code
-----------------------------------------------------------------------------------*/
int PosnExport (DWORD Mesg, WPARAM wParam, LPARAM lParam) { //Types typedef struct { char FileName[MAX_PATH]; HANDLE hFile; }POSNEXPORTRECORD, *LPPOSNEXPORTRECORD;
//Vars LPAME_TESTDATA AME_TestData; LPAME_POSNDATA AME_PosnData; LPPOSNEXPORTRECORD PosnExportRec; char str[32]; DWORD BytesWritten;
//BEGIN //Action depends on the message switch (Mesg) { case AME_TESTSTART : //The lParam points at a AME_TESTDATA record and the wParam is the //size of the record. Check the size assert (wParam >= sizeof(AME_TESTDATA)); if (wParam < sizeof(AME_TESTDATA)) return AME_ERROR;
//Access the AME_TESTDATA record AME_TestData = (LPAME_TESTDATA)lParam;
//Allocate a PosnExportRecord - if this fails return Context set to NULL. //We'll use this in subsequent calls to determine that we can't export PosnExportRec = new POSNEXPORTRECORD; if (PosnExportRec == NULL) { AME_TestData->Context = NULL; return AME_ERROR; }
//Build the file name for the file we'll export to wsprintf (PosnExportRec->FileName, "c:\\Animal %u Trial %u.csv", AME_TestData->AnimalNum, AME_TestData->TrialNum);
//Create the file, replacing any file that already exists PosnExportRec->hFile = CreateFile (PosnExportRec->FileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
//If failed junk the PosnExportRec and return Context set to NULL if (PosnExportRec->hFile == INVALID_HANDLE_VALUE) { delete PosnExportRec; AME_TestData->Context = NULL; return AME_ERROR; }
//Success. Set Context to the address of the PosnExportRec AME_TestData->Context = (LPVOID)PosnExportRec; return AME_OK;
case AME_POSN : //The lParam points at a AME_POSNDATA record and the wParam is the //size of the record. Check the size assert (wParam >= sizeof(AME_POSNDATA)); if (wParam < sizeof(AME_POSNDATA)) return AME_ERROR;
//Access the AME_POSNDATA record AME_PosnData = (LPAME_POSNDATA)lParam;
//Access the PosnExportRec if (AME_PosnData->Context == NULL) return AME_ERROR; PosnExportRec = (LPPOSNEXPORTRECORD)AME_PosnData->Context;
//Build an entry that describes this posn. This is of the form "time,x,y" wsprintf (str, "%u,%d,%d\r\n", AME_PosnData->Time_xy, AME_PosnData->x, AME_PosnData->y);
//Write the entry to the file WriteFile (PosnExportRec->hFile, str, (DWORD)strlen(str), &BytesWritten, NULL);
//We don't return any result AME_PosnData->ResultType = RT_NONE;
//Done return AME_OK;
case AME_TESTEND : case AME_TESTABORT : //The lParam is our context value if (lParam == NULL) return AME_ERROR; PosnExportRec = (LPPOSNEXPORTRECORD)lParam;
//Close the file CloseHandle (PosnExportRec->hFile);
//If Type is Abort then delete the file if (Mesg == AME_TESTABORT) DeleteFile (PosnExportRec->FileName);
//Delete the PosnExportRec delete PosnExportRec; return AME_OK;
default : //What's this assert (false); return AME_NOTIMPLEMENTED;
} }
/*************************** In centre analysis routines **************************
Name InCentreAnalysisSettingsDlgProc
Desc Dialog proc for the in centre analysis settings dialog - this allows the user to set the distance from the centre that the analysis uses
Params hDlg Std Message Std wParam Std lParam Std
Return Std
-----------------------------------------------------------------------------------*/
BOOL CALLBACK InCentreAnalysisSettingsDlgProc (HWND hDlg, UINT Mesg, WPARAM wParam, LPARAM lParam) { //Vars static LPINCENTREANALYSISSETTINGS Settings;
//BEGIN //Action depends on the mesg switch (Mesg) { case WM_INITDIALOG : //lParam points at the settings we're to edit. Note the pointer Settings = (LPINCENTREANALYSISSETTINGS)lParam;
//Centre the dialog CentreWindow (hDlg);
//Check for uninitialised settings (in which case all fields are zero) and //set them to some sensible defaults if (!Settings->Initialised) { Settings->Distance = 10; Settings->Initialised = true; }
//Set the edit control to the current distance and limit the control to //3 characters, i.e. 999cm SetDlgItemInt (hDlg, IDC_DISTANCE, Settings->Distance, false); SendDlgItemMessage (hDlg, IDC_DISTANCE, EM_LIMITTEXT, 3, 0); break;
case WM_COMMAND : //Action depends on the control switch (LOWORD(wParam)) { case IDOK : //Save the user's entry in the Settings Settings->Distance = max (1, GetDlgItemInt (hDlg, IDC_DISTANCE, NULL, false));
//Close the dialog, returning IDOK to signal that the settings need to be //saved EndDialog (hDlg, IDOK); break;
case IDCANCEL : //Close the dialog returning IDCANCEL to signal that the settings need not be //saved EndDialog (hDlg, IDCANCEL); break; } break;
default: //We didn't process the message return 0; } //We did process the message return 1; }
/*-----------------------------------------------------------------------------------
Name EditInCentreAnalysisSettings
Desc Displays a dialog box where the user can set the distance from the centre for the analysis
Params Settings Pointer to the InCentreAnalysis settings record that the user will be editing
Return True if the user edited the settings and they should now be saved
-----------------------------------------------------------------------------------*/
bool EditInCentreAnalysisSettings (LPINCENTREANALYSISSETTINGS Settings) { //BEGIN //Display the in centre analysis settings dialog, passing the settings //pointer as lParam. The dialog will edit the settings in place return (DialogBoxParam (hInstance, MAKEINTRESOURCE(IDD_INCENTREANALYSISSETTINGS), NULL, InCentreAnalysisSettingsDlgProc, (LPARAM)Settings) == IDOK); }
/*----------------------------------------------------------------------------------
Name InCentreAnalysis
Desc Part of the example analysis: This routine determines when the animal is in the centre of the apparatus. "Centre" is defined as within a certain distance of the centre which the user can set using the analysis settings (the default is 10cm)
Params Mesg The type of message ANY-maze is sending to the plug in. This code handles the "Test" messages: AME_TESTSTART, AME_POSN, AME_TESTEND and AME_TESTABORT wParam wParam sent with the message - depends on the message lParam lParam sent with the message - depends on the message
Return And AME_xxx return code
-----------------------------------------------------------------------------------*/
int InCentreAnalysis (DWORD Mesg, WPARAM wParam, LPARAM lParam) { //Types typedef struct { double Distance; int AppCentreX; int AppCentreY; bool InCentre; }INCENTREANALYSISRECORD, *LPINCENTREANALYSISRECORD;
//Vars LPAME_TESTDATA AME_TestData; LPAME_POSNDATA AME_PosnData; LPINCENTREANALYSISRECORD InCentreAnalysisRec; LPINCENTREANALYSISSETTINGS Settings; double d, dx, dy; bool NowInCentre;
//BEGIN //Action depends on the type switch (Mesg) { case AME_TESTSTART : //The lParam points at a AME_TESTDATA record and the wParam is the //size of the record. Check the size assert (wParam >= sizeof(AME_TESTDATA)); if (wParam < sizeof(AME_TESTDATA)) return AME_ERROR;
//Access the AME_TESTDATA record AME_TestData = (LPAME_TESTDATA)lParam;
//Allocate a InCentreAnalysisRecord - if this fails return AME_ERROR InCentreAnalysisRec = new INCENTREANALYSISRECORD; if (InCentreAnalysisRec == NULL) return AME_ERROR;
//Calculate and note the centre point of the apparatus InCentreAnalysisRec->AppCentreX = (AME_TestData->ApparatusRect.left + AME_TestData->ApparatusRect.right) / 2; InCentreAnalysisRec->AppCentreY = (AME_TestData->ApparatusRect.top + AME_TestData->ApparatusRect.bottom) / 2;
//The passed AME_TestData record includes a pointer to the settings //for this analysis. The settings define the distance from the centre //that we consider to be the "centre" of the apparatus. Convert this //distance to pixels and note it in the InCentreAnalysisRec. If the //distance is zero then the user has not specified a distance so //use a default of 10cm Settings = (LPINCENTREANALYSISSETTINGS)AME_TestData->Settings; if (!Settings->Initialised) Settings->Distance = 10; d = (double)Settings->Distance;
//Convert from cm to mm d = d * 10;
//Convert from mm to pixels InCentreAnalysisRec->Distance = d * AME_TestData->Scaling;
//Start by assuming the animal is not in the centre of the apparatus InCentreAnalysisRec->InCentre = false;
//Set Context to the address of the InCentreAnalysisRec AME_TestData->Context = (LPVOID)InCentreAnalysisRec; return AME_OK;
case AME_POSN : //The lParam points at a AME_POSNDATA record and the wParam is the //size of the record. Check the size assert (wParam >= sizeof(AME_POSNDATA)); if (wParam < sizeof(AME_POSNDATA)) return AME_ERROR;
//Access the AME_POSNDATA record AME_PosnData = (LPAME_POSNDATA)lParam;
//Access the InCentreAnalysisRec if (AME_PosnData->Context == NULL) return false; InCentreAnalysisRec = (LPINCENTREANALYSISRECORD)AME_PosnData->Context;
//Calculate the distance from the animal's posn to the apparatus centre dx = AME_PosnData->x - InCentreAnalysisRec->AppCentreX; dy = AME_PosnData->y - InCentreAnalysisRec->AppCentreY; d = sqrt((dx * dx) + (dy * dy));
//Is the animal within the required distance of the apparatus centre NowInCentre = (d < InCentreAnalysisRec->Distance);
//Has the animal's "InCentre" state changed if (NowInCentre != InCentreAnalysisRec->InCentre) { //Yes, so return a suitable result AME_PosnData->ResultType = RT_ONOFF; AME_PosnData->ResultValue = NowInCentre ? 1 : 0;
//Update the in centre state InCentreAnalysisRec->InCentre = NowInCentre; } else { //We're not returning a result AME_PosnData->ResultType = RT_NONE; }
//Processed successfully return AME_OK;
case AME_TESTEND : case AME_TESTABORT : //The lParam is our context value if (lParam == NULL) return false; InCentreAnalysisRec = (LPINCENTREANALYSISRECORD)lParam;
//Delete the InCentreAnalysisRec delete InCentreAnalysisRec;
//Done return AME_OK;
default : //What's this? assert (false); return AME_NOTIMPLEMENTED; } }
/*----------------------------------------------------------------------------------
Name ANYmazeExtension_Main
Desc When starting up ANY-maze will search the same directory as the executable file for any DLLs. For each one it finds it will call GetProcAddress for a routine with the name "ANYmazeExtension_Main". DLLs which export this routine will be considered to be ANY-maze plug-ins.
This routine is the interface between ANY-maze and the plug-in. ANY-maze will call this routine passing it a Mesg and a w- and l- param (much like windows message processing). This routine should process the obligatory messages and can implement as many of the options messages as it wishes. For details of each message and its parameters refer to the ANY-maze help.
Params guid Pointer to a GUID that identifies the analysis to which the message is addressed. When the message is AME_ENUMERATE this param will hold the guid if the analysis returned by the prior call, or it will be GUID_NULL when the enumeration is starting. Mesg One of the AME_xxx message constants wParam Depends on the message, but typically used for integer parameters. lParam Depends on the message, but typically a pointer to a buffer
Return One of the AME_xxx return constants. For any messages not implmented for a certain analysis the return should be AME_NOTIMPLEMENTED
-----------------------------------------------------------------------------------*/
extern "C" __declspec(dllexport) int ANYmazeExtension_Main (LPGUID guid, DWORD Mesg, WPARAM wParam, LPARAM lParam) { //Vars int RscID;
//BEGIN //Action depends on the Mesg switch (Mesg) { case AME_ENUMERATE : //Simply enumerate the GUIDs of the analysis we can perform, returning the //next GUID in the buffer pointed at by lParam. When enumeration starts we //are passed a null GUID if (*guid == GUID_NULL) *((LPGUID)lParam) = GUID_INCENTREANALYSIS; else if (*guid == GUID_INCENTREANALYSIS) *((LPGUID)lParam) = GUID_POSNEXPORT; else return AME_NOTIMPLEMENTED; return AME_OK;
case AME_GETNAME : case AME_GETHELPTITLE : case AME_GETHELPINTRO : case AME_GETHELPDETAILS : //These all return strings, which we load from the DLL's resources. The //strings could be hard coded here instead if ((Mesg == AME_GETNAME) && (*guid == GUID_INCENTREANALYSIS)) RscID = IDS_INCENTREANALYSIS_HELP_NAME; else if ((Mesg == AME_GETNAME) && (*guid == GUID_POSNEXPORT)) RscID = IDS_POSNEXPORT_HELP_NAME; else if ((Mesg == AME_GETHELPTITLE) && (*guid == GUID_INCENTREANALYSIS)) RscID = IDS_INCENTREANALYSIS_HELP_TITLE; else if ((Mesg == AME_GETHELPTITLE) && (*guid == GUID_POSNEXPORT)) RscID = IDS_POSNEXPORT_HELP_TITLE; else if ((Mesg == AME_GETHELPINTRO) && (*guid == GUID_INCENTREANALYSIS)) RscID = IDS_INCENTREANALYSIS_HELP_INTRO; else if ((Mesg == AME_GETHELPINTRO) && (*guid == GUID_POSNEXPORT)) RscID = IDS_POSNEXPORT_HELP_INTRO; else if ((Mesg == AME_GETHELPDETAILS) && (*guid == GUID_INCENTREANALYSIS)) RscID = IDS_INCENTREANALYSIS_HELP_DETAILS; else if ((Mesg == AME_GETHELPDETAILS) && (*guid == GUID_POSNEXPORT)) RscID = IDS_POSNEXPORT_HELP_DETAILS; else return AME_ERROR;
//Load the resource return ((LoadString (hInstance, RscID, (LPSTR)lParam, (int)wParam) != 0) ? AME_OK : AME_ERROR);
case AME_GETRESULTTYPE : //Return the result type in the unsigned char pointed to by lParam if (*guid == GUID_INCENTREANALYSIS) *((unsigned char*)lParam) = RT_ONOFF; else if (*guid == GUID_POSNEXPORT) *((unsigned char*)lParam) = RT_NONE; else return AME_ERROR; return AME_OK;
case AME_EDITSETTINGS : //Edit the settings of the analysis whose GUID is passed. wParam is size of a buffer //pointed to by lParam which holds the current settings. If return is false //which the title should be returned if (*guid == GUID_INCENTREANALYSIS) { //Check the passed buffer is no larger than the settings we maintain //for in centre analysis assert (wParam >= sizeof (INCENTREANALYSISSETTINGS)); return (EditInCentreAnalysisSettings ((LPINCENTREANALYSISSETTINGS)lParam) ? AME_OK : AME_CANCELLED); } else if (*guid == GUID_POSNEXPORT) { //Posn export has no settings return AME_NOTIMPLEMENTED; } else { //What's this? return AME_ERROR; } break;
case AME_TESTSTART : case AME_POSN : case AME_TESTEND : case AME_TESTABORT : //Pass to analysis specific routine for processing if (*guid == GUID_INCENTREANALYSIS) return InCentreAnalysis (Mesg, wParam, lParam); else if (*guid == GUID_POSNEXPORT) return PosnExport (Mesg, wParam, lParam); else return AME_ERROR; break;
default : //What's this message? return AME_NOTIMPLEMENTED; } }
/*----------------------------------------------------------------------------------
Name DllMain
Desc Main entry point for the DLL. Just notes the instance handle
Params Std
Return Std
-----------------------------------------------------------------------------------*/
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { //BEGIN switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH : //Note the module handle hInstance = hModule; break; case DLL_THREAD_ATTACH : case DLL_THREAD_DETACH : case DLL_PROCESS_DETACH : //Nothing to do break; }
//Done return TRUE; }
//===================================================================================
© Copyright 2003-2026 Stoelting Co. All rights reserved ANY-maze help topic T1002 |