Skip to content

Instantly share code, notes, and snippets.

@UNC1739
Created January 31, 2025 15:43
Show Gist options
  • Select an option

  • Save UNC1739/b77e095944df9061091684ea40ecd576 to your computer and use it in GitHub Desktop.

Select an option

Save UNC1739/b77e095944df9061091684ea40ecd576 to your computer and use it in GitHub Desktop.
#include <windows.h>
#include <atlbase.h>
#include <atlcom.h>
#include <msctf.h>
#include <strsafe.h>
#include <psapi.h>
#pragma comment(lib, "psapi.lib")
#define TEXTSERVICE_DESC L"Universal Text Service"
#define TEXTSERVICE_LANGID MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)
#define TEXTSERVICE_MODEL TEXT("Apartment")#define TEXTSERVICE_DESC L"Universal Text Service"
#define TEXTSERVICE_LANGID MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)
#define TEXTSERVICE_MODEL TEXT("Apartment")
class __declspec(uuid("e7ea138e-69f8-11d7-a6ea-00065b84435c")) CTextService;
class CTextServiceModule : public CAtlDllModuleT<CTextServiceModule> {};
CTextServiceModule _AtlModule;
class ATL_NO_VTABLE CTextService :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CTextService, &__uuidof(CTextService)>,
public ITfTextInputProcessor
{
public:
DECLARE_NOT_AGGREGATABLE(CTextService)
BEGIN_COM_MAP(CTextService)
COM_INTERFACE_ENTRY(ITfTextInputProcessor)
END_COM_MAP()
CTextService() : _pThreadMgr(nullptr), _tfClientId(TF_CLIENTID_NULL) {
MessageBoxW(NULL, L"CTextService Constructor", L"Debug", MB_OK);
}
~CTextService() {
if (_pThreadMgr) {
_pThreadMgr->Release();
}
}
STDMETHODIMP Activate(ITfThreadMgr* pThreadMgr, TfClientId tfClientId) {
MessageBoxW(NULL, L"Activate called", L"Debug", MB_OK);
if (_pThreadMgr) _pThreadMgr->Release();
_pThreadMgr = pThreadMgr;
_tfClientId = tfClientId;
if (_pThreadMgr) _pThreadMgr->AddRef();
return S_OK;
}
STDMETHODIMP Deactivate() {
MessageBoxW(NULL, L"Deactivate called", L"Debug", MB_OK);
if (_pThreadMgr) {
_pThreadMgr->Release();
_pThreadMgr = nullptr;
}
_tfClientId = TF_CLIENTID_NULL;
return S_OK;
}
private:
ITfThreadMgr* _pThreadMgr;
TfClientId _tfClientId;
};
static BOOL RegisterTextService() {
CComPtr<ITfCategoryMgr> pCategoryMgr;
HRESULT hr = pCategoryMgr.CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER);
if (SUCCEEDED(hr)) {
static const GUID GUID_TFCAT_TIP_KEYBOARD_ACTUAL =
{ 0x25504FB4, 0x7BAB, 0x4BC1, {0x9C, 0x69, 0xCF, 0x81, 0x89, 0x0F, 0x0E, 0xF5} };
hr = pCategoryMgr->RegisterCategory(
__uuidof(CTextService),
GUID_TFCAT_TIP_KEYBOARD_ACTUAL,
__uuidof(CTextService));
}
CComPtr<ITfInputProcessorProfiles> pInputProcessProfiles;
hr = pInputProcessProfiles.CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER);
if (FAILED(hr)) return FALSE;
hr = pInputProcessProfiles->Register(__uuidof(CTextService));
if (FAILED(hr)) return FALSE;
WCHAR achIconFile[MAX_PATH];
GetModuleFileNameW(ATL::_AtlBaseModule.GetModuleInstance(), achIconFile, ARRAYSIZE(achIconFile));
hr = pInputProcessProfiles->AddLanguageProfile(
__uuidof(CTextService),
TEXTSERVICE_LANGID,
__uuidof(CTextService),
TEXTSERVICE_DESC,
(ULONG)wcslen(TEXTSERVICE_DESC),
achIconFile,
(ULONG)wcslen(achIconFile),
0);
if (FAILED(hr)) return FALSE;
CComPtr<ITfInputProcessorProfileMgr> pProfileMgr;
if (SUCCEEDED(pInputProcessProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr, (void**)&pProfileMgr))) {
hr = pProfileMgr->ActivateProfile(
TF_PROFILETYPE_INPUTPROCESSOR,
TEXTSERVICE_LANGID,
__uuidof(CTextService),
__uuidof(CTextService),
NULL,
TF_IPPMF_FORPROCESS | TF_IPPMF_ENABLEPROFILE | TF_IPPMF_FORSESSION
);
}
return SUCCEEDED(hr);
}
static BOOL UnregisterTextService() {
CComPtr<ITfInputProcessorProfiles> pInputProcessProfiles;
HRESULT hr = pInputProcessProfiles.CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER);
if (SUCCEEDED(hr)) {
hr = pInputProcessProfiles->Unregister(__uuidof(CTextService));
}
return SUCCEEDED(hr);
}
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) {
switch (dwReason) {
case DLL_PROCESS_ATTACH: {
WCHAR processName[MAX_PATH] = L"Unknown";
DWORD processId = GetCurrentProcessId();
HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
if (processHandle != NULL) {
GetModuleFileNameExW(processHandle, NULL, processName, MAX_PATH);
CloseHandle(processHandle);
}
WCHAR msg[512];
StringCchPrintfW(msg, ARRAYSIZE(msg),
L"DLL_PROCESS_ATTACH\nProcess: %s\nPID: %d",
processName, processId);
MessageBoxW(NULL, msg, L"DllMain Debug", MB_OK);
break;
}
case DLL_PROCESS_DETACH:
break;
}
return _AtlModule.DllMain(dwReason, lpReserved);
}
STDAPI DllCanUnloadNow(void) {
return _AtlModule.DllCanUnloadNow();
}
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
return _AtlModule.DllGetClassObject(rclsid, riid, ppv);
}
STDAPI DllRegisterServer(void) {
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr)) return hr;
WCHAR szCLSID[50];
StringFromGUID2(__uuidof(CTextService), szCLSID, ARRAYSIZE(szCLSID));
WCHAR szSubkey[MAX_PATH];
StringCchPrintfW(szSubkey, ARRAYSIZE(szSubkey), L"CLSID\\%s", szCLSID);
HKEY hKey;
LONG lRet = RegCreateKeyExW(HKEY_CLASSES_ROOT, szSubkey, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
if (lRet != ERROR_SUCCESS) {
CoUninitialize();
return E_FAIL;
}
lRet = RegSetValueExW(hKey, NULL, 0, REG_SZ,
(BYTE*)TEXTSERVICE_DESC, (DWORD)(wcslen(TEXTSERVICE_DESC) + 1) * sizeof(WCHAR));
RegCloseKey(hKey);
if (lRet != ERROR_SUCCESS) {
CoUninitialize();
return E_FAIL;
}
StringCchPrintfW(szSubkey, ARRAYSIZE(szSubkey), L"CLSID\\%s\\InProcServer32", szCLSID);
lRet = RegCreateKeyExW(HKEY_CLASSES_ROOT, szSubkey, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
if (lRet != ERROR_SUCCESS) {
CoUninitialize();
return E_FAIL;
}
WCHAR szModule[MAX_PATH];
GetModuleFileNameW(ATL::_AtlBaseModule.GetModuleInstance(), szModule, ARRAYSIZE(szModule));
lRet = RegSetValueExW(hKey, NULL, 0, REG_SZ,
(BYTE*)szModule, (DWORD)(wcslen(szModule) + 1) * sizeof(WCHAR));
if (lRet == ERROR_SUCCESS) {
lRet = RegSetValueExW(hKey, L"ThreadingModel", 0, REG_SZ,
(BYTE*)TEXTSERVICE_MODEL, (DWORD)(wcslen(TEXTSERVICE_MODEL) + 1) * sizeof(WCHAR));
}
RegCloseKey(hKey);
if (lRet != ERROR_SUCCESS) {
CoUninitialize();
return E_FAIL;
}
BOOL bResult = RegisterTextService();
CoUninitialize();
return bResult ? S_OK : E_FAIL;
}
STDAPI DllUnregisterServer(void) {
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr)) {
UnregisterTextService();
WCHAR szCLSID[50];
StringFromGUID2(__uuidof(CTextService), szCLSID, ARRAYSIZE(szCLSID));
WCHAR szSubkey[MAX_PATH];
StringCchPrintfW(szSubkey, ARRAYSIZE(szSubkey), L"CLSID\\%s", szCLSID);
RegDeleteTreeW(HKEY_CLASSES_ROOT, szSubkey);
CoUninitialize();
}
return S_OK;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment