Нарушение прав доступа при вызове C ++ DLL из Delphi - PullRequest
0 голосов
/ 24 октября 2019

Я пишу Unicode DLL в Visual C ++ 6.0. Затем попробуйте вызвать функцию DLL из Delphi XE3.

Когда я отлаживаю в 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;

Я считаю, что проблема возникает в DLLФункция тестирования, когда она пытается загрузить строку из ресурса, используя CString ((LPCSTR) IDS_MYTEST). Если я изменяю код на CString (_T ("Test")), то проблема исчезает.

Спасибо

Ответы [ 2 ]

0 голосов
/ 26 октября 2019

Я наконец понял, что это ошибка кодов MFC (версия VC6.0).

Я не знаю, могу ли я публиковать исходные коды MFC, поэтому я просто вставлю заголовки функций и связанные частитолько.

В Microsoft Visual Studio \ VC98 \ MFC \ SRC \ STRCORE.CPP мы можем видеть следующие 3 функции:

//////////////////////////////////////////////////////////////////////////////
// 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\n", 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

Как мы видим в приведенном выше фрагменте кода,только функция 1 содержит коды, которые возьмут специальный процесс на lpsz и проверит, является ли он строковым идентификатором ресурса; если да, то загрузите строку из ресурса. Обе функции 2 и 3 не имеют таких специальных процессов.

Когда мы создаем проект в VS6, настройки по умолчанию для проекта - _MBCS, в таком случае функция 1 станет

CString::CString(LPCSTR lpsz)

поэтому CString ((LPCSTR) nResID) фактически вызовет функцию 1 и правильно загрузит строковый ресурс.

Функция 2 будет отключена, поскольку _UNICODE не определен. И функция 3 работает с широкими символьными строками.

Следовательно, для проекта _MBCS все работает идеально и согласованно с документом MSDN.

Однако, когда я изменяю _MBCS на _UNICODE, функция 1 становится

CString::CString(LPCWSTR lpsz)

Функция 2 будет включена и функция 3 будет отключена.

Таким образом, CString ((LPCSTR) nResID) фактически вызовет функцию 2, которая НЕ имеет специального процесса для загрузки строкиресурс, который создает проблему.

Существует два решения этой проблемы:

  1. Всегда использовать CString ((LPCTSTR) nResID) вместо CString ((LPCSTR) nResID) загрузить строку из ресурса. Однако это использование несовместимо с документом MSDN, поэтому мы должны называть его недокументированным использованием.

  2. Всегда использовать LoadString для загрузки строкового ресурса.

Хотя решение 1 немного проще, оно недокументировано, поэтому я, наконец, выбрал решение 2 для решения своей проблемы.

Большое спасибо за помощь в решенииэтот выпуск.

0 голосов
/ 24 октября 2019

Как вы и предполагали, этот оператор не будет работать:

CString((LPCSTR)IDS_MYTEST)

Хотя этот конструктор CString позволяет вам передать ему идентификатор ресурса, он попытается найти ресурс в вызывающем процессе. (т. е. ресурсы Delphi EXE), а не ресурсы DLL. Вам нужно использовать HINSTANCE библиотеки DLL, как это предусмотрено библиотекой DLL DllMain(), при загрузке строк из ресурсов библиотеки DLL. Для этого вы можете использовать метод CString::LoadString(), например:

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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...