1. Trang chủ
  2. » Công Nghệ Thông Tin

Excel add in development in c and c phần 9 ppsx

43 322 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Excel Add-in Development in C/C++ phần 9 PPSX
Chuyên ngành Excel Add-in Development
Thể loại Bài giảng
Năm xuất bản 2023
Định dạng
Số trang 43
Dung lượng 373,22 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

enum {TASK_PENDING = 0, TASK_CURRENT = 1, TASK_READY = 2, TASK_UNCLAIMED = 4, TASK_COMPLETE = 8}; typedef struct tag_task { tag_task *prev; // prev task in list, NULL if this is top tag_

Trang 1

324 Excel Add-in Development in C/C++

9.10.5 Organising the task list

The example in this section uses the following simple structure to represent a task Notethat a more sensible approach would be to use a Standard Template Library (STL) con-tainer class The, some would say, old-fashioned linked list used here could easily bereplaced with such a container The intention is not to propose the best way of codingsuch things, but simply to lay out a complete approach that can be modified to suit codingpreferences and experience

enum {TASK_PENDING = 0, TASK_CURRENT = 1, TASK_READY = 2,

TASK_UNCLAIMED = 4, TASK_COMPLETE = 8};

typedef struct tag_task

{

tag_task *prev; // prev task in list, NULL if this is top

tag_task *next; // next task in list, NULL if this is last

long start_clock; // set by TaskList class

long end_clock; // set by TaskList class

bool break_task; // if true, processing of this task should end

short status; // PENDING,CURRENT,READY,UNCLAIMED,COMPLETE

char *caller_name; // dll-internal Excel name of caller

bool (* fn_ptr)(tag_task *); // passed in function ptr

xloper fn_ret_val; // used for intermediate and final value

to be made about whether modified tasks are also moved to the end or left where theyare In the former case, the algorithm for deciding which task is next to be processedsimply goes to the next in the list In the latter case, it would need to start looking atthe top of the list, just in case a task that had already been completed had subsequentlybeen modified

Trang 2

Miscellaneous Topics 325

The decision made here is that modified tasks are moved to the end of the list TheTaskList class, discussed below and listed in full on the CD ROM, contains threepointers, one to the top of the list, m_pHead, one to the bottom of the list, m_pTail,and one to the task currently being executed,m_pCurrent

A more sophisticated queuing approach would in general be better, for example, one

with a pending queue and a done queue, or even a queue for each state The above

approach has been chosen in the interests of simplicity

It is important to analyse how a list of these tasks can be altered and by what thread,background or foreground The pointersm_pHeadand m_pTailwill only be modified

by the foreground thread (Excel) as it adds, moves or deletes tasks Them_pCurrentpointer is modified by the background thread as it completes one task and looks for thenext one Therefore, the foreground thread must be extremely careful when accessing them_pCurrentpointer or assuming it knows what it is, as it can alter from one moment

to the next The foreground can freely read through the list of tasks but must use acritical section when altering a task that is, or could at any moment become, pointed

to bym_pCurrent If it wants to update m_pCurrent’s arguments, then it must firstbreak the task so that it is no longer current If it wants to change the order of tasks inthe list, it must enter a critical section to avoid this being done at the same time that thebackground thread is looking for the next task

By limiting the scope of the background thread to the value ofm_pCurrent, and thetask it points to, the class maintains a fairly simple thread-safe design, only needing touse critical sections in a few places

The strategy assigns a state to a task at each point in its life cycle Identifying thestates, what they mean, and how they change from one to another, is an important part ofmaking any complex multi-threaded strategy work reliably For more complex projectsthan this example, it is advisable to use a formal architectural design standard, such asUML, with integral state-transition diagrams For this example, the simple table of thestates below is sufficient

Table 9.8 Task states and transitions for a background thread strategy

Pending • The task has been placed on the list and is waiting its turn to be processed.

• The foreground thread can delete pending tasks.

Current • The state is changed from pending to current by the background thread with a

critical section

• The background thread is processing the task

• If the task’s execution is interrupted, its state goes back to pending

Ready • The task has been completed by the background thread which has changed the

state from current to ready

• The task is ready for the foreground loop to retrieve the result

Unclaimed • The foreground thread has seen that the task is either ready or complete and

has marked it as unclaimed pending recalculation of the workbook(s)

• If still unclaimed after a workbook recalculation, the task should be deleted

(continued overleaf )

Trang 3

326 Excel Add-in Development in C/C++

Table 9.8 (continued )

Complete • The recalculation of the worksheet cell (that originally scheduled the task)

changes the state from unclaimed to complete

• The task has been processed and the originating cell has been given the final value

• A change of inputs will change the status back to pending

The unclaimed state ensures that the foreground thread can clean up any orphaned tasks:those whose originating cells have been deleted, overwritten, or were in worksheets thatare now closed The distinction between ready and unclaimed ensures that tasks completed

immediately after a worksheet recalculation don’t get mistakenly cleaned up as unclaimed

before their calling cell has had a chance to retrieve the value

9.10.6 Creating, deleting, suspending, resuming the thread

In this example, where management of the thread is embedded in a class, the most obviousplace to start and finally stop the thread might seem to be the constructor and destructor

It is preferable, in fact, to have more control than this and start the thread with an explicitcall to a class member function, ideally from xlAutoOpen Similarly, it is better todelete the thread in the same way fromxlAutoClose

Threads under Windows can be created in a suspended state This gives you two choicesabout how you run your thread: firstly, you can create it in a suspended state and bring it

to life later, perhaps only when it has some work to do Secondly, you can create it in anactive state and have the main function that the thread executes loop and sleep until there

is something for it to do Again for simplicity, the second approach has been adopted inthis example

Similarly, when it comes to suspending and resuming threads, there are two Windowscalls that will do this Or you can set some flag in foreground that tells your backgroundloop not to do anything until you reset the flag The latter approach is simpler and easier todebug, and, more importantly, it also allows the background thread to clean up its currenttask before becoming inactive For these reasons, this is the approach chosen here

9.10.7 The task processing loop

Most of the code involved in making this strategy work is not listed in this book (It isincluded on the CD ROM in the source filesBackground.cppand Background.hwhich also call on other code in the example project.) Nevertheless, it is helpful to discussthe logic in this code behind the main function that the thread executes (When creatingthe thread, the wrapper functionbackground thread main()is passed as an argu-ment together with a pointer to the instance of theTaskList class that is creating thethread.) The loop references three flags, all private class data members, that are used tosignal between the fore- and background threads These are:

• m ThreadExitFlagSet: Signals that the thread should exit the loop and return,thereby terminating the thread This is set by the foreground thread in theDeleteTaskThread()member function of the TaskListclass

Trang 4

Miscellaneous Topics 327

• m SuspendAllFlagSet: Signals that the background thread is to stop (suspend)processing tasks after the next task has been completed This is set by the fore-ground thread in the SuspendTaskThread() member function of the TaskListclass

• m ThreadIsRunning: This flag tells both the background and foreground threadswhether tasks are being processed or not It is cleared by the background thread inresponse to m SuspendAllFlagSet being set This gives the foreground thread

a way of confirming that the background thread is no longer processing tasks It isset by the foreground thread in theResumeTaskThread()member function of theTaskListclass

// This is the function that is passed to Windows when creating

// Find next task to be executed Sets m_pCurrent to

// point to the next task, or to NULL if no more to do.

Trang 5

328 Excel Add-in Development in C/C++

9.10.8 The task interface and main functions

In this example, the only constraint on the interface function is that it is registered asvolatile It is also helpful to register it as a macro-sheet equivalent function which onlytakesoperarguments Its responsibilities are:

1 To validate arguments and place them into an array ofxlopers

imple-// LongTaskExampleMain() executes the task and does the work.

// It is only ever called from the background thread It is

// required to check the break_task flag regularly to see if the

// foreground thread needs execution to stop It is not required

// that the task populates the return value, fn_ret_val, as it does

// in this case It could just wait till the final result is known.

bool long_task_example_main(tag_task *pTask)

Trang 6

// LongTaskExampleInterface() is a worksheet function called

// directly by Excel from the foreground thread It is only

// required to check arguments and call ExampleTaskList.UpdateTask()

// which returns either an error, or the intermediate or final value

// of the calculation UpdateTask() errors can be returned directly

// or, as in this case, the function can return the current

// (previous) value of the calling cell This function is registered

// with Excel as a volatile macro sheet function.

xloper * stdcall LongTaskExampleInterface(xloper *arg)

xloper arg_array[1]; // only 1 argument in this case

static xloper ret_val;

// UpdateTask makes deep copies of all the supplied arguments

// so passing in an array of shallow copies is safe.

arg_array[0] = *arg;

// As there is only one argument in this case, we could instead

// simply pass a pointer to this instead of creating the array

Trang 7

330 Excel Add-in Development in C/C++

}

ret_val.xltype |= xlbitDLLFree; // memory to be freed by the DLL

return &ret_val;

}

