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);
});
});
});
}
});
}
Related
I am storing js callbacks in vector:
std::vector<std::unique_ptr<Persistent<Function>>> callbacks;
Everything is working fine. The problem is that I do not want to store duplicated callbacks because I do not want to notify the same callback twice. I have to compare them somehow. Here is my full function but that does not work:
void ProcessCallback(const FunctionCallbackInfo<Value>& args)
{
std::string returnInfo;
Isolate* isolate = args.GetIsolate();
Local<Function> notifyFunction = Local<Function>::Cast(args[0]);
auto predicate = [&](const std::unique_ptr<Persistent<Function>>& c)
{
return c->Get(isolate)->StrictEquals(notifyFunction);
};
auto it = std::find_if(callbacks.begin(), callbacks.end(), predicate);
if (it == callbacks.end())
{
returnInfo = "Did not find callback. Adding..." + std::to_string(callbacks.size());
auto persistentCallback = std::make_unique<Persistent<Function>>(isolate, notifyFunction);
callbacks.emplace_back(std::move(persistentCallback));
}
else
{
returnInfo = "Callback already exist in a list.\n";
}
args.GetReturnValue().Set(String::NewFromUtf8(isolate, returnInfo.c_str()).ToLocalChecked());
}
In js:
function callback(msg) {
console.log(msg);
}
let addon = require('./build/Release/addon');
console.log(addon.on(callback));
console.log(addon.on(callback));
Is there something that I can rely on to uniquely identify function that is passed from js? Thanks.
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.
This is not C++/CLI. This is UWP C++/CX
I am trying to send an HttpRequestMessage outside of a managed class in C++. I looked at the UWP samples, but their requests occur inside of a managed class.
All I want to do is send the request, and then have a callback function. I don't need fancy async/await patterns. This is looking to be a lot more difficult than it should be.
EDIT: I have gotten it to work, but the error handling is atrocious. The extra error handling code from the UWP HttpClient example was not compiling.
client = ref new Windows::Web::Http::HttpClient();
client->DefaultRequestHeaders->UserAgent->Append(ref new Windows::Web::Http::Headers::HttpProductInfoHeaderValue("Windows", "10"));
cancellation_token_source cancellationTokenSource = cancellation_token_source();
create_task(client->SendRequestAsync(message)).then([=](Windows::Web::Http::HttpResponseMessage^ response)
{
auto operation = response->Content->ReadAsBufferAsync();
auto task = create_task(operation);
if (task.wait() == task_status::completed)
{
webResponse->statusCode = (int)response->StatusCode;
auto buffer = task.get();
size_t length = buffer->Length;
if (length > 0)
{
Array<byte>^ array = nullptr;
CryptographicBuffer::CopyToByteArray(buffer, &array);
webResponse->contentLength = array->Length;
webResponse->data = (byte*)malloc(webResponse->contentLength);
memcpy(webResponse->data, array->Data, webResponse->contentLength);
delete array;
}
for each(IKeyValuePair<String^, String^>^ pair in response->Headers)
{
std::string key = PlatformStringToString(pair->Key);
std::string value = PlatformStringToString(pair->Value);
if (key == "Content-Type" && false)
{
// Should have this for completeness, but do we really care?
}
else
{
Web::WebHeader *header = new Web::WebHeader(key.c_str(), value.c_str());
webResponse->AddHeader(header);
}
}
if (request->receiveDoneCallback)
request->receiveDoneCallback(webResponse, request->userPtr);
}
else
abort();
delete request;
delete response;
});
I'm having some trouble with the following method and I need some help trying to figure out what I am doing wrong.
I want to return a reference to a Value in a document. I am passing the Document from outside the function so that when I read a json file into it I don't "lose it".
const rapidjson::Value& CTestManager::GetOperations(rapidjson::Document& document)
{
const Value Null(kObjectType);
if (m_Tests.empty())
return Null;
if (m_current > m_Tests.size() - 1)
return Null;
Test& the_test = m_Tests[m_current];
CMyFile fp(the_test.file.c_str()); // non-Windows use "r"
if (!fp.is_open())
return Null;
u32 operations_count = 0;
CFileBuffer json(fp);
FileReadStream is(fp.native_handle(), json, json.size());
if (document.ParseInsitu<kParseCommentsFlag>(json).HasParseError())
{
(...)
}
else
{
if (!document.IsObject())
{
(...)
}
else
{
auto tests = document.FindMember("td_tests");
if (tests != document.MemberEnd())
{
for (SizeType i = 0; i < tests->value.Size(); i++)
{
const Value& test = tests->value[i];
if (test["id"].GetInt() == the_test.id)
{
auto it = test.FindMember("operations");
if (it != test.MemberEnd())
{
//return it->value; is this legitimate?
return test["operations"];
}
return Null;
}
}
}
}
}
return Null;
}
Which I am calling like this:
Document document;
auto operations = TestManager().GetOperations(document);
When I inspect the value of test["operations"] inside the function I can see everything I would expect (debug code removed from the abode code).
When I inspect the returned value outside the function I can see that it's an array (which I expect). the member count int the array is correct as well, but when print it out, I only see garbage instead.
When I "print" the Value to a string inside the methods, I get what I expect (i.e. a well formated json), but when I do it outside all keys show up as "IIIIIIII" and values that aren't strings show up correctly.
rapidjson::StringBuffer strbuf2;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer2(strbuf2);
ops->Accept(writer2);
As this didn't work I decided to change the method to receive a Value as a parameter and do a deep copy into it like this
u32 CTestManager::GetOperationsEx(rapidjson::Document& document, rapidjson::Value& operations)
{
(...)
if (document.ParseInsitu<kParseCommentsFlag>(json).HasParseError())
{
(...)
}
else
{
if (!document.IsObject())
{
(...)
}
else
{
auto tests = document.FindMember("tests");
if (tests != document.MemberEnd())
{
for (SizeType i = 0; i < tests->value.Size(); i++)
{
const Value& test = tests->value[i];
if (test["id"].GetInt() == the_test.id)
{
const Value& opv = test["operations"];
Document::AllocatorType& allocator = document.GetAllocator();
operations.CopyFrom(opv, allocator); //would Swap work?
return operations.Size();
}
}
}
}
}
return 0;
}
Which I'm calling like this:
Document document;
Value operations(kObjectType);
u32 count = TestManager().GetOperationsEx(document, operations);
But... I get same thing!!!!
I know that it's going to be something silly but I can't put my hands on it!
Any ideas?
The problem in this case lies with the use of ParseInSitu. When any of the GetOperations exist the CFileBuffer loses scope and is cleaned up. Because the json is being parsed in-situ when the buffer to the file goes, so goes the data.
I'm using C# to call methods from an dll file to record video.
This is the Marshal methods file
http://pastebin.com/YrVvBfZ9
This is my CameraUtilities file
http://pastebin.com/0AZNtnhk
This is my camera file
http://pastebin.com/ZE3HD1zq
When I call StartRecord method (in camera file) to start Record video.
public void StartRecord()
{
if (this.ListCameras != null)
{
bool bRes = true;
try
{
int tmpError = -1;
m_tmpContext = new IntPtr();
m_handle = new GCHandle();
m_callBackChannels = new ulong[m_ListCameras.Length];
for (int i = 0; i < m_ListCameras.Length; i++)
{
m_callBackChannels[i] = 10;
cameraCurrentIndex = 0;
IntPtr channelHandle = T1800.T18_ChannelOpen(cameraCurrentIndex);
m_ListCameras[cameraCurrentIndex].ChannelHandle = channelHandle;
T1800.T18_CaptureIFrame(m_ListCameras[i].ChannelHandle);
m_ListCameras[i].BeginRecord();
if (AllowRecordVideo)
{
m_del = new T1800.STREAM_READ_CALLBACK(StreamReadCallBack);
m_tmpContext = m_ListCameras[i].ChannelHandle;
tmpError = T1800.T18_RegisterStreamReadCallback(m_del, ref m_tmpContext);
}
}
if (tmpError == -1) bRes = false;
}
catch (Exception ex)
{
Log.Logger.Error(ex);
bRes = false;
}
}
}
It throw an exception
- System.Runtime.InteropServices.MarshalDirectiveException: Invalid PInvoke calling convention.
Thiscall requires that the first parameter is present and can be enregistered.
at System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegateInternal(Delegate d)
at System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(Delegate d)
at TH.Parking.Wrapper.HBCamera.T1800.dll_T18_RegisterStreamReadCallback(STREAM_READ_CALLBACK STREAM_READ_CALLBACK, IntPtr& context)
at TH.Parking.Wrapper.HBCamera.CameraUtilities.StartRecord() in d:\TH.Parking\TH.Parking\Wrapper\HBCamera\CameraUtilities.cs:line 93
I can't find any reason for this error. Can somebody help me to fix this.
I think the callback you pass to create the STREAM_READ_CALLBACK object should be static. Otherwise, the thispointer is lost during the marshalling.
Instead of:
private int StreamReadCallBack(ulong channelHandle, IntPtr context)
{
...
}
try:
private static int StreamReadCallBack(ulong channelHandle, IntPtr context)
{
...
}
And you'll need to somehow put your instance of CameraUtility into the context parameter.