问题
I'm debugging an example app that deploys an Windows Metro App Package (".Appx" file). It call a WinRT method "PackageManager.AddPackageAsync" which fails with detailed error code text (retrieved from the call return value after the operation was finished):
error 0x80070002: Windows cannot register the package because of an internal error or low memory.
My target is to find where exactly this error arises in the WinRT call. I think the best way for achieving this is by finding where the error code is set. I've done this before with the old simple Win32 API but now with this new complex com-based, async Interface I got completely lost.
The example project files can be found at. Its main function looks like this:
[MTAThread]
int __cdecl main(Platform::Array<String^>^ args)
{
wcout << L"Copyright (c) Microsoft Corporation. All rights reserved." << endl;
wcout << L"AddPackage sample" << endl << endl;
if (args->Length < 2)
{
wcout << "Usage: AddPackageSample.exe packageUri" << endl;
return 1;
}
HANDLE completedEvent = nullptr;
int returnValue = 0;
String^ inputPackageUri = args[1];
try
{
completedEvent = CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
if (completedEvent == nullptr)
{
wcout << L"CreateEvent Failed, error code=" << GetLastError() << endl;
returnValue = 1;
}
else
{
auto packageUri = ref new Uri(inputPackageUri);
auto packageManager = ref new PackageManager();
auto deploymentOperation = packageManager->AddPackageAsync(packageUri, nullptr, DeploymentOptions::None);
deploymentOperation->Completed =
ref new AsyncOperationWithProgressCompletedHandler<DeploymentResult^, DeploymentProgress>(
[&completedEvent](IAsyncOperationWithProgress<DeploymentResult^, DeploymentProgress>^ operation, AsyncStatus)
{
SetEvent(completedEvent);
});
wcout << L"Installing package " << inputPackageUri->Data() << endl;
wcout << L"Waiting for installation to complete..." << endl;
WaitForSingleObject(completedEvent, INFINITE);
if (deploymentOperation->Status == AsyncStatus::Error) //Here I decided to track "deploymentOperation->Status"
{
auto deploymentResult = deploymentOperation->GetResults();
wcout << L"Installation Error: " << deploymentOperation->ErrorCode.Value << endl;
wcout << L"Detailed Error Text: " << deploymentResult->ErrorText->Data() << endl;
}
else if (deploymentOperation->Status == AsyncStatus::Canceled)
{
wcout << L"Installation Canceled" << endl;
}
else if (deploymentOperation->Status == AsyncStatus::Completed)
{
wcout << L"Installation succeeded!" << endl;
}
}
}
catch (Exception^ ex)
{
wcout << L"AddPackageSample failed, error message: " << ex->ToString()->Data() << endl;
returnValue = 1;
}
if (completedEvent != nullptr)
CloseHandle(completedEvent);
return returnValue;
}
As the operation (PackageManager.AddPackageAsync) should be async and am not really sure how to track the code executed in the new thread I decided to search where the "deploymentOperation->Status" variable was set (it appeared that this was actually a function call) to "AsyncStatus::Error" (or the integer 3). After I gone through a LOT of code and function calls I understand that whenever this variable will be set or no (it seems it doesn't matter BUT it's certain that this function retrieves the operation error data) depends of a member of variable initialised by an Undocumented ntdll function call named "NtGetCompleteWnfStateSubscription" by it's pointer. It was called from ntdll too. The structure of the variable member I mentioned is the following:
struct Unknown
{
AsyncStatus /*? 32bit long*/ dw0; // it was set to '3' during the current operation as the AsyncStatus::error enum value so I think it should belong to it
DWORD dw4; //was set to 0x5F
DWORD dw8; //was set to 0x80073CF6 (some generic error)
} ;
The code in ntdll where the "NtGetCompleteWnfStateSubscription" function was called, initing the variable which member have this structure type (in assembly, IDA PRO generated):
ntdll.dll:77906200 loc_77906200: ; CODE XREF: ntdll.dll:ntdll_strncpy_s+1A3j
ntdll.dll:77906200 push 1030h
ntdll.dll:77906205 push esi ; the variable pointer
ntdll.dll:77906206 push edi
ntdll.dll:77906207 push eax
ntdll.dll:77906208 lea eax, [ebp-0Ch]
ntdll.dll:7790620B push eax
ntdll.dll:7790620C push ebx
ntdll.dll:7790620D call near ptr ntdll_NtGetCompleteWnfStateSubscription
ntdll.dll:77906212 test eax, eax ; now "[esi+2Ch] + esi" contains data from the "Unkown" structure and contains the operation error data
ntdll.dll:77906214 jns short loc_7790623F
The above code is actually called 3 times but with the same "esi" pointer. So now my question is how to find where the error code is set to be retrieved using this function. I tried capturing most of the function in ntdll that looks like doing this but without success. I can't debug NtGetCompleteWnfStateSubscription for some strange reason. Any suggestions will be helpful. I'm using IDA PRO 6.5, VS 2013 U1, Windows 8.1 x64 U1.
EDIT: If you don't want to bother with problem specific details my generic question is how to locate where WinRT async methods sets the "IAsyncInfo::Status" property or what function or method is called when an error arrises while executing them.
来源:https://stackoverflow.com/questions/26003511/how-to-locate-where-an-error-arises-in-a-packagemanager-addpackageasync-method