2016-11-30 3 views
0

В расширении пространства имен я создаю поток и передаю путь файла к функции потока. Проблема, которую я вижу, - это первый символ пути к файлу, который поврежден. D: \ temp0.csv передается в и в функции потока он становится Y: \ temp0.csv или другим случайным поврежденным первым wchar. В Win2k8R2 и Win10 он работал нормально, но иногда он терпел неудачу точно так же. Я попытался отключить оптимизацию безрезультатно. Переменная fileName заполняется из IShellItem, которая поступает из IFileOpenDialog. Что мне нужно сделать, чтобы исправить это?Почему мой входной параметр CreateThread поврежден?

Caller функции потока:

LPWSTR filePath  = NULL; 
IFileOpenDialog *ofd = NULL; 
IShellItem *file  = NULL; 
hrPath    = file->GetDisplayName(SIGDN_FILESYSPATH, &filePath); 
CreateThread(NULL, 0, CCsv::BuildTree, static_cast<LPVOID>(filePath), 0, NULL); 

статический класс резьбы функция

DWORD WINAPI CCsv::BuildTree(LPVOID lpParam) { 
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 
    LPWSTR filePath = static_cast<LPWSTR>(lpParam); 
} 

Вот минимальная программа, но она не Репрографический с этим кодом. Одно отличие - я добавил ожидание функции потока. Это не существует в расширении пространства имен.

main.cpp

// buf.cpp : Defines the entry point for the console application. 
// 

#pragma once 
#include "stdafx.h" 


using std::wstring; 
static CRITICAL_SECTION g_TreeLock; 
static CRITICAL_SECTION g_MountQueueLock; 


class CCsv 
{ 
public: 
    CCsv(); 
    ~CCsv(); 
    static DWORD WINAPI BuildTree(LPVOID lpParam); 
}; 
class CMountPath { 
public: 
    CMountPath(); 
    ~CMountPath(); 
    BOOL Mount(); 
    BOOL PathExists(LPWSTR path); 
private: 
    CSimpleArray<wstring> m_MountQueue; 
}; 
extern CCsv g_Csv; 

CCsv::CCsv() { 
    InitializeCriticalSection(&g_TreeLock); 
} 

CCsv::~CCsv() { 
    DeleteCriticalSection(&g_TreeLock); 
} 
DWORD WINAPI CCsv::BuildTree(LPVOID lpParam) { 
    LPWSTR name = static_cast<LPWSTR>(lpParam); 
    MessageBox(NULL, name, L"", MB_OK); 
    CoTaskMemFree(name); 
    return 0; 
} 

CMountPath::CMountPath() { 
    InitializeCriticalSection(&g_MountQueueLock); 
} 

CMountPath::~CMountPath() { 
DeleteCriticalSection(&g_MountQueueLock); 
} 
BOOL CMountPath::PathExists(LPWSTR path) { 
    return FALSE; 
} 
BOOL CMountPath::Mount() { 
    IEnumIDList *idl     = NULL; 
    LPITEMIDLIST pidl     = NULL; 
    LPITEMIDLIST desktopPidl   = NULL; 
    LPCITEMIDLIST pidlRelative   = NULL; 
    BOOL success      = FALSE; 
    HRESULT hr, hrPath     = S_FALSE; 
    LPWSTR filePath      = NULL; 
    PWSTR filePathHeap     = NULL; 
    WCHAR msg[MAXPATH+MAXMSG]   = {0}; 
    IFileOpenDialog *ofd    = NULL; 
    IShellItem *file     = NULL; 
    DWORD idx       = 0; 
    BOOL isQueued      = FALSE; 

    const COMDLG_FILTERSPEC fileSpec[] = { 
      { L"CSV Text Files", L"*.csv" }, 
      { L"All Files", L"*.*" }, 
    }; 
    hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&ofd)); 
    if (SUCCEEDED(hr)) { 
     if (SUCCEEDED(hr)){ 
      ofd->SetTitle(L"Choose file"); 
      ofd->SetFileTypes(ARRAYSIZE(fileSpec), fileSpec); 
      hr = ofd->Show(NULL); 
      if(SUCCEEDED(hr)) 
       hr = ofd->GetResult(&file); 
      if(SUCCEEDED(hr)) 
       hrPath = file->GetDisplayName(SIGDN_FILESYSPATH, &filePath); 
      if(SUCCEEDED(hrPath)){ 
       LPWSTR filePathHeap = (LPWSTR)CoTaskMemAlloc(MAXPATH * sizeof(WCHAR)); 
       if(filePathHeap) { 
        StringCchCopy(filePathHeap, MAXPATH, filePath); 
        if(PathExists(filePathHeap)) { 
         StringCchPrintf(msg, MAXPATH+MAXMSG, L"The file %s is already loaded.", filePathHeap); 
         MessageBox(NULL, msg, L"appname", MB_OK); 
        } 
        else { 
         EnterCriticalSection(&g_MountQueueLock); 
         isQueued = !m_MountQueue.Find(wstring(filePathHeap)) ? TRUE : FALSE; 
         if(!isQueued) 
          m_MountQueue.Add(wstring(filePathHeap)); 
         LeaveCriticalSection(&g_MountQueueLock); 
         if(!isQueued) { 
          HANDLE hThread = CreateThread(NULL, 0, CCsv::BuildTree, static_cast<LPVOID>(filePathHeap), 0, NULL); 
          // there is no wait in the namespace extension. the wait is just to keep the console app main thread running. 
          if(INVALID_HANDLE_VALUE != hThread) 
           WaitForSingleObject(hThread, INFINITE); 
         } 
         else { 
          StringCchPrintf(msg, MAXPATH+MAXMSG, L"The file %s is already being loaded.", filePathHeap); 
          MessageBox(NULL, msg, L"appname", MB_OK); 
         } 
        } 
       } 
       CoTaskMemFree(filePath); 
       file->Release(); 
      } 
     } 
     ofd->Release(); 
    } 

    return success; 
} 

int main() { 
    CoInitialize(NULL); 
    CMountPath m; 
    m.Mount(); 
    CoUninitialize(); 

    return 0; 
} 

stdafx.h

#pragma once 
#define MAXPATH 32767 
#define MAXMSG 128 
#define WIN32_LEAN_AND_MEAN 
#define WINVER 0x0600 
#define _WIN32_WINNT 0x0600 

#include "targetver.h" 

#include <stdio.h> 
#include <tchar.h> 

#include <windows.h> 
#include <atlbase.h> 
#include <atlstr.h> 
#include <atlcoll.h> 
#include <shlobj.h> 
#include <Shobjidl.h> 
#include <ShlGuid.h> 
#include <shellapi.h> 
#include <OleAuto.h> 
#include <shlwapi.h> 
#include <strsafe.h> 
#include <string> 
+1

Это пахнет классическим состоянием гонки. Вы передаете указатель на хранилище, которое будет перезаписано до того, как «BuildTree» сможет его прочитать. –

+0

нет - здесь нет состояние гонки - после 'file-> GetDisplayName' return' filePath' мы владелец этой памяти. и вам нужно освободить его с помощью 'CoTaskMemFree', когда он больше не нужен. это выглядит как куча коррумпированной. быстрее всего переполнения буфера – RbMm

+0

Это происходит все время на 8-процессорной машине, но у моей dev-бокса (медленнее 2 ядра) нет этой проблемы. Система 8 proc имеет 56 ГБ оперативной памяти, а devbox - только 16 ГБ. После file-> GetDisplayName я скопировал строку в кучу, а затем выпустил IShellItem и освободил filePath в вызывающем. Это оставляет новую строку в куче для использования функции потока, но все же первый wchar повреждается, прежде чем он будет запущен. Любые идеи о том, как исправить или обойти это? , –

ответ

2

Почему вы используете нити на всех? Когда вы создаете новый поток, вы блокируете свой код, ожидая завершения потока, прежде чем продолжить, так что вы будете сериализовать весь свой код. Вы можете даже не использовать потоки вообще.

Кроме того, в вашем коде есть утечки памяти и различные логические ошибки.

Попробуйте вместо этого:

// buf.cpp : Defines the entry point for the console application. 
// 

#pragma once 
#include "stdafx.h" 

using std::wstring; 

class CCsv 
{ 
public: 
    CCsv(); 
    ~CCsv(); 

    void BuildTree(LPCWSTR name); 

private: 
    CRITICAL_SECTION m_TreeLock; 
}; 

class CMountPath { 
public: 
    CMountPath(); 
    ~CMountPath(); 

    BOOL Mount(); 
    BOOL PathExists(LPCWSTR path); 

private: 
    CSimpleArray<wstring> m_MountQueue; 
    CRITICAL_SECTION m_MountQueueLock; 
}; 

