0

ここでアイデアが不足しています。キーコードをユニコード文字に変換するためにhttp://thetechnofreak.com/technofreak/keylogger-visual-c/から適合させたコードがあります。64 ビット Windows から 32 ビット バージョンを実行しようとする場合を除き、すべての状況で問題なく動作します。何らかの理由で pKbd->pVkToWcharTable が NULL を返し続けます。kbd dll パスに SysWOW64 と System32 を明示的に指定するだけでなく、__ptr64 も試しました。この正確な問題または非常によく似た問題を参照しているアイテムをインターネット上でいくつか見つけましたが、解決策がうまくいかないようです (参照: KbdLayerDescriptor は 64 ビット アーキテクチャで NULL を返します)) 以下は、Windows XP で mingw-32 (gcc -std=c99 Wow64Test.c) でコンパイルされ、Windows 7 64 ビットで実行された私のテスト コードです。Windows XP では有効なポインターを取得していますが、Windows 7 では NULL を取得しています。

***更新:私が抱えている問題は、sizeof 操作が Visual Studio によって与えられた 8 バイトではなく 4 バイトを与えるため、mingw が __ptr64 を正しく実装していないことが原因のようです。したがって、実際の解決策は、KBD_LONG_POINTER のサイズを動的または少なくとも 64 ビットにする方法を考え出すことですが、それが可能かどうかはわかりません。何か案は?

#include <windows.h>
#include <stdio.h>

#define KBD_LONG_POINTER __ptr64
//#define KBD_LONG_POINTER

typedef struct {
    BYTE Vk;
    BYTE ModBits;
} VK_TO_BIT, *KBD_LONG_POINTER PVK_TO_BIT;

typedef struct {
    PVK_TO_BIT pVkToBit;
    WORD wMaxModBits;
    BYTE ModNumber[];
} MODIFIERS, *KBD_LONG_POINTER PMODIFIERS;

typedef struct _VK_TO_WCHARS1 {
    BYTE VirtualKey;
    BYTE Attributes;
    WCHAR wch[1];
} VK_TO_WCHARS1, *KBD_LONG_POINTER PVK_TO_WCHARS1;

typedef struct _VK_TO_WCHAR_TABLE {
    PVK_TO_WCHARS1 pVkToWchars;
    BYTE nModifications;
    BYTE cbSize;
} VK_TO_WCHAR_TABLE, *KBD_LONG_POINTER PVK_TO_WCHAR_TABLE;

typedef struct {
    DWORD dwBoth;
    WCHAR wchComposed;
    USHORT uFlags;
} DEADKEY, *KBD_LONG_POINTER PDEADKEY;

typedef struct {
    BYTE vsc;
    WCHAR *KBD_LONG_POINTER pwsz;
} VSC_LPWSTR, *KBD_LONG_POINTER PVSC_LPWSTR;

typedef struct _VSC_VK {
    BYTE Vsc;
    USHORT Vk;
} VSC_VK, *KBD_LONG_POINTER PVSC_VK;

typedef struct _LIGATURE1 {
    BYTE VirtualKey;
    WORD ModificationNumber;
    WCHAR wch[1];
} LIGATURE1, *KBD_LONG_POINTER PLIGATURE1;

typedef struct tagKbdLayer {
    PMODIFIERS pCharModifiers;
    PVK_TO_WCHAR_TABLE pVkToWcharTable;
    PDEADKEY pDeadKey;
    PVSC_LPWSTR pKeyNames;
    PVSC_LPWSTR pKeyNamesExt;
    WCHAR *KBD_LONG_POINTER *KBD_LONG_POINTER pKeyNamesDead;
    USHORT *KBD_LONG_POINTER pusVSCtoVK;
    BYTE bMaxVSCtoVK;
    PVSC_VK pVSCtoVK_E0;
    PVSC_VK pVSCtoVK_E1;
    DWORD fLocaleFlags;
    BYTE nLgMax;
    BYTE cbLgEntry;
    PLIGATURE1 pLigature;
    DWORD dwType;
    DWORD dwSubType;
} KBDTABLES, *KBD_LONG_POINTER PKBDTABLES;


typedef PKBDTABLES(CALLBACK *KbdLayerDescriptor) (VOID);

int main() {
    PKBDTABLES pKbd;
    HINSTANCE kbdLibrary = NULL;

    //"C:\\WINDOWS\\SysWOW64\\KBDUS.DLL"
    //"C:\\WINDOWS\\System32\\KBDUS.DLL"
    kbdLibrary = LoadLibrary("C:\\WINDOWS\\SysWOW64\\KBDUS.DLL");

    KbdLayerDescriptor pKbdLayerDescriptor = (KbdLayerDescriptor) GetProcAddress(kbdLibrary, "KbdLayerDescriptor");

    if(pKbdLayerDescriptor != NULL) {
        pKbd = pKbdLayerDescriptor();

        printf("Is Null? %d 0x%X\n", sizeof(pKbd->pVkToWcharTable), pKbd->pVkToWcharTable);
    }

    FreeLibrary(kbdLibrary);
    kbdLibrary = NULL;
}
4

1 に答える 1

3

遅いかもしれませんが、同じ問題を抱えている人のための解決策があります。このデモと不完全な説明は役立ちますが、Visual Studio でのみ機能します: http://www.codeproject.com/Articles/439275/Loading-keyboard-layout-KbdLayerDescriptor-in-32-6

kbd.hすべての構造体のポインターにはマクロがあり、これは64 ビット オペレーティング システムKBD_LONG_POINTERで定義されています。*__ptr64*Visual Studio では、これにより、通常の 4 つの 32 ビット プログラムではなく、ポインターが 8 バイトを占有します。残念ながら MinGW では、*__ptr64*は何もしないように定義されています。

