Skip to content

6.2 C#에서 C99 SDK 연동

개요

C# 애플리케이션은 DllImport (P/Invoke)를 통해 C99 SDK 동적 라이브러리를 호출하여 언어 간 통합을 구현할 수 있습니다.

6.2.1 환경 준비

의존성설명
.NETFramework 4.5+ 또는 .NET Core 3.1+
C99 SDKc_arm_api.dll (Windows)

6.2.2 최소 예제: 연결 및 연결 해제

csharp
using System;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("c_arm_api.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr Arm_Create();

    [DllImport("c_arm_api.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void Arm_Destroy(IntPtr handle);

    [DllImport("c_arm_api.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int Arm_Connect(IntPtr handle, string controllerIp, string teachPanelIp);

    [DllImport("c_arm_api.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void Arm_Disconnect(IntPtr handle);

    static void Main(string[] args)
    {
        // 핸들을 생성합니다.
        IntPtr handle = Arm_Create();

        // 로봇에 연결합니다.
        int ret = Arm_Connect(handle, "10.27.1.2", "10.27.1.102");
        Console.WriteLine($"연결 결과: {ret}");

        // 연결을 해제합니다.
        Arm_Disconnect(handle);

        // 핸들을 파괴합니다.
        Arm_Destroy(handle);
        Console.WriteLine("완료");
    }
}

6.2.3 객체 지향 래퍼(퍼사드 패턴)

Arm 클래스를 만들어 C99 함수를 C# 객체 지향 API로 래핑할 수 있습니다.

csharp
using System;
using System.Runtime.InteropServices;

public class Arm : IDisposable
{
    private const string DllName = "c_arm_api.dll";
    private IntPtr _handle;

    [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
    private static extern IntPtr Arm_Create();

    [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
    private static extern void Arm_Destroy(IntPtr handle);

    [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
    private static extern int Arm_Connect(IntPtr handle, string controllerIp, string teachPanelIp);

    [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
    private static extern void Arm_Disconnect(IntPtr handle);

    public void Connect(string controllerIp, string teachPanelIp = null)
    {
        if (_handle == IntPtr.Zero)
            _handle = Arm_Create();
        int ret = Arm_Connect(_handle, controllerIp, teachPanelIp);
        if (ret != 0)
            throw new InvalidOperationException($"연결 실패: {ret}");
    }

    public void Disconnect()
    {
        if (_handle != IntPtr.Zero)
            Arm_Disconnect(_handle);
    }

    public void Dispose()
    {
        Disconnect();
        if (_handle != IntPtr.Zero)
        {
            Arm_Destroy(_handle);
            _handle = IntPtr.Zero;
        }
    }
}

// 사용 예제
using (var arm = new Arm())
{
    arm.Connect("10.27.1.2", "10.27.1.102");
    Console.WriteLine("연결 성공");
}

6.2.4 주의 사항

  1. 호출 규칙: C99 SDK는 cdecl 을 사용하므로 CallingConvention = CallingConvention.Cdecl 을 지정해야 합니다.
  2. 플랫폼 대상: C# 프로젝트의 플랫폼 대상이 DLL과 일치하는지 확인합니다(x64).
  3. DLL 경로: DLL이 검색 가능한 경로에 있는지 확인합니다.
  4. 스레드 안전: 같은 핸들을 여러 스레드에서 동시에 접근할 수 없습니다.
  5. BasScript Builder: C99는 ArmBasScriptHandleArmBasExtraParamHandle 을 노출하므로, C# 측에서 객체화된 스크립트 구성 인터페이스로 래핑할 수 있습니다.
  6. Builder 편의 래퍼: Arm_BasScript_SetName / AppendLine(s)Arm_BasValue_* 는 C# 객체 메서드와 정적 팩토리로 매핑할 수 있습니다.