feat: scaffold OpenLess TSF IME DLL
This commit is contained in:
1 parent
39d766d44d
commit
e950d1ddc7
11 files changed
+626
No files matched your search
@@ -0,0 +1,21 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{BC8A1FFA-BEE3-4634-8014-F334798102B3}") = "OpenLessIme", "OpenLessIme.vcxproj", "{F45287CB-1F31-4D7C-9B62-5748C2F8C9B0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F45287CB-1F31-4D7C-9B62-5748C2F8C9B0}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F45287CB-1F31-4D7C-9B62-5748C2F8C9B0}.Debug|x64.Build.0 = Debug|x64
|
||||
{F45287CB-1F31-4D7C-9B62-5748C2F8C9B0}.Release|x64.ActiveCfg = Release|x64
|
||||
{F45287CB-1F31-4D7C-9B62-5748C2F8C9B0}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{F45287CB-1F31-4D7C-9B62-5748C2F8C9B0}</ProjectGuid>
|
||||
<RootNamespace>OpenLessIme</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings" />
|
||||
<ImportGroup Label="Shared" />
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_WINDOWS;_USRDLL;OPENLESSIME_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<AdditionalDependencies>msctf.lib;ole32.lib;uuid.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;OPENLESSIME_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>msctf.lib;ole32.lib;uuid.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\class_factory.cpp" />
|
||||
<ClCompile Include="src\dllmain.cpp" />
|
||||
<ClCompile Include="src\registry.cpp" />
|
||||
<ClCompile Include="src\text_service.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\class_factory.h" />
|
||||
<ClInclude Include="src\guids.h" />
|
||||
<ClInclude Include="src\registry.h" />
|
||||
<ClInclude Include="src\text_service.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="src\resource.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "class_factory.h"
|
||||
|
||||
#include <new>
|
||||
#include <windows.h>
|
||||
|
||||
#include "text_service.h"
|
||||
|
||||
extern LONG g_lock_count;
|
||||
extern LONG g_object_count;
|
||||
|
||||
OpenLessClassFactory::OpenLessClassFactory() {
|
||||
InterlockedIncrement(&g_object_count);
|
||||
}
|
||||
|
||||
OpenLessClassFactory::~OpenLessClassFactory() {
|
||||
InterlockedDecrement(&g_object_count);
|
||||
}
|
||||
|
||||
STDMETHODIMP OpenLessClassFactory::QueryInterface(REFIID iid, void** object) {
|
||||
if (object == nullptr) {
|
||||
return E_POINTER;
|
||||
}
|
||||
*object = nullptr;
|
||||
|
||||
if (iid == IID_IUnknown || iid == IID_IClassFactory) {
|
||||
*object = static_cast<IClassFactory*>(this);
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) OpenLessClassFactory::AddRef() {
|
||||
return static_cast<ULONG>(InterlockedIncrement(&ref_count_));
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) OpenLessClassFactory::Release() {
|
||||
const ULONG count = static_cast<ULONG>(InterlockedDecrement(&ref_count_));
|
||||
if (count == 0) {
|
||||
delete this;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
STDMETHODIMP OpenLessClassFactory::CreateInstance(IUnknown* outer,
|
||||
REFIID iid,
|
||||
void** object) {
|
||||
if (object == nullptr) {
|
||||
return E_POINTER;
|
||||
}
|
||||
*object = nullptr;
|
||||
|
||||
if (outer != nullptr) {
|
||||
return CLASS_E_NOAGGREGATION;
|
||||
}
|
||||
|
||||
auto* service = new (std::nothrow) OpenLessTextService();
|
||||
if (service == nullptr) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
const HRESULT hr = service->QueryInterface(iid, object);
|
||||
service->Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
STDMETHODIMP OpenLessClassFactory::LockServer(BOOL lock) {
|
||||
if (lock) {
|
||||
InterlockedIncrement(&g_lock_count);
|
||||
} else {
|
||||
InterlockedDecrement(&g_lock_count);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <unknwn.h>
|
||||
|
||||
class OpenLessClassFactory final : public IClassFactory {
|
||||
public:
|
||||
OpenLessClassFactory();
|
||||
OpenLessClassFactory(const OpenLessClassFactory&) = delete;
|
||||
OpenLessClassFactory& operator=(const OpenLessClassFactory&) = delete;
|
||||
~OpenLessClassFactory() override;
|
||||
|
||||
STDMETHODIMP QueryInterface(REFIID iid, void** object) override;
|
||||
STDMETHODIMP_(ULONG) AddRef() override;
|
||||
STDMETHODIMP_(ULONG) Release() override;
|
||||
STDMETHODIMP CreateInstance(IUnknown* outer, REFIID iid, void** object) override;
|
||||
STDMETHODIMP LockServer(BOOL lock) override;
|
||||
|
||||
private:
|
||||
LONG ref_count_ = 1;
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include "class_factory.h"
|
||||
#include "guids.h"
|
||||
#include "registry.h"
|
||||
|
||||
HINSTANCE g_module = nullptr;
|
||||
LONG g_lock_count = 0;
|
||||
LONG g_object_count = 0;
|
||||
|
||||
BOOL APIENTRY DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
|
||||
UNREFERENCED_PARAMETER(reserved);
|
||||
|
||||
if (reason == DLL_PROCESS_ATTACH) {
|
||||
g_module = instance;
|
||||
DisableThreadLibraryCalls(instance);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
STDAPI DllCanUnloadNow() {
|
||||
return (g_lock_count == 0 && g_object_count == 0) ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
STDAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void** object) {
|
||||
if (object == nullptr) {
|
||||
return E_POINTER;
|
||||
}
|
||||
*object = nullptr;
|
||||
|
||||
if (clsid != CLSID_OpenLessTextService) {
|
||||
return CLASS_E_CLASSNOTAVAILABLE;
|
||||
}
|
||||
|
||||
auto* factory = new (std::nothrow) OpenLessClassFactory();
|
||||
if (factory == nullptr) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
const HRESULT hr = factory->QueryInterface(iid, object);
|
||||
factory->Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
STDAPI DllRegisterServer() {
|
||||
return RegisterOpenLessTextService(g_module);
|
||||
}
|
||||
|
||||
STDAPI DllUnregisterServer() {
|
||||
return UnregisterOpenLessTextService();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <msctf.h>
|
||||
#include <windows.h>
|
||||
|
||||
inline constexpr CLSID CLSID_OpenLessTextService = {
|
||||
0x6b9f3f4f,
|
||||
0x5ee7,
|
||||
0x42d6,
|
||||
{0x9c, 0x61, 0x9f, 0x80, 0xb0, 0x3a, 0x5d, 0x7d},
|
||||
};
|
||||
|
||||
inline constexpr GUID GUID_OpenLessProfile = {
|
||||
0x9b5f5e04,
|
||||
0x23f6,
|
||||
0x47da,
|
||||
{0x9a, 0x26, 0xd2, 0x21, 0xf6, 0xc3, 0xf0, 0x2e},
|
||||
};
|
||||
|
||||
inline constexpr wchar_t kOpenLessImeName[] = L"OpenLess Voice Input";
|
||||
inline constexpr LANGID kOpenLessLangId = 0x0804;
|
||||
@@ -0,0 +1,176 @@
|
||||
#include "registry.h"
|
||||
|
||||
#include <msctf.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#include "guids.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr wchar_t kClsidKey[] =
|
||||
L"Software\\Classes\\CLSID\\{6B9F3F4F-5EE7-42D6-9C61-9F80B03A5D7D}";
|
||||
constexpr wchar_t kInprocServer32Key[] =
|
||||
L"Software\\Classes\\CLSID\\{6B9F3F4F-5EE7-42D6-9C61-9F80B03A5D7D}\\InprocServer32";
|
||||
|
||||
HRESULT HResultFromWin32Error(LSTATUS status) {
|
||||
return status == ERROR_SUCCESS ? S_OK : HRESULT_FROM_WIN32(status);
|
||||
}
|
||||
|
||||
HRESULT SetStringValue(HKEY key, const wchar_t* name, const wchar_t* value) {
|
||||
const auto byte_count =
|
||||
static_cast<DWORD>((wcslen(value) + 1) * sizeof(wchar_t));
|
||||
return HResultFromWin32Error(
|
||||
RegSetValueExW(key, name, 0, REG_SZ,
|
||||
reinterpret_cast<const BYTE*>(value), byte_count));
|
||||
}
|
||||
|
||||
HRESULT RegisterComServer(HINSTANCE module) {
|
||||
wchar_t module_path[MAX_PATH] = {};
|
||||
const DWORD path_len =
|
||||
GetModuleFileNameW(module, module_path, ARRAYSIZE(module_path));
|
||||
if (path_len == 0) {
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
if (path_len == ARRAYSIZE(module_path)) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
HKEY clsid_key = nullptr;
|
||||
LSTATUS status =
|
||||
RegCreateKeyExW(HKEY_CURRENT_USER, kClsidKey, 0, nullptr,
|
||||
REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr, &clsid_key,
|
||||
nullptr);
|
||||
HRESULT hr = HResultFromWin32Error(status);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = SetStringValue(clsid_key, nullptr, kOpenLessImeName);
|
||||
RegCloseKey(clsid_key);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
HKEY inproc_key = nullptr;
|
||||
status = RegCreateKeyExW(HKEY_CURRENT_USER, kInprocServer32Key, 0, nullptr,
|
||||
REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr,
|
||||
&inproc_key, nullptr);
|
||||
hr = HResultFromWin32Error(status);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = SetStringValue(inproc_key, nullptr, module_path);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = SetStringValue(inproc_key, L"ThreadingModel", L"Apartment");
|
||||
}
|
||||
RegCloseKey(inproc_key);
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT DeleteComServerRegistration() {
|
||||
const LSTATUS status = RegDeleteTreeW(HKEY_CURRENT_USER, kClsidKey);
|
||||
if (status == ERROR_FILE_NOT_FOUND) {
|
||||
return S_OK;
|
||||
}
|
||||
return HResultFromWin32Error(status);
|
||||
}
|
||||
|
||||
class ScopedComInit {
|
||||
public:
|
||||
ScopedComInit() : hr_(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED)) {}
|
||||
ScopedComInit(const ScopedComInit&) = delete;
|
||||
ScopedComInit& operator=(const ScopedComInit&) = delete;
|
||||
~ScopedComInit() {
|
||||
if (hr_ == S_OK || hr_ == S_FALSE) {
|
||||
CoUninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT hr() const {
|
||||
return hr_ == RPC_E_CHANGED_MODE ? S_OK : hr_;
|
||||
}
|
||||
|
||||
private:
|
||||
HRESULT hr_;
|
||||
};
|
||||
|
||||
HRESULT CreateProfiles(ITfInputProcessorProfiles** profiles) {
|
||||
return CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_ITfInputProcessorProfiles,
|
||||
reinterpret_cast<void**>(profiles));
|
||||
}
|
||||
|
||||
HRESULT RegisterLanguageProfile() {
|
||||
ScopedComInit com;
|
||||
HRESULT hr = com.hr();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
ITfInputProcessorProfiles* profiles = nullptr;
|
||||
hr = CreateProfiles(&profiles);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = profiles->Register(CLSID_OpenLessTextService);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = profiles->AddLanguageProfile(
|
||||
CLSID_OpenLessTextService, kOpenLessLangId, GUID_OpenLessProfile,
|
||||
const_cast<wchar_t*>(kOpenLessImeName),
|
||||
static_cast<ULONG>(ARRAYSIZE(kOpenLessImeName) - 1), nullptr, 0, 0);
|
||||
}
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = profiles->EnableLanguageProfile(CLSID_OpenLessTextService,
|
||||
kOpenLessLangId, GUID_OpenLessProfile,
|
||||
TRUE);
|
||||
}
|
||||
|
||||
profiles->Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT UnregisterLanguageProfile() {
|
||||
ScopedComInit com;
|
||||
HRESULT hr = com.hr();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
ITfInputProcessorProfiles* profiles = nullptr;
|
||||
hr = CreateProfiles(&profiles);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = profiles->Unregister(CLSID_OpenLessTextService);
|
||||
profiles->Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
HRESULT RegisterOpenLessTextService(HINSTANCE module) {
|
||||
if (module == nullptr) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
HRESULT hr = RegisterComServer(module);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = RegisterLanguageProfile();
|
||||
if (FAILED(hr)) {
|
||||
DeleteComServerRegistration();
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT UnregisterOpenLessTextService() {
|
||||
const HRESULT profile_hr = UnregisterLanguageProfile();
|
||||
const HRESULT registry_hr = DeleteComServerRegistration();
|
||||
return FAILED(profile_hr) ? profile_hr : registry_hr;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
HRESULT RegisterOpenLessTextService(HINSTANCE module);
|
||||
HRESULT UnregisterOpenLessTextService();
|
||||
@@ -0,0 +1,33 @@
|
||||
#include <winver.h>
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 0,1,0,0
|
||||
PRODUCTVERSION 0,1,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "OpenLess"
|
||||
VALUE "FileDescription", "OpenLess Voice Input TSF IME"
|
||||
VALUE "FileVersion", "0.1.0.0"
|
||||
VALUE "InternalName", "OpenLessIme.dll"
|
||||
VALUE "OriginalFilename", "OpenLessIme.dll"
|
||||
VALUE "ProductName", "OpenLess Voice Input"
|
||||
VALUE "ProductVersion", "0.1.0.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
@@ -0,0 +1,95 @@
|
||||
#include "text_service.h"
|
||||
|
||||
extern LONG g_object_count;
|
||||
|
||||
OpenLessTextService::OpenLessTextService() {
|
||||
InterlockedIncrement(&g_object_count);
|
||||
}
|
||||
|
||||
OpenLessTextService::~OpenLessTextService() {
|
||||
Deactivate();
|
||||
InterlockedDecrement(&g_object_count);
|
||||
}
|
||||
|
||||
STDMETHODIMP OpenLessTextService::QueryInterface(REFIID iid, void** object) {
|
||||
if (object == nullptr) {
|
||||
return E_POINTER;
|
||||
}
|
||||
*object = nullptr;
|
||||
|
||||
if (iid == IID_IUnknown || iid == IID_ITfTextInputProcessor ||
|
||||
iid == IID_ITfTextInputProcessorEx) {
|
||||
*object = static_cast<ITfTextInputProcessorEx*>(this);
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) OpenLessTextService::AddRef() {
|
||||
return static_cast<ULONG>(InterlockedIncrement(&ref_count_));
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) OpenLessTextService::Release() {
|
||||
const ULONG count = static_cast<ULONG>(InterlockedDecrement(&ref_count_));
|
||||
if (count == 0) {
|
||||
delete this;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
STDMETHODIMP OpenLessTextService::Activate(ITfThreadMgr* thread_mgr,
|
||||
TfClientId client_id) {
|
||||
return ActivateEx(thread_mgr, client_id, 0);
|
||||
}
|
||||
|
||||
STDMETHODIMP OpenLessTextService::ActivateEx(ITfThreadMgr* thread_mgr,
|
||||
TfClientId client_id,
|
||||
DWORD flags) {
|
||||
UNREFERENCED_PARAMETER(flags);
|
||||
|
||||
if (thread_mgr == nullptr) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
Deactivate();
|
||||
|
||||
thread_mgr_ = thread_mgr;
|
||||
thread_mgr_->AddRef();
|
||||
client_id_ = client_id;
|
||||
|
||||
const HRESULT hr = StartIpcServer();
|
||||
if (FAILED(hr)) {
|
||||
Deactivate();
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OpenLessTextService::Deactivate() {
|
||||
StopIpcServer();
|
||||
|
||||
if (thread_mgr_ != nullptr) {
|
||||
thread_mgr_->Release();
|
||||
thread_mgr_ = nullptr;
|
||||
}
|
||||
client_id_ = TF_CLIENTID_NULL;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT OpenLessTextService::SubmitTextFromPipe(
|
||||
const std::wstring& session_id,
|
||||
const std::wstring& text) {
|
||||
UNREFERENCED_PARAMETER(session_id);
|
||||
UNREFERENCED_PARAMETER(text);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT OpenLessTextService::StartIpcServer() {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void OpenLessTextService::StopIpcServer() {}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <msctf.h>
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
|
||||
class OpenLessTextService final : public ITfTextInputProcessorEx {
|
||||
public:
|
||||
OpenLessTextService();
|
||||
OpenLessTextService(const OpenLessTextService&) = delete;
|
||||
OpenLessTextService& operator=(const OpenLessTextService&) = delete;
|
||||
~OpenLessTextService() override;
|
||||
|
||||
STDMETHODIMP QueryInterface(REFIID iid, void** object) override;
|
||||
STDMETHODIMP_(ULONG) AddRef() override;
|
||||
STDMETHODIMP_(ULONG) Release() override;
|
||||
|
||||
STDMETHODIMP Activate(ITfThreadMgr* thread_mgr, TfClientId client_id) override;
|
||||
STDMETHODIMP Deactivate() override;
|
||||
STDMETHODIMP ActivateEx(ITfThreadMgr* thread_mgr,
|
||||
TfClientId client_id,
|
||||
DWORD flags) override;
|
||||
|
||||
HRESULT SubmitTextFromPipe(const std::wstring& session_id,
|
||||
const std::wstring& text);
|
||||
|
||||
private:
|
||||
HRESULT StartIpcServer();
|
||||
void StopIpcServer();
|
||||
|
||||
LONG ref_count_ = 1;
|
||||
ITfThreadMgr* thread_mgr_ = nullptr;
|
||||
TfClientId client_id_ = TF_CLIENTID_NULL;
|
||||
};
|
||||
Reference in new issue
Block a user