Following logic is implemented to open a file by a "filename.extension" in a C++ application using managed-C++:
try
{
CoInitialize(nullptr);
auto task = Concurrency::create_task(Windows::Storage::StorageFile::GetFileFromPathAsync(filePath));
// an excpetion is thrown in the next line
Concurrency::task_status status = task.then([&](Windows::Storage::StorageFile^ file){
if (file != nullptr)
{
concurrency::task<bool> launchFileOperation(Windows::System::Launcher::LaunchFileAsync(file));
launchFileOperation.then([&](bool success)
{
if (!success)
return 0;
}).wait();
}
}).wait();
}
catch (...)
{
CoUninitialize(); // an exeption is catched
return 0;
}
Since the above code throws an exception, we go further to an alternative file open approach via ::ShellExecuteEx
SHELLEXECUTEINFO exec_info = {0};
exec_info.cbSize = sizeof exec_info;
exec_info.fMask = SEE_MASK_NOCLOSEPROCESS
| SEE_MASK_DOENVSUBST;
exec_info.fMask &= ~SEE_MASK_NOASYNC;
exec_info.lpVerb = "open";
exec_info.lpFile = full_path_str;
exec_info.nShow = SW_SHOW;
bool result_b = ::ShellExecuteEx(&exec_info) ? true : false;
The ::ShellExecuteEx fails and ends up in Microsofts ppltasks.h
_REPORT_PPLTASK_UNOBSERVED_EXCEPTION();.
::ShellExecuteEx works correctly if the managed-C++ Concurrency::create_task approach is removed.
Why does Concurrency::create_task affect the further call of ::ShellExecuteEx?
This issue appears only in release build.
Adding try/catch-blocks to the innermost .wait()-block solved the issue
try {
concurrency::task<bool> launchFileOperation(Windows::System::Launcher::LaunchFileAsync(file));
launchFileOperation.then([&](bool success) {
// logic
}).wait();
}
catch (concurrency::invalid_operation& ex)
{
...
}
catch (concurrency::task_canceled& ex)
{
...
}
Related
Here is a method:
// Override to be notified about every file processed
bool MultiActionsNext(LPCTSTR lpszFileInZip = NULL)
{
if (CZipActionCallback::MultiActionsNext())
{
CMultiActionsInfo* pMulti = GetMultiActionsInfo();
CString strProgress, strPercentComplete;
strPercentComplete.Format(_T("%d%%"),
theApp.CalculatePercent(pMulti->m_uFilesProcessed, pMulti->m_uTotalFilesToProcess));
if (lpszFileInZip != NULL)
{
strProgress = lpszFileInZip;
}
else
{
strProgress.Format(_T("Files Processed: %u of %u"),
pMulti->m_uFilesProcessed, pMulti->m_uTotalFilesToProcess);
}
if (m_pWndProgressText != nullptr)
{
m_pWndProgressText->SetWindowText(strProgress);
m_pWndProgressText->UpdateWindow();
}
if (m_pWndProgressPercentText != nullptr)
{
m_pWndProgressPercentText->SetWindowText(strPercentComplete);
m_pWndProgressPercentText->UpdateWindow();
}
if (m_pProgressCtrl != nullptr)
m_pProgressCtrl->SetPos(pMulti->m_uFilesProcessed);
return true;
}
else
// Processing was requested to be stopped.
// It won't happen in this sample code, because
// the Callback method always returns true.
return false;
}
My problem is with this line of code:
m_pWndProgressText->SetWindowText(strProgress);
If I am in DEBUG mode the result is fine:
But when I am in RELEASE mode the result is garbage:
If I put a break point in this function for lpszFileInZip:
Debug:
Release:
I can't work out why one is OK and one is not.
In Release 32 bit mode I do now notice that I get an exception that indicates the passed in parameter is not valid:
I'm a little bit desperate trying to get the result of a winrt asynchronous method with Windows::Foundation::IAsyncOperation interface. The project is a Visual Studio Community 2017 c++ project with winRT extension enabled.
I've tried using std::future / co_await functions and also task method but the call to GetResults() generated always "call at unexpected time" exception.
With the following code I don't get the exception but GetResults() returns nullptr. I have tried also to declare async_op as a shared_ptr auto async_op = std::make_shared<Windows::Foundation::IAsyncOperation<Windows::Devices::Bluetooth::BluetoothLEDevice^> ^> and access to it with *async_op inside the Handler code, but I get the same result.
Any ideas will be welcome.
Thanks in advance.
void BleEnumeration::PerformConnectDevice(std::string* bleDevId)
{
std::wstring bleDevId_w_str = std::wstring((*bleDevId).begin(), (*bleDevId).end());
const wchar_t* bleDevId_w_char = bleDevId_w_str.c_str();
Platform::String^ bleDevId_refStr = ref new Platform::String(bleDevId_w_char);
auto async_op = Windows::Devices::Bluetooth::BluetoothLEDevice::FromIdAsync(bleDevId_refStr);
async_op->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<Windows::Devices::Bluetooth::BluetoothLEDevice^>(
[async_op] (Windows::Foundation::IAsyncOperation< Windows::Devices::Bluetooth::BluetoothLEDevice^ >^ operation, Windows::Foundation::AsyncStatus status)
{
if (async_op->Status == Windows::Foundation::AsyncStatus::Completed)
{
auto bleDevTest = async_op->GetResults();
}
});
}
I made it work:
Windows::Devices::Bluetooth::BluetoothLEDevice^ BleEnumeration::getBleDevice(Windows::Foundation::IAsyncOperation<Windows::Devices::Bluetooth::BluetoothLEDevice^>^ async)
{
if ((async)->Status != Windows::Foundation::AsyncStatus::Completed)
{
HANDLE signal = CreateEvent(nullptr, true, false, nullptr);
(async)->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<Windows::Devices::Bluetooth::BluetoothLEDevice^>(
[&signal](Windows::Foundation::IAsyncOperation< Windows::Devices::Bluetooth::BluetoothLEDevice^ >^ operation, Windows::Foundation::AsyncStatus status)
{
SetEvent(signal);
});
WaitForSingleObject(signal, INFINITE);
}
Windows::Devices::Bluetooth::BluetoothLEDevice^ device = async->GetResults();
return device;
}
bool BleEnumeration::PerformConnectDevice(std::string* bleDevId)
{
bool success = false;
Windows::Devices::Bluetooth::BluetoothLEDevice^ bleDevTest = nullptr;
std::wstring bleDevId_w_str = std::wstring((*bleDevId).begin(), (*bleDevId).end());
const wchar_t* bleDevId_w_char = bleDevId_w_str.c_str();
Platform::String^ bleDevId_refStr = ref new Platform::String(bleDevId_w_char);
Windows::Foundation::IAsyncOperation<Windows::Devices::Bluetooth::BluetoothLEDevice^>^ asyncOp = Windows::Devices::Bluetooth::BluetoothLEDevice::FromIdAsync(bleDevId_refStr);
bleDevTest = getBleDevice(asyncOp);
if (bleDevTest != nullptr)
{
success = true;
}
return success;
}
And, the reason why GetResults() gave nullptr was this: Enable capability in appxmanifest
All needed capabilities must be enabled in Visual Studio project Package.appxmanifest file, in my case: "Bluetooth". I noticed I was getting error: onecoreuap\drivers\wdm\bluetooth\user\winrt\device\bluetoothledevice.cpp(728)\Windows.Devices.Bluetooth.dll!00007FFC1601AE2F: (caller: 00007FFC1602081A) Exception(1) tid(212c) 80070005 Access is denied.
Well, I'm trying to build this line of code, bit I get a compiler error. I've tryed to build without the compiler, but that didn't work either. Its about the __try and __except. Someone told me to move the code in the try block to another function. But I don't understand this:
Error 12 error C2712: Cannot use __try in functions that require
object unwinding
Error 437 error LNK1181: cannot open input file
void MSocketThread::Run()
{
__try{
//throw(pThread);
while (true) { // Waiting for SafeUDP Settting...
DWORD dwVal = WaitForSingleObject(m_KillEvent.GetEvent(), 100);
if (dwVal == WAIT_OBJECT_0) {
return;
}
else if (dwVal == WAIT_TIMEOUT) {
if (m_pSafeUDP)
break;
}
}
WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
WORD wEventIndex = 0;
bool bSendable = false;
WSANETWORKEVENTS NetEvent;
WSAEVENT hFDEvent = WSACreateEvent();
EventArray[wEventIndex++] = hFDEvent;
EventArray[wEventIndex++] = m_ACKEvent.GetEvent();
EventArray[wEventIndex++] = m_SendEvent.GetEvent();
EventArray[wEventIndex++] = m_KillEvent.GetEvent();
WSAEventSelect(m_pSafeUDP->GetLocalSocket(), hFDEvent, FD_READ | FD_WRITE);
while (TRUE) {
DWORD dwReturn = WSAWaitForMultipleEvents(wEventIndex, EventArray, FALSE, SAFEUDP_SAFE_MANAGE_TIME, FALSE);
if (dwReturn == WSA_WAIT_TIMEOUT) { // Time
m_pSafeUDP->LockNetLink();
SafeSendManage();
m_pSafeUDP->UnlockNetLink();
}
else if (dwReturn == WSA_WAIT_EVENT_0) { // Socket Event
WSAEnumNetworkEvents(m_pSafeUDP->GetLocalSocket(), hFDEvent, &NetEvent);
if ((NetEvent.lNetworkEvents & FD_READ) == FD_READ) {
// OutputDebugString("SUDP> FD_READ \n");
m_pSafeUDP->LockNetLink();
Recv();
m_pSafeUDP->UnlockNetLink();
}
if ((NetEvent.lNetworkEvents & FD_WRITE) == FD_WRITE) {
bSendable = true;
// OutputDebugString("SUDP> FD_WRITE \n");
}
}
else if (dwReturn == WSA_WAIT_EVENT_0 + 1) { // ACK Send Event
// OutputDebugString("SUDP> ACK_EVENT \n");
FlushACK();
}
else if (dwReturn == WSA_WAIT_EVENT_0 + 2) { // Packet Send Event
// OutputDebugString("SUDP> SEND_EVENT \n");
if (bSendable == true)
FlushSend();
}
else if (dwReturn == WSA_WAIT_EVENT_0 + 3) { // Kill the Thread
break; // Stop Thread
}
}
WSACloseEvent(hFDEvent);
// Clear Queues
LockSend();
{
for (SendListItor itor = m_SendList.begin(); itor != m_SendList.end();) {
delete (*itor);
itor = m_SendList.erase(itor);
}
}
{
for (SendListItor itor = m_TempSendList.begin(); itor != m_TempSendList.end();) {
delete (*itor);
itor = m_TempSendList.erase(itor);
}
}
UnlockSend();
LockACK();
{
for (ACKSendListItor itor = m_ACKSendList.begin(); itor != m_ACKSendList.end();) {
delete (*itor);
itor = m_ACKSendList.erase(itor);
}
}
{
for (ACKSendListItor itor = m_TempACKSendList.begin(); itor != m_TempACKSendList.end();) {
delete (*itor);
itor = m_TempACKSendList.erase(itor);
}
}
UnlockACK();
}
__except (this->CrashDump(GetExceptionInformation()))
Basically, SEH and C++ unwinding aren't exactly compatible; they require the compiler to modify the function on the machine code level, and MS apparently decided not to support the modifications for the two at the same time, so any function can only support either SEH unwind actions (__except or __finally) or C++ unwind actions (catch or objects with destructors).
I suspect the problem in your case are the iterators in your loops; they might have destructors. Although iterators are usually simple and don't need destructors, this is not the case for debug iterators, which often register their existence on construction and deregister it on destruction, in order to detect invalidated iterators and other invalid usage.
The usual workaround is to split the function. Make your run function contain just this:
void MSocketThread::Run()
{
__try {
RunNoSeh();
} __except (this->CrashDump(GetExceptionInformation())) {
}
}
void MSocketThread::RunNoSeh()
{
// Code that was inside the __try goes here.
}
What I'm trying to do is check for the existence of a file in the local folder and then copy it there if it isn't found (the file was previously added to the project as an asset).
Here is the code:
Windows::Storage::StorageFile^ MainPage::GetCustomFileAsync(Platform::String^ fileName)
{
using Windows::Storage::StorageFile;
using Windows::Storage::StorageFolder;
auto localFolder = Windows::Storage::ApplicationData::Current->LocalFolder;
auto localTask = concurrency::create_task(localFolder->GetFileAsync(fileName));
StorageFile^ retVal = nullptr;
localTask.then([&](StorageFile^ t){
retVal = t;
}).then([](concurrency::task<void> t)
{
try
{
t.get();
OutputDebugString(L"Found\n");
}
catch (Platform::COMException^ e)
{
OutputDebugString(e->Message->Data());
}
}).wait();
return retVal;
}
StorageFile^ fileVar;
if ((fileVar = this->GetCustomFileAsync("somefile.txt")) == nullptr)
{
String^ path = Windows::ApplicationModel::Package::Current->InstalledLocation->Path + "\\Assets";
concurrency::create_task(Windows::Storage::StorageFolder::GetFolderFromPathAsync(path)).then([](StorageFolder^ folder){
return (folder->GetFileAsync("somefile.txt"));
}).then([](StorageFile^ file){
return (file->CopyAsync(Windows::Storage::ApplicationData::Current->LocalFolder));
}).then([&](StorageFile^ file){
fileVar = file;
OutputDebugString(file->DisplayName->Data());
});
}
What happens is that I get an access violation exception at the point where "file" is being assigned to "fileVar" (because of cross-thread access perhaps?). How to fix this?
Edit: I can't do all the processing there because the file will be accessed many times. In short I need to know when it has been successfully copied and get a handle to it. Here is the code that works
Windows::Storage::StorageFile^ GetFile(Platform::String^ fileName)
{
using Windows::Storage::StorageFile;
using Windows::Storage::StorageFolder;
using Windows::Foundation::AsyncOperationCompletedHandler;
using Windows::Foundation::AsyncStatus;
using Windows::Foundation::IAsyncOperation;
using Platform::String;
auto localFolder = Windows::Storage::ApplicationData::Current->LocalFolder;
bool completed = false;
StorageFile^ retVal = nullptr;
localFolder->GetFileAsync(fileName)->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>([&completed, &retVal, &fileName](IAsyncOperation<StorageFile^>^ fileOperation, AsyncStatus status)
{
if (status == AsyncStatus::Error)
{
String^ path = Windows::ApplicationModel::Package::Current->InstalledLocation->Path + "\\Assets";
Windows::Storage::StorageFolder::GetFolderFromPathAsync(path)->Completed = ref new AsyncOperationCompletedHandler<Windows::Storage::StorageFolder^>(
[&completed, &retVal, &fileName](IAsyncOperation<Windows::Storage::StorageFolder^>^ folderOperation, AsyncStatus status)->void{
auto assetFolder = folderOperation->GetResults();
assetFolder->GetFileAsync(fileName)->Completed = ref new AsyncOperationCompletedHandler<Windows::Storage::StorageFile^>([&completed, &retVal, &fileName](IAsyncOperation<Windows::Storage::StorageFile^>^ fileOperation, AsyncStatus status)->void{
auto file = fileOperation->GetResults();
file->CopyAsync(Windows::Storage::ApplicationData::Current->LocalFolder)->Completed = ref new AsyncOperationCompletedHandler<Windows::Storage::StorageFile^>
([&completed, &retVal, &fileName](IAsyncOperation<Windows::Storage::StorageFile^>^ fileOperation, AsyncStatus status)->void {
retVal = fileOperation->GetResults();
completed = true;
});
});
});
}
else
{
retVal = fileOperation->GetResults();
completed = true;
}
});
while (completed == false);
return retVal;
}
Rather than passing a delegate as an argument and returning void, make your method return task<StorageFile^> and then the caller can do a .then() to continue working once the operation has succeeded.
Or if this is exposed as a public WinRT method (not an internal / private C++ method) then use IAsyncOperation<StorageFile^>^ as the return type, and wrap the whole thing in create_async():
IAsyncOperation<StorageFile^>^ DoStuff(params)
{
return concurrency::create_async([params]
{
// function body goes here
});
}
Here's a solution I put together. Two things that are important to know:
When executing an asynchronous operation using concurrency::create_task the async operation(s) can still be executing when the parent function returns. So the captured variables MUST outlive the context of the parent function. Which obviously won't happen if they are being passed by reference. It took a while to realize this.
WinRT imposes certain restrictions on the concurrency runtime. Calling concurrency::task::get() or concurrency::task::wait() will throw an exception in an STA thread, unless the call is in a task continuation.
More information in this post:
http://social.msdn.microsoft.com/Forums/windowsapps/en-US/ae54980b-41ce-4337-a059-2213b549be4b/concurrencyinvalidoperation-when-calling-tasktget?forum=winappswithnativecode
In that case how to know when the function has finished doing it's job? I opted to pass in a callback (AKA delegate).
delegate void FileOperation(Windows::Storage::StorageFile^ file);
void GetFileConcurrency(Platform::String^ fileName, FileOperation^ fileOp)
{
using Windows::Storage::StorageFile;
using Windows::Storage::StorageFolder;
using Platform::String;
auto localFolder = Windows::Storage::ApplicationData::Current->LocalFolder;
String^ assetFolderPath = Windows::ApplicationModel::Package::Current->InstalledLocation->Path + "\\Assets";
auto localFolderTask = concurrency::create_task(localFolder->GetFileAsync(fileName));
localFolderTask.then([localFolder, assetFolderPath, fileName, fileOp](concurrency::task<StorageFile^> theTask){
try
{
StorageFile^ theFile = theTask.get();
fileOp(theFile);
}
catch (Platform::Exception^ e)
{
OutputDebugString(e->Message->Data());
auto assetFolderTask = concurrency::create_task(StorageFolder::GetFolderFromPathAsync(assetFolderPath));
assetFolderTask.then([localFolder, assetFolderPath, fileName, fileOp](StorageFolder^ assetFolder){
auto assetFileTask = concurrency::create_task(assetFolder->GetFileAsync(fileName));
assetFileTask.then([localFolder, assetFolderPath, fileName, fileOp](StorageFile^ file){
auto copyFileTask = concurrency::create_task(file->CopyAsync(localFolder));
copyFileTask.then([localFolder, assetFolderPath, fileName, fileOp](StorageFile^ file){
OutputDebugString(file->Path->Data());
fileOp(file);
});
});
});
}
});
}
I have the following workflow in my (web)application:
download a pdf file from an archive
index the file
delete the file
My problem is that after indexing the file, it remains locked and the delete-part throws an exception.
Here is my code-snippet for indexing the file:
try
{
ContentStreamUpdateRequest req = new ContentStreamUpdateRequest("/update/extract");
req.addFile(file, type);
req.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true);
NamedList<Object> result = server.request(req);
Assert.assertEquals(0, ((NamedList<?>) result.get("responseHeader")).get("status"));
}
Do I miss something?
EDIT:
I tried this way too, but with the same result...
ContentStream contentStream = null;
try
{
contentStream = new ContentStreamBase.FileStream(document);
ContentStreamUpdateRequest req = new ContentStreamUpdateRequest(UPDATE_EXTRACT_REQUEST);
// req.addFile(document, context.getProperty(FTSConstants.CONTENT_TYPE_APPLICATION_PDF));
req.addContentStream(contentStream);
req.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true);
NamedList<Object> result = server.request(req);
if (!((NamedList<?>) result.get("responseHeader")).get("status").equals(0))
{
throw new IDSystemException(LOG, "Document could not be indexed. Status returned: " +
((NamedList<?>) result.get("responseHeader")).get("status"));
}
}
catch (FileNotFoundException fnfe)
{
throw new IDSystemException(LOG, fnfe.getMessage(), fnfe);
}
catch (IOException ioe)
{
throw new IDSystemException(LOG, ioe.getMessage(), ioe);
}
catch (SolrServerException sse)
{
throw new IDSystemException(LOG, sse.getMessage(), sse);
}
finally
{
try
{
if(contentStream != null && contentStream.getStream() != null)
{
contentStream.getStream().close();
}
}
catch (IOException ioe)
{
throw new IDSystemException(LOG, ioe.getMessage(), ioe);
}
}
This seems like a bug,
a patch is proposed here
https://issues.apache.org/jira/browse/SOLR-1744
Also checkout
http://lucene.472066.n3.nabble.com/ContentStreamUpdateRequest-addFile-fails-to-close-Stream-td485429.html
you can check if the stream is not null and close it.
It may be due to lock acquired by file system. Instead of addFile(), you can try the following.
ContentStreamUpdateRequest req = new ContentStreamUpdateRequest("/update/extract");
ContentStreamBase.FileStream fileStream = new FileStream(file);
req.addContentStream(fileStream);
Shishir