9.10.9 The polling command

The polling command only has the following two responsibilities:

• Detect when a recalculation is necessary in order to update the values of volatile longtask functions (In the example code below the recalculation is done on every call intothe polling function.)

• Reschedule itself to be called again in a number of seconds determined by a configurableTaskListclass data member

int stdcall long_task_polling_cmd(void)

{

if(ExampleTaskList.m_BreakPollingCmdFlag)

return 0; // return without rescheduling next call

// Run through the list of tasks setting TASK_READY tasks to

// TASK_UNCLAIMED Tasks still unclaimed after recalculation are

// assumed to be orphaned and deleted by DeleteUnclaimedTasks().

bool need_racalc = ExampleTaskList.SetDoneTasks();

// if(need_racalc) // Commented out in this example

{

// Cause Excel to recalculate This forces all volatile fns to be

// re-evaluated, including the long task functions, which will then

// return the most up-to-date values This also causes status of

// tasks to be changed to TASK_COMPLETE from TASK_UNCLAIMED.

// Use command name as given to Excel in xlfRegister 4th arg

cpp_xloper CmdName("LongTaskPoll"); // as registered with Excel

Trang 8

Miscellaneous Topics 331

9.10.10 Configuring and controlling the background thread

TheTaskList::CreateTaskThread()member function creates a thread that is active

as far as the OS is concerned, but inactive as far as the handling of background worksheetcalculations is concerned The user, therefore, needs a way to activate and deactivate thethread and the polling command

As stressed previously, the C API is far from being an ideal way to create dialogsthrough which the user can interact with your application In this case, however, it is veryconvenient to place a dialog within the same body of code as the long task functions Youcan avoid using C API dialogs completely by exporting a number of accessor functionsand calling them from a VBA dialog

The example project source file, Background.cpp, contains a command functionlong task config cmd(), that displays the following C API dialog that enables the

user to control the thread and see some very simple statistics (See section 8.13 Working

with custom dialog boxes on page 273.)

Figure 9.1 Long task thread configuration dialog

This dialog needs to be accessed from either a toolbar or menu The same source filealso contains a command function long task menu setup()that, when called forthe first time, sets up a menu item on theToolsmenu (A second call removes this menuitem.) (The spreadsheet used to design and generate the dialog definition table for thisdialog,XLM ThreadCfg Dialog.xls, is included on the CD ROM.)

9.10.11 Other possible background thread applications and strategies

The strategy and example outlined above lends itself well to certain types of lengthybackground calculations There are other reasons for wanting to run tasks in background,most importantly for communicating with remote applications and servers Examples

of this are beyond the scope of this book, but can be implemented fairly easily as anextension to the above One key difference in setting up a strategy for communication

between worksheet cells and a server is the need to include a sent/waiting task state that

enables the background thread to move on and send the next task without having to waitfor the server to respond to the last The other key difference is that the backgroundthread, or even an additional thread, must do the job of checking for communication backfrom the server

Trang 9

332 Excel Add-in Development in C/C++

This section is, of course, about how not to crash Excel Old versions of Excel were not

without their problems, some of which were serious enough to cause occasional crashesthrough no fault of the user This has caused some to view Excel as an unsafe choicefor a front-end application This is unfair when considering modern versions Excel, iftreated with understanding, can be as robust as any complex system Third-party add-insand users’ own macros are usually the most likely cause of instability This brief sectionaims to expose some of the more common ways that these instabilities arise, so that theycan be avoided more easily

There are a few ways to guarantee a crash in Excel One is to call the C API whenExcel is not expecting it: from a thread created by a DLL or from a call-back functioninvoked by Windows Another is to mismanage memory Most of the following examplesinvolve memory abuse of one kind or another

If Excel allocated some memory, Excel must free it If the DLL allocated some memory,the DLL must free it Using one to free the other’s memory will cause a heap error Over-running the bounds of memory that Excel has set aside for modify-in-place arguments toDLL functions is an equally effective method of bringing Excel to its knees Over-runningthe bounds of DLL-allocated memory is also asking for trouble

Passingxlopertypes with invalid memory pointers toExcel4()will cause a crash.Such types are strings (xltypeStr), external range references (xltypeRef), arrays(xltypeMulti) and string elements within arrays

Memory Excel has allocated in calls to Excel4() or Excel4v() should be freedwith calls to xlFree Leaks resulting from these calls not being made will eventuallyresult in Excel complaining about a lack of system resources Excel may have difficultyredrawing the screen, saving files, or may crash completely

Memory can be easily abused within VBA despite VB’s lack of pointers For example,overwriting memory allocated by VB in a call toString(), will cause heap errors thatmay crash Excel

Great care must be taken where a DLL exposes functions that take data types that are (orcontain) pointers to blocks of memory Two examples of this are strings andxl arrays

(See section 6.2.2 Excel floating-point array structure: xl array on page 107.) The

danger arises when the DLL is either fooled into thinking that more memory has beenallocated than is the case, say, if the passed-in structure was not properly initialised, or

if the DLL is not well behaved in the way it reads or writes to the structure’s memory

In the case of thexl array, whenever Excel itself is passing such an argument, it can

be trusted Where this structure has been created in a VB macro by the user’s own code,care must be taken Such dangers can usually be avoided by only exposing functions that

take safe arguments such asVARIANTorBSTRstrings andSAFEARRAYs

Excel is very vulnerable to stress when it comes close to the limits of its availablememory Creating very large spreadsheets and performing certain operations can crashExcel, or almost as bad, bring it to a virtual grinding halt Even operations such as copy

or delete can have this effect Memory leaks will eventually stress Excel in this way.Calls to C API functions that take array arguments, xlfAddMenufor example, maycrash Excel if the arrays are not properly formed One way to achieve this is to havethe memory allocated for the array to be smaller than required for the specified rowsand columns

Trang 10

Miscellaneous Topics 333

There are some basic coding errors that will render Excel useless, although not sarily crashing it, for example, a loop that might never end because it waits for a conditionthat might never happen From the user’s perspective, Excel will be dead if control hasbeen passed to a DLL that does this

neces-A more subtle version of the previous problem can occur when using a backgroundthread and critical sections Not using critical sections to manage contention for resources

is, in itself, dangerous and inadvisable However, if thread A enters a critical section and

then waits for a state to occur set by thread B, and if thread B is waiting for thread A to

leave the critical section before it can set this state, then both threads effectively freezeeach other Careful design is needed to avoid such deadlocks

Only slightly better than this are DLL functions, especially worksheet functions, thatcan take a very large amount of time to complete Worksheet functions cannot reportprogress to the user It is, therefore, extremely important to have an idea of the worst-case execution time of worksheet functions, say, if they are given an enormous range toprocess If this worst-case time is unacceptable, from the point of view of Excel appearing

to have hung, then you must either check for and limit the size of your inputs or use

a background thread and/or remote process Or your function can check for user breaks(the user pressing Esc in Windows) – see section 8.7.7 on page 206

Care should be taken with some of the C API functions that request information about

or modify Excel objects For example, xlSheetNm must be passed a valid sheet IDotherwise Excel will crash or become unstable

Trang 12

10 Example Add-ins and Financial Applications

Developers are always faced with the need to balance freedoms and constraints whendeciding the best way to implement a model Arguably the most important skill a devel-oper can have is that of being able to choose the most appropriate approach all thingsconsidered: Failure can result in code that is cumbersome, or slow, or difficult to maintain

or extend, or bug-ridden, or that fails completely to meet a completion time target.This chapter aims to do two things:

1 Present a few simple worksheet function examples that demonstrate some of the basicconsiderations, such as argument and return types For these examples source code isincluded on the CD ROM in the example project Sections 10.1 to 10.5 cover thesefunctions

2 Discuss the development choices available and constraints for a number of cial markets applications These applications are not fully worked through in the book,and source code is not provided on the CD ROM Sections 10.6 and beyond coverthese functions and applications

finan-Some of the simple example functions could easily be coded in VB or duplicated withperhaps only a small number of worksheet cells The point is not to say that these thingscan only be done in C/C++ or using the C API If you have decided that you want orneed to use C/C++, these examples aim to provide a template or guide

The most important thing that an add-in developer must get right is the function interface.The choices made as to the types of arguments a function takes, are they required or optional;

if optional what the default behaviour is; and so on, are often critical Much of the discussion

in this chapter is on this and similar issues, rather than on one algorithm versus another.The discussion of which algorithm to use, etc., is left to other texts and to the reader whoseown experience may very well be more informed and advanced than the author’s

Important note: You should not rely on any of these examples, or the methods they

contain, in your own applications without having completely satisfied yourself thatthey are correct and appropriate for your needs They are intended only to illustratehow techniques discussed in earlier chapters can be applied

Excel has a number of very efficient basic string functions, but string operations canquickly become unnecessarily complex when just using these Consider, for example, thecase where you want to substitute commas for stops (periods) dynamically This is easilydone using Excel’s SUBSTITUTE() However, if you want to simultaneously substitutecommas for stops and stops for commas things are more complex (You could do this inthree applications ofSUBSTITUTE(), but this is messy.) Writing a function in C that doesthis is straightforward (seereplace_mask()below)

Trang 13

336 Excel Add-in Development in C/C++

The C and C++ libraries both contain a number of low-level string functions that caneasily be given Excel worksheet wrappers or declared and used from VBA (The latter

is a good place to start when optimising VB code.) This section presents a number ofexample functions, some of which are just wrappers of standard library functions andsome of which are not The code for all of these functions is listed in the Example project

on the CD ROM in the source fileXllStrings.cpp When registered with Excel, theyare added to the Text category

Function

name

count_char(exported)CountChar(registered with Excel)Description Counts the number of occurrences of a given character

Prototype short stdcall count_char(char *text, short ch);Type string "ICI"

Notes Safe to return a short as Excel will only pass a 255-max character

string to the function Function does not need to be volatile anddoes not access any C API functions that might require it to beregistered as a macro sheet equivalent function

short stdcall count_char(char *text, short ch)

corresponding characters from a replacement string, or removes all

such occurrences if no replacement string is provided

Prototype void stdcall replace_mask(char *text, char

*old_chars, xloper *op_new_chars);

Type string "1CCP"

Trang 14

Example Add-ins and Financial Applications 337

Notes Declared as returning void Return value is the 1st argument

modified in place Third argument is optional and passed as anoper(see page 119) to avoid the need to dereference a range reference

void stdcall replace_mask(char *text, char *old_chars, xloper

// Remove all occurrences of all characters in old_chars

for(; *text; text++)

*p = p[1];

} while (*(++p));

Trang 15

338 Excel Add-in Development in C/C++

Function name reverse_text(exported)

Reverse(registered with Excel)Description Reverses a string

Prototype void stdcall reverse_text(char *text);

Type string "1F"

Notes Declared as returning void Return value is the 1st argument

modified in place This function is simply a wrapper for the Clibrary functionstrrev() This function is useful in thecreation of Halton quasi-random number sequences, for example

void stdcall reverse_text(char *text)

search string, or zero if none found

Prototype short stdcall first_inclusive(char *text, char

*search_text);

Type string "ICC"

Notes Any error in input is reflected with a zero return value, rather than

an error type This function is simply a wrapper for the C libraryfunctionstrpbrk()

short stdcall find_first(char *text, char *search_text)

Trang 16

Example Add-ins and Financial Applications 339

Function

name

find_first_excluded(exported)

FindFirstExcl(registered with Excel)

Description Returns the position of the first occurrence of any character that is not

in a search string, or zero if no such character is found

Prototype short stdcall find_first_excluded(char *text,

char * search_text);

Type string "ICC"

Notes Any error in input is reflected with a zero return value, rather than an

zero if not found

Prototype short stdcall find_last(char *text, short ch);Type string "ICI"

Notes Any error in input is reflected with a zero return value, rather than

an error type This function is simply a wrapper for the C libraryfunction strrchr()

short stdcall find_last(char *text, short ch)

{

if(!text || ch <= 0 || ch > 255)

return 0;

Trang 17

340 Excel Add-in Development in C/C++

B (return 1), case sensitive (by default) or not

Prototype xloper * stdcall compare_text(char *Atext,

char *Btext, xloper *op_is_case_sensitive);Type string "RCCP"

Notes Any error in input is reflected with an Excel#VALUE! error Return

type does not need to allow for referencexlopers Excel’scomparison operators<, > and = are not case-sensitive and Excel’s

EXACT()function only performs a case-sensitive check for equality.This function is a wrapper for the C library functionsstrcmp()andstricmp()

xloper * stdcall compare_text(char *Atext, char *Btext,

xloper *op_is_case_sensitive) {

static xloper ret_oper = {0, xltypeNum};

(return 0), A< B (return −1), A > B (return 1), case sensitive (by

default) or not

Trang 18

Example Add-ins and Financial Applications 341

Prototype xloper * stdcall compare_nchars(char *Atext,

char *Btext, short n_chars, xloper

*op_is_case_sensitive);

Type string "RCCIP"

Notes Any error in input is reflected with an Excel#VALUE!error Return

type does not need to allow for referencexlopers This function is

a wrapper for the C library functionsstrncmp()andstrincmp()

xloper * stdcall compare_nchars(char *Atext, char *Btext,

short n_chars, xloper *op_is_case_sensitive) {

static xloper ret_oper = {0, xltypeNum};

if(!Atext || !Btext || n_chars <= 0 || n_chars > 255)

Concat(registered with Excel)

Description Concatenate the contents of the given range (row-by-row) using the

given separator (or comma by default) Returned string length limit is

255 characters by default, but can be set lower Caller can specify thenumber of decimal places to use when converting numbers

Prototype xloper * stdcall concat(xloper *inputs, xloper

*p_delim, xloper *p_max_len, xloper *p_num_decs);Type string "RPPPP"

xloper * stdcall concat(xloper *inputs, xloper *p_delim,

Trang 19

342 Excel Add-in Development in C/C++

if(num_decs >= 0 && num_decs < 16

&& Inputs.GetArrayElementType(i) == xltypeNum)

{

xloper *p_op = Inputs.GetArrayElement(i);

Excel4(xlfRound, p_op, 2, p_op, &Rounding);

Function name parse(exported)

ParseText(registered with Excel)

Trang 20

Example Add-ins and Financial Applications 343

Description Parse the input string using the given separator (or comma by

default) and return an array Caller can request conversion of allfields to numbers, or zero if no conversion possible Caller canspecify a value to be assigned to empty fields (zero by default).Prototype xloper * stdcall parse(char *input, xloper

*p_delim, xloper *p_numeric, xloper *p_empty);Type string "RCPP"

Notes Registered name avoids conflict with the XLMPARSE()function

xloper * stdcall parse(char *input, xloper *p_delim,

xloper *p_numeric, xloper *p_empty) {

// Can' t use strtok as it ignores empty fields

char *p_last = input;

Trang 21

344 Excel Add-in Development in C/C++

in Excel 2002.) This can lead to accumulated errors in some cases or complete failure.The functionNORMSDIST(X)is accurate to about±7.3 × 10−8and appears to be based onthe approximation given in Abramowitz and Stegun (1970), section 26.2.17, except thatforX> 6 it returns 1 andX< −8.3 it returns zero.2

There is no Excel function that returns a random sample from the normal distribution.The compoundNORMSINV(RAND())will provide this, but is volatile and therefore may not

be desirable in all cases In addition to its volatility, it is not the most efficient way tocalculate such samples

1 See Jackson and Staunton (2001) for numerous examples of applications of these functions to finance.

2 Inaccuracies in these functions could cause problems when, say, evaluating probability distribution functions from certain models.

Ngày đăng: 09/08/2014, 16:20

TỪ KHÓA LIÊN QUAN