delphi 调用 c语言 dll

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了delphi 调用 c语言 dll相关的知识,希望对你有一定的参考价值。

我用C语言写了一个Dll,编译成功。
是一个非常简单的加密算法,代码如下:
#include<stdio.h>
__declspec(dllexport) void Encipher(char a[16])

int i,len;
int s=1;
len=strlen(a);
for(i=0;i<len;i++)

a[i]=a[i]+s;
s=s+2;



现在我在Delphi中如何调用它并实现这样的功能,点击Button1,获取edit1.text的内容,存于数组中,然后调用Dll中的函数,进行简单加密,然后将结果赋给edit2.text。

很疑问你这个函数能传新的字符数组??
型参了吧。
不管怎么说,照着你的要求做了 delphi程序。

type
TArrChar = array[0..15] of char;
procedure Encipher(a:TArrChar);cdecl;external 'xxx.dll';

implementation

$R *.dfm

procedure TForm1.Button1Click(Sender: TObject);
var
a:TArrChar;
begin
CopyMemory(@a,@Edit1.text[1],16);
Encipher(a);
Edit2.Text:=a;
end;
参考技术A 用delphi写dll啊,那样调用方便些。看不懂C语言的代码

从Delphi调用C ++ DLL时发生访问冲突

我在Visual C ++ 6.0中编写了Unicode DLL。然后尝试从Delphi XE3调用DLL函数。

当我在Delphi中调试时,当跨过线路调用DLL函数时,我总是会遇到访问冲突异常。

但是,当我在Visual C ++中调试时,我可以看到从Delphi传递的所有参数都是正确的,并且我可以毫无例外地跳过所有代码行。

如果在调试器之外运行,那么我将看不到任何“访问冲突异常。

我尝试了许多方法,但是仍然无法弄清楚在Delphi中调试时如何消除异常。

下面是Visual C ++ 6.0部分中的代码:

TestDLL.cpp:

extern "C"  VOID WINAPI Test(CONST MESSAGEPROC lpMessageProc, LPVOID lParam)
{
    if (lpMessageProc != NULL)  
        (*lpMessageProc)(1500, (const LPVOID)(LPCTSTR)CString((LPCSTR)IDS_MYTEST), lParam);
    /*
    if (lpMessageProc != NULL)  
        (*lpMessageProc)(1500, (const LPVOID)(LPCTSTR)CString(_T("Test")), lParam);*/
}

TestDLL.h:

// TestDLL.h : main header file for the TESTDLL DLL
//

#if !defined(AFX_TESTDLL_H__38054A53_5CEE_4ABF_9BA8_BCE427FCB8E1__INCLUDED_)
#define AFX_TESTDLL_H__38054A53_5CEE_4ABF_9BA8_BCE427FCB8E1__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef __AFXWIN_H__
    #error include 'stdafx.h' before including this file for PCH
#endif

#include "resource.h"       // main symbols

#ifdef __cplusplus
extern "C" {
#endif  /* __cplusplus */

    typedef BOOL (CALLBACK* MESSAGEPROC)(CONST DWORD dwMessageId, CONST LPVOID lp, LPVOID lParam);

    VOID WINAPI Test(CONST MESSAGEPROC lpMessageProc, LPVOID lParam);

#ifdef __cplusplus
}
#endif


/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TESTDLL_H__38054A53_5CEE_4ABF_9BA8_BCE427FCB8E1__INCLUDED_)

下面是Delphi XE3部分中的代码:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  public
    { Public declarations }
  end;

  PForm1 = ^TForm1;

  TMessageProc = function (const dwMessageId: DWORD; const lp: Pointer; lParam: Pointer): BOOL; stdcall;
  {$EXTERNALSYM TMessageProc}

var
  Form1: TForm1;

procedure Test(const lpMessageProc: TMessageProc; lParam: Pointer); stdcall;

implementation

{$R *.dfm}

procedure Test; external 'TestDLL.dll' index 2;

function MessageProc(const dwMessageId: DWORD; const lp: Pointer; lParam: Pointer): BOOL; stdcall;
begin
  Result := True;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Test(MessageProc, @Self);  //  <---- This code line will cause "access violation
