c#delphi가 쓴 dll을 호출하여 해결하고 있는 문제

5541 단어 .NET
이 글을 쓰는 것은 저에게 큰 의미가 있습니다. 블로그를 쓰는 습관을 기르고 싶었지만 게으르기 때문에 하지 않았습니다. 일하면서 했던 것을 꺼내서 필요한 사람에게 말할 수 있는 것도 좋은 일입니다.
배경.
다른 사람delphi가 쓴 dll를 호출해야 하는 프로젝트가 있는데 그 안에 여러 가지 방법이 있습니다. 방법이 있는 파라미터는 구조체를 전달하는 지침이나 구조체의 2급 지침이 필요합니다. c#로 호출하는 과정에서 많은 시간을 들였기 때문에 기록할 필요가 있다고 생각합니다.
매개변수에는 다음과 같은 레벨 포인터가 있습니다.
Delphi에 정의된 구조체:
type
  PUserInfo = ^UserInfo;
  UserInfo = packed record
    nCardNo : Cardinal;           
    nBalance : Integer;           
    aName : array [0..19] of char;
  end;

UserInfo는 세 개의 멤버 변수를 포함하는 구조체이며, PUserInfo는 이 구조체를 가리키는 지침이다.
Delphi 메소드 포털:
//      
//  : nCardNo   , pUserInfo       
//  : pUserInfo       
//  :     ,0    
function GetUserInfoByCardNo(nCardNo : Cardinal; pUserInfo : PUserInfo) : Integer; stdcall;

Delphi에 정의된 구조체와 방법은 다음과 같습니다. c#이 호출되는 방법은 다음과 같습니다.
c# 구조체 정의:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct UserInfo
        {
            public uint nCardNo;
            public Int32 nBalance;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public char[] aName;
        }

여기에 Pack=1을 더하면 하나의 메모리 정렬과 관련된 문제이다. 메모리 정렬은 컴파일러의 관할 범위에 속한다. 심층적인 원리인 나 같은 소백도 잘 알지 못한다. 델파이와 c#의 다른 컴파일러 메커니즘만 알고 있기 때문에 메모리 정렬 방식을 가리키지 않으면 하나의 그룹 데이터를 꺼내야 할 때 문제가 생긴다. 나는 처음부터 이것을 추가하지 않아서 문제가 생겼다.charset 인코딩 방식에 대해서도 구체적인 상황을 보고 결정해야 한다.
c# 방법:
//    
	[DllImport("yourdelphi.dll")]
        public extern int GetUserInfoByCardNo(uint nCardNo, IntPtr inptr);
		
	//    
	public UserInfo GetUser(string id)
        {
            UserInfo rui = new UserInfo();
            int len = Marshal.SizeOf(rui);//      
            IntPtr ptr = Marshal.AllocHGlobal(len);//           
            Marshal.StructureToPtr(rui, ptr, true);//                 
            int res = 0;
            try
            {
                uint cardid = Convert.ToUInt32(id, 16);
                res = GetUserInfoByCardNo(cardid, ptr);//       
		//                          
                rui = (UserInfo)Marshal.PtrToStructure(ptr, typeof(UserInfo));
                Marshal.FreeHGlobal(ptr);//              
                return rui;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

사실 실현의 관건은delphi에서 필요한 바늘이 c#에서 어떻게 정의되는지에 있다. 여기서 Inptr라는 유형을 사용하면 된다. 물론ref 키워드를 사용해도 될 것이다.
매개변수에는 다음과 같은 2단계 포인터가 있습니다.
Delphi에 정의된 구조체:
type
  PPLogInfo = ^PLogInfo;
  PLogInfo = ^LogInfo;
  LogInfo = packed record
    nLogID : Cardinal;
    nChange : Integer;
    nAmount : Integer;
    nBalance : Integer;
    aName : array [0..15] of Char;
  end;

LogInfo는 5개의 멤버를 포함하는 구조체로 PlogInfo는 구조체를 가리키는 바늘이고 PPLogInfo는 PlogInfo를 가리키는 바늘이다.다음 방법에서는 PPLogInfo라는 2단계 포인터를 전달해야 합니다.
Delphi 메소드 포털:
//    
//  : nDate     , nDay       ppData        
//  :  
//  :     
function GetLog(nDate : Cardinal; nDay : Cardinal; ppData : PPLogInfo) : Cardinal; stdcall;

c# 호출 방법:
c# 구조체 정의:
	[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]   
        public struct LogInfo
        {
            public uint nLogID;
            public int nChange;
            public int nAmount;
            public int nBalance;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
            public char[] aName;
        }

c# 방법:
//    
	[DllImport("yourdelphi.dll")]
        public extern uint GetLog(int nDate, int nDay, ref IntPtr ryIntPtr);
		
        //    
	public LogInfo[] GetWaterLog(DateTime date,int nday)
        {
            LogInfo ryLogInfo = new LogInfo();
            int len = Marshal.SizeOf(typeof(LogInfo));//      
            IntPtr ptr = Marshal.AllocHGlobal(len);//           (           )
            Marshal.StructureToPtr(ryLogInfo, ptr, true);//                 
            int nDate = ConvertDateTimeInt(date);//          
            try
            {
                uint result = GetLog(nDate, nday, ref ptr);//    ,      
                LogInfo[] ryLogInfos = new LogInfo[result];//          ,      
		//          
                for (int i = 0; i < result; i++)
                {
		    //     ,              
                    IntPtr infoPtr = (IntPtr)((uint)ptr + i * len);
                    ryLogInfos[i] = (LogInfo)Marshal.PtrToStructure(infoPtr, typeof(LogInfo));
                }
                FreeLog(pro);//      ,   delphi     
                return ryLogInfos;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

1급 지침과 달리 2급 지침은 Inptr의 토대에ref 키워드를 하나 더 붙이면 된다. 생각보다 그렇게 복잡하지는 않지만 이 뒤의 메커니즘을 탐구하는 것도 학문이 있다.
나는 지침의 개념을 간단하게 연구한 결과 2급 지침이 동적 분배 메모리를 필요로 하는 장면에서 많이 활용된다는 것을 알았다. 즉, 내가 2급 지침을 하나의 매개 변수로 방법에 전달하면 방법이 지침의 조작에 내가 지침을 전달하기 전의 변수에 영향을 주고 방법이 끝난 후에도 변화가 여전히 효과적임을 알 수 있다.
이것 또한 기본적으로 나의 의혹을 설명했다. 바로 이델파이 방법이 왜 2급 지침으로 파라미터를 만들어야 하는지 나는 이 방법을 조정할 때 나는 흐르는 물의 갯수를 몰라서 흐르는 물의 갯수를 모르면 미리 메모리를 분배할 수 없다고 생각한다.그래서 2급 지침을 통해delphi 방법으로 나에게 동적 메모리를 분배하라고 했다. 그래서 방법은'여기 메모리 크기는 중요하지 않을 거야'라고 적혀 있었다. 어차피 다시 분배해야 하기 때문이다.
메모리가 델파이 방법으로 분배된 이상 자연히 델파이 방법으로 방출해야 한다. 사실은 그렇다. 왜냐하면 내가 c#로 방출을 시도했을 때 잘못 보고했기 때문이다.이때델파이의 인터페이스를 살펴보자. 과연 흐르는 물을 방출하기 위한 인터페이스가 하나 더 있다.
    
비록 문제가 해결되었지만델파이라는 소수의 강력한 언어나 지침, 메모리에 대한 이해는 여전히 간단명료하다.글에서 틀린 말이 있으니 바로잡아 주십시오.
   The end.

좋은 웹페이지 즐겨찾기