Saf C ile Yerel Windows Todo Uygulaması Geliştirme (278KB, framework kullanılmıyor)
(github.com/Efeckc17)- Simple Todo, C ve Win32 API ile yapılmış yerel bir Windows Todo uygulamasıdır; Windows GUI programlama ve sistem entegrasyonu özelliklerini gösteren bir projedir
- Başlıca özellikler: Todo oluşturma·düzenleme·silme, tamamlandı olarak işaretleme, sistem tepsisine küçültme, Windows ile otomatik başlatma seçeneği ve büyük/küçük harf duyarsız gerçek zamanlı arama
- Veriler
%APPDATA%\TodoApp\todos.datiçinde ikili dosya olarak saklanır; tüm Todo listesini içerir ve en fazla 100 Todo destekler - Derleme için Windows üzerinde MinGW-w64 ve Windows SDK gerekir; depoyu klonladıktan sonra
.\build.batile derlenir - Yapı
main.c,todo.c,gui.c,utils/search.colarak ayrılmıştır ve GUI, Win32 API·Common Controls·UXTheme kullanır
Projenin niteliği
- Simple Todo, C ve Win32 API ile yazılmış yerel bir Windows Todo uygulamasıdır
- Hafif bir yerel çalıştırılabilir dosyayı hedefler ve Windows'un varsayılan görünümünü ve hissini, ayrıca modern Windows görsel stillerini kullanır
- GUI, Win32 API tabanlıdır; sistem tepsisi entegrasyonu ve dosya G/Ç üzerinden veri kalıcılığı içerir
Todo özellikleri
- Todo öğeleri oluşturulabilir, düzenlenebilir ve silinebilir
- Görevler tamamlandı olarak işaretlenebilir
- Her Todo için öncelik ayarlanabilir
- Arama özelliği gerçek zamanlı çalışır ve büyük/küçük harf duyarsız eşleşme destekler
- Uygulama sistem tepsisine küçültülebilir
- Windows başlangıcında otomatik çalıştırma seçeneği sunar
Veri saklama yöntemi
- Todo verileri
%APPDATA%\TodoApp\todos.datiçinde saklanır - Kayıt biçimi, tüm Todo listesini içeren bir ikili dosyadır
- Depolama kapasitesi en fazla 100 Todo ile sınırlıdır
Derleme ve çalıştırma
- Gereksinimler
- Windows OS
- MinGW-w64 GCC derleyicisi
- MinGW ile gelen Windows SDK
- Derleme adımları
- MinGW-w64'ü kurun ve
bindizinini sistemPATHdeğişkenine ekleyin - Depoyu klonladıktan sonra
simple-todo-cdizinine geçin - Derlemek için
.\build.batkomutunu çalıştırın
- MinGW-w64'ü kurun ve
- Uygulama
bin/todo.exeile başlatılır
Uygulama yapısı
- Proje yapısı işlevlere göre C dosyalarına ayrılmıştır
src/main.c: uygulama giriş noktasısrc/todo.c,src/todo.h: Todo yönetim mantığı ve veri yapılarısrc/gui.c: GUI uygulamasısrc/utils/search.c,src/utils/search.h: arama işlevinin uygulanması ve bildirimisrc/app.manifest: Windows görsel stilleri için manifest
- Temel bileşenler Win32 API, Common Controls, UXTheme ve File I/O'dur
- Hata ayıklama için Visual Studio, VS Code vb. ile kullanılabilen
DEBUGGING.mdkılavuzu sağlanır
Lisans ve katkı
- Lisans MIT License'dır
- Pull Request yoluyla katkı kabul edilir
3 yorum
Tam bir nostalji havası var.
Hacker News yorumları
Win32 GUI programlamada garip biçimde hoşuma giden bir şey var
Biraz tuhaf, ama Raymond Chen’in blogunu okursanız nedenini görürsünüz
Win32 API’nin kökleri 8088 işlemci dönemine dayanıyor; belirli bir şekilde yapınca 40 bayt kod tasarrufu etmek ya da bir register’ı daha az kullanmak gibi arka planları var
Eskiden mingw ve Petzold kitabıyla çok sayıda oyuncak GUI uygulaması yapmıştım; özel kontrol yazmak, grafik ve metin çizmek, kaydırmayı işlemek, hit test yapmak gibi şeylerin hepsi epey eğlenceliydi
Uygulamada
strcpy,sprintfkullanıldığı görülüyor; ciddi programlama için uzunluk kontrolü yapan varyantları kullanmak gerekir. Derleyicinin uyarı yağdırmamış olması şaşırtıcıWin32 API’de C standart kütüphanesindekilerin yerine geçen çok sayıda işlev var. Çalıştırılabilir dosya boyutunu gerçekten küçültmek istiyorsanız uygulamayı C standart kütüphanesi olmadan yazmayı deneyebilirsiniz.
memset()yerineZeroMemory(),memcpy()yerineCopyMemory()varBir noktadan sonra saf C kodunu elle yazmak acı verici hale geliyor, ama ilk birkaç denemeyi saf C ile yapmanın öğrenmek için en iyisi olduğunu düşünüyorum. Ayrıntıları kendiniz yönettiğinizde öğrenme sürecinde içeride neler döndüğünü daha iyi kavrıyorsunuz
Win32 GUI programlamayı daha fazla denemek istiyorsanız WTL(Windows Template Library)’ye bakmanızı öneririm. Win32 API’yi saran bir C++ sarmalayıcısı olduğu için davranışını anlamak çok daha kolaylaşıyor
Çok fazla özelliği olmasına rağmen bu sayede boyutu yalnızca yaklaşık 3MB. Framework yok, tek bir çalıştırılabilir dosyadan ibaret; keşke tüm yazılımlar hâlâ böyle olsa
strcpyyerinestrncpykullanmazsanız, yalnızca insanlardan değil yapay zeka araçlarından bile bunu yapmamanız gerektiğini tekrar tekrar duymayı göze almanız gerekirKişisel olarak Zig kullanmamın başlıca nedenlerinden biri, bu tür yaygın tuzakların çoğunu azaltması. Elbette C de gayet iyi
memset()yerineZeroMemory(),memcpy()yerineCopyMemory()olduğunu söylediniz ama MSVC intrinsic muhtemelenrep stos/movskomutlarını kullanırBir fonksiyon çağrısından daha küçük olabilir. Çünkü fonksiyon çağrısına import table girdisinin boyutu da dahildir
fopen/fread/fcloseda kullanıyor;CreateFile/ReadFile/WriteFilegibi Win32 API’lerini kullanmıyor gibi görünüyorHer kontrol için zahmetle
CreateWindow()çağırmak yerine, geleneksel olarak.rcdosyasına dialog resource yerleştirilir (Visual Studio’da bunu görsel olarak yapabilen dialog editor hâlâ var) veCreateWindow()yerineCreateDialog()kullanılırdıBöylece tüm kontroller otomatik olarak oluşturulur. Uygulama manifest’i eklerseniz modern UI stili ve yüksek DPI desteği de elde edebilirsiniz
Ancak yeniden boyutlandırma istiyorsanız bunu yine elle işlemeniz gerekir; ama genellikle kolaydır ve kod da birkaç yüz baytı geçmez
K&R C vardı, ama UNIX dışındaki her işletim sistemi hangi işlevleri sunacağını kendi seçiyordu
Üstelik UNIX ailesinden olmayan yerlerde standart kütüphaneden platformun değil C derleyicisinin sorumlu olduğu bir gelenek vardı
Bu yüzden C kütüphanesini Borland, Watcom, Symantec, Microsoft, Green Hills, Zortech gibi firmalar sağlıyordu
Mac OS de MPW çıkana kadar böyleydi; IBM ve Unisys, mikro bilgisayarlar ve ana bilgisayarlar, OpenVMS öncesi VMS de öyleydi
Windows 10’dan beri Universal C Runtime da var
Resource compiler’a da, resource DLL’i oluşturacak linker’a da gerek yok
Resource dosyaları ve bunların ikili formatı iyi bir API değil
Makul ölçüde yüksek seviyeli bir dilde
CreateWindowçağrıları varsa, dile iyi oturan resource benzeri bir DSL’i metaprogramlama ile oluşturmak mümkün olabilirEskiden Linux’ta assembly ile buna benzer bir şeyi 2KiB’nin altında yapmıştım: https://gaultier.github.io/blog/x11_x64.html
Başkalarının da söylediği gibi saf C ile yapıp dinamik linking kullanırsanız, en azından Linux’ta 20KiB’nin altında tutmak kolaydır. Windows’ta işletim sistemine varsayılan olarak dahil olan çok daha fazla şey olduğundan daha da kolay olabilir
Her hâlükârda denemeyi destekliyorum. Yazımın sonunda belirttiğim linker seçeneklerini denerseniz boyutu küçültmeye yardımcı olacaktır
Henüz musl ile ncurses derlemeyi deneyemedim
“Native Windows look and feel” denince, gerçekten çalıştırmadan önce liste görünümünde bir context menu olmasını ve belirli alanlara çift tıklayınca satır içinde sayılabilecek bir açılır menü ya da metin kutusu çıkmasını beklemiştim
Artık Win32 UI/UX’e uygun program tasarlamayı bilmiyorlar mı, yoksa ben mi çok yaşlandım, bilmiyorum
“Framework yok” denmiş ama DPI ölçeklemede yazı tipleri bulanık, Tab desteği yok, metin alanında Ctrl-A ile tümünü seçme çalışmıyor; modern öncesi framework’lerin bile sunduğu birçok özellik eksik ve satır eklerken hata da oluşuyor
“Modern” derken ne kastediliyor bilmiyorum
Bu kod
user32:SetProcessDpiAwarenessContext,shcore:SetProcessDpiAwareness,user32:SetProcessDPIAwareseçeneklerinden hangisinin mevcut olduğunu dinamik olarak kontrol edip çağırıyorWindows sürümü çok eski olup bunların hiçbirini uygulamamışsa (Windows XP öncesi), hiçbirini çağırmıyor
Eksik diye sayılan özelliklerin çoğunu eklemek çok kolay; özellikle kontroller arasında Tab ile geçiş gibi şeyleri
İçimdeki 6502 programcısı, 278KB’nin artık hafif sayıldığını gördükçe içten içe ölüyor
Karşılaştığım ilk engel, git
core.autocrlf=falseolarak ayarlıysabuild.batdosyasının çalışmamasıydı.core.autocrlf=trueyapıp tekrar klonlayınca derleyebildimToolchain olarak https://github.com/niXman/mingw-builds-binaries/releases/tag... adresindeki
x86_64-15.1.0-release-win32-seh-msvcrt-rt_v12-rev0.7zpaketini kullandımBu durumda 102KB’lik bir
.exedosyası çıkıyor. Öne sürülen 278KB’den en baştan çok daha iyi. Yazar farklı bir toolchain veya ayar kullanıyor olabilir; bu yüzden tam yeniden üretim adımları olsa iyi olurduGCC’ye birkaç seçenek vererek daha da küçültmek mümkün
gcc -Os=> 100KBgcc -Oz=> 99KBgcc -flto=> 101KBgcc -s=> 51KBgcc -s -Oz -flto=> 47KBTek derdiniz küçük
.exeboyutuysa hâlâ ciddi iyileştirme alanı varStack trace bilgisi, dinamik linkleme altyapısı, exception handling tabloları gibi şeyler olmasa çok daha hafif olabilir. Exception handling tabloları, exception’ların C fonksiyonlarının içinden geçebilmesi nedeniyle C’de de gerekli
Eskiden böyle bir şey yazdığımda daha küçük olduğunu hatırlıyorum. Acaba MinGW yüzünden mi?
Muhtemelen shareware indirme boyutlarının büyümesine verilen bir tepkiydi. MFC’nin karanlık çağıydı
Erken dönem Palm Pilot 68k programlamayla birlikte, retrocomputing olmayan assembly’nin son alkışları olarak hatırlıyorum
Arkadaşlar, bu sadece denenip eğlence için yapılmış bir uygulama. Yorumlar haklı; böyle bir şey muhtemelen C++ veya başka bir dille daha makul şekilde yapılabilirdi
Nedenini tam bilmiyorum ama Windows API belgeleri C tarzı kodu C++ derleyicisiyle yazma yönünde yönlendiriyor gibiydi. Microsoft, C++’ı C’nin geliştirilmiş bir üst kümesi olarak görüp C tarzı kod için de kullanılmasının doğru olduğunu düşünmüş olmalı
Win32 API kullanacaksanız başka bir dil temelde hiçbir şeyi değiştirmez; hatta işleri daha da kafa karıştırıcı hâle getirebilir
İnsanlar sık sık C++ tarzı düşünmeye saplanıyor ve Win32 API’ye aşina olmayan biri için bütünü daha da karmaşık bir yığın hâline getirirdi
Çok sevimli bir deneme ve başkaları ne düşünürse düşünsün Win32 API’ye aşinalık iyi bir temel yetkinliktir
Burada, kullanıcı fareyi her oynattığında 278KB telemetri göndermek için birkaç MB JS veya C# yükleyen yazılım ya da web siteleri yapan insanların epey alay ettiğini görüyorum
Bu uygulama 1,5MB RAM kullanıyor; ikisi de anında açılıyor
Windows uygulaması geliştirirken her şeyi C ile yapabilmek insanı her zaman iyi hissettiriyor
En düşük seviyedeki temel yapı taşlarından başlayıp üstüne koyarak ilerlemek gayet doğal geliyor
Buna karşılık macOS’te her şey Objective-C nesnesi olduğu için, saf C ile uygulama yazmak mümkün olsa da Apple’ın amaçladığı soyutlamanın bir seviye altına inmiş oluyorsunuz; bu da neredeyse 1000 kat daha laf kalabalığına dönüşüyor. Bir şey yapmak için fiilen Objective-C sınıf hiyerarşisini kukla gibi oynatmak gerekiyor; bu gerçekten rahatsız edici
Objective-C’nin sınıf tabanlı API’sinin neden temel Win32 tarzı prosedürel API’nin üstüne tekrar oturtulamadığını bilmiyorum. Teknik olarak Win32 de “sınıf tabanlı” sayılır ama bu asgari düzeyde
Bu yüzden şimdilik C kodumu macOS’e port etmeye pek içim elvermiyor
Ancak COM arayüzleri için MIDL derleyicisi, arayüzleri C’de kullanıp uygulayabileceğiniz header’lar üretiyor: https://www.codeproject.com/KB/com/com_in_c1.aspx
Bildiğim kadarıyla bugün WinRT’de de hâlâ çalışıyor. Yalnız üretilen C struct’larının ve fonksiyon adlarının tüm namespace’i içermesi gerektiğinden çok çirkinleşiyor: https://stackoverflow.com/a/7437006/111335
user32.dll’in bariz biçimde kopyaladığı Macintosh Toolbox API’si için neredeyse drop-in bir ikameydiAncak programcının hissettiği sıcaklık dışında, UI gerçekten nesne yönelimli olmak ister. Bazı ortak davranışları paylaşan farklı nesnelerden oluşan bir ağaç, UI kodunun %99’unu neredeyse birebir açıklar
Toolbox ve Carbon’da bu nesne yönelimlilik eksikti, bu yüzden epey zorlandılar. Windows’un gerçekten eklediği daha özgün fikirlerden biri de pencere sınıfıydı
Kişisel olarak C’den Objective-C sınıflarıyla doğrudan uğraşırken hissettiğim tuhaflığın aynısını,
user32.dllkodunda pencere sınıfları veya pencere prosedürleri tanımlamak zorunda kaldığım her seferinde hissediyorum[0]UI nasıl nesne yönelimliliği istiyorsa, nesne yönelimlilik de kendine özel dil özellikleri ister. Onlar olmadan da yapılabilir ama kullanılabilirlik 2000 kat kötüleşir
[0] Ya da GTK/GObject ile ilgili herhangi bir şey
Statik bir kütüphaneye link ediyormuş gibi görünüyor
Statik kütüphane yerine DLL’e link etmeli; böylece uygulama boyutu ciddi ölçüde küçülür
DLL’i zaten programla birlikte dağıtmanız gerekiyorsa (işletim sisteminin parçası olmadığı için), DLL tüm işlevleri içerir ve her DLL kendi C runtime’ına bile sahip olabilir
Her şeyi tek bir EXE’ye statik olarak derlerseniz yalnızca bir C runtime olur ve kullanılmayan fonksiyonlar kolayca çıkarılabilir
DLL ancak o kod birden fazla program arasında paylaşıldığında boyutu küçültür
MSVCRxx/VCRUNTIME’a dinamik link verirseniz kullanıcının Microsoft’tan tam olarak o DLL’i indirmesi gerekirMSVCRT’ye dinamik link verirseniz bu sorun olmaz ama Visual Studio’da bunu yapmak çok zordurCRT’ye statik link vermenin gerçekten sorun olacağı durum muhtemelen yalnızca LGPL uyumluluğudur
Ağabeylerin nefesi buraya kadar ulaşıyor gibi bir his...