end;

我相信,当它尝试使用CString((LPCSTR)IDS_MYTEST)从资源加载字符串时,DLL测试功能中会出现问题。如果我将代码更改为CString(_T(“ Test”)),则问题消失。

谢谢

答案

您猜想,此声明将无效:

CString((LPCSTR)IDS_MYTEST)

尽管CString的此构造函数确实允许您向其传递资源ID,但它将尝试在调用进程的资源(即Delphi EXE的资源)而不是DLL的资源中查找资源。从DLL的资源加载字符串时,需要使用DLL的HINSTANCE(由DLL的DllMain()提供)。您可以为此使用CString::LoadString()方法,例如:

CString::LoadString()
另一答案

我终于弄清楚这是MFC代码(VC6.0版本)的错误。

我不知道是否可以发布MFC源代码,因此仅粘贴函数头和相关部分。

在Microsoft Visual Studio VC98 MFC SRC STRCORE.CPP中,我们可以看到以下三个功能:

HINSTANCE hInst;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    hInst = hinstDLL;
    return TRUE;
}

extern "C" VOID WINAPI Test(CONST MESSAGEPROC lpMessageProc, LPVOID lParam)
{
    if (lpMessageProc != NULL)
    {
        CString str;
        str.LoadString(hInst, IDS_MYTEST);
        (*lpMessageProc)(1500, (LPCTSTR)str, lParam);
    }
}

正如我们在上面的代码片段中看到的那样,只有函数1包含将在lpsz上进行特殊处理并检查它是否是字符串资源ID的代码,如果是,则从资源中加载字符串。函数2和3都没有这样的特殊过程。

当我们在VS6中创建项目时,项目的默认设置为_MBCS,在这种情况下,功能1将变为[]。

//////////////////////////////////////////////////////////////////////////////
// More sophisticated construction

CString::CString(LPCTSTR lpsz)   // Function 1
{
    Init();
    if (lpsz != NULL && HIWORD(lpsz) == NULL)
    {
        UINT nID = LOWORD((DWORD)lpsz);
        if (!LoadString(nID))
            TRACE1("Warning: implicit LoadString(%u) failed
", nID);
    }
    else
    {
            // Construct string normally
    }
}

/////////////////////////////////////////////////////////////////////////////
// Special conversion constructors

#ifdef _UNICODE
CString::CString(LPCSTR lpsz)   // Function 2
{
       // Construct string normally
}
#else //_UNICODE
CString::CString(LPCWSTR lpsz)  // Function 3
{
       // Construct string normally
}
#endif //!_UNICODE

所以CString(((LPCSTR)nResID)将实际上调用函数1并正确加载字符串资源。

由于未定义_UNICODE,因此功能2将被禁用。函数3适用于宽字符字符串。

因此,对于_MBCS项目,一切都与MSDN文档完美且一致地工作。

但是,当我将_MBCS更改为_UNICODE时,功能1将变为

CString::CString(LPCSTR lpsz)

将启用功能2,并且将禁用功能3。

所以CString((LPCSTR)nResID)实际上会调用函数2,该函数没有特殊的过程来加载字符串资源,这会造成问题。

此问题有两种解决方案:

  1. 始终使用CString((LPCTSTR)nResID)而不是CString((LPCSTR)nResID)从资源加载字符串。但是,此用法与MSDN文档不一致,因此我们必须将其称为未记录的用法。

  2. 始终使用LoadString加载字符串资源。

  3. 尽管解决方案1稍微简单一些,但它是未记录的用法,所以我最终选择了解决方案2来解决我的问题。

非常感谢您为解决此问题所提供的所有帮助。

以上是关于delphi 调用 c语言 dll的主要内容,如果未能解决你的问题,请参考以下文章

在delphi中,主程序和dll调用的成功,但是关闭的时候报错

Delphi 的 64 位 DLL c/c++ 接口

delphi中函数的声明和调用是啥意思?

C#调用delphi记录结构体问题

Delphi调用Dll返回结构体的问题?

VS2017写的exe调用Delphi 7写的DLL