リンクされた説明に書かれているように、KbdLayerDescriptor関数は 32 ビット Windows と 64 ビット Windows で異なるポインターを返します。ポインターのサイズは、実行中のプログラムではなく、オペレーティング システムに依存しているようです。実際には、ポインターは 64 ビット オペレーティング システムで 32 ビット プログラムに対して 4 バイトのままですが、VS では、__ptr64キーワードはそうではないということです。

たとえば、一部の構造は次のようになりますkbd.h

typedef struct {
    BYTE Vk;
    BYTE ModBits;
} VK_TO_BIT, *KBD_LONG_POINTER PVK_TO_BIT;

typedef struct {
    PVK_TO_BIT pVkToBit;
    WORD wMaxModBits;
    BYTE ModNumber[];
} MODIFIERS, *KBD_LONG_POINTER PMODIFIERS;

これは、MinGW でも 64 ビット Windows 上の 32 ビット プログラムの VS でも機能しません。のpVkToBitメンバMODIFIERSは . なしで 4 バイトしかないため__ptr64です。解決策は、忘れてKBD_LONG_POINTER(すべて削除することもできます)、上記のような構造を定義することです。すなわち:

struct VK_TO_BIT64
{
    BYTE Vk;
    BYTE ModBits;
};

struct MODIFIERS64
{
    VK_TO_BIT64 *pVkToBit;
    int _align1;
    WORD wMaxModBits;
    BYTE ModNumber[];
};

(それらは同じであるVK_TO_BITため、独自の を使用しても定義しなくてもかまいませんが、別の定義を使用すると、何が起こっているのかを理解するのに役立ちます。)VK_TO_BIT64

メンバーpVkToBitは引き続き 4 バイトを使用しKbdLayerDescriptorますが、64 ビット OS ではポインターを 8 バイトにパディングするため、パディング ( int _align1) を挿入する必要があります。

の他の構造についても同じことを行う必要がありますkbd.h。たとえば、これは次のように置き換えられKBDTABLESます。

struct WCHARARRAY64
{
    WCHAR *str;
    int _align1;
};

struct KBDTABLES64
{
    MODIFIERS64 *pCharModifiers;
    int _align1;
    VK_TO_WCHAR_TABLE64 *pVkToWcharTable;
    int _align2;
    DEADKEY64 *pDeadKey;
    int _align3;
    VSC_LPWSTR64 *pKeyNames;
    int _align4;
    VSC_LPWSTR64 *pKeyNamesExt;
    int _align5;
    WCHARARRAY64 *pKeyNamesDead;
    int _align6;
    USHORT *pusVSCtoVK;
    int _align7;
    BYTE bMaxVSCtoVK;
    int _align8;
    VSC_VK64 *pVSCtoVK_E0;
    int _align9;
    VSC_VK64 *pVSCtoVK_E1;
    int _align10;
    DWORD fLocaleFlags;
    byte nLgMax;
    byte cbLgEntry;
    LIGATURE64_1 *pLigature;
    int _align11;
    DWORD dwType;
    DWORD dwSubType;
};

_align8(メンバーはポインターの後に来ないことに注意してください。)

これをすべて使用するには、これを使用して 64 ビット Windows で実行しているかどうかを確認する必要があります

kbd.hそうでない場合は、ポインタが正しく動作するため、の元の構造を使用してください。それらは 4 バイトを使用します。プログラムが 64 ビット OS で実行されている場合は、作成した構造を使用します。これでそれを達成できます:

typedef __int64 (CALLBACK *LayerDescriptor64)(); // Result should be cast to KBDTABLES64.
typedef PKBDTABLES (CALLBACK *LayerDescriptor)(); // This is used on 32 bit OS.

static PKBDTABLES kbdtables = NULL;
static KBDTABLES64 *kbdtables64 = NULL;

そして、いくつかの初期化関数で:

if (WindowsIs64Bit()) // Your function that checks the OS version.
{
    LayerDescriptor64 KbdLayerDescriptor = (LayerDescriptor64)GetProcAddress(kbdLibrary, "KbdLayerDescriptor");
    if (KbdLayerDescriptor != NULL)
        kbdtables64 = (KBDTABLES64*)KbdLayerDescriptor();
    else
        kbdtables64 = NULL;
}
else
{
    LayerDescriptor KbdLayerDescriptor = (LayerDescriptor)GetProcAddress(kbdLibrary, "KbdLayerDescriptor");
    if (KbdLayerDescriptor != NULL)
        kbdtables = KbdLayerDescriptor();
    else
        kbdtables = NULL;
}

このソリューションはまったく使用せず__ptr64、VS と MinGW の両方で機能します。あなたが気をつけなければならないことは次のとおりです。

  1. 構造体は 8 バイト境界に揃える必要があります。(少なくとも C++ では、これが現在の VS または MinGW のデフォルトです。)
  2. KBD_LONG_POINTERに定義し__ptr64たり、どこからでも削除したりしないでください。ただし、kbd.h を変更しない方がよいでしょう。
  3. 構造体メンバーの位置合わせがどのように機能するかを理解します。(これを C ではなく C++ としてコンパイルしました。アラインメント規則が C で異なるかどうかはわかりません。)
  4. OS に応じて、正しい変数 ( または のいずれkbdtablesか) を使用します。kbdtables64
  5. 64 ビット プログラムをコンパイルする場合、このソリューションは明らかに必要ありません。
于 2013-05-19T13:18:26.220 に答える