Nasty BUG! Calling OrderSelect from a library will not leave you with the order selected in the main file!

 
If you have make an OrderSelect call in a library you can not expect the order to be useable when your program control returns back to you main expert file! In my case executing code like this:

 
    bool bHaveSellOrders = SelectFirstSellPosition( _MagicNumber );
 
    while(bHaveSellOrders)
    {
        int iTicket = OrderTicket();
	Print("iTicket:", iTicket);

        double fLotsToCloseThisOrder = OrderLots();
 
        if( !OrderClose(iTicket , OrderLots(), 
                Ask, SLIPPAGE_ON_CLOSE, colorCloseSell) )
        {
            Alert ( "InspectSELLPositions: Unable to Close Order #",
                    iTicket, " Error # ", GetLastError() );
        }
 
        bHaveSellOrders = SelectNextSellPosition( _MagicNumber );
    }
, in the main file of the expert results in an invalid Ticket number (in my case the Ticket number was 4 more than the real ticket regardless of how many orders I have).

The library functions SelectFirst.. and SelectNext are given below, need to put them in a library and add to a header file. When I put these functions into the main expert file it works properly.

// This is in the library
 
// counters for SelectFirst... SelectNext... functions
static int s_iCurrBuyPosition=0;
static int s_iCurrSellPosition=0;
 
//+------------------------------------------------------------------+
// an internal function - the main workhorse function behind these 
// exported function:
//    bool SelectFirstBuyPosition( int iMagicNumber )
//    bool SelectFirstSellPosition( int iMagicNumber )
//    bool SelectNextBuyPosition( int iMagicNumber )
//    bool SelectNextSellPosition( int iMagicNumber )
//
bool privSelectNextBuyOrSellPosition( int iMagicNumber, int iOrderType )
{
    int iFirstPosition;
 
    if( OP_BUY == iOrderType )
        iFirstPosition = s_iCurrBuyPosition;
    else if ( OP_SELL == iOrderType )
        iFirstPosition = s_iCurrSellPosition;
    else
    {
        Alert ( "SelectNextBuyOrSellPosition: Invalid OrderType ",
                iOrderType );
 
        return (false);
    }
 
    iFirstPosition = MathMin(iFirstPosition, OrdersTotal()-1);
 
    for( int iOrderPos = iFirstPosition; iOrderPos>=0; iOrderPos-- )
    {
        if( !OrderSelect( iOrderPos, SELECT_BY_POS ) )
        {
            Alert ( "IsBuyOrSellPosition: Unable to select order at position ",
                    iOrderPos, " Error # ", GetLastError() );
            continue;
        }
        
        if( OrderType() == iOrderType &&
            OrderSymbol() == Symbol() &&
            OrderMagicNumber() == iMagicNumber )
        {
            if( OP_BUY == iOrderType )
            {
                Print("privSelectNextBuyOrSellPosition: Found a BUY order at ", iOrderPos, ", Ticket is ", OrderTicket() );
                s_iCurrBuyPosition = iOrderPos-1;
            }
            else if ( OP_SELL == iOrderType )
            {
                Print("privSelectNextBuyOrSellPosition: Found a SELL order at ", iOrderPos, ", Ticket is ", OrderTicket() );
                s_iCurrSellPosition = iOrderPos-1;
            }
 
            return (true);
        }
    }
 
    return (false);
}
//+------------------------------------------------------------------+
// The following 4 functions need to be exported in the mqh file of the library
//+------------------------------------------------------------------+
bool SelectFirstBuyPosition( int iMagicNumber )
{
    s_iCurrBuyPosition = OrdersTotal()-1;
    return(privSelectNextBuyOrSellPosition( iMagicNumber, OP_BUY ));
}
//+------------------------------------------------------------------+
bool SelectFirstSellPosition( int iMagicNumber )
{
    s_iCurrSellPosition = OrdersTotal()-1;
    return(privSelectNextBuyOrSellPosition( iMagicNumber, OP_SELL ));
}
//+------------------------------------------------------------------+
bool SelectNextBuyPosition( int iMagicNumber )
{
    return(privSelectNextBuyOrSellPosition( iMagicNumber, OP_BUY ));
}
//+------------------------------------------------------------------+
bool SelectNextSellPosition( int iMagicNumber )
{
    return(privSelectNextBuyOrSellPosition( iMagicNumber, OP_SELL ));
}
//+------------------------------------------------------------------+
 
source code is uploaded here: 'More BUGs: Time[0] does not return correct time when testing in tester!', howver in order to overcome this probelm I removed all of the 5 functions listed above from TradingLib and put them into the main faile of the expert (DLobotomy). If you want to reproduce this you'll need to cut and past them back into TradingLib. ...
 
MoveToTopSell(_MagicNumber);
 
while(OrderSelect(SelectNextSellPosition( _MagicNumber ), SELECT_BY_TICKET)
{
}
 
 
 
// this replaces selectfirstsellposition in the library
 
bool MoveToTopSell( int iMagicNumber )
{
    s_iCurrBuyPosition = OrdersTotal()-1;
}

I never came up against this problem, but if it is the case, you can change your function to return ticket number or -1 instead true/false and then just call orderselect again in main file, like this: (sorry the code came out on top, oops).
 
irusoh1, returning the ticket number is a good idea, thank you. It may provide me with a workaround for the problem. I'll give it a try.

However it would be really great if MetaQuotes fixed this, cause the whole idea of libraries is of course to encapsulate and jide finicky, hard-to-maintain code and be able to easily reuse it when needed without having to copy it around...

 
I don't know if MQ have a workaround for that, but you have to remember that libraries are not as much a copy code but more are like self styled DLLs and therefore do not communicate their state implicitly back to the main body.
If you want to have access to variables assuming they are there you need to put whole functions into mqh file which will then just copy code into your program just like it was part of the source.
 
Oh dear, that's a bummer, I have lot's of order selection code in header files, I wonder what has actually been happening...
 
irusoh1:
I don't know if MQ have a workaround for that, but you have to remember that libraries are not as much a copy code but more are like self styled DLLs and therefore do not communicate their state implicitly back to the main body.
If you want to have access to variables assuming they are there you need to put whole functions into mqh file which will then just copy code into your program just like it was part of the source.
Oh interesting - I thought libraries in MQL are like in C - they become part of your program. I did not think of them as DLLs. I now understand why you and Rosh suggested that I should move this code to a mqh file.

However, what confuses me is that you are saying "if you want to have access to variables.." - but that's not what I am doing. I am calling OrderSelect in the library (which I imagine sets certain internal global data structs in the MQL/MT4 framework) and I thought that these structs will remain the same when I return from the library. In particular I thought that once an order is selected - this is THE ONE and ONLY selected order in your mql application. I thought of this like an OS file handle - if you open a file in a lib or DLL it will still be open when control returns to your main executable. Is that not how OrderSelect works in MQL? (Obviously not - because I ran a coupe of tests with yours/Rosh's suggestion to put the problematic function into the mqh header - but I am curious to understand how it all works behind the scenes :) )

Is there some link/article I could read about using libraries in MQL and what I can and cannot expect from code in a library, so I can avoid problems like this in the future?

thank you.
Reason: