11

Visual Studio 2010 を使用して Excel アドインを作成しています。ユーザーがキーの組み合わせをクリックしたときにコードを実行したいと考えています。

ここに私が持っているコードがあります

Public Class CC

Private Sub ThisAddIn_Startup() Handles Me.Startup
    EnableShortCut()
End Sub

Sub A1()
    MsgBox "A1"
End Sub

Sub A2()
    MsgBox "A2"
End Sub

Sub A3()
    MsgBox "A3"
End Sub

Public Sub EnableShortCut()
    With Application
        .OnKey "+^{U}", "A1"  'action A1 should be performed when user clicks  Ctrl + Shift + U
        .OnKey "+^{L}", "A2"  'action A2 should be performed when user clicks  Ctrl + Shift + L
        .OnKey "+^{P}", "A3"  'action A3 should be performed when user clicks  Ctrl + Shift + P
    End With
End Sub

End Class

アドインをインストールすると、ショートカットをクリックするとエラーが表示されます。特定のマクロが見つからないと表示されます。下のコードSub EnableShortCut()は、Excel vba モジュールにある場合にうまく機能します。Visual Studio で作成された Excel アドインに追加された場合、同じことは機能しません。この問題を解決するために私を助けてください。

4

4 に答える 4

9

「ユーザーがキーの組み合わせを押したときにコードを実行したいと思います。」

トリッキーであり、外部依存関係なしでそれを行うには、VSTO Excel アドインでそれを達成するためにキーボード フックに頼ります。

Imports System
Imports System.Runtime.CompilerServices
Imports System.Runtime.InteropServices
Imports System.Windows.Forms

Friend Class KeyboardHooking
    ' Methods
    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function CallNextHookEx(ByVal hhk As IntPtr, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
    End Function

    <DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function GetModuleHandle(ByVal lpModuleName As String) As IntPtr
    End Function

    Private Shared Function HookCallback(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
        If ((nCode >= 0) AndAlso (nCode = 0)) Then
            Dim keyData As Keys = DirectCast(CInt(wParam), Keys)
            If (((BindingFunctions.IsKeyDown(Keys.ControlKey) AndAlso BindingFunctions.IsKeyDown(Keys.ShiftKey)) AndAlso BindingFunctions.IsKeyDown(keyData)) AndAlso (keyData = Keys.D7)) Then
'DO SOMETHING HERE
            End If
            If ((BindingFunctions.IsKeyDown(Keys.ControlKey) AndAlso BindingFunctions.IsKeyDown(keyData)) AndAlso (keyData = Keys.D7)) Then
'DO SOMETHING HERE
            End If
        End If
        Return CInt(KeyboardHooking.CallNextHookEx(KeyboardHooking._hookID, nCode, wParam, lParam))
    End Function

    Public Shared Sub ReleaseHook()
        KeyboardHooking.UnhookWindowsHookEx(KeyboardHooking._hookID)
    End Sub

    Public Shared Sub SetHook()
        KeyboardHooking._hookID = KeyboardHooking.SetWindowsHookEx(2, KeyboardHooking._proc, IntPtr.Zero, Convert.ToUInt32(AppDomain.GetCurrentThreadId))
    End Sub

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal lpfn As LowLevelKeyboardProc, ByVal hMod As IntPtr, ByVal dwThreadId As UInt32) As IntPtr
    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function UnhookWindowsHookEx(ByVal hhk As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function


    ' Fields
    Private Shared _hookID As IntPtr = IntPtr.Zero
    Private Shared _proc As LowLevelKeyboardProc = New LowLevelKeyboardProc(AddressOf KeyboardHooking.HookCallback)
    Private Const WH_KEYBOARD As Integer = 2
    Private Const WH_KEYBOARD_LL As Integer = 13
    Private Const WM_KEYDOWN As Integer = &H100

    ' Nested Types
    Public Delegate Function LowLevelKeyboardProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
End Class

Public Class BindingFunctions
    ' Methods
    <DllImport("user32.dll")> _
    Private Shared Function GetKeyState(ByVal nVirtKey As Integer) As Short
    End Function

    Public Shared Function IsKeyDown(ByVal keys As Keys) As Boolean
        Return ((BindingFunctions.GetKeyState(CInt(keys)) And &H8000) = &H8000)
    End Function

End Class

C# バージョン - 上記の vb.net コードが変換された元のバージョン - しかし、CodeConverter & devfusion がそれを正しく行わなかったため、Reflector を使用する必要がありました。

class KeyboardHooking
{
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod,
    uint dwThreadId);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);

public delegate int LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;

//declare the mouse hook constant.
//For other hook types, you can obtain these values from Winuser.h in the Microsoft SDK.

private const int WH_KEYBOARD = 2; // mouse
private const int HC_ACTION = 0;

private const int WH_KEYBOARD_LL = 13; // keyboard
private const int WM_KEYDOWN = 0x0100;

public static void SetHook()
{
    // Ignore this compiler warning, as SetWindowsHookEx doesn't work with ManagedThreadId
    #pragma warning disable 618
    _hookID = SetWindowsHookEx(WH_KEYBOARD, _proc, IntPtr.Zero, (uint)AppDomain.GetCurrentThreadId());
    #pragma warning restore 618

}

public static void ReleaseHook()
{
    UnhookWindowsHookEx(_hookID);
}

//Note that the custom code goes in this method the rest of the class stays the same.
//It will trap if BOTH keys are pressed down.
private static int HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode < 0)
    {
        return (int)CallNextHookEx(_hookID, nCode, wParam, lParam);
    }
    else
    {

        if (nCode == HC_ACTION)
        {
            Keys keyData = (Keys)wParam;

            // CTRL + SHIFT + 7
            if ((BindingFunctions.IsKeyDown(Keys.ControlKey) == true)
                && (BindingFunctions.IsKeyDown(Keys.ShiftKey) == true)
                && (BindingFunctions.IsKeyDown(keyData) == true) && (keyData == Keys.D7))
            {
                // DO SOMETHING HERE
            }

            // CTRL + 7
            if ((BindingFunctions.IsKeyDown(Keys.ControlKey) == true)
                && (BindingFunctions.IsKeyDown(keyData) == true) && (keyData == Keys.D7))
            {
                // DO SOMETHING HERE
            }



        }
        return (int)CallNextHookEx(_hookID, nCode, wParam, lParam);
    }
}
}

