如何在Native侧获取窗口ID
================

#### 问题现象

在进行系统级功能开发、输入事件拦截、无障碍服务、屏幕录制、窗口管理时，如何在Native侧需要获取窗口的ID，以便后续的功能开发。

#### 背景知识

HarmonyOS的窗口模块将窗口界面分为系统窗口、应用窗口两种基本类型。

*   系统窗口：系统窗口指完成系统特定功能的窗口。如音量条、壁纸、通知栏、状态栏、导航栏等。
*   应用窗口：应用窗口区别于系统窗口，指与应用显示相关的窗口。根据显示内容的不同，应用窗口又分为应用主窗口、应用子窗口两种类型。应用窗口开发可以参考[窗口类型开发概述](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/window-type-overview)。
    *   应用主窗口：应用主窗口用于显示应用界面，会在"任务管理界面"显示。
    *   应用子窗口：应用子窗口用于显示应用的弹窗、悬浮窗等辅助窗口，不会在"任务管理界面"显示。应用子窗口的生命周期跟随应用主窗口。设置方式参考[子窗口开发指导](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/subwindow-guide)。

ArkTS与Native跨语言交互开发详见[使用Node-API实现跨语言交互开发流程](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/use-napi-process)。

#### 解决方案

实现Native侧获取窗口ID可参考如下步骤：

1.  由于在Native侧无法直接获取window实例，需要在ArkTS侧EntryAbility的onWindowStageCreate生命周期中获取。
    
    onWindowStageCreate(windowStage: window.WindowStage): void {
     _ // Main window is created,set main page for this ability_
      hilog.info(0x0000, 'testTag', '%{public}s','Ability onWindowStageCreate');
      _// 1.获取应用主窗口。_
      let windowClass: window.Window | null = null;
      windowStage.getMainWindow((err: BusinessError, data) => {
        let errCode: number = err.code;
        if (errCode) {
          console.error(\`Failed to obtain the main window. CCode:${err.code}, message:${err.message}\`);
          return;
        }
        windowClass = data;
      _  // 通过windowClass的getWindowProperties方法获取id信息_
        this.windowID = windowClass.getWindowProperties().id;
        AppStorage.setOrCreate<number>('windowID', this.windowID);
        console.info(\`Succeeded in obtaining the main window. Result:${data}\`);
      });
      windowStage.loadContent('pages/Index', (err) => {
        if (err.code) {
          hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
          return;
        }
        hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
      });
    }
    
2.  获取到windowID后，调用Native方法将windowID传入Native侧。
    
    import testNapi from 'libentry.so';
    
    @Entry
    @Component
    struct Index {
      @StorageLink('windowID') windowID: number = 0;
      build() {
        Row() {
          Column() {
            Text('setWindowID')
              .fontSize($r('app.float.page\_text\_font\_size'))
              .fontWeight(FontWeight.Bold)
              .onClick(() => {
                testNapi.setWindowID(this.windowID);
              });
          }
          .width('100%');
        }
        .height('100%');
      }
    }
    
3.  在Native侧接收到windowID后，将windowID进行固化用于后续使用。
    
    int32\_t g\_WindowID;
    static napi\_value setWindowID(napi\_env env, napi\_callback\_info info)
    {
        size\_t argc = 1;
        napi\_value args\[1\] = {nullptr};
        napi\_typedarray\_type type\_napi;
    
        napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
    
        napi\_get\_value\_int32(env, args\[0\], &g\_WindowID);
    
        OH\_LOG\_Print(LOG\_APP, LOG\_INFO, 0x0, "setWindowID", "get windowID %{public}d", g\_WindowID);
    
        return nullptr;
    }
    

完整代码：

entry>src>main>ets>entryability>EntryAbility.ets:

import { ConfigurationConstant, UIAbility } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
const DOMAIN = 0x0000;

export default class EntryAbility extends UIAbility {
  windowID: number = 0;
  onCreate(): void {
    this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR\_MODE\_NOT\_SET);
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
  }

  onDestroy(): void {
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
  }
  onWindowStageCreate(windowStage: window.WindowStage): void {
 _   // Main window is created,set main page for this ability_
    hilog.info(0x0000, 'testTag', '%{public}s','Ability onWindowStageCreate');
  _  // 1.获取应用主窗口。_
    let windowClass: window.Window | null = null;
    windowStage.getMainWindow((err: BusinessError, data) => {
      let errCode: number = err.code;
      if (errCode) {
        console.error(\`Failed to obtain the main window. CCode:${err.code}, message:${err.message}\`);
        return;
      }
      windowClass = data;
   _   // 通过windowClass的getWindowProperties方法获取id信息_
      this.windowID = windowClass.getWindowProperties().id;
      AppStorage.setOrCreate<number>('windowID', this.windowID);
      console.info(\`Succeeded in obtaining the main window. Result:${data}\`);
    });
    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
    });
  }

  onWindowStageDestroy(): void {
   _ // Main window is destroyed, release UI related resources_
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
  }

  onForeground(): void {
 _   // Ability has brought to foreground_
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
  }

  onBackground(): void {
  _  // Ability has back to background_
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
  }
};

entry>src>main>ets>pages>Index.ets:

import testNapi from 'libentry.so';

@Entry
@Component
struct Index {
  @StorageLink('windowID') windowID: number = 0;
  build() {
    Row() {
      Column() {
        Text('setWindowID')
          .fontSize($r('app.float.page\_text\_font\_size'))
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            testNapi.setWindowID(this.windowID);
          });
      }
      .width('100%');
    }
    .height('100%');
  }
}

entry>src>main>cpp>types>libentry>index.d.ts:

export const setWindowID: (windowID: number) => void;

entry>src>main>cpp>napi\_init.cpp:

/\*
 \* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 \*/
#include "napi/native\_api.h"
#include "hilog/log.h"
int32\_t g\_WindowID;
static napi\_value setWindowID(napi\_env env, napi\_callback\_info info)
{
    size\_t argc = 1;
    napi\_value args\[1\] = {nullptr};
    napi\_typedarray\_type type\_napi;

    napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);

    napi\_get\_value\_int32(env, args\[0\], &g\_WindowID);

    OH\_LOG\_Print(LOG\_APP, LOG\_INFO, 0x0, "setWindowID", "get windowID %{public}d", g\_WindowID);

    return nullptr;
}
EXTERN\_C\_START
static napi\_value Init(napi\_env env, napi\_value exports)
{
    napi\_property\_descriptor desc\[\] = {
        { "setWindowID", nullptr, setWindowID, nullptr, nullptr, nullptr, napi\_default, nullptr }
    };
    napi\_define\_properties(env, exports, sizeof(desc) / sizeof(desc\[0\]), desc);
    return exports;
}
EXTERN\_C\_END

static napi\_module demoModule = {
    .nm\_version = 1,
    .nm\_flags = 0,
    .nm\_filename = nullptr,
    .nm\_register\_func = Init,
    .nm\_modname = "entry",
    .nm\_priv = ((void\*)0),
    .reserved = { 0 },
};

extern "C" \_\_attribute\_\_((constructor)) void RegisterEntryModule(void)
{
    napi\_module\_register(&demoModule);
}

#### 常见FAQ

Q：Native侧有办法对窗口进行管理和控制么？

A：可以通过[WindowManager](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-windowmanager)模块的C API对窗口进行控制，由于其接口均需要windowID作为入参，需要在调用前获取到windowID。

Q：如何传入多个windowID？

A：如果需要传入多个windowID可以通过构建Int32Array进行填充，在Native侧创建数组接收。