GrabWeb не работает на MT4 Build 600

 

Этот код работал безупречно до тех пор, пока я не протестировал его в MT4 build 600, где он больше не верифицирует счета с моего сервера.



bool bWinInetDebug = false;

string errorMsg;
 
int hSession_IEType;
int hSession_Direct;
int Internet_Open_Type_Preconfig = 0;
int Internet_Open_Type_Direct = 1;
int Internet_Open_Type_Proxy = 3;
int Buffer_LEN = 250;
string answer;

#import "wininet.dll"
 
#define  INTERNET_FLAG_PRAGMA_NOCACHE    0x00000100 // Forces the request to be resolved by the origin server, even if a cached copy exists on the proxy.
#define  INTERNET_FLAG_NO_CACHE_WRITE    0x04000000 // Does not add the returned entity to the cache. 
#define  INTERNET_FLAG_RELOAD            0x80000000 // Forces a download of the requested file, object, or directory listing from the origin server, not from the cache.
#define  INTERNET_FLAG_KEEP_CONNECTION   0x00400000  // use keep-alive semantics
#define  INTERNET_OPEN_TYPE_PRECONFIG                    0   // use registry configuration
#define  INTERNET_SERVICE_HTTP   3
#define  HTTP_ADDREQ_FLAG_ADD            0x20000000
#define  HTTP_ADDREQ_FLAG_REPLACE        0x80000000
int InternetOpenA(
    string     sAgent,
    int        lAccessType,
    string     sProxyName="",
    string     sProxyBypass="",
    int     lFlags=0
);
 
int InternetOpenUrlA(
    int     hInternetSession,
    string     sUrl, 
    string     sHeaders="",
    int     lHeadersLength=0,
    int     lFlags=0,
    int     lContext=0 
);
 
int InternetReadFile(
    int     hFile,
    string     sBuffer,
    int     lNumBytesToRead,
    int&     lNumberOfBytesRead[]
);
 
int InternetCloseHandle(
    int     hInet
);

int HttpOpenRequestA(
    int hConnect,
    string lpszVerb,
    string lpszObjectName,
    string lpszVersion,
    string lpszReferrer,
    string lplpszAcceptTypes,
    int  dwFlags,
    int  dwContext);
    
int  InternetOpenA(
    string lpszAgent,
    int dwAccessType,
    string lpszProxy,
    string lpszProxyBypass,
    int dwFlags
    );    
    
int InternetConnectA(
    int hInternet,
    string lpszServerName,
    int nServerPort,
    string lpszUserName,
    string lpszPassword,
    int dwService,
    int dwFlags,
    int dwContext
    );

bool HttpSendRequestA(
    int hRequest,
    string lpszHeaders,
    int dwHeadersLength,
    string lpOptional,
    int dwOptionalLength
    );  
    
bool HttpAddRequestHeadersA(
    int hRequest,
    string lpszHeaders,
    int dwHeadersLength,
    int  dwModifiers
    );          
#import



int logId;
void Log(string st)
{
   if(logId>=0)
   {
      
      FileWrite(logId, TimeToStr(TimeCurrent(),TIME_DATE|TIME_SECONDS) + ": " + st);
   }
}



int init() 
{ 
GrabWeb("http://www.website.com/query.php?accountnumber="+AccountNumber()+"&login="+User, answer);
}



int hSession(bool Direct)
{
    string InternetAgent;
    if (hSession_IEType == 0)
    {
        InternetAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Q312461)";
        hSession_IEType = InternetOpenA(InternetAgent, Internet_Open_Type_Preconfig, "0", "0", 0);
        hSession_Direct = InternetOpenA(InternetAgent, Internet_Open_Type_Direct, "0", "0", 0);
    }
    if (Direct) 
    { 
        return(hSession_Direct); 
    }
    else 
    {
        return(hSession_IEType); 
    }
}

bool GrabWeb(string strUrl, string& strWebPage)
{
    int     hInternet;
    int        iResult;
    int     lReturn[]={1};
    string     sBuffer="x";
    int     bytes;
    
    hInternet = InternetOpenUrlA(hSession(FALSE), strUrl, "0", 0, 
                                INTERNET_FLAG_NO_CACHE_WRITE | 
                                INTERNET_FLAG_PRAGMA_NOCACHE | 
                                INTERNET_FLAG_RELOAD, 0);
                                
 
    if (hInternet == 0) 
        return(false);
         
    iResult = InternetReadFile(hInternet, sBuffer, Buffer_LEN, lReturn);     

    if (iResult == 0) 
        return(false);
    bytes = lReturn[0];

    strWebPage = StringSubstr(sBuffer, 0, lReturn[0]);    
    // If there's more data then keep reading it into the buffer
    while (lReturn[0] != 0)
    {
        iResult = InternetReadFile(hInternet, sBuffer, Buffer_LEN, lReturn);
        if (lReturn[0]==0) 
            break;
        bytes = bytes + lReturn[0];
       
        strWebPage = strWebPage + StringSubstr(sBuffer, 0, lReturn[0]);
    }
    
  
   
      
    iResult = InternetCloseHandle(hInternet);
    if (iResult == 0) 
        return(false);
   
    return(true);
}