public class BindingFunctions
{
[DllImport("user32.dll")]
static extern short GetKeyState(int nVirtKey);

public static bool IsKeyDown(Keys keys)
{
    return (GetKeyState((int)keys) & 0x8000) == 0x8000;
}

}

キーの組み合わせが押されたときにイベントをトラップするには、上記のコードの HookCallback() メソッドにコードを配置する必要があります。ここでは、Ctrl + Shift + 7 と Ctrl + 7 の 2 つの例を示します。

次に、Excel アドインで次のように接続します。

Private Sub ThisAddIn_Startup() Handles Me.Startup

'enable keyboard intercepts
KeyboardHooking.SetHook()

完了したら、無効にすることを忘れないでください。

Private Sub ThisAddIn_Shutdown() Handles Me.Shutdown

'disable keyboard intercepts
KeyboardHooking.ReleaseHook()
于 2012-04-21T07:53:13.050 に答える
3

Excel-DNA (私が開発したオープン ソースの .NET / Excel 統合ライブラリ) を使用すると、.NET コード内のメソッドとユーザー定義関数が C API を通じて Excel に登録されます。その結果、動作は VBA の動作に近くなり、Application.OnKey "..." 文字列を含むコードも機能します。

Excel-DNA を使用すると、コードをコンパイル済みの .NET .dll アセンブリに配置したり、アドインのロード時に処理される '.dna' ファイルにテキストとして直接配置したりできます。このようなテキスト ファイルの例を次に示します (コンパイルされたプロジェクトにある場合、コードは同じように見えます)。他の回答の1つで述べたように、マクロの名前を変更して、マクロの名前がセル名A1などと衝突しないようにしました。

アドインを作成するには

  1. コードをOnKeyTest.dnaというテキスト ファイルとして保存し、
  2. これをCodePlexのリリースからの Excel-DNA ホスト ライブラリ ExcelDna.xll のコピーと一致させます。これをコピーしてOnKeyTest.xllに名前を変更します(一致する名前は、.xll をロードしたときに .dna ファイルを見つける方法です)。 .

これら 2 つのファイルは完全なアドインを形成し、マシン上で .NET を実行する必要があるだけです。

<DnaLibrary Language="VB" RuntimeVersion="v2.0" >
<![CDATA[
Imports ExcelDna.Integration

Public Class MyAddIn
    Implements IExcelAddIn

    Private Sub AutoOpen() Implements IExcelAddIn.AutoOpen
        EnableShortCut()
    End Sub

    Private Sub AutoClose() Implements IExcelAddIn.AutoClose
    End Sub

    Sub EnableShortCut()
        With ExcelDnaUtil.Application
            .OnKey("+^{U}", "MacroA1")  'action A1 should be performed when user clicks  Ctrl + Shift + U
            .OnKey("+^{L}", "MacroA2")  'action A2 should be performed when user clicks  Ctrl + Shift + L
            .OnKey("+^{P}", "MacroA3")  'action A3 should be performed when user clicks  Ctrl + Shift + P
        End With
    End Sub
End Class

Public Module MyMacros
    Sub MacroA1()
        MsgBox("A1")
    End Sub

    Sub MacroA2()
        MsgBox("A2")
    End Sub

    Sub MacroA3()
        MsgBox("A3")
    End Sub
End Module
]]>
</DnaLibrary>
于 2012-04-22T07:00:05.123 に答える
2

