在命名空间扩展中,我正在创建一个线程并将文件路径传递给线程函数。 我看到的问题是文件路径的第一个字符被破坏。 D:\ temp0.csv传入并在线程函数中变为Y:\ temp0.csv或其他一些随机损坏的第一个wchar。在Win2k8R2和Win10中它运行良好,但有时它会以同样的方式失败。我试过禁用优化无济于事。 fileName变量从来自IFileOpenDialog的IShellItem填充。 我需要做些什么来解决这个问题?
线程函数调用者:
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>
答案 0 :(得分: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;
}
答案 1 :(得分: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
- 在此之后你不能直接使用它指针,而是有工作线程( pool )来自{ {1}},处理并释放它。或者如果您直接使用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
返回 - 因此仅针对文件句柄。创建api的所有另一个对象(包括Socket
在出错时返回0,但不是CreateThread
( - 1)) - 所以你错误地检查错误条件。
最后谁会打电话给INVALID_HANDLE_VALUE
?线程退出时句柄不自动关闭