当前位置: 首页 > 知识库问答 >
问题:

非Unicode Inno设置中的C#字符串

左丘繁
2023-03-14

我尝试发送一个inno-setup字符串到一个C# Dll,就像这个线程从一个C# DLL返回一个带有非托管导出到Inno Setup脚本的字符串

在这种情况下,我的问题是我有非 unicode inno 设置,因此无法使用宽字符串。在一些 delphi 论坛中,我读到我应该使用的字符串类型是宽字符串。在上面的线程中,它还使用宽字符串。当我尝试使用普通字符串时,我只收到来自 C# 消息的一个字母。编辑:警报仅显示一个字母,RTFText显示消息,中间有空格 是否有我可以使用的解决方法,以便我获得完整的消息?

切换到unicode inno设置会很好,但在目前的开发阶段,这不是一个选项。

由于评论说你需要代码在这里,它只是在上面提到的线程中,但也许我在这里是错的;)。

function GetInformationEx(out message: String):Integer;
external 'GetInformationEx@{src}\data\tools\ZipLib.dll stdcall loadwithalteredsearchpath';

procedure ProgressCallback(progress:Integer);
var 
    AStr: String;
    returnCode : Integer;
begin
WriteDebugString('ProgressCallback called');
    if(progress > pbStateZip.position) then 
    begin
        pbStateZip.position := progress;
        lblState2.Caption  := IntToStr(progress)+' %';
        try
            returnCode := GetInformationEx(AStr);
            if returnCode = 0 then begin
                alert(AStr);
                revProgresses.UseRichEdit := True;
                revProgresses.RTFText := AStr;
            end
        except
            ShowExceptionMessage;
        end;
    end
    if(progress >= 100)then 
    begin
        KillTimer(0,m_timer_ID);
        WriteManufacturerTextFile(ExpandConstant('{app}\projects'));
        FileOperationsAfterExtraction();
    end
WriteDebugString('ProgressCallback leave');
end;

和C#位:

[DllExport("GetInformationEx", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
public static int GetInformationEx([MarshalAs(UnmanagedType.BStr)] out string strout)
{
    int returnCode = 0; //success
    try
    {
        string something = "ladida\0";
        strout = something;
    }
    catch 
    {
        strout = "";
        returnCode = 1; //Error
    }
    return returnCode; 
}

共有2个答案

宗政浩慨
2023-03-14

除了Martin Prikryl的回答之外,我发现了一种不同的方法,我不知道这是否有问题,但是当我将非托管类型更改为(UnmanagedType.AnsiBStr)时,它似乎可以工作。

    [DllExport("GetInformationEx", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
    public static int GetInformationEx([MarshalAs(UnmanagedType.AnsiBStr)] out string strout)
    {
        int returnCode = 0; //success
        try
        {
            string something = "ladida";
            strout = something;
        }
        catch 
        {
            strout = "";
            returnCode = 1; //Error
        }
        return returnCode; 
    }

如果有人知道不同解决方案的好处是什么,我很乐意阅读并给予正面评价

编辑:我修改了C#和Inno代码,见下面的例子。

C#代码:

[DllExport("GetInformationEx", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
public static int GetInformationEx([MarshalAs(UnmanagedType.AnsiBStr)] out string strout, int length)
{
    int returnLength = length;
    try
    {
        string info = _setupInformation.ToString();
        if (info.Length < 1000) 
        {
            returnLength = info.Length;   
        }
        strout = info.Substring(0, returnLength);
    }
    catch 
    {
        strout = "";
        returnLength = 0;
    }
    return returnLength; 
}

Inno设置代码:

function GetInformationEx(out message: string; length : Integer):Integer;
external 'GetInformationEx@{src}\data\tools\ZipLib.dll stdcall loadwithalteredsearchpath';

procedure ProgressCallback(progress:Integer);
var 
    progressInfo: String;
    returnCode : Integer;
begin
WriteDebugString('ProgressCallback called');
    if(progress > pbStateZip.position) then 
    begin
        pbStateZip.position := progress;
        lblState2.Caption  := IntToStr(progress)+' %';
        try
            progressInfo := StringOfChar('C', 1000);
            SetLength(progressInfo, GetInformationEx(progressInfo, 1000));
            if(Length(progressInfo) > 0) then begin
                revProgresses.UseRichEdit := True;
                revProgresses.RTFText := progressInfo;
            end
        except
            ShowExceptionMessage;
        end;
    end
    if(progress >= 100)then 
    begin
        KillTimer(0,m_timer_ID);
        WriteManufacturerTextFile(ExpandConstant('{app}\projects'));
        FileOperationsAfterExtraction();
    end
WriteDebugString('ProgressCallback leave');
end;

这里的区别是,我在Inno Setup端分配内存,只使用字符串,不在C#端分配内存。

我使用AnsiBStr的原因是,据我所知,BStr使用unicode。非unicode Inno Setup不提供宽字符串

如果我用BStr填充我的Inno安装字符串,我会得到以下结果

而不是

据我所知,这是由于编码。

我在Inno Setup新闻组中问了这个问题 http://news.jrsoftware.org/read/article.php?id=29498

卢黎明
2023-03-14

您使用AnsiBStr是错误的。您的C#代码使用COM堆,而Delphi代码使用自己的私有堆。这是错误的。您必须从同一堆进行分配和解除分配。

我想我会这样做:

[DllExport("GetInformationEx", CallingConvention = CallingConvention.StdCall)]
public static void GetInformationEx(out IntPtr strout)
{
    try
    {
        strout = Marshal.StringToCoTaskMemAnsi(_setupInformation.ToString());
    }
    catch 
    {
        // not very keen on catch all exception handler, but it's your choice
        strout = IntPtr.Zero; 
    }
}

在Inno方面,它看起来像这样:

procedure CoTaskMemFree(pv: PChar);
  external 'CoTaskMemFree@ole32.dll stdcall';
procedure GetInformationEx(out message: PChar);
  external 'GetInformationEx@{src}\data\tools\ZipLib.dll \
  stdcall loadwithalteredsearchpath';

procedure ProgressCallback(progress: Integer);
var 
  progressInfoPtr: PChar;
  progressInfo: string;
begin
  GetInformationEx(progressInfoPtr);
  if progressInfoPtr <> nil then begin
    progressInfo := progressInfoPtr;
    CoTaskMemFree(progressInfoPtr);

    // do something with progressInfo
  end;
end;

我去掉了很多逻辑,专注于互操作。我相信你明白我的意思。

我对Inno一无所知,所以其中一些可能不起作用。我不知道指针的使用,或字符串转换,或者CoTaskMemFree是否可用。我把它当成Delphi代码来写。如果CoTaskMemFree不可用,您可以从DLL导出另一个函数,该函数公开了Marshal. FreeCoTaskMem

 类似资料:
  • 这是一个简单的问题,但请听我说完--Java家庭作业的一部分有一个密码验证器方法。要求是简单的-密码必须在6-10个字符之间,必须只由数字或字母,并必须有至少2个数字在它是有效的。我使用if语句并使用regex实现了这一点,出于某种原因,我无法使非单词字符regex匹配,尽管每个在线regex检查器都显示这应该是有效的,甚至用于regex检查的jetbrains插件也显示这应该是有效的。(我也明白

  • 问题内容: 我有一个包含非ASCII字符的URI,例如: http://www.abc.de/qq/qq.ww?MIval=typo3_bsl_int_Smtliste&p_smtbez=Schmalbl -ttrigeSomerzischeruchtanb 如何从此URI中删除“ …” 问题答案: 我猜想URL的来源更多是错误的。也许您正在解决错误的问题?从URI中删除“奇怪”字符可能会赋予它完

  • 问题内容: 我在Matplotlib中显示非ASCII字符时遇到问题,这些字符呈现为小框而不是适当的字体,看起来像(我用红色油漆填充了这些框以突出显示它们): 我如何解决它? 一个相关的问题是 Matplotlib中的重音字符 。 问题答案: 实际上,此问题可能有两个不同的原因: 默认字体不包含这些字形 您可以使用以下方法更改默认字体(在完成任何绘制之前!) 在某些版本的matplotlib中,您

  • 注意:  Typekit 现已更名为 Adobe Fonts,包含在 Creative Cloud 和其他订阅中。了解详情。 After Effects 提供了各种创意选项,用于文本的格式化和自定义。使用“字符”面板设置字符格式。如果选择了文本,您在“字符”面板中所做的更改仅影响选定文本。如果没有选择文本,您在“字符”面板中所做的更改将影响所选文本图层和文本图层的选定源文本关键帧(如果存在)。如果

  • 问题内容: 如果我有一个PHP字符串,如何有效地确定它是否至少包含一个非ASCII字符?所谓非ASCII字符,是指不属于此表的任何字符,http://www.asciitable.com/,其位置为32-126(含)。 因此,它不仅必须是ASCII表的一部分,而且还必须是可打印的。我想检测一个包含至少一个不符合这些规范的字符的字符串(不可打印的ASCII字符或完全不同的字符,例如不属于该表的Uni

  • 问题内容: 我得到的任务是从文本文件或字符串中删除所有非数字字符,包括空格,然后在旧字符旁边打印新结果,例如: 之前: 后: 由于我是初学者,所以我不知道从哪里开始。请帮忙 问题答案: 最简单的方法是使用正则表达式