Я пытаюсь автоматизировать на первый взгляд очень простую задачу через C# - передать строковое значение в inputBox, вызываемый через функцию VBA.
Для этого я использовал Visual C# .NET Automation Client для создания экземпляра MS Access и открытия необходимой базы данных:
using System.Management.Automation;
using Access = Microsoft.Office.Interop.Access;
Access.Application oAccess = new Access.Application();
oAccess.Visible = true;
oAccess.OpenCurrentDatabase("D:\\path\\to\\database", false, "");
В упомянутой базе данных у нас есть макрос, который вызывает функцию VBA через действие «RunCode
». Затем этот public Function
вызывает другой private Sub
:
Public Function ImportMappingDatabases()
ImportMappings InputBox("Specify Path", "Path", mstrcDefaultPath), _
InputBox("Choose Package", "Package", "")
End Function
Private Sub ImportMappings(Optional Path As String = mstrcDefaultPath, Optional Package As String = "")
Вот где я застрял. Поле ввода с заголовком «Path
» хочет, чтобы пользователь ввел определенный путь, который более поздний код использует по причинам, которые не нужно объяснять. То же самое относится и к полю ввода с заголовком «Package
». Я предполагаю, что есть способ "передать" строковое значение из кода С# в поле ввода поля ввода, но я не знаю, как обратиться к полю ввода и установить значение. После этого от пользователя требуется нажать кнопку «ОК» или «Отмена», применяется тот же вопрос, я попытаюсь обобщить его в 1 предложении:
Как получить ссылку на это поле ввода, установить значение ввода и «щелкнуть» одну из кнопок?
Больше информации:
Я вызываю публичную функцию через: oAccess.Run("ImportMappingDatabases");
Я не могу изменить функции/подпрограммы VBA.
Обновление 1:
Проверьте мой ответ ниже.
Жаль, что нельзя изменить VBA Sub. Если бы у Sub был аргумент, значение можно было бы передать аргументу через Run. По крайней мере, я могу в VBScript, так что предположим, что C# тоже может. oAccess.Run "ImportMappingDatabases", varInput;
Макросы не использую.
Вы не пытаетесь использовать потребление и автоматизацию объектов, а пытаетесь эмулировать клавиатуру - и это не очень практично. Это очень похоже на попытку очистить веб-сайт и ввести значения — сложно, подвержено ошибкам и не так уж надежно. Если общедоступная подпрограмма (для функции) была создана в VBA, вы можете использовать + вызывать + использовать эту подпрограмму VBA. Но такие подпрограммы не могут запрашивать у пользователя ввод с клавиатуры, так как теперь вам нужно использовать нажатия клавиш для эмуляции нажатий клавиш. Вы больше не можете делать это, а затем использовать программу .net как класс, которому требуется ввод с клавиатуры консоли.
@TimWilliams Можете ли вы предоставить какой-либо ресурс, указывающий мне правильное направление, пожалуйста? .
@AlbertD.Kallal В этом конкретном случае мы получаем «нажатия клавиатуры» в виде автоматически сгенерированного массива строк, который впоследствии можно использовать для ввода, что является лишь частью шагов, которые ранее выполнялись вручную и, как вы догадались, - очень подвержен ошибкам. Попытка здесь состоит в том, чтобы использовать то, что у нас есть, в качестве устаревшего кода — у владельца программного обеспечения все еще нет разрешения на прикосновение к VBA.
Я нашел способ получить дескриптор inputBox и способ передать ему строковое значение. Вам нужен следующий импорт:
[DllImport("User32.dll", CharSet = CharSet.Auto)] static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll", CharSet = CharSet.Auto)] static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter,
string lpcClassName, string lpcWindowTitle);
[DllImport("User32.dll", CharSet = CharSet.Auto)] static extern UInt32 SendMessage(IntPtr handle, UInt32 message,
IntPtr wParam, string lParam);
Затем вы можете использовать вызовы Win API, чтобы найти окно MS Access и дескрипторы окна inputBox:
IntPtr parent = FindWindow(null, "The access Window title here");
IntPtr childInputBox = IntPtr.Zero;
IntPtr TextBox = IntPtr.Zero;
if (parent != IntPtr.Zero)
{
childInputBox = FindWindowEx(parent, IntPtr.Zero, string.Empty, "InputBox name here");
if (childInputBox != IntPtr.Zero)
{
TextBox = FindWindowEx(childInputBox, IntPtr.Zero, "Edit",
string.Empty);
if (TextBox != IntPtr.Zero)
{
SendMessage(TextBox, WM_SETTEXT, new IntPtr(0), "Your out string to the input here");
}
}
}
Здесь я наткнулся на стену, потому что на самом деле мне нужно вызвать свой макрос, прежде чем я попытаюсь захватить ручку. Итак, вызов макроса с помощью:
oAccess.Run("TheNameOfTheMacro");
Будет запускать его в том же потоке, поэтому наше приложение Window Forms станет непригодным для использования, пока макрос не будет выполнен. Это усложнение привело к тому, что я получил разрешение на расширение кода VBA с помощью общедоступной функции, принимающей строку в качестве параметра (как обсуждалось в комментариях), и продолжил оттуда. Если вы когда-нибудь решите, что вам нужно использовать поле ввода, вам нужно запустить макрос в другом потоке и каким-то образом проверить, «вызвано ли» поле ввода, если да, то отправьте сообщение.
Я думаю, вам нужно будет использовать вызовы Win API для заполнения полей ввода.