2016-11-30 1 views
0

Dans une extension d'espace de noms, je crée un thread et passe un chemin de fichier à la fonction thread. Le problème que je vois est le premier caractère du chemin de fichier est corrompu. D: \ temp0.csv est passé et dans la fonction de thread il devient Y: \ temp0.csv ou un autre wchar d'abord corrompu. Dans Win2k8R2 et Win10 cela fonctionnait bien, mais parfois il échouerait de la même manière. J'ai essayé de désactiver les optimisations en vain. La variable fileName est remplie à partir de IShellItem provenant de IFileOpenDialog. Que dois-je faire pour résoudre ce problème?Pourquoi mon paramètre d'entrée CreateThread est-il corrompu?

appelant la fonction fil:

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); 

fonction de fil de classe statique

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

Voici un programme minimal, mais il ne REPRO pas avec ce code. Une différence est que j'ai ajouté une attente pour la fonction de thread. Ce nom n'existe pas dans l'extension de l'espace de noms.

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

Ça sent comme une condition de course classique. Vous passez un pointeur sur le stockage qui est écrasé avant que BuildTree n'ait la chance de le lire. –

+0

non - ici pas de condition de course - après 'file-> GetDisplayName' renvoie' filePath' nous avons le propriétaire de cette mémoire. et besoin de le libérer avec 'CoTaskMemFree' quand il n'est plus nécessaire. Cela ressemble à un tas corrompu. plus rapide de tous les dépassements de tampon – RbMm

+0

Cela arrive tout le temps sur une machine 8 proc, mais ma boîte de dev (plus lente 2 core) n'a pas ce problème. Le système de 8 proc a 56 Go de RAM et la devbox n'a que 16 Go. Après file-> GetDisplayName, j'ai copié la chaîne sur le tas puis libéré IShellItem et libéré filePath dans l'appelant. Cela laisse une nouvelle chaîne sur le tas pour la fonction de thread à utiliser, mais toujours le premier wchar est corrompu avant d'être casté. Des idées sur la façon de résoudre ou contourner cela? . –

Répondre

2

Pourquoi utilisez-vous des fils du tout? Lorsque vous créez un nouveau thread, vous bloquez votre code en attendant que le thread se termine avant de continuer, ce qui vous permet de sérialiser tout votre code. Vous pouvez aussi ne pas utiliser de threads du tout.

De plus, vous avez des fuites de mémoire et diverses erreurs logiques dans votre code.

Essayez ceci:

// 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

Pourquoi est-ce que j'utilise un thread?Parce que je ne vais pas travailler sur un thread d'interface utilisateur. –

+1

Le code que vous avez affiché bloque le thread principal en attendant que le thread de travail fasse son travail. Et il doit attendre, car la mémoire finit par être libérée après que 'Mount()' se termine, provoquant des conditions de course et autres si le thread est toujours en cours d'exécution. Il n'y a donc aucun avantage à utiliser un thread de travail dans votre exemple. Si ce que vous avez montré n'est pas ce que votre NSE fait vraiment, alors vous devez poster votre vrai code NSE, pas un code différent. –

0

votre erreur conceptuelle ici:

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

si vous insérez filePathHeap dans une base de données et de transmettre simultanément ce filePathHeap à un fil. question - Qui est propriétaire de filePathHeap? qui doit le libérer? si vous le libérez de BuildTree - dans m_MountQueue sera pointeur invalide? vous ne montrez pas le code qui et comment gérer m_MountQueue - peut être ce code pop et gratuit filePathHeap avant qu'il utilisé dans BuildTree.

en général - si vous appuyez sur filePathHeap-m_MountQueue - vous ne devez pas utiliser directement ce pointeur après, mais ont fil de travail (pool) qui les données de pop m_MountQueue, processus et la libérer. ou si vous utilisez directement filePathHeap, vous ne devez pas l'insérer dans m_MountQueue.

dans le cas où vous avez besoin de travailler simultanément avec filePathHeap à partir de plusieurs threads - vous avez besoin de ref count. certains comme ceci:

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); 
    } 
... 
} 

En second lieu - ce code

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

absolument inutile. vous pouvez utiliser directement filePath tel quel mais ne pas le réaffecter.pour quoi ?! Troisièmement - comme dans la réponse précédente - presque aucun sens créer un fil et après cela il suffit d'attendre la sortie.

    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); 

pourquoi dans ce cas ne pas appeler directement avec CCsv::BuildTree(filePathHeap); même effet?

peut également noter que INVALID_HANDLE_VALUE renvoyé uniquement pour CreateFile ou Socket - donc pour les handles de fichiers seulement. tout autre objet créant des API (y compris CreateThread renvoie 0 en cas d'erreur, mais pas INVALID_HANDLE_VALUE (-1)) - donc vous vérifiez incorrectement la condition d'erreur. et enfin qui appellera CloseHandle(hThread)? poignée non fermée automatiquement à la sortie du fil

+0

m_MountQueue.Add() accepte un wstring, je ne vois aucun problème avec celui-ci. Le INVALID_HANDLE_VALUE est une bonne prise. Il y a beaucoup plus à choisir dans le code que j'ai posté. C'est rapide et sale. Le CoTaskMemAlloc() devait s'assurer que le tampon envoyé à CreateThread() n'était pas corrompu/libéré par moi. Ce n'est pas un code de production. Je cherche à savoir pourquoi je reçois une corruption de tampon. Si vous avez des idées à ce sujet, j'aimerais les entendre. –

+0

@ 505HPC6Z06 'm_MountQueue.Add (wstring (filePathHeap));' et 'CreateThread (NULL, 0, CCsv :: BuildTree, static_cast (filePathHeap), 0, NULL);' - Ceci est principal. pour ce que vous insérez 'filePathHeap' dans' m_MountQueue.'? comment vous utilisez cela? qui libère le 'filePathHeap'? – RbMm

+0

@RbMm: 'm_MountQueue' contient des valeurs' std :: wstring'. Passer 'filePathHeap' à' Add() 'fait une * copie * des données de caractères. 'filePathHeap' lui-même doit être libéré par la suite. Cela est fait dans la procédure thread quand il appelle 'CoTaskMemFree (nom)'. –