Я использую InstallShield 2013 Premium. Я создал C ++ dll в Visual Studio 2010, чтобы обеспечить некоторую функциональность, которую я не смог бы достичь с помощью только InstallScript. Моя функция C ++ должна вернуть маленькую строку (имя пользователя) в InstallScript после значительной работы, чтобы получить это значение.
В C ++ я использую CStringW для представления своих строк. В идеале я хотел бы вернуть его как Unicode, но я доволен ANSI, если это мой единственный вариант. Я пробовал многочисленные подходы с CStringW, std :: wstring, std :: string, LPCTSTR, LPSTR, char * … Я пробовал прямой возврат и пытается вернуться по ссылке. Ничего не работает!
Иногда функция dll зависает, иногда выдает исключение, в лучшем случае возвращает значения мусора с непечатными символами. Официальная документация на это не кажется точной (она не работает для меня!). Обширный поиск в Google и поиск плат Flexera дают «решения» от других, борющихся с той же нелепой проблемой, и все же ни одна из них не работает и для меня …
Я не пробовал это до конца, так как считал само собой разумеющимся, что вы можете достаточно легко передавать строки между dll и InstallScript. Оглядываясь назад, я должен был начать с интерфейса между ними, а затем разработать dll-функциональность после этого.
Спасибо за помощь, ребята! Я наконец понял это для себя все же. Однако у этого решения есть несколько аспектов, которые я не нашел ни в документированном, ни в другом месте.
Основные моменты
1) вернуть WCHAR * из C ++
2) использовать WSTRING в качестве соответствующего возвращаемого типа в прототипе InstallScript
3) вернуть его в обычную переменную STRING в InstallScript и обработать как любую другую
4) сохранить значение, на которое указывает WCHAR * в dll C ++, в статической переменной, в противном случае оно, очевидно, удаляется и указатель становится недействительным
Если вы продвинулись достаточно далеко, чтобы оказаться в одной лодке, мне, вероятно, не нужно разбираться во всех деталях, но вот пример кода, который поможет вам в этом:
Visual Studio Def File
LIBRARY MyIsDllHelper
EXPORTS
getSomeStringW @1
Заголовок C ++
#ifdef MYISDLLHELPER_EXPORTS
#define MYISDLLHELPER_API __declspec(dllexport)
#else
#define MYISDLLHELPER_API __declspec(dllimport)
#endif
#include <stdexcept>
#include <atlstr.h>
namespace MyIsDllHelper
{
class MyIsDllHelper
{
public:
static MYISDLLHELPER_API WCHAR * getSomeStringW();
};
}
C ++ Source
#include "stdafx.h"#include "MyIsDllHelper.h"
static CStringW someStringRetained;
CStringW getTheString()
{
CStringW s;
// do whatever...
return s;
}
WCHAR * MyIsDllHelper::MyIsDllHelper::getSomeStringW()
{
someStringRetained = getTheString();
return someStringRetained.GetBuffer( someStringRetained.GetLength() ) + L'\0';
}
InstallScript
#define HELPER_DLL_FILE_NAME "MyIsDllHelper.dll"prototype WSTRING MyIsDllHelper.getSomeStringW();
function DoSomething( hMSI )
STRING svSomeString;
STRING svDllPath;
begin
// Find the .dll file path. (A custom function)
GetSupportFilePath( HELPER_DLL_FILE_NAME, TRUE, svDllPath );
// Load the .dll file into memory.
if( UseDLL( svDllPath ) != 0 ) then
MessageBox ("Could not load dll: " + svDllPath, SEVERE );
abort;
endif;
// Get the string from the dll
try
svSomeString = MyIsDllHelper.getSomeStringW();
catch
MessageBox( "Could not execute dll function: MyIsDllHelper.getSomeStringW", SEVERE );
abort;
endcatch;
// Remove the .dll file from memory.
if( UnUseDLL( svDllPath ) < 0 ) then
MessageBox ("Could not unload dll: " + svDllPath, SEVERE );
abort;
endif;
// Use the string
MessageBox( "svSomeString: [" + svSomeString + "]", INFORMATION );
end;
Лучше всего, когда вы можете заставить свой интерфейс использовать подходы C, а не C ++. Подходим интерфейс функций, таких как GetEnvironmentVariable в котором ваша функция принимает указатель на буфер (и для правильности размер этого буфера), а затем записывает в этот буфер. Большая часть вашей реализации не должна изменяться, если вы можете закончить с чем-то вроде StringCchCopy из вашей CString в буфер.
Поскольку вы специально упоминаете CStringW и другие типы строк Unicode, я бы предложил выбрать LPWSTR
(скорее, чем LPTSTR
) для типа интерфейса.
Тогда все, что осталось, это объявить это для использования с помощью InstallScript. Это означает, что прототип должен использовать WSTRING а также ByRef. Если интерфейс функции такой же, как GetEnvironmentVariableW, прототип должен выглядеть примерно так:
prototype MyFunc(WSTRING, BYREF WSTRING, NUMBER);
Вы можете использовать строки, но я думаю, что проблема с кодировкой.
Посмотрите здесь: https://adventuresinscm.wordpress.com/2014/01/12/unicode-files-and-installshield/