Подключить dll к excel
Заранее прошу меня простить за возможно неграмотное изложение, ибо я не профессионал в работе с excel, а лишь любитель, которому приходится много с ним работать….
Столкнулся с такой проблемой. Сменил комп с 32-битной разрядной системой на другой с 64-битной. Скопировал на него нужный мне файл excel c макросом, использующий динамическую библиотеку dll. Сначала возникла проблема совместимости 64-битной системы и кода (как я понимаю) предназначенного для 32-битной системы в участке кода, имеющего вид
Declare Function XAXL Lib "CXA32.dll" (ByVal XAControl&, ByVal XAWndHnd&, ByVal XAMaxMem&, ByVal XAMaxRow&, ByVal XAMaxCol&, ByVal XAMaxNonZro&) As Integer
Declare Function XAXLREV Lib "CXA32.dll" (ByVal XAControl&) As Integer
Declare Function XAXLDONE Lib "CXA32.dll" (ByVal XAControl&) As Integer
Погуглив данную проблему добавил ключевое слово PtrSafe после каждого слова Declare – вроде проблема совместимости кода решилась… но теперь при запуске макроса возникает ошибка “Run-time error ‘48’: File not found: CXA32.dll” – то есть макрос не видит файл…
По инструкции к данному макросу файлы (библиотеки), относящиеся к данному макросу должны располагаться в папке Windows и вроде бы не требуют дополнительной регистрации. “В интренeтах” также пишут, что 64-битной системе подобные файлы должны быть в папке SysWOW64… Я пробовал копировать нужные файлы в эти и в разные другие папки (типа System32 и др.), а также пробовал прописывать в кавычках прямой путь к файлу, но результат один – макрос не находит данный файл.
Сначала думал, может в данном случае есть проблема работы с файлами «вообще», однако если через VBA просить excel открыть другую книгу, он это делает, то есть файлы видит… Возникает ощущение, что-либо я что-то не знаю о коде и там должно быть что-то допрописано для работы в 64-битной системе, или косяк в настройках excel (пользуюсь excel 2013).
Может кто сталкивался с подобным и подскажет что это за проблема и как ее решить?
Заранее прошу меня простить за возможно неграмотное изложение, ибо я не профессионал в работе с excel, а лишь любитель, которому приходится много с ним работать….
Столкнулся с такой проблемой. Сменил комп с 32-битной разрядной системой на другой с 64-битной. Скопировал на него нужный мне файл excel c макросом, использующий динамическую библиотеку dll. Сначала возникла проблема совместимости 64-битной системы и кода (как я понимаю) предназначенного для 32-битной системы в участке кода, имеющего вид
Declare Function XAXL Lib "CXA32.dll" (ByVal XAControl&, ByVal XAWndHnd&, ByVal XAMaxMem&, ByVal XAMaxRow&, ByVal XAMaxCol&, ByVal XAMaxNonZro&) As Integer
Declare Function XAXLREV Lib "CXA32.dll" (ByVal XAControl&) As Integer
Declare Function XAXLDONE Lib "CXA32.dll" (ByVal XAControl&) As Integer
Погуглив данную проблему добавил ключевое слово PtrSafe после каждого слова Declare – вроде проблема совместимости кода решилась… но теперь при запуске макроса возникает ошибка “Run-time error ‘48’: File not found: CXA32.dll” – то есть макрос не видит файл…
По инструкции к данному макросу файлы (библиотеки), относящиеся к данному макросу должны располагаться в папке Windows и вроде бы не требуют дополнительной регистрации. “В интренeтах” также пишут, что 64-битной системе подобные файлы должны быть в папке SysWOW64… Я пробовал копировать нужные файлы в эти и в разные другие папки (типа System32 и др.), а также пробовал прописывать в кавычках прямой путь к файлу, но результат один – макрос не находит данный файл.
Сначала думал, может в данном случае есть проблема работы с файлами «вообще», однако если через VBA просить excel открыть другую книгу, он это делает, то есть файлы видит… Возникает ощущение, что-либо я что-то не знаю о коде и там должно быть что-то допрописано для работы в 64-битной системе, или косяк в настройках excel (пользуюсь excel 2013).
Может кто сталкивался с подобным и подскажет что это за проблема и как ее решить? timur1987
Заранее прошу меня простить за возможно неграмотное изложение, ибо я не профессионал в работе с excel, а лишь любитель, которому приходится много с ним работать….
Столкнулся с такой проблемой. Сменил комп с 32-битной разрядной системой на другой с 64-битной. Скопировал на него нужный мне файл excel c макросом, использующий динамическую библиотеку dll. Сначала возникла проблема совместимости 64-битной системы и кода (как я понимаю) предназначенного для 32-битной системы в участке кода, имеющего вид
Declare Function XAXL Lib "CXA32.dll" (ByVal XAControl&, ByVal XAWndHnd&, ByVal XAMaxMem&, ByVal XAMaxRow&, ByVal XAMaxCol&, ByVal XAMaxNonZro&) As Integer
Declare Function XAXLREV Lib "CXA32.dll" (ByVal XAControl&) As Integer
Declare Function XAXLDONE Lib "CXA32.dll" (ByVal XAControl&) As Integer
Погуглив данную проблему добавил ключевое слово PtrSafe после каждого слова Declare – вроде проблема совместимости кода решилась… но теперь при запуске макроса возникает ошибка “Run-time error ‘48’: File not found: CXA32.dll” – то есть макрос не видит файл…
По инструкции к данному макросу файлы (библиотеки), относящиеся к данному макросу должны располагаться в папке Windows и вроде бы не требуют дополнительной регистрации. “В интренeтах” также пишут, что 64-битной системе подобные файлы должны быть в папке SysWOW64… Я пробовал копировать нужные файлы в эти и в разные другие папки (типа System32 и др.), а также пробовал прописывать в кавычках прямой путь к файлу, но результат один – макрос не находит данный файл.
Сначала думал, может в данном случае есть проблема работы с файлами «вообще», однако если через VBA просить excel открыть другую книгу, он это делает, то есть файлы видит… Возникает ощущение, что-либо я что-то не знаю о коде и там должно быть что-то допрописано для работы в 64-битной системе, или косяк в настройках excel (пользуюсь excel 2013).
Может кто сталкивался с подобным и подскажет что это за проблема и как ее решить? Автор - timur1987
Дата добавления - 26.12.2017 в 19:58
Вы можете получить доступ к функции или команде DLL в Microsoft Excel несколькими способами:
с помощью модуля кода Microsoft Visual Basic для приложений (VBA), в котором функция или команда была сделана доступной с помощью выражения Declare;
через лист макросов XLM, используя функции CALL или REGISTER;
непосредственно с листа или из настроенного элемента в пользовательском интерфейсе.
В этой документации не рассматриваются функции XLM. Рекомендуем использовать один из двух других подходов.
Для непосредственного доступа с листа или из настроенного элемента пользовательского интерфейса функцию или команду необходимо сначала зарегистрировать в Excel. Сведения о регистрации команд и функций см. в статье Accessing XLL Code in Excel.
Вызов функций и команд DLL из VBA
Вы можете получать доступ к функциям и командам DLL в VBA с помощью выражения Declare. Это выражение имеет один синтаксис для команд и другой — для функций.
Синтаксис 1: команды
Синтаксис 2: функции
Команды должны возвращать тип void. Функции должны возвращать типы, которые VBA может распознавать ByVal. Это означает, что некоторые типы легче возвращать путем изменения аргументов: строк, массивов, пользовательских типов и объектов.
VBA не может проверить совпадение списка аргументов и возвращаемого значения в модуле Visual Basic со значениями, закодированными в DLL. Это необходимо внимательно проверить самостоятельно, поскольку ошибка может привести к сбою Excel.
Когда аргументы функции или команды не передаются по ссылке или указателю, им должно предшествовать ключевое слово ByVal в объявлении arglist. Если функция C/C++ принимает указатели в качестве аргументов или функция C++ принимает в качестве аргументов ссылки, то их следует передавать ByRef. Ключевое слово ByRef можно не указывать в списках аргументов, поскольку этот режим выбран в VBA по умолчанию.
Типы аргументов в C/C++ и VBA
Следует помнить об указанных ниже фактах при сравнении объявлений типов аргументов в C/C++ и VBA.
Тип String в VBA передается как указатель на структуру BSTR байтовых строк в режиме ByVal и как указатель на указатель в режиме ByRef.
Тип Variant в VBA, содержащий строку, передается как указатель на структуру BSTR строк Юникода из двухбайтовых знаков при передаче в режиме ByVal и как указатель на указатель в режиме ByRef.
Integer VBA — это 16-битный тип, эквивалентный signed short в C/C++.
Long VBA — это 32-битный тип, эквивалентный signed int в C/C++.
VBA и C/C++ допускают определяемые пользователем типы данных (применяются операторы Type и struct соответственно).
VBA и C/C++ поддерживают тип данных Variant, заданный для C/C++ в файлах заголовка Windows OLE/COM как VARIANT.
Массивы VBA — это объекты OLE SafeArrays, определенные для C/C++ в файлах заголовка Windows OLE/COM как SAFEARRAY.
Тип данных Currency в VBA передается как структура типа CY, определенная в файле заголовка Windows wtypes.h, в режиме ByVal и как указатель на него в режиме ByRef.
Ниже приведен пример определений эквивалентных пользовательских типов.
Excel передает только значения Variant указанных ниже типов в определяемую пользователем функцию VBA.
Тип данных VBA | Битовые флаги типа Variant C/C++ | Описание |
---|---|---|
Double | VT_R8 | |
Boolean | VT_BOOL | |
Date | VT_DATE | |
String | VT_BSTR | Строка байтов OLE Bstr |
Диапазон | VT_DISPATCH | Ссылки на ячейку и диапазон |
Переменная, содержащая массив | VT_ARRAY VT_VARIANT | Литеральные массивы |
Ccy | VT_CY | 64-битное целое число, масштабированное для достижения точности 4 знака после запятой. |
Переменная, содержащая ошибку | VT_ERROR | |
VT_EMPTY | Пустые ячейки или пропущенные аргументы |
Вы можете проверить тип переданного аргумента Variant в VBA с помощью VarType, если функция не возвращает тип значений диапазона при вызове со ссылками. Чтобы определить, является ли Variant объектом ссылки Range, можно использовать функцию IsObject.
В VBA можно создавать Variant, содержащие массивы переменных, назначая свойство Value Range для Variant. Ячейки в исходном диапазоне, отформатированные с использованием стандартного денежного формата для действующих региональных параметров, преобразуются в элементы массива типа Currency. Ячейки, отформатированные как даты, преобразуются в элементы массива типа Date. Ячейки, содержащие строки, преобразуются в Variant BSTR с расширенными символами. Ячейки, содержащие ошибки, преобразуются в Variant типа VT_ERROR. Ячейки, содержащие значения True или False типа Boolean, преобразуются в Variant типа VT_BOOL.
Variant хранит значение True как –1, а значение False — как 0. Числа, не отформатированные как даты или суммы валют, преобразуются в Variant типа VT_R8.
Аргументы строк и переменных
Excel внутренне работает со строками Юникода из расширенных символов. Когда пользовательская функция VBA объявляется как принимающая аргумент String, Excel преобразует предоставленную строку в байтовую строку в соответствии с региональными стандартами. Если вам нужно, чтобы функции передавалась строка Юникода, пользовательская функция VBA должна принимать Variant вместо аргумента String. Тогда функция DLL сможет принять эту строку BSTR из расширенных символов типа Variant из VBA.
Чтобы из DLL возвращались строки Юникода в VBA, следует изменить имеющийся аргумент строки Variant. Для этого следует объявить функцию DLL как принимающую указатель на Variant в коде C/C++, а также объявить аргумент в коде VBA как ByRef varg As Variant . Память предыдущей строки следует освободить, а значение новой строки, созданное с использованием строки OLE Bstr, работает только в DLL.
Чтобы байтовая строка возвращалась в VBA из DLL, следует изменить имеющийся аргумент BSTR байтовой строки. Для этого следует объявить функцию DLL как принимающую указатель на BSTR в коде C/C++, а также объявить аргумент в коде VBA как ByRef varg As String.
Строки, переданные такими способами из VBA, следует обрабатывать только с помощью строковых функций BSTR OLE. Это позволит избежать проблем, связанных с памятью. Например, необходимо вызывать функцию SysFreeString, чтобы освободить память, прежде чем перезаписывать переданную строку, и функцию SysAllocStringByteLen или SysAllocStringLen, чтобы выделить место для новой строки.
Вы можете создавать ошибки листов Excel как Variants в VBA с помощью функции CVerr с аргументами, показанными в следующей таблице. Ошибки листов также можно возвращать в VBA из DLL с помощью объектов Variants типа VT_ERROR со следующими значениями в поле ulVal.
Обратите внимание на то, что значение Variant ulVal эквивалентно значению аргумента CVerr с шестнадцатеричным значением x800A0000.
Вызов функций DLL непосредственно с листа
Вы не сможете получить доступ к функциям DLL Win32 с листа, если не используете, к примеру, интерфейсы VBA или XLM либо не сообщите Excel заранее о функции, ее аргументах и типе возвращаемого значения. Этот процесс называется регистрацией.
Ниже приведены способы, которыми можно получить доступ к функциям DLL на листе.
Объявите функцию в VBA, как показано выше, и получите доступ к ней через пользовательскую функцию VBA.
Сначала обеспечьте вызов функции DLL с помощью CALL на листе макросов XLM, а затем — доступ к ней с помощью определяемой пользователем функции XLM.
Используйте команду XLM или VBA, чтобы вызвать функцию XLM REGISTER, которая предоставляет сведения, необходимые Excel для опознания функции при ее вводе в ячейке листа.
Преобразуйте DLL в XLL и зарегистрируйте функцию с помощью функции xlfRegister C API после активации XLL.
Четвертый подход изолированный: код, регистрирующий функции, и код функций хранятся в одном объекте кода. Изменение надстройки не включает изменение листа XLM или модуля кода VBA. Чтобы сделать это с широкими возможностями управления, оставаясь в рамках возможностей API C, необходимо преобразовать DLL в XLL и загрузить получившуюся надстройку с помощью диспетчера настроек. Это позволяет Excel вызывать функцию, предоставленную библиотекой DLL, при загрузке или активации надстройки, из которой затем можно зарегистрировать все функции, которые содержит XLL, и выполнять другие задачи инициализации DLL.
Вызов команд DLL непосредственно из Excel
Команды DLL Win32 недоступны напрямую из диалоговых окон и меню Excel без интерфейса, например VBA, или без предварительной регистрации команд.
Получать доступ к командам DLL можно следующими способами:
Объявите команду в VBA так, как описано выше, и получите к ней доступ с помощью макроса VBA.
Сначала обеспечьте вызов команды DLL с помощью CALL на листе макросов XLM, а затем — доступ к ней с помощью макроса XLM.
Используйте команду XLM или VBA, чтобы вызвать функцию XLM REGISTER, которая предоставляет сведения, необходимые Excel для опознания команды при ее вводе в диалоговом окне, которое запрашивает имя команды макроса.
Преобразуйте DLL в XLL и зарегистрируйте команду с помощью функции xlfRegister C API.
Excel игнорирует возвращаемое значение, если оно не вызывается с листа макросов XLM. В этом случае возвращаемое значение преобразуется в значение TRUE или FALSE. Таким образом, следует возвращать значение 1, если команда выполнена успешно, и 0, если она завершилась с ошибкой или была отменена пользователем.
Память DLL и многочисленные экземпляры DLL
Когда приложение загружает библиотеку DLL, ее исполняемый код загружается в глобальную кучу, чтобы его можно было запустить, и в глобальной куче выделяется место для его структур данных. Windows использует сопоставление памяти, чтобы эти области памяти отображались как часть процесса приложения и приложение могло получать к ним доступ.
Если второе приложение затем загрузит библиотеку DLL, Windows не будет создавать еще одну копию ее исполняемого кода, так как эта память доступна только для чтения. Windows сопоставляет память исполняемого кода DLL с процессами в обоих приложениях. Тем не менее выделяется дополнительное место для личной копии структур данных DLL, а эта копия сопоставляется только со вторым процессом. Это гарантирует, что ни одно из приложений не будет конфликтовать с данными DLL другого приложения.
Это означает, что разработчикам DLL не следует беспокоиться о том, что несколько приложений (или несколько экземпляров одного приложения) будут получать доступ к статическим и глобальным переменным и структурам данных. Каждый экземпляр всех приложений получает собственную копию данных DLL.
Разработчикам DLL следует позаботиться о том, чтобы один и тот же экземпляр приложения не вызывал DLL много раз из разных потоков, так как это может привести к состязанию за данные этого экземпляра. Дополнительные сведения см. в статье Управление памятью в Excel.
You can access a DLL function or command in Microsoft Excel in several ways:
Through a Microsoft Visual Basic for Applications (VBA) code module in which the function or command has been made available using a Declare statement.
Through an XLM macro sheet by using the CALL or REGISTER functions.
Directly from the worksheet or from a customized item in the user interface (UI).
This documentation does not cover XLM functions. It is recommended that you use either of the other two approaches.
To be accessed directly from the worksheet or from a customized item in the UI, the function or command must first be registered with Excel. For information about registering commands and functions, see Accessing XLL Code in Excel.
Calling DLL functions and commands from VBA
You can access DLL functions and commands in VBA by using the Declare statement. This statement has one syntax for commands and one for functions.
Syntax 1 - commands
Syntax 2 - functions
Commands should return void. Functions should return types that VBA can recognize ByVal. This means that some types are more easily returned by modifying arguments in place: strings, arrays, user-defined types, and objects.
VBA cannot check that the argument list and return stated in the Visual Basic module are the same as coded in the DLL. You should check this yourself very carefully, because a mistake could cause Excel to crash.
When the function or command's arguments are not passed by reference or pointer, they must be preceded by the ByVal keyword in the arglist declaration. When a C/C++ function takes pointer arguments, or a C++ function takes reference arguments, they should be passed ByRef. The keyword ByRef can be omitted from argument lists because it is the default in VBA.
Argument types in C/C++ and VBA
You should note the following when you compare the declarations of argument types in C/C++ and VBA.
A VBA String is passed as a pointer to a byte-string BSTR structure when passed ByVal, and as a pointer to a pointer when passed ByRef.
A VBA Variant that contains a string is passed as a pointer to a Unicode wide-character string BSTR structure when passed ByVal, and as a pointer to a pointer when passed ByRef.
The VBA Integer is a 16-bit type equivalent to a signed short in C/C++.
The VBA Long is a 32-bit type equivalent to a signed int in C/C++.
Both VBA and C/C++ allow the definition of user-defined data types, using the Type and struct statements respectively.
Both VBA and C/C++ support the Variant data type, defined for C/C++ in the Windows OLE/COM header files as VARIANT.
VBA arrays are OLE SafeArrays, defined for C/C++ in the Windows OLE/COM header files as SAFEARRAY.
The VBA Currency data type is passed as a structure of type CY, defined in the Windows header file wtypes.h, when passed ByVal, and as a pointer to this when passed ByRef.
The following is an example of equivalent user type definitions.
Excel only passes Variants of the following types to a VBA user-defined function.
VBA data type | C/C++ Variant type bit flags | Description |
---|---|---|
Double | VT_R8 | |
Boolean | VT_BOOL | |
Date | VT_DATE | |
String | VT_BSTR | OLE Bstr byte string |
Range | VT_DISPATCH | Range and cell references |
Variant containing an array | VT_ARRAY VT_VARIANT | Literal arrays |
Ccy | VT_CY | 64-bit integer scaled to permit 4 decimal places of accuracy. |
Variant containing an error | VT_ERROR | |
VT_EMPTY | Empty cells or omitted arguments |
You can check the type of a passed-in Variant in VBA using the VarType, except that the function returns the type of the range's values when called with references. To determine if a Variant is a Range reference object, you can use the IsObject function.
You can create Variants that contain arrays of variants in VBA from a Range by assigning its Value property to a Variant. Any cells in the source range that are formatted using the standard currency format for the regional settings in force at the time are converted to array elements of type Currency. Any cells formatted as dates are converted to array elements of type Date. Cells containing strings are converted to wide-character BSTR Variants. Cells containing errors are converted to Variants of type VT_ERROR. Cells containing Boolean True or False are converted to Variants of type VT_BOOL.
The Variant stores True as -1 and False as 0. Numbers not formatted as dates or currency amounts are converted to Variants of type VT_R8.
Variant and string arguments
Excel works internally with wide-character Unicode strings. When a VBA user-defined function is declared as taking a String argument, Excel converts the supplied string to a byte-string in a locale-specific way. If you want your function to be passed a Unicode string, your VBA user-defined function should accept a Variant instead of a String argument. Your DLL function can then accept that Variant BSTR wide-character string from VBA.
To return Unicode strings to VBA from a DLL, you should modify a Variant string argument in place. For this to work, you must declare the DLL function as taking a pointer to the Variant and in your C/C++ code, and declare the argument in the VBA code as ByRef varg As Variant . The old string memory should be released, and the new string value created by using the OLE Bstr string functions only in the DLL.
To return a byte string to VBA from a DLL, you should modify a byte-string BSTR argument in place. For this to work, you must declare the DLL function as taking a pointer to a pointer to the BSTR and in your C/C++ code, and declare the argument in the VBA code as ' ByRef varg As String'.
You should only handle strings that are passed in these ways from VBA using the OLE BSTR string functions to avoid memory-related problems. For example, you must call SysFreeString to free the memory before overwriting the passed in string, and SysAllocStringByteLen or SysAllocStringLen to allocate space for a new string.
You can create Excel worksheet errors as Variants in VBA by using the CVerr function with arguments as shown in the following table. Worksheet errors can also be returned to VBA from a DLL using Variants of type VT_ERROR, and with the following values in the ulVal field.
Note that the Variant ulVal value given is equivalent to the CVerr argument value plus x800A0000 hexadecimal.
Calling DLL functions directly from the worksheet
You cannot access Win32 DLL functions from the worksheet without, for example, using VBA or XLM as interfaces, or without letting Excel know about the function, its arguments, and its return type in advance. The process of doing this is called registration.
The ways in which the functions of a DLL can be accessed in the worksheet are as follows:
Declare the function in VBA as described previously and access it via a VBA user-defined function.
Call the DLL function using CALL on an XLM macro sheet, and access it via an XLM user-defined function.
Use an XLM or VBA command to call the XLM REGISTER function, which provides the information that Excel needs to recognize the function when it is entered into a worksheet cell.
Turn the DLL into an XLL and register the function using the C API xlfRegister function when the XLL is activated.
The fourth approach is self-contained: the code that registers the functions and the function code are both contained in the same code project. Making changes to the add-in does not involve making changes to an XLM sheet or to a VBA code module. To do this in a well-managed way while still staying within the capabilities of the C API, you must turn your DLL into an XLL and load the resulting add-in by using the Add-in Manager. This enables Excel to call a function that your DLL exposes when the add-in is loaded or activated, from which you can register all of the functions your XLL contains, and carry out any other DLL initialization.
Calling DLL commands directly from Excel
Win32 DLL commands are not accessible directly from Excel dialog boxes and menus without there being an interface, such as VBA, or without the commands being registered in advance.
The ways in which you can access the commands of a DLL are as follows:
Declare the command in VBA as described previously and access it via a VBA macro.
Call the DLL command using CALL on an XLM macro sheet, and access it via an XLM macro.
Use an XLM or VBA command to call the XLM REGISTER function, which provides the information Excel needs to recognize the command when it is entered into a dialog box that expects the name of a macro command.
Turn the DLL into an XLL and register the command using the C API xlfRegister function.
As discussed earlier in the context of DLL functions, the fourth approach is the most self-contained, keeping the registration code close to the command code. To do this, you must turn your DLL into an XLL and load the resulting add-in using the Add-in Manager. Registering commands in this way also lets you attach the command to an element of the user interface, such as a custom menu, or to set up an event trap that calls the command on a given keystroke or other event.
All XLL commands that are registered with Excel are assumed by Excel to be of the following form.
Excel ignores the return value unless it is called from an XLM macro sheet, in which case the return value is converted to TRUE or FALSE. You should therefore return 1 if your command executed successfully, and 0 if it failed or was canceled by the user.
DLL memory and multiple DLL instances
When an application loads a DLL, the DLL's executable code is loaded into the global heap so that it can be run, and space is allocated on the global heap for its data structures. Windows uses memory mapping to make these areas of memory appear as if they are in the application's process so that the application can access them.
If a second application then loads the DLL, Windows does not make another copy of the DLL executable code, as that memory is read-only. Windows maps the DLL executable code memory to the processes of both applications. It does, however, allocate a second space for a private copy of the DLL's data structures and maps this copy to the second process only. This ensures that neither application can interfere with the DLL data of the other.
This means that DLL developers do not have to be concerned about static and global variables and data structures being accessed by more than one application, or more than one instance of the same application. Every instance of every application gets its own copy of the DLL's data.
DLL developers do need to be concerned about the same instance of an application calling their DLL many times from different threads, because this can result in contention for that instance's data. For more information, see Memory Management in Excel.
Всем привет!
Подскажите, пожалуйста, а то я не догоняю что я делаю не так.
Я не силен в "ООП", поэтому прошу помощи у профессионалов.
- Как прописать вызов функции из excel без указания параметров, так как нужно чтобы эта функция не возвращала значение, а работала с ячейками?
- Как из DLL определить нужную книгу (ту из которой была вызвана эта DLL)?
- Как передаются массивы?
- Почему excel не видит DDL?
Вот например короткий код и как я это пишу, что не правильно?
Это в DLL Проект1 в Класс1:
Добавлено через 2 часа 48 минут
Мега мозги, помогите пжл.!
Создание dll для динамического подключения в EXCEL без регистрации
Доброго времени суток! Дорогие друзья, переработал большой объем информации из интернета, но.
Как зарыть файл без сохранения (Excel/VBA )?
Как закрыть .xls без сохранения, чтобы не выскакивало окно с вопросом о сохранении изменений в.
Как сохранить VBA-массив в CSV-файл без Excel
Как можно сохранить VBA-массив в CSV-файл без использования Excel? Сейчас я сначала вставляю.
Как создать файл DLL так, чтобы его можно было подключить к VBA Excel?
Пытаясь научиться подключать к VBA хотя бы самую тривиальную функцию на C++. Для примера написал.
Вопрос задан двусмысленно: если развернуто он звучит так "вызов функции, расположенной в DLL, из Excel", то
если развернуто он звучит так: "вызов функции из Excel, если эта функция является функцией Excel", то Вам сюда
Программирование с использованием C API в Excel
Только если Вы передадите книгу (или ее имя, если нужно только определить) как параметр (по ссылке).
Куда? Если в функцию, то минимально затратно - по ссылке.
Судя по строкам 4,5,7,8 и т.д. в Вашем верхнем коде, если Вы записали его в редакторе VBA и он не выделил написанное красным, то прекрасно видит.
Если же Вы про свой код
то, простите меня, это не DLL. И еще "печалька" - на VBA DLL не создать - это интерпретируемый язык.
так не вызвать.
Если "DLL Проект1 в Класс1" у Вас в одном проекте с "Это в excel", попробуйте набрать Класс1.FnVoz1, правда что делает весь Ваш код я не знаю.
Вопрос задан двусмысленно: если развернуто он звучит так "вызов функции, расположенной в DLL, из Excel", то
Сорри, может я коряво изложил вопрос.
Я спрашивал как например вызвать функцию, которую я перенес из excel в DLL (VB6) - FnVoz1()?
FnVoz1()- эта функция, которая делает некоторые манипуляции в книге excel и она написана мною для примера и
показана в примере (что она делает), а так же у нее нет аргументов (параметров для передачи обратно), т.е. она просто
выполняет какие то действия с объектом excel из которого она была вызвана.
Так вот, мне не понятно, почему excel ругается, что не может найти эту функцию, хотя DLL лежит в одной папке вместе с excel из которого она вызывается. пишет что DLL не найдена (ошибка 53)
И правильно ли я пишу в DLL обращение обратно в excel, а так же принцип написания кода при обращении к ячейкам excel из Dll?
где Класс1 - имя модуля класса , где Вы расположили код №2 (смотреть в свойствах, слева в редакторе VBA). Это - обычный вызов функции, расположенной в другом общем модуле, модуле класса или модуле формы, если она объявлена как Public .
Хотя я DLL даже подключал в ручную через Tools->References.
Добавлено через 2 минуты
Я пробовал через Класс1.FnVoz1, так он другую ошибку выдает, complite error: Variable not defined
выделяет класс1
Мда. не увидел, и что у Вас вот-такие выражения скомпилировались в VB6
т.е Excel.ShTab.Cells(2, intColum + 1).Select- так нельзя в VB6 писать?
. а все дальнейшие строки-обращения в этом примере обрабатывает уже не этот код, а экземпляр приложения Excel: при компиляции примера на VB6 эти строки будут "переведены" в вызовы API Excel, которые будет принимать и выполнять экземпляр приложения Excel. То есть в памяти компьютера будет "висеть" и Ваше приложение - транслятор и приложение Excel. Вам это нужно было? Зачем тогда это делать на VB6 если можно оставить на VBA? На практике это необходимо, если Вам нужны возможности VB6, отсутствующие в VBA, или когда Ваше приложение вообще делает что-то другое и ему просто понадобились вычислительные возможности Excel. но тогда тему лучше перенести-создать в соответствующем языку программирования разделе форума.
P.S. К сожалению, мне не удалось открыть ссылку, так-что по ней комментариев не будет.
В данной статье описаны основные способы работы с DLL в языке программирования Visual Basic. Рассчитана прежде всего на начинающих программистов. Она поможет заинтересованному читателю ответить на ряд вопросов: что такое DLL, зачем его используют, как его правильно использовать и создавать.
Итак, что же такое DLL? Этой английской аббревиатурой словосочетания Dynamic-Link Library, что переводится как "динамически подключая библиотека", называют в операционных системах семейства Microsoft Windows динамические библиотеки, содержащие процедуры и функции, которые могут многократно использоваться различными программными приложениями.
Ранние версии Windows работали в условиях жёсткого ограничения памяти, а потому использование DLL должно было позволить более активно использовать её, ведь один экземпляр DLL, загруженный в память, может использоваться сразу несколькими программами. Однако существенных преимуществ полностью получить не удалось. Причиной является такое явление, как DLL Hell ("DLL ад") - несколько программ работают с одинаковой библиотекой, однако с разными не полностью совместимыми версиями. Это приводило к большому количеству ошибок. Сейчас в Windows используется технология SxS, разрешающая параллельное использование разные версий библиотеки, что иногда сводит на нет её главное преимущество.
Тем не менее, DLL (к этому понятию также относят библиотеки ActiveX и драйверы) активно используются при разработке ПО.
Но в каких же случаях лучше использовать модули, а в каких DLL? Во-первых, модули, подключаемые к проекту, должны быть написаны на том языке, с которым работает среда разработки. DLL могут быть написаны на другом языке, хранятся они в уже откомпилированном виде. Во-вторых, использование DLL позволяет экономить память в том случае, если ею пользуются две или более программ одновременно (точнее, при использовании одной и той же версии). В-третьих, если функции не будут использоваться другими программами, то нет смысла создавать DLL. Тоже самое можно сказать про те случаи, когда нужно просто разбить для удобства большой код по модулям - удобнее работать с ними в одном проекте.
Говоря о DLL как о библиотеках подпрограмм, следует рассказать о соглашения вызова - это часть интерфейса приложения, которая регламентирует технические особенности вызова подпрограммы, передачи параметров, возврата из неё, передача результата в основную программу. Говоря простым языком - разные стандарты использования подпрограмм (функций и процедур).
Например, это расположения параметров подпрограммы: они могут быть в стеке или в регистрах. Различие существенное, от того-то эти соглашения не совместимы и нужно знать, какие используются в той или иной библиотеке.
Существует немало разных соглашений, но из них обратим внимание на stdcall и cdecl. Первый используют библиотеки WinAPI функций. Второй же пригодиться нам для использования функций, написанных на языке Си (cdecl - C Declaration). В обоих соглашениях аргументы передаются через стек, справа налево. Но в stdcall очистку стека производит вызываемая подпрограмма, а в cdecl - вызывающая программа.
Если что-то не поняли - не спешите ломать голову этими соглашениями, ведь для полного понимания работы с ними вам нужно знание основ Ассемблера. Не знаете? Вернетесь ещё к ним потом.
Внимание мы уделим stdcall и cdecl. Большинство библиотек у вас будут работать и с этим.
Каким же образом использовать DLL? Как воспользоваться функциями из них? Для этого их нужно подключить к программе. Различают два способа подключения: статический и динамический.
Статический. При использовании DLL загружается в память сразу при запуске программы, а выгружаются лишь при завершении работы программы. Преимущества: возможность вызова любой функции из библиотеки в любое время. Недостатки: нерациональное использование памяти в том случае, если какие-либо функции и процедуры из неё используются лишь 1 раз за всё время работы. Ведь она будет всё время в памяти, хотя логичнее её от туда выгрузить за ненадобностью.
Динамический. Загрузить и выгрузить DLL можно в любое время при работе программы. Преимущества: экономное использование памяти в тех случаях, когда функция используется один раз за всё время работы программы, после чего они уже не нужны. Недостатки: большее, по сравнению со статическим подключением, количество кода.
При данном способе подключения нужные функции объявляют в разделе деклараций:
Здесь:
Declare - ключевое слово, обозначающее, что дальше следует объявление функции из библиотеки
название_функции - название функции из библиотеки, которое будет использоваться у вас в программе. Может отличаться от названия функции в самой библиотеке (см. Alias)
Lib "имя_библиотеки" - название подключаемой библиотеки. Она должна находиться в папке System32, либо вместе с исполняемым файлом
Alias "название_функции_в_библиотеке" - необязательный параметр. Используется в том случае, если имя объявляемой функции в программе отличается от имени функции в библиотеке.
В библиотеке функция сложения двух чисел называется Summ, а у нас Symma.
Используются подключенные таким образом функции точно так же, как и обычные. Например:
Ну, это всё описываются библиотеки с stdcall. А как же cdecl? Ну, в VB есть ключевое слово cDecl, причём использоваться оно должно так:
Однако вероятно вы получите Bad DLL Calling Convention (Error 49). Точнее, ваша программа будет работать лишь в Native (машинном) коде, т.е. уже полностью откомпилированная. А в режиме отладки (P-код), как и в самой IDE - нет. Дальнейшая разработка программы будет очень осложнена.
Мда, вот они какие - недокументированные возможности! Может есть шанс подключать cdecl библиотеки с динамическим подключением?
Как уже было сказано выше, при данном способе подключения библиотек кода будет намного больше.
Ну во-первых сразу скажу, что в Visual Basic нет возможности стандартными средствами подключать библиотеки динамическим способом.
Для этого нам нужно 3 WinAPI функции: LoadLibrary, GetProcAddress, FreeLibrary. Объявим их в разделе деклараций, а также 3 переменные, куда будем сохранять результаты:
Как видите, используем мы эти функции из стандартной библиотеки kernel32.dll! Так что это статическое подключение, а значит как минимум эта библиотека у нас будет всегда в памяти.
Разберем подробнее эти функции, новые для нас. Как они работают, что делают и для чего нужны?
Эта функция отображает заданный исполняемый модуль в адресное пространство вызывающего процесса, или, проще говоря, загружает библиотеку в память.
Единственный её параметр lpLibFileName (строковой тип) содержит путь либо имя подключаемой библиотеки.
Если функция завершается успешно, то она возвращает дескриптор модуля (библиотеки). Если происходит ошибка - 0.
Эта функция извлекает адрес нужной процедуры или функции из DLL.
Имеет два параметра: hModule ("хэндл", обычно целочисленный тип) и lpProcName (строковой тип). Первый параметр - дескриптор модуля. Получить его нам позволяет вышеописанная функция LoadLibrary. Второй параметр - имя нужной функции.
Если функция завершается успешно, то она возвращает адрес экспортируемой функции из модуля (библиотеки). Если происходит ошибка - 0.
Эта функция уменьшает итоговое число ссылок на DLL (если она используется одновременно несколькими приложениями), а если число ссылок становится равно 0, то выгружает библиотеку из памяти.
Имеет один параметр: hModule, т.е. дескриптор библиотеки (который, напомню, возвращает функция LoadLibrary).
Если функция завершается успешно, возвращаемой значение не равно нулю. Если происходит ошибка, то оно равно нулю.
Но основе этих функций составим программу, которая будет подключать динамически DLL (stdcall):
Как вы сами видите, длина кода намного больше, чем при статической загрузке, поскольку мы включили сюда ещё и обработку ошибок при помощи конструкции if. Налицо - гибкость!
А теперь рассмотрим возможность динамического (впрочем, как я писал выше, работать со статическим очень сложно) подключения cdecl. Для этого, кроме упомянутых выше, нам понадобятся ещё функции CallWindowProc и CopyMemory. Сразу замечу, что необходимо знание Ассемблера для полного понимания работы такой программы.
Имеет 5 параметров: lpFunc, который указывает на процедуру, и Param1-Param4, через которые передаются параметры функции. Все параметры имеют тип Long.
Возвращаемое значение определяет результат обработки и зависит от параметров.
Эта функция копирует содержимое одного блока памяти в другой.
Имеет 3 параметра: Dest - указатель в тот блок памяти, куда нужно произвести копирование, Src - указатель на тот блок памяти, который копируется, Length - количество байтов, которое должно передаваться.
Возвращаемое значение отсутствует.
В качестве примера использования мы будем вызывать функцию qsort из библиотеки ntdll.dll, написанной на Си (cdecl). Данная функция используется для быстрой сортировки массива.
Подробное описание работы - в комментариях к коду.
Альтернативный код - с CallBack функцией. В отличие от предыдущего варианта, он гибче, т.к. позволяет менять критерий сравнения, а во-вторых здесь используется CopyMemory. Вместо функции RtlMoveMemory здесь используем API-функции GetMem4, PutMem4, GetMem2.
По каким-то причинам в Microsoft решили, что возможность создавать обычные DLL в Visual Basic будет лишней, а потому создавать их так просто мы не сможем, во всяком случае, без бубна. Да, я говорю про обычные DLL, потому что можно создавать лишь ActiveX DLL. В чём же разница обычных DLL от ActiveX?
ActiveX была внедрена в Windows компанией Microsoft в 1996 году как продолжение развития её технологий OLE (Object Linking and Embedding) и COM (Component Object Model).
OLE - технология связи и внедрения объектов в другие документы и объекты. Она позволяет передавать данные от одной программы для редактирования другой. Так, например, изображение или рисунок в программе Microsoft Word можно редактировать в Paint, и при сохранении он автоматически измениться и в документе Word.
COM - технология создания программного обеспечения на основе взаимосвязанных компонентов. Воплощение идей объектно-ориентированного программирования (ООП): инкапсуляции и полиморфизма.
И вот мы имеем фреймворк ActiveX, совмещающий эти две технологии. Он активно используется при разработке форм приложений. Все кнопки, списки, диалоговые окна, "этикетки" (Label), поля ввода (Textbox), их которых мы собираем форму - всё это ActiveX. Вообще многие продукты Microsoft используют управляющий элементы ActiveX, что позволяет использовать их функционал в других приложениях. Подробнее об этом смотрите в моей статье об использовании компонента Windows Media Player для создания музыкального проигрывателя.
Итак, приступим к созданию ActiveX DLL! Запускаем Visual Basic, в окне New Project выбираем ActiveX DLL (либо в меню File-New Project). И вот уже перед нами и редактор кода! Процесс написания кода здесь ничем не отличается от написания обычной программы. Пишете код, сохраняете проект, компилируете библиотеку.
Но не забывайте тот факт, что ActiveX DLL, в отличие от обычной DLL, не библиотека процедур и функций, а библиотека классов! Так что здесь забудьте про процедурное программирование - только ООП! Пишем классы: поля, свойства, методы, инкапсуляция, наследование (кстати, в Visual Basic нет обычного наследования, так что это делать нужно через наследование интерфейсов), полиморфизм. В принципе это и есть главное отличие. Хотя написать процедуру как метод не составит проблем.
Коротко об использовании ActiveX DLL. Подключаются они точно так же, как и обычные, но с той разницей, что необходимо создать объект и вызывать его методы - это называют связыванием. Различают ранее и позднее связывание.
Ранее связывание осуществляется командой
где myObj - имя объекта, а myClass - название класса в DLL. При таком связывании вся информация о методах, свойствах и событиях класса известна во время компиляции, потому вызов методов происходит также быстро, как и обычных функций в
программе.
При позднем связывании нужно сначала создать объект:
после чего можно вызывать его метод:
Проверка методов/свойств происходит во время выполнения программы, так что мы проигрываем раннему связыванию во времени, но зато этот способ более гибкий.
Если вас заинтересовала технология ActiveX и её возможности, рекомендую вам ознакомится со статьей Б.Л. Файфеля "COM в действии": "Создаем ActiveX DLL": и "Вопросы" (часто задаваемые вопросы про ActiveX).
Вообще-то создавать обычные DLL в Visual Basic, как уже было сказано выше, нельзя без "танцев с бубном". Но энтузиастами был создан Add-In для среды разработки Visual Basic, который позволяет создавать почти полноценные DLL на Visual Basic. Единственный, и самый существенный минус - невозможность использования созданных таким образом библиотек в других языках программирования. Загрузить Add-In можно по этой ссылке К архиву прилагается файл ReadMe с подробным описанием установки дополнения и его использования, а также пара примеров создания DLL.
Каким же образом мы компилируем библиотеки, если такой возможности в VB нет? Весь секрет в недокументированных возможностях link.exe ("линкеры", как известно, выполняют компиляцию в всё с этим связанное). Если запустить его в DOS командой link.exe > c:/link.txt, то мы увидим множество различных ключиков. Их них наибольший интерес представляют /DLL и /DRIVER. Понятно, что компилятор Visual Basic 6 способен ещё и компилировать библиотеки с драйверами! Ну вот именно на первом ключе и основан принцип работы этой надстройки: перехватываем командную строку линкера, изменяем, вызываем настоящий линкер.
Только одно но. это скорее не продуманная реализация линкера, а баг. Он нарушает компоновку библиотеки, из-за чего она будет работать не стабильно. А именно - лишь в Visual Basic!
Если вас заинтересовало это, то подробнее ознакомиться с работой надстройки вы можете здесь.
Читайте также: