注册 登录
编程论坛 VB.NET论坛

C编写的DLL函数,返回值类型为char *,VB.net如何调用这个函数

ThinkerHua 发布于 2022-06-19 16:09, 1056 次点击
我用C编写了一个DLL,其中的函数原型为:
char *SectionSteelAW(char const *RawText, unsigned const CtrlCode);
返回值为char *类型。
同时我还用C编写了一个Demo程序,调用这个DLL,是能够正常执行的。
(这个DLL项目及Demo所有源码我已上传到github上,地址为:https://

但是在程序里,始终不能正常执行,具体表现为:
1、返回值始终是nothing
2、执行此函数后,实参RawText的值会被改变
3、执行几次之后,报错:System.AccessViolationException:“尝试读取或写入受保护的内存。这通常指示其他内存已损坏。”
(这个程序所有源码我也已上传到github上,地址为:https://,见分支V2)

请问如何正确调用此函数?
另请问,正确调用此函数后,得到的返回值字符串不需要使用时,如何正确释放内存空间?
(函数内部是通过动态分配内存提供的返回值,由于我的Demo程序是C编写的,可以用free(Resault)来释放。但是中该如何做?)

恳请前辈们不吝赐教!!!

SectionSteelAW函数由于底层实现代码较多,不便全部贴出,仅贴出最终实现代码:
程序代码:

#include "dll.h"
#include "calculation.h"
#include <string.h>
#include <windows.h>

DLLIMPORT char *SectionSteelAW(char const *RawText, unsigned const CtrlCode) {
    char *FormatedText = NULL;
    char *SecSteType = NULL;
    char *Resault = NULL;
    void *obj = NULL;
    if (formatting(RawText, &FormatedText) == 0)
        return NULL;
    SecSteType = getSecSteType(FormatedText);
    if (SecSteType == NULL)
        return NULL;
    obj = NewObj(SecSteType);
    if (obj == NULL)
        return NULL;
    if (SetData(obj, SecSteType, FormatedText) == 0) {
        FreeObj(&obj, SecSteType);
        return NULL;
    }
    Resault = getResault(obj, SecSteType, CtrlCode);

    FreeObj(&obj, SecSteType), obj = NULL;
    free(FormatedText), FormatedText = NULL;
    //SecSteType来自const字符串数组,不需要释放,只需赋值NULL
    SecSteType = NULL;
    return Resault;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) {
    switch(fdwReason) {
        case DLL_PROCESS_ATTACH: {
            break;
        }
        case DLL_PROCESS_DETACH: {
            break;
        }
        case DLL_THREAD_ATTACH: {
            break;
        }
        case DLL_THREAD_DETACH: {
            break;
        }
    }

    /* Return TRUE on success, FALSE on failure */
    return TRUE;
}


C语言Demo程序的具体代码如下:
程序代码:

#include <windows.h>
#include <stdio.h>
/*
#define TYPE_AREA                    1
#define TYPE_EXCLUDE_TOPSURFACE        2
#define TYPE_WEIGHT                    4
#define METHOD_ROUGHLY                8
#define METHOD_PRECISELY            16
#define METHOD_LOOKUP                32

DLLIMPORT char *SectionSteelAW(char const *RawText, unsigned const CtrlCode);
*/
typedef char *(*p_SectionSteelAW)(char const *RawText, unsigned const CtrlCode);

int main (int argc, char *argv[]) {
    HMODULE module = NULL;
    p_SectionSteelAW SSAW = NULL;
    char *resault = NULL;
    char RawText[64] = {'\0'};
    unsigned ctrlcode = 0;
   
    module = LoadLibraryA("SectionSteelAW.dll");
    if (module == NULL) {
        MessageBox(0,"Cannot start this program because of missing \"SectionSteelAW.dll\" file!\n","Error",MB_ICONERROR);
        return 0;
    }
    SSAW = (char *(*)(char const *, unsigned const))GetProcAddress(module, "SectionSteelAW");
    if (SSAW == NULL)
        goto clean;
        
    while (scanf("%s %u", RawText, &ctrlcode) != EOF) {
        resault = SSAW(RawText, ctrlcode);
        if (resault != NULL)
            printf("%s\n", resault);
        free(resault);
        resault = NULL;
    }
   
clean:
    FreeLibrary(module);
   
    return 0;
}

测试结果是能够正常工作的
输入:H244*175 19
输出:0.244*2+0.175*3-0.007*2
输入:D210*5 33
输出:PI()*0.21
输入:PL500*600*12-PLT120*100*12-PLD150*12 36
输出:(0.5*0.6-0.12*0.5*0.1-PI()*0.075^2)*0.012*7850
这里想插入截图的,不知道怎么插入

程序中的具体应用代码如下(全局变量的赋值修改对此问题没有影响,所以相关代码就不贴出来了):
程序代码:

Module SectionSteelAW
    Public Const _TESTFLAG = 0                  '测试代码控制符

    Public Const TYPE_AREA = 1                  '计算类型:面积
    Public Const TYPE_EXCLUDE_TOPSURFACE = 2    '计算类型:面积子项,扣除顶面
    Public Const TYPE_WEIGHT = 4                '计算类型:重量
    Public Const METHOD_ROUGHLY = 8             '计算模式:粗略
    Public Const METHOD_PRECISELY = 16          '计算模式:精细
    Public Const METHOD_LOOKUPINTABLE = 32      '计算模式:查表

    Public CTRLCODE As Integer = 0              '控制码
    Public Offset_Rows As Integer = 0           '目标行偏移参数
    Public Offset_Columns As Integer = 1        '目标列偏移参数
    Public Overwrite As Integer = 0             '目标已有数据是否覆盖参数

    Public Declare Function SectionSteelAW Lib "SectionSteelAW.dll" (ByVal RawText As String, ByVal CtrlCode As UInteger) As String

    Public Sub Generate()
        Dim xlApp As Object = Nothing       'Excel对象
        Dim xlWorkbook As Object = Nothing  '工作薄对象
        'Dim xlSheet As Object = Nothing     '工作表对象
        Dim xlRange As Object = Nothing     '单元格区域
        Dim xlCell As Object = Nothing      '单元格

        Dim sRawText As String = Nothing
        Dim sResault As String = Nothing

        Dim starttime As Date
        Dim endtime As Date

        '获取Excel程序、工作薄、工作表、选定区域
        On Error Resume Next
        xlApp = GetObject(, "Excel.Application")
        If Err.Number <> 0 Then
            Err.Clear()
            MsgBox("Please open an Excel application first!", vbOKOnly + vbExclamation, "Waring")
            Exit Sub
        End If
        xlWorkbook = xlApp.ActiveWorkbook
        If xlWorkbook Is Nothing Then
            MsgBox("Please open an Workbook first!", vbOKOnly + vbExclamation, "Waring")
            Exit Sub
        End If
        xlRange = xlApp.Selection
        On Error GoTo 0

        If _TESTFLAG Then starttime = System.DateTime.Now
        'xlApp.screenupdating = False
        For Each xlCell In xlRange
            '不覆写且目标不为空时直接跳过
            If (Overwrite = 0) And (xlCell.offset(Offset_Rows, Offset_Columns).value IsNot Nothing) Then Continue For

            sRawText = xlCell.Value
            sResault = SectionSteelAW(sRawText, CTRLCODE)

            '输出到Excel
            If sResault IsNot Nothing Then
                xlCell.offset(Offset_Rows, Offset_Columns).Value = "=" & sResault
                '赋值为Nothing是否能正确释放???
                sResault = Nothing
            Else
                xlCell.offset(Offset_Rows, Offset_Columns).Value = Nothing
            End If
        Next
        'xlApp.screenupdating = True
        If _TESTFLAG Then endtime = System.DateTime.Now : MsgBox("执行持续时间:" & (endtime - starttime).ToString)

        '释放对象内存
        xlCell = Nothing : xlRange = Nothing : xlWorkbook = Nothing : xlApp = Nothing
    End Sub
End Module



[此贴子已经被作者于2022-6-21 20:23编辑过]

1 回复
#2
ThinkerHua2022-06-22 20:51
问题已经解决
这个问题涉及的托管概念,目前还没有完全搞懂,但是不妨碍解决问题。
C中的char *类型,在中对应声明应为IntPtr
本问题的C函数原型,在中完整对应声明应修改为:
Public Declare Function SectionSteelAW Lib "SectionSteelAW.dll" (ByVal RawText As IntPtr, ByVal CtrlCode As UInteger) As IntPtr

IntPtr类型与String类型相互转换需使用命名空间System.Runtime.InteropServices的Marshal类,详见微软的说明文档。
有一个问题是Marshal.FreeHGlobal()函数释放本问题中的字符串内存空间会报错。也可能是我自己理解错了,根本不是这样用的。
为解决这个问题,又在DLL中添加了一个free_dallocstr()函数,以供使用。(其实就是C里的free()函数套了个壳)

详细代码参见主贴的github仓库(主程序仓库的V2分支已合并为主分支)
1