본문 바로가기

勉強/C#

문서 중앙화 프로젝트 - C# Windows 클라이언트 (SMB, USB 차단, 화면잠금, 소켓 통신) 및 프로젝트 실패 원인 정리

프로젝트 개요

문서 중앙화 시스템의 Windows 클라이언트 파트입니다. C# WinForms로 SMB 네트워크 드라이브 연결, USB 차단, 화면 잠금, 관리자 소켓 통신을 구현했습니다. Linux 서버 파트는 이전 포스팅(C,C++ 카테고리)을 참고하세요.

GitHub: https://github.com/LEntropy/Document_Management-Failed-

구현한 기능 목록

프로젝트 기능 상태
smbEx01 / smbEx02 Samba 네트워크 드라이브 연결/해제 ✅ 동작
UsbTest USB 저장장치 차단/허용 (레지스트리) ✅ 동작
ScreenLock01 화면 잠금 (전체 화면 오버레이) ✅ 동작
SocketEx01 / My_Client / My_Server TCP 소켓 클라이언트-서버 통신 ✅ 동작
DB_conn MariaDB 연결 테스트 ✅ 동작
Admin Ex01 관리자 UI 통합 (회원관리 + 원격제어) ⚠️ 부분 동작
ClientTest 클라이언트 통합 (SMB+USB+잠금+소켓) ❌ 통합 미완

구현 1 - SMB 네트워크 드라이브 연결 (smbEx01, smbEx02)

Windows API WNetUseConnection을 P/Invoke로 호출하여 Samba 서버에 네트워크 드라이브를 연결합니다.

// NetConn.cs
[DllImport("mpr.dll", CharSet = CharSet.Auto)]
public static extern int WNetUseConnection(
    IntPtr hwndOwner,
    [MarshalAs(UnmanagedType.Struct)] ref NETRESOURCE lpNetResource,
    string lpPassword,
    string lpUserID,
    uint dwFlags,
    StringBuilder lpAccessName,
    ref int lpBufferSize,
    out uint lpResult);

public int TryConnectNetwork(string remotePath, string userID, string pwd)
{
    int capacity = 1028;
    uint resultFlags = 0;
    StringBuilder sb = new StringBuilder(capacity);

    NetResource.dwType      = 1;            // RESOURCETYPE_DISK
    NetResource.IpLocalName = null;         // 드라이브 문자 자동 할당
    NetResource.IpRemoteName = remotePath;  // \\192.168.0.24\share
    NetResource.IpProvider  = null;

    return WNetUseConnection(IntPtr.Zero, ref NetResource,
        pwd, userID, 0, sb, ref capacity, out resultFlags);
}

// 드라이브 연결 해제
[DllImport("mpr.dll")]
public static extern int WNetCancelConnection2A(string lpName, int dwFlags, int fForce);

public void DisconnectNetwork()
{
    WNetCancelConnection2A(NetResource.IpRemoteName, 1, 0);
}

구현 2 - USB 차단/허용 (UsbTest, ClientTest)

Windows 레지스트리의 USBSTOR 서비스 시작 값을 변경하여 USB 저장장치를 차단하거나 허용합니다.

// USB_con.cs / MainForm.cs
private RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(
    @"SYSTEMCurrentControlSetservicesUSBSTOR", true);  // true = 쓰기 허용

// USB 차단 (Start 값을 4로 설정)
private void BlockUsb()
{
    registryKey.SetValue("Start", 4);   // 4 = 비활성화
    // USB 드라이브가 즉시 사라지지 않으므로 재시작 또는 장치 재연결 필요
}

// USB 허용 (Start 값을 3으로 설정)
private void AllowUsb()
{
    registryKey.SetValue("Start", 3);   // 3 = 수동 시작 (정상)
}

⚠️ 레지스트리 쓰기는 관리자 권한이 필요합니다. UAC 설정 또는 manifest에서 권한을 요청해야 합니다.

구현 3 - 화면 잠금 (ScreenLock01)

관리자 명령을 받으면 전체 화면을 덮는 오버레이 폼을 띄워 사용자의 입력을 차단합니다.

// WallForm.cs - 화면 잠금 오버레이
public partial class WallForm : Form
{
    public WallForm()
    {
        InitializeComponent();
        this.TopMost     = true;              // 항상 최상위
        this.WindowState = FormWindowState.Maximized;
        this.FormBorderStyle = FormBorderStyle.None;
        this.BackColor   = Color.Black;
        this.Opacity     = 0.9;

        // Alt+F4, Alt+Tab 등 단축키 차단
        this.KeyPreview = true;
        this.KeyDown += WallForm_KeyDown;
    }

    private void WallForm_KeyDown(object sender, KeyEventArgs e)
    {
        // 모든 키 입력 무시
        e.Handled    = true;
        e.SuppressKeyPress = true;
    }
}

// MainForm.cs - 화면 잠금/해제
// WindowsHelper.cs로 잠금 관련 Windows API 래핑
private WindowsHelper helper;

private void lockButton_Click(object sender, EventArgs e)
{
    WallForm wall = new WallForm();
    wall.Show();
}

private void unlockButton_Click(object sender, EventArgs e)
{
    // 관리자 인증 후 잠금 해제
    helper.UnlockScreen();
}

구현 4 - TCP 소켓 통신 (SocketEx01 / My_Client / My_Server)

관리자 앱(서버)이 클라이언트에 명령(USB 차단, 화면 잠금 등)을 전송합니다.

관리자 앱 (서버 역할 - Admin Ex01)

// Form1.cs - 관리자가 클라이언트의 연결을 기다림
string Server_ip = "192.168.0.24";
string Server_port = "5000";

private void connect()  // 별도 스레드에서 실행
{
    TcpListener listener = new TcpListener(
        IPAddress.Parse(Server_ip), int.Parse(Server_port));
    listener.Start();

    TcpClient client = listener.AcceptTcpClient();  // 클라이언트 접속 대기

    StreamReader  sr = new StreamReader(client.GetStream());
    StreamWriter  sw = new StreamWriter(client.GetStream());
    sw.AutoFlush = true;

    while (client.Connected)
    {
        string data = sr.ReadLine();
        Alram_data(data);   // 수신 데이터 처리 (USB 경고 등)
    }
}

클라이언트 (My_Client)

// Form1.cs - 관리자 서버에 접속
TcpClient tcpClient = new TcpClient();
tcpClient.Connect("192.168.0.24", 5000);

StreamReader sr = new StreamReader(tcpClient.GetStream());
StreamWriter sw = new StreamWriter(tcpClient.GetStream());
sw.AutoFlush = true;

// 관리자 명령 수신 루프
while (tcpClient.Connected)
{
    string cmd = sr.ReadLine();
    if (cmd == "LOCK")   LockScreen();
    if (cmd == "USB_OFF") BlockUsb();
    if (cmd == "USB_ON")  AllowUsb();
}

구현 5 - MariaDB 연결 (DB_conn)

// MySqlConnector 패키지 사용
using MySqlConnector;

public static void ConnectionTest()
{
    string connStr = "Server=192.168.0.24;Port=3307;Database=my_site;" +
                     "Uid=root;Pwd=security;";
    using var conn = new MySqlConnection(connStr);
    conn.Open();
    Console.WriteLine("MariaDB 연결 성공!");
}

왜 실패했나 - 프로젝트 실패 원인 분석

각 기능별 예제는 모두 동작했지만, 다음 이유로 최종 통합에 실패했습니다.

1. 기술 범위가 너무 광범위했음

하나의 프로젝트에 너무 많은 기술이 혼재했습니다.

  • Linux C (inotify, OpenSSL, MySQL, Samba)
  • Windows C# (WinForms, P/Invoke, Registry, Socket, MariaDB)
  • 네트워크 설계 (SMB 프로토콜, TCP 소켓)

각 기술을 따로 학습하며 예제를 만들었지만, 이를 하나로 묶는 설계가 부족했습니다.

2. 통합 설계 부재

기능별로 독립된 예제 프로젝트를 만들었지만, 최종 통합 아키텍처가 명확하지 않았습니다.

  • ClientTest가 통합을 시도했지만 SMB 연결 + USB 차단 + 화면잠금 + 소켓 통신을 하나의 폼에서 관리하는 상태 관리가 복잡했음
  • 관리자 앱이 서버인지 클라이언트인지 역할이 혼재 (Admin Ex01이 TcpListener를 열고 클라이언트 접속을 기다림)

3. 암호화 키 관리 문제

Linux 서버에서 생성된 Key/IV를 Windows 클라이언트가 복호화할 때 어떻게 가져올지 설계가 없었습니다. DB에 Key/IV를 저장했지만 클라이언트에서 파일을 열 때 자동으로 복호화하는 투명 암호화 레이어 구현이 너무 복잡했습니다.

4. 환경 구성의 어려움

  • Linux(Ubuntu) + Windows 혼합 환경 테스트가 번거로웠음
  • Samba 권한 설정, MariaDB 외부 접속 허용, 방화벽 설정 등 인프라 작업이 많았음
  • Visual Studio에서 Linux C 코드를 직접 빌드·테스트 불가

배운 점 및 개선 방향

항목 내용
시작 전 설계 기능 목록보다 데이터 흐름(파일 → 암호화 → 저장 → 클라이언트 접근)을 먼저 설계했어야 함
범위 축소 처음부터 "SMB + 자동 암호화"만 먼저 완성 후 USB 차단 등 추가 기능을 붙였어야 함
Key 관리 파일별 Key/IV를 DB에 저장하는 것까지는 맞았으나, 클라이언트 투명 복호화 레이어 설계가 필요
테스트 환경 Docker로 Linux 환경을 Windows에서 빠르게 재현할 수 있었을 것

undefined