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>
Ç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. –
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
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? . –