int deinit() {
   FileClose(logId);
   return (0);
}
 
thili55:

Этот код работал безупречно вплоть до тестирования его в MT4 build 600, где он больше не верифицирует счета с моего сервера.


Строки теперь представляются в формате Unicode, хотя раньше они были в формате ANSI (однобайтовые). Это следует учитывать, если программа использует DLL и передает им строковые переменные. При вызове функций Windows API следует использовать версии этих функций в формате Unicode.

Вы должны адаптировать свой код. Используйте InternetOpenW вместо InternetOpenA, то же самое для других функций DLL, которые заканчиваются на 'a' (для ANSI).
 
Спасибо за ваше предложение angevoyageur, но я попробовал переключить все ansi 'A' на W, но все равно ничего не получилось.
 
thili55:
Спасибо за ваше предложение, angevoyageur, но я попробовал поменять все Ansi 'A' на W, но все равно безрезультатно.

Ваша проблема будет связана с функцией InternetReadFile(). Смотрите EasyXml.mqh на https://www.mql5.com/en/code/1998 для примера использования функций WinInet в новом MQL4 - код работает как в MQL4, так и в MQL5.

По сути, вы передаете массив uchar[] в InternetReadFile(), а затем преобразуете массив в строку с помощью CharArrayToString(). По сути, теперь в MQL4 вы можете выделять управляемые буферы памяти произвольной длины, передавать их DLL, а затем преобразовывать данные из Ansi или Unicode в зависимости от ситуации.

 
gchrmt4:

По сути, вы передаете массив uchar[] в InternetReadFile(), а затем преобразуете массив в строку с помощью CharArrayToString(). По сути, теперь в MQL4 вы можете выделять управляемые буферы памяти произвольной длины, передавать их DLL, а затем преобразовывать данные из Ansi или Unicode в зависимости от ситуации.

Немного расширяя тему и ответ... в новом MQL4 можно вызывать как A, так и W версии многих функций. Например, следующий скрипт получает временный каталог Windows, используя оба вызова GetTempPathA и GetTempPathW:

#import "kernel32.dll"
   int GetTempPathA(int,uchar & arr[]);
   int GetTempPathW(int,short & arr[]);
#import

void OnStart()
{
   uchar AnsiStringBuffer[256];
   GetTempPathA(255, AnsiStringBuffer);
   string strTempPathFromA = CharArrayToString(AnsiStringBuffer);

   short UnicodeStringBuffer[256];
   GetTempPathW(255, UnicodeStringBuffer);
   string strTempPathFromW = ShortArrayToString(UnicodeStringBuffer);

   Print("Temp path via GetTempPathA(): ", strTempPathFromA);
   Print("Temp path via GetTempPathW(): ", strTempPathFromW);
}

Таким образом, можно продолжать использовать многие вызовы Ansi-only DLL из нового MQL4: не обязательно обновлять как код MQL4 , так и DLL.

 
gchrmt4:

Поэтому можно продолжать использовать многие вызовы Ansi-only DLL из нового MQL4: не обязательно обновлять как код MQL4 , так и DLL.


... Другой пример: передача строковых значений в вызов Ansi DLL из нового MQL4. (В реальной жизни вы, очевидно, просто вызовете MessageBoxW, а не будете использовать это обходное решение для вызова MessageBoxA, но общий смысл полезен).

#import "user32.dll"
   // Declare the Ansi function as taking uchar[] input parameters instead of strings
   int MessageBoxA(int,uchar & arr1[],uchar & arr2[],int);
#import

void OnStart()
{
   string strMessage = "Hello";
   string strTitle = "Hi!";
   
   // Convert the strings to uchar[] arrays
   uchar ucMessage[], ucTitle[];
   StringToCharArray(strMessage, ucMessage);
   StringToCharArray(strTitle, ucTitle);
   
   MessageBoxA(0, ucMessage, ucTitle, 64);
}
 
Спасибо gchrmt4 за подробное объяснение, к сожалению, я не очень большой программист, поэтому не похоже, что я смогу справиться с этим самостоятельно. Я смотрю на свой код и примеры, которые вы опубликовали, и не уверен, что делать, так что, похоже, мне придется найти кого-то, кто сделает это за меня.
 
Я играю с этим уже несколько часов, но все еще безрезультатно. Так что да, ищу кого-нибудь, кто сделает это для меня ;)
 
thili55:
Я играю с этим уже несколько часов, но все еще безрезультатно. Так что да, ищу кого-нибудь, кто сделает это для меня ;)
https://www.mql5.com/en/job
 
thili55:
Я играю с этим уже несколько часов, но все еще безрезультатно. Так что да, ищу кого-нибудь, кто сделает это для меня ;)
См. https://www. mql5.com/en/forum/149360 - я собирался опубликовать это как ответ здесь, но потом обнаружил проблему...
 
gchrmt4:
См. https://www. mql5.com/en/forum/149360 - Я собирался опубликовать это как ответ здесь, но потом обнаружил проблему...
...Тем не менее, этот скрипт должен работать для получения коротких ответов сервера, таких как результаты запроса лицензирования. Проблемы возникают только в том случае, если размер ответа сервера превышает 1 КБ.