我正在使用 Flutter Method Channel 开发 Windows 桌面应用程序,并且遇到了应用内购买功能的问题。触发后,购买模式对话框出现在主应用程序窗口后面,使其不可见。
这是发生的事情:
RequestPurchaseAsync
在 UI 线程上运行,这是预期的。尝试使用
BringWindowToTop(hwnd)
和 SetForegroundWindow(hwnd)
尚未成功,因为它们会影响整个应用程序,而不仅仅是弹出窗口。
我正在寻找一种解决方案,将应用内购买弹出窗口带到最前面,但我对 C++ 和 Win32 的了解非常有限。
#include <Unknwn.h>
#include <flutter/event_channel.h>
#include <flutter/event_sink.h>
#include <flutter/event_stream_handler_functions.h>
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>
#include <winrt/Windows.Services.Store.h>
#include <winrt/Windows.Foundation.h>
#include <windows.h>
#include <memory>
#include <optional>
#include <string>
#include <future>
#include <shobjidl.h>
#include "flutter_window.h"
#include "flutter/generated_plugin_registrant.h"
using namespace winrt;
using namespace Windows::Services::Store;
std::string store_id = "ABCDEFASD";
std::string success_key = "SUCCESS";
StoreContext storeContext = StoreContext::GetDefault();
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
: project_(project) {}
FlutterWindow::~FlutterWindow() {}
std::string upgrade() {
if (!storeContext) {
return "storeContext not initialized";
}
// Retrieve the IInitializeWithWindow interface from the StoreContext
auto initWindow = storeContext.as<IInitializeWithWindow>();
// Get the current process main window handle
HWND hwnd = GetActiveWindow();
// Initialize the StoreContext with the window handle
initWindow->Initialize(hwnd);
try {
std::future<StorePurchaseResult> purchaseTask = std::async(std::launch::async, [&]() {
return storeContext.RequestPurchaseAsync(winrt::to_hstring(store_id)).get();
});
StorePurchaseResult result = purchaseTask.get();
if (result.ExtendedError().value != S_OK) {
return "ExtendedError";
}
switch (result.Status())
{
case StorePurchaseStatus::Succeeded:
return success_key;
case StorePurchaseStatus::AlreadyPurchased:
return "Already Purchased";
case StorePurchaseStatus::NotPurchased:
return "Purchase Not Completed";
case StorePurchaseStatus::NetworkError:
return "Network Error";
case StorePurchaseStatus::ServerError:
return "Server Error";
default:
return "Unknown Error";
}
}
catch (const winrt::hresult_error& ex) {
auto errorMessage = winrt::to_string(ex.message());
return "Exception: " + errorMessage;
}
catch (const std::exception& ex) {
return "Exception: " + std::string(ex.what());
}
}
bool FlutterWindow::OnCreate() {
if (!Win32Window::OnCreate()) {
return false;
}
RECT frame = GetClientArea();
// The size here must match the window dimensions to avoid unnecessary surface
// creation / destruction in the startup path.
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
frame.right - frame.left, frame.bottom - frame.top, project_);
// Ensure that basic setup of the controller was successful.
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
return false;
}
RegisterPlugins(flutter_controller_->engine());
// Method Channel
flutter::MethodChannel<> channel(
flutter_controller_->engine()->messenger(), "method.channel.id",
&flutter::StandardMethodCodec::GetInstance());
channel.SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<>> result) {
if (call.method_name() == "purchase") {
std::string purchaseResult = upgrade();
if (purchaseResult == success_key) {
result->Success();
}
else {
result->Error("ERROR", purchaseResult);
}
}
else {
result->NotImplemented();
}
});
SetChildContent(flutter_controller_->view()->GetNativeWindow());
flutter_controller_->engine()->SetNextFrameCallback([&]() {
this->Show();
});
// Flutter can complete the first frame before the "show window" callback is
// registered. The following call ensures a frame is pending to ensure the
// window is shown. It is a no-op if the first frame hasn't completed yet.
flutter_controller_->ForceRedraw();
return true;
}
void FlutterWindow::OnDestroy() {
if (flutter_controller_) {
flutter_controller_ = nullptr;
}
Win32Window::OnDestroy();
}
LRESULT
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
// Give Flutter, including plugins, an opportunity to handle window messages.
if (flutter_controller_) {
std::optional<LRESULT> result =
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
lparam);
if (result) {
return *result;
}
}
switch (message) {
case WM_FONTCHANGE:
flutter_controller_->engine()->ReloadSystemFonts();
break;
}
return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
}
经过几个小时的努力,我终于找到了解决方案。下面的代码有效地在中心显示应用内购买弹出窗口,并确保它位于前台的显着位置。
之前:
HWND hwnd = GetActiveWindow();
initWindow->Initialize(hwnd);
之后:
HWND activeHwnd = GetActiveWindow();
HWND parentHwnd = GetAncestor(activeHwnd, GA_PARENT);
initWindow->Initialize(parentHwnd);