これを行うのは難しく、Application.OnKey() メソッドは非常に制限されています。マクロを呼び出すことしかできず、引数を渡すことはできません。つまり、一連のマクロを提供する必要があります。ワークブック固有のものは必要ありません。どのドキュメントでも機能するマクロが必要です。まずはそこに取り組みましょう。

  • Excel を起動し、[ファイル] + [閉じる] を使用して既定のブックを閉じます。
  • [マクロの記録] をクリックします。「マクロの保存先」設定を個人用マクロ ブックに変更します。
  • [OK] をクリックしてダイアログを閉じ、[記録の停止] をクリックします。
  • Visual Basic をクリックします。Module1 を持つ PERSONAL.XLSB という名前の VBA プロジェクトが作成されたことに注意してください。

Macro1 を削除し、次の VBA コードをコピーして貼り付けます。

Sub MyAddinCommand1()
  Application.COMAddIns("ExcelAddin1").Object.Command 1
End Sub

Sub MyAddinCommand2()
  Application.COMAddIns("ExcelAddin1").Object.Command 2
End Sub

Sub MyAddinCommand3()
  Application.COMAddIns("ExcelAddin1").Object.Command 3
End Sub

必要に応じて繰り返します。定義するショートカット キーごとに 1 つ必要です。[保存] をクリックします。これで、c:\users\yourname\appdata\roaming\microsoft\excel\xlstart\personal.xlsb にマクロを含むファイルが作成されました。拡張機能を使用する予定のユーザーは、このファイル (展開の詳細) も必要です。

次に行う必要があるのは、コマンドを公開することです。アドインに記述した Sub A1() は機能しません。メソッドは、COM 可視クラスのメソッドとして公開する必要があります。プロジェクトに新しいクラスを追加し、コードを次のようにします。

Imports System.Runtime.InteropServices

<InterfaceType(Runtime.InteropServices.ComInterfaceType.InterfaceIsIDispatch)> _
<ComVisible(True)> _
Public Interface IMyAddinCommand
    Sub Command(ByVal index As Integer)
End Interface

<ClassInterface(Runtime.InteropServices.ClassInterfaceType.None)> _
<ComVisible(True)> _
Public Class MyAddinCommand
    Implements IMyAddinCommand

    Public Sub Command(index As Integer) Implements IMyAddinCommand.Command
        MsgBox("Command #" + CStr(index))
    End Sub
End Class

整数を取る Command() という名前の単一のメソッドを公開する単純なものです。私は MsgBox を使用しています。インデックスの値に基づいてコマンドを実装する Select ステートメントを作成する必要があります。また、グローバル マクロ内のコードとの一致にも注意してください。

もう 1 つ、このクラスを明示的に公開する必要があります。アドインで RequestComAddInAutomationService 関数をオーバーライドします。これをテストするために使用したものは次のようになりました。

Public Class ThisAddIn

    Private Sub ThisAddIn_Startup() Handles Me.Startup
        Application.OnKey("+^{U}", "Personal.xlsb!MyAddinCommand1")  '' Ctrl + Shift + U
    End Sub

    Protected Overrides Function RequestComAddInAutomationService() As Object
        If commands Is Nothing Then commands = New MyAddinCommand
        Return commands
    End Function

    Private commands As MyAddinCommand

End Class

F5 キーを押してコンパイルし、Excel を起動します。Ctrl+Shift+UI を押すと、次のようになります。

ここに画像の説明を入力

于 2012-04-21T00:49:36.787 に答える
0

まず、A1、A2、A3 がセル アドレスと見なされます。

  1. .xlsm ファイルを作成し、これらの VBA コードを追加します

    Sub AOne()
    MsgBox "Message from AOne"
    End Sub
    
    Sub ATwo()
    MsgBox "Message from ATwo"
    End Sub
    
    Sub AThree()
    MsgBox "Message from AThree"
    End Sub
    
  2. Visual Studio で Excel ワークブック プロジェクトを作成し、既存のファイルを追加して、上記で作成した .xlsm ファイルを選択します。

    private void ThisWorkbook_Startup(object sender, System.EventArgs e)
    {
        EnableShortCut();
    }
    
    private void ThisWorkbook_Shutdown(object sender, System.EventArgs e)
    {
    }
    
    public void EnableShortCut()
    {
        Excel.Application app = Globals.ThisWorkbook.Application;
        app.OnKey("+^{U}", "AOne"); //action A1 should be performed when user clicks  Ctrl + Shift + U
        app.OnKey("+^{L}", "ATwo");//action A2 should be performed when user clicks  Ctrl + Shift + L
        app.OnKey("+^{P}", "AThree"); //action A3 should be performed when user clicks  Ctrl + Shift + P
    }
    

プロジェクトを実行すると、Excel の Application.OnKey または Word の Application.Keybindings がマクロ名をパラメーターとして受け取ります。

ここでWordでショートカットキーを使用するための私の答えを確認してください

于 2012-04-20T16:19:33.627 に答える