CCsv g_Csv; 

CCsv::CCsv() { 
    InitializeCriticalSection(&m_TreeLock); 
} 

CCsv::~CCsv() { 
    DeleteCriticalSection(&m_TreeLock); 
} 

void CCsv::BuildTree(LPCWSTR name) { 
    MessageBoxW(NULL, name, L"", MB_OK); 
} 

CMountPath::CMountPath() { 
    InitializeCriticalSection(&m_MountQueueLock); 
} 

CMountPath::~CMountPath() { 
    DeleteCriticalSection(&m_MountQueueLock); 
} 

BOOL CMountPath::PathExists(LPCWSTR path) { 
    return FALSE; 
} 

BOOL CMountPath::Mount() { 
    BOOL success      = FALSE; 
    HRESULT hr       = S_FALSE; 
    LPWSTR filePath      = NULL; 
    WCHAR msg[MAXPATH+MAXMSG]   = {0}; 
    IFileOpenDialog *ofd    = NULL; 
    IShellItem *file     = NULL; 
    BOOL isQueued      = FALSE; 

    const COMDLG_FILTERSPEC fileSpec[] = { 
      { L"CSV Text Files", L"*.csv" }, 
      { L"All Files", L"*.*" }, 
    }; 

    hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&ofd)); 
    if (SUCCEEDED(hr)) { 
     ofd->SetTitle(L"Choose file"); 
     ofd->SetFileTypes(ARRAYSIZE(fileSpec), fileSpec); 
     hr = ofd->Show(NULL); 
     if(SUCCEEDED(hr)) 
      hr = ofd->GetResult(&file); 
     if(SUCCEEDED(hr)) { 
      hr = file->GetDisplayName(SIGDN_FILESYSPATH, &filePath); 
      if(SUCCEEDED(hr)){ 
       if(PathExists(filePath)) { 
        StringCchPrintf(msg, ARRAYSIZE(msg), L"The file %s is already loaded.", filePath); 
        MessageBox(NULL, msg, L"appname", MB_OK); 
       } 
       else { 
        EnterCriticalSection(&m_MountQueueLock); 
        isQueued = !m_MountQueue.Find(filePath) ? TRUE : FALSE; 
        if(!isQueued) 
         m_MountQueue.Add(filePath); 
        LeaveCriticalSection(&m_MountQueueLock); 
        if(!isQueued) { 
         CCsv::BuildTree(filePath); 
        } 
        else { 
         StringCchPrintf(msg, ARRAYSIZE(msg), L"The file %s is already being loaded.", filePath); 
         MessageBox(NULL, msg, L"appname", MB_OK); 
        } 
       } 
       CoTaskMemFree(filePath); 
      } 
      file->Release(); 
     } 
     ofd->Release(); 
    } 

    return success; 
} 

int main() { 
    CoInitialize(NULL); 
    CMountPath m; 
    m.Mount(); 
    CoUninitialize(); 

    return 0; 
} 
+0

Почему я использую нить?Потому что я не буду работать над потоком пользовательского интерфейса. –

+1

Код, который вы указали, блокирует основной поток, ожидая, когда рабочий поток выполнит свою работу. И он должен ждать, так как память заканчивается освобождением после выхода 'Mount()', вызывая условия гонки и такие, если поток все еще работает. Таким образом, нет никакой пользы для использования рабочего потока вообще в вашем примере. Если то, что вы показали, не то, что действительно делает NSE, тогда вам нужно опубликовать свой настоящий код NSE, а не другой код. –

0

ваша концептуальная ошибка здесь:

if(!isQueued) 
{ 
    m_MountQueue.Add(wstring(filePathHeap)); 
    CreateThread(NULL, 0, CCsv::BuildTree, static_cast<LPVOID>(filePathHeap), 0, NULL); 
} 

так вставить filePathHeap в какой-то базе данных и одновременно передать эту filePathHeap в какой-то теме. вопрос - кто ВЛАДЕЛЕЦ filePathHeap? кто должен освободить его? если вы освободите его от BuildTree - в m_MountQueue будет недействительным указатель? вы не показываете код, кто и как обрабатывает m_MountQueue - может быть этот код поп и бесплатно filePathHeap, прежде чем он используется в BuildTree.

вообще - если нажать filePathHeap к m_MountQueue - вы не должны использовать его прямой указатель после этого, но есть рабочая нить (бассейн), которые поп-данные из m_MountQueue, процесса и освободить его. или если вы используете использование filePathHeap, вы не должны вставлять его в m_MountQueue.

в случае, если вам необходимо одновременно работать с filePathHeap из нескольких потоков - вам нужно указать количество ссылок. некоторые, как это:

class CFilePath 
{ 
    PWSTR _filePath; 
    LONG _dwRef; 

    ~CFilePath() 
    { 
     CoTaskMemFree(_filePath); 
    } 

public: 

    PCWSTR getPath() { return _filePath; } 

    CFilePath(PWSTR filePath) 
    { 
     _filePath = filePath; 
     _dwRef = 1; 
    } 

    void AddRef() 
    { 
     InterlockedIncrement(&_dwRef); 
    } 

    void Release() 
    { 
     if (!InterlockedDecrement(&_dwRef)) delete this; 
    } 
}; 

ULONG CALLBACK CCsv::BuildTree(CFilePath* p) 
{ 
    MessageBoxW(0, p->getPath(), L"use path in thread 2", MB_OK); 
    p->Release(); 
    return 0; 
} 

BOOL CMountPath::Mount() { 

... 
    if (CFilePath* p = new CFilePath(filePath)) 
    { 
     p->AddRef(); 

     if (HANDLE hThread = CreateThread(0, 0, reinterpret_cast<PTHREAD_START_ROUTINE>(CCsv::BuildTree), p, 0, 0)) 
     { 
      CloseHandle(hThread); 
     } 
     else 
     { 
      p->Release(); 
     } 

     MessageBoxW(0, p->getPath(), L"use path in thread 1", MB_OK); 
     p->Release(); 
    } 
    else 
    { 
     CoTaskMemFree(filePath); 
    } 
... 
} 

Во-вторых - это код

LPWSTR filePathHeap = (LPWSTR)CoTaskMemAlloc(MAXPATH * sizeof(WCHAR)); 
if(filePathHeap) StringCchCopy(filePathHeap, MAXPATH, filePath); 

абсолютно ненужным. вы можете направить использование filePath как есть, но не перераспределите его.для чего ?!

в-третьих - как в предыдущем ответе - почти нет смысла создавать нить, и после этого просто подождите, пока он выйдет.

    HANDLE hThread = CreateThread(NULL, 0, CCsv::BuildTree, static_cast<LPVOID>(filePathHeap), 0, NULL); 
        // there is no wait in the namespace extension. the wait is just to keep the console app main thread running. 
        if(INVALID_HANDLE_VALUE != hThread) 
         WaitForSingleObject(hThread, INFINITE); 

почему в этом случае не прямой вызов CCsv::BuildTree(filePathHeap); с таким же эффектом?

также можно отметить, что INVALID_HANDLE_VALUE возвращается только для CreateFile или Socket - поэтому для файлов ручек только. все другие объекты, создающие api (включая CreateThread return 0 при ошибке, но не INVALID_HANDLE_VALUE (-1)) - поэтому вы ошибочно проверяете состояние ошибки. и, наконец, кто будет звонить CloseHandle(hThread)? ручка не авто закрыта при выходе нити

+0

m_MountQueue.Add() принимает wstring, я не вижу проблемы с ним. INVALID_HANDLE_VALUE - хороший улов. В коде, который я разместил, есть намного больше, чтобы выбрать. Его быстро и грязно. CoTaskMemAlloc() должен был гарантировать, что буфер, отправленный в CreateThread(), не был поврежден/освобожден мной. Это не производственный код. Я ищу, чтобы узнать, почему я получаю буферовую коррупцию. Если у вас есть идеи об этом, я бы хотел их услышать. –

+0

@ 505HPC6Z06 'm_MountQueue.Add (wstring (filePathHeap));' и 'CreateThread (NULL, 0, CCsv :: BuildTree, static_cast (filePathHeap), 0, NULL);' - это основная. для чего вы вставляете 'filePathHeap' в' m_MountQueue.'? как вы это используете? кто бесплатно «filePathHeap»? – RbMm

+0

@RbMm: 'm_MountQueue' содержит значения' std :: wstring'. Передача 'filePathHeap' в' Add() 'делает * копию * символьных данных. 'filePathHeap' сам должен быть освобожден после этого. Это выполняется в процедуре потока, когда она вызывает 'CoTaskMemFree (name)'. –

Смежные вопросы