2

我正在尝试解决这个问题,似乎我正在访问超出范围的索引,但是 VS 无法在错误发生的地方停止,这让我对造成这种情况的原因感到困惑。

错误:

调试断言失败!程序:.... 文件:c:\program files\microsoft visual studio 10.0\vc\include\vector 行:1440 表达式:字符串下标超出范围

该程序的作用:

有两个线程:

线程 1:

第一个线程使用 查找(除其他外)当前窗口中的更改GetForegroundWindow(),检查不是在循环上发生,而是在WH_MOUSE_LL触发事件时发生。数据被分成固定大小的结构,以便可以通过 tcp 发送到服务器。第一个线程将数据(窗口标题)记录到std::list当前结构中。

if(change_in_window)
{
    GetWindowTextW(hActWin,wTitle,256);
    std::wstring title(wTitle);
    current_struct->titles.push_back(title);
}

线程 2:

调用第二个线程查找尚未发送的结构,并将它们的内容放入char缓冲区,以便可以通过 tcp 发送它们。虽然我不知道错误的确切位置,但从错误的类型来看,它与字符串或列表有关,这是我整个应用程序中使用列表/字符串的唯一代码(其余是常规数组)。还注释代码注释中提到的 if 块可以阻止错误的发生。

  BOOL SendStruct(DATABLOCK data_block,bool sycn)
    {
    [..]

                int _size = 0;
// Important note, when this if block is commented the error ceases to exist, so it has something to do with the following block
                if(!data_block.titles.empty()) //check if std::list is empty
                {

                    for (std::list<std::wstring>::iterator itr  = data_block.titles.begin(); itr != data_block.titles.end() ; itr++) {
                        _size += (((*itr).size()+1) * 2); 
                    } //calculate size required. Note the +1 is for an extra character between every title
                    wchar_t* wnd_wbuffer = new wchar_t[_size/2](); //allocate space
                    int _last = 0;
    //loop through every string and every char of a string and write them down
                    for (std::list<std::wstring>::iterator itr = data_block.titles.begin(); itr != data_block.titles.end(); itr++)
                    {
                        for(unsigned int i = 0; i <= (itr->size()-1); i++)
                        {

                            wnd_wbuffer[i+_last] = (*itr)[i] ;
                        }
                        wnd_wbuffer[_last+itr->size()] = 0x00A6; // separator
                        _last += itr->size()+1;
                    }

                    unsigned char* wnd_buffer = new unsigned char[_size];
                    wnd_buffer = (unsigned char*)wnd_wbuffer;
                    h_io->header_w_size = _size;
                    h_io->header_io_wnd = 1;
                    Connect(mode,*header,conn,buffer_in_bytes,wnd_buffer,_size);
                    delete wnd_wbuffer;
                }
                else
            [..]
                return true;
            }

我对线程同步的尝试: 有一个指向创建的第一个数据块的指针(db_main)指向当前数据块的指针(db_cur)

//datablock format
    typedef struct _DATABLOCK
        {
            [..]
            int logs[512];
            std::list<std::wstring> titles;
            bool bPrsd; // has this datablock been sent true/false
            bool bFull; // is logs[512] full true/false
            [..]
            struct _DATABLOCK *next;
        } DATABLOCK;    


//This is what thread 1 does when it needs to register a mouse press and it is called like this:
    if(change_in_window)
    {
        GetWindowTextW(hActWin,wTitle,256);
        std::wstring title(wTitle);
        current_struct->titles.push_back(title);
    }
    RegisterMousePress(args);
    [..]
//pseudo-code to simplify things , although original function does the exact same thing. 
    RegisterMousePress()
        {
            if(it_is_full)
            {
                db_cur->bFull= true;
                if(does db_main exist)
                {
                    db_main = new DATABLOCK;
                    db_main = db_cur;
                    db_main->next = NULL;
                }
                else
                {
                    db_cur->next = new DATABLOCK;
                    db_cur = db_cur->next;
                    db_cur->next = NULL;

                }
                SetEvent(eProcessed); //tell thread 2 there is at least one datablock ready
            }
            else
            {
            write_to_it();
            }
        }
//this is actual code and entry point of thread 2 and my attempy at synchronization
    DWORD WINAPI InitQueueThread(void* Param)
    {
        DWORD rc;
        DATABLOCK* k;
        SockWClient writer;
        k = db_main;
        while(true)
        {
            rc=WaitForSingleObject(eProcessed,INFINITE);
            if (rc== WAIT_OBJECT_0)
            {   
                do
                {
                    if(k->bPrsd)
                    {
                        continue;
                    }
                    else
                    {   
                        if(!k)
                        {break;}
                        k->bPrsd = TRUE;
    #ifdef DEBUG_NET
                        SendStruct(...);
    #endif

                    }
                    if(k->next == NULL || k->next->bPrsd ==TRUE || !(k->next->bFull))
                    {
                        ResetEvent(eProcessed);
                        break;
                    }

                } while (k = k->next); // next element after each loop
            }
        }
        return 1;

    }

细节:

现在有些事情让我相信错误不存在,因为子字符串错误非常罕见。当按下 Mouse_Down+Wnd+Tab 滚动窗口并按住它一段时间时,我只能以 100% 的机会重现它(虽然它当然也发生在其他情况下)。我避免发布整个代码,因为它有点大并且不可避免地会造成混乱。如果错误不在这里,我将编辑帖子并添加更多代码。

提前致谢

4

2 回答 2

0

这里似乎没有任何线程同步。如果一个线程从结构中读取而另一个写入,则可能在初始化期间读取它,其中包含一个空字符串(或介于两者之间的无效字符串)的非空列表。

如果发布的函数之外没有互斥锁或信号量,则可能是问题所在。

尽管我没有尝试运行它,但所有大小计算似乎对 Windows 都是有效的……而<= … -1不是<in i <= (itr->size()-1)2而不是sizeof (wchar_t)in new wchar_t[_size/2]();有点奇怪。

于 2012-04-23T14:41:06.437 回答
0

您的代码的问题在于,虽然线程 2 正确地等待数据并且线程 1 正确地通知了它们,但线程 2 并没有阻止线程 1 在它仍在处理数据的同时对其进行任何操作。用于解决此类问题的典型设备是监视器模式。

它由一个互斥锁(用于保护数据,在您访问它们时保存)和一个条件变量(=Windows 术语中的事件)组成,它将向消费者传达有关新数据的信息。

生产者通常会获取互斥体,生成数据,释放互斥体,然后触发事件。

消费者更棘手 - 它必须获取互斥锁,检查是否有新数据可用,然后使用暂时释放互斥锁的 SignalObjectAndWait 函数等待事件,然后处理新获取的数据,然后释放互斥锁。

于 2012-04-23T22:49:36.073 回答