顯示具有 windows 標籤的文章。 顯示所有文章
顯示具有 windows 標籤的文章。 顯示所有文章

2021年7月14日 星期三

Build x264 by Using Visual Studio

    x264 is a wirely-use H264 encoder for its profound performance, thus to study how it works is an interesting issue. However, its sourcecode build is bash-shell  + gcc in default. It leads to trace the code is not as simple as in an full integrated IDE. For this case, this post wants to instruct how to build x264 by Visual Studio + Microsoft Visual C. 

I use Visual Studio 2015, and the X264_POINTVER= "0.164.x" (2021/6/13 release). 

零. Build and run x264 in the MinGW system (skippable)

     MinGW could be download from here.

     Download and decompress the x264 tar ball, and we configure x264 as:

$ ./configure  --disable-asm --disable-thread --bit-depth=8 --disable-opencl
    ※For study of x264 purpose, we disable asm, thread opengl, and leave bit-depth works for 8 bit only. It brings x264 workflow simpler.

    After make, we test x264 by below command and use VLC player to verify the output.    
$ x264 --input-res 704x480 --bitrate 200 --output out.h264 plush-704x480.yuv
yuv [info]: 704x480p 0:0 @ 25/1 fps (cfr)
:

※ You could download the test yuv420 file from here.

壹. Build x264 by Visual Studio

  甲. Copy  those files into the same folder.
common folder: 
cabac.c -> common_cabac.c
macroblock.c -> common_macroblock.c
set.c -> common_set.c

encoder folder:
cabac.c -> encoder_cabac.c
macroblock.c -> encoder_macroblock.c
set.c -> encoder_set.c

input folder :
raw.c -> input_raw.c

output folder:
raw.c -> output_raw.c

  乙. Create an empty VS solution & project.
  The project file I assume you place at x264 source folder root,  named x264. we add those source file into the project :
common\base.c
common\bitstream.c
common\common.c
common\common_cabac.c
common\common_macroblock.c
common\common_set.c
common\cpu.c
common\dct.c
common\deblock.c
common\frame.c
common\mc.c
common\mvpred.c
common\osdep.c
common\pixel.c
common\predict.c
common\quant.c
common\rectangle.c
common\tables.c
common\vlc.c

encoder\analyse.c
encoder\api.c
encoder\cavlc.c
encoder\encoder.c
encoder\encoder_cabac.c
encoder\encoder_macroblock.c
encoder\encoder_set.c
encoder\lookahead.c
encoder\me.c
encoder\ratecontrol.c
encoder\slicetype-cl.c

filters\filters.c
filters\video\cache.c
filters\video\crop.c
filters\video\depth.c
filters\video\fix_vfr_pts.c
filters\video\internal.c
filters\video\resize.c
filters\video\select_every.c
filters\video\source.c
filters\video\video.c

input\avs.c
input\input.c
input\input_raw.c
input\timecode.c
input\y4m.c

output\flv.c
output\flv_bytestream.c
output\matroska.c
output\matroska_ebml.c
output\output_raw.c

extras\getopt.c

autocomplete.c
x264.c

If you do not build x264 in MinGW, you need to create a file, config.h in the x264 root, which file content is as below :
#define ARCH_X86 1
#define SYS_WINDOWS 1
#define STACK_ALIGNMENT 64
#define HAVE_LOG2F 1
#define HAVE_AVS 1
#define USE_AVXSYNTH 0
#define HAVE_VECTOREXT 1
#define fseek fseeko64
#define ftell ftello64
#define HAVE_BITDEPTH8 1
#define HAVE_GPL 1
#define HAVE_INTERLACED 1
#define HAVE_MALLOC_H 0
#define HAVE_ALTIVEC 0
#define HAVE_ALTIVEC_H 0
#define HAVE_MMX 0
#define HAVE_ARMV6 0
#define HAVE_ARMV6T2 0
#define HAVE_NEON 0
#define HAVE_AARCH64 0
#define HAVE_BEOSTHREAD 0
#define HAVE_POSIXTHREAD 0
#define HAVE_WIN32THREAD 0
#define HAVE_THREAD 0
#define HAVE_SWSCALE 0
#define HAVE_LAVF 0
#define HAVE_FFMS 0
#define HAVE_GPAC 0
#define HAVE_CPU_COUNT 0
#define HAVE_OPENCL 0
#define HAVE_THP 0
#define HAVE_LSMASH 0
#define HAVE_X86_INLINE_ASM 0
#define HAVE_AS_FUNC 0
#define HAVE_INTEL_DISPATCHER 0
#define HAVE_MSA 0
#define HAVE_MMAP 0
#define HAVE_WINRT 0
#define HAVE_VSX 0
#define HAVE_ARM_INLINE_ASM 0
#define HAVE_STRTOK_R 0
#define HAVE_CLOCK_GETTIME 0
#define HAVE_BITDEPTH10 0

  丙. Change the setting of  the VS project :
Main menu ->project -> x264 properies -> C/C++ -> General -> Add include directorie :
.
common
encoder
input
output
filters
filters/video
extras

Main menu ->project -> x264 properies -> C/C++ -> Preprocessor-> Preprocessor Definitions:
HAVE_STRING_H=1
HIGH_BIT_DEPTH=0
BIT_DEPTH=8
fseeko64=_fseeki64
ftello64=_ftelli64

Main menu ->project -> x264 properies -> C/C++ -> Advanced-> Disable Specific Warnings :
4996

  丁. Modify those code to pass the compilation:
common\osdep.h, aboult line 340, avoid MS VC defines REALIGN_STACK:
#define ALIGNED_ARRAY_32 ALIGNED_ARRAY_16
#define ALIGNED_ARRAY_64 ALIGNED_ARRAY_16
#endif

#if (STACK_ALIGNMENT > 16 || (ARCH_X86 && STACK_ALIGNMENT > 4) ) && defined(__GNUC__)
#define REALIGN_STACK __attribute__((force_align_arg_pointer))
#else
#define REALIGN_STACK
#endif

encoder\api.c, about line 97, comment out the "if else" :
        api->x264 = x264_8_encoder_open( param, api );
    }
#if(0)
    else if( HAVE_BITDEPTH10 && param->i_bitdepth == 10 )
    {
        api->nal_encode = x264_10_nal_encode;
        api->encoder_reconfig = x264_10_encoder_reconfig;
        api->encoder_parameters = x264_10_encoder_parameters;
        api->encoder_headers = x264_10_encoder_headers;
        api->encoder_encode = x264_10_encoder_encode;
        api->encoder_close = x264_10_encoder_close;
        api->encoder_delayed_frames = x264_10_encoder_delayed_frames;
        api->encoder_maximum_delayed_frames = x264_10_encoder_maximum_delayed_frames;
        api->encoder_intra_refresh = x264_10_encoder_intra_refresh;
        api->encoder_invalidate_reference = x264_10_encoder_invalidate_reference;

        api->x264 = x264_10_encoder_open( param, api );

    }
#endif
    else
        x264_log_internal( X264_LOG_ERROR, "not compiled with %d bit depth support\n", param->i_bitdepth );

To here, the VS is able to build x264.

參. Run x264 on Visual Studio :
Add the command line arguments, it is in 
Main menu ->project -> x264 properies ->Debugging->Command Arguments :
--input-res 704x480   --output out.h264 plush-704x480.yuv --bitrate 200

The arguments are indentical with what we run in MinGW command line.


Now press the VS "play" button, everything should be fine. If you are in the debug mode, do not be surprised at the encoding is very slow:




2018年1月6日 星期六

USB HID Host on Windows : Based on SiLab C8051 Example Firmware, USBHIDtoUART


      C8051 (F34X/F32X and F38X) series released by Silicon Labs is very famous and popular microcontroller for its bargain price and multipurpose for the applications : the chips are based on 8051 with various interfaces, ADC, I2C master/slave, SPI master/slave...etc. And the chips support USB signal and interrupt with 8 endpoints, they satisfy most USB 2.0 use case.

     C8051 (and EFM8 Universal Bee) development boards are the excellent entry points for USB firmware beginner. But it is pitiful that the HIDtoUART host side code is not full opensource(or it is difficult to be download), there is no SLABHIDDEVICE.dll sourcecode in the the Silicon Labs's example packet, there is only GUI code could be found.
     This post wants to remove this pain : forget the SLABHIDDEVICE.dll, it is redundant.

 
 零. Create a Visual Studio project, named as C8051HIDtoUARTExampleHost, the project you need include/depended in DDK (Driver Development Kit):

  If you do not download DDK, my code depends on DDK only a small piece, you could down the necessary headers and library from here. I assume the decompressed folder you downloaded in the  C8051HIDtoUARTExampleHost folder :




   And set the project setting include and library directory in the ddk_hid, the additional dependencies on hid.lib and setupapi.lib:







一.   Add a main.c file in the project, the code be :


#include <stdio.h>
#include <stdlib.h>

#include <tchar.h>

#include <windows.h>

#ifdef _cplusplus
extern "C" {
#endif

#include "hidsdi.h"
#include "setupapi.h"
#include "dbt.h"

#ifdef _cplusplus
}
#endif

#define DEVICE_VENDOR_ID    (0x10C4)
#define DEVICE_PRODUCT_ID    (0x8468)
#define DEVICE_VERSION_NUMER   (0x0000)



BOOL SeekCustomedHIdDdvice(DWORD deviceVendorId, DWORD deviceProductId, 
         DWORD deviceVersionNumber, 
         TCHAR *pDevicePath, int pathBufferSize)
{ 

 BOOL isFoundDevice;

 GUID hidGuid;
 HDEVINFO hDevInfoSet;
 DWORD memberIndex;

 PSP_DEVICE_INTERFACE_DETAIL_DATA pDevDetailData;

 pDevDetailData = NULL;


 HidD_GetHidGuid(&hidGuid);
 hDevInfoSet = SetupDiGetClassDevs(&hidGuid, NULL, NULL, 
  DIGCF_DEVICEINTERFACE| DIGCF_PRESENT);

 printf(TEXT("seeking device\r\n"));

 memberIndex = 0;
 isFoundDevice = FALSE;

 while(1)
 {
  BOOL result;
  SP_DEVICE_INTERFACE_DATA devInterfaceData;
  
  DWORD requiredSize;
  TCHAR devicePath[MAX_PATH];

  HANDLE hDevHandle;
  HIDD_ATTRIBUTES devAttributes;

  devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
 

  result = SetupDiEnumDeviceInterfaces(hDevInfoSet, NULL, &hidGuid, 
   memberIndex, &devInterfaceData);

  if(FALSE == result) 
   break;
  memberIndex++;


  result = SetupDiGetDeviceInterfaceDetail(hDevInfoSet, &devInterfaceData,
   NULL, NULL, &requiredSize, NULL);

  pDevDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize);
  if(NULL == pDevDetailData)
  {
   _tprintf(TEXT("malloc pDevDetailData fail!\r\n"));
   break;
  }/*if NULL*/

  pDevDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
  
  result = SetupDiGetDeviceInterfaceDetail(hDevInfoSet, &devInterfaceData,
   pDevDetailData, requiredSize, NULL, NULL);

  _tcscpy_s(&devicePath[0], MAX_PATH, pDevDetailData->DevicePath);
  free(pDevDetailData); pDevDetailData = NULL;

  if(FALSE == result)
   continue;

  hDevHandle = CreateFile(&devicePath[0], GENERIC_READ|GENERIC_WRITE, 
   FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

  if(INVALID_HANDLE_VALUE == hDevHandle)
   continue;


  devAttributes.Size = sizeof(HIDD_ATTRIBUTES);
  result = HidD_GetAttributes(hDevHandle, 
   &devAttributes);

  CloseHandle(hDevHandle);

  if(FALSE == result)
   continue;

  if(deviceVendorId == devAttributes.VendorID 
   && deviceProductId == devAttributes.ProductID 
   && deviceVersionNumber == devAttributes.VersionNumber)
  {
   _tprintf(TEXT("hid device found\r\n"));
   _tcscpy_s(pDevicePath, pathBufferSize, &devicePath[0]);
   isFoundDevice = TRUE;
   break;
  }/**/
 }/*while*/

 if(NULL != pDevDetailData)
  free(pDevDetailData); 
 pDevDetailData = NULL;

 SetupDiDestroyDeviceInfoList(hDevInfoSet);

 return isFoundDevice;

}/*SeekCustomedHIdDdvice*/


BOOL gIsLeaveThread = FALSE;

#define HID_BUFFER_SIZE     (64)

DWORD WINAPI ReadCustomedHIDThread(LPVOID lpParameter)
{
 TCHAR *pDevicePath;
 HANDLE hDevHandle;
 OVERLAPPED overlapped;

 pDevicePath = (TCHAR*)lpParameter;
 ZeroMemory(&overlapped, sizeof(OVERLAPPED));

 hDevHandle = CreateFile(pDevicePath, GENERIC_READ, 
  FILE_SHARE_READ|FILE_SHARE_WRITE, 
  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL);


 while(1)
 {
  int i;
  BYTE readBuffer[HID_BUFFER_SIZE];
  DWORD readSize;
  DWORD dwRes;

  if(FALSE != gIsLeaveThread)
   break;

  ZeroMemory(&readBuffer[0], sizeof(readBuffer));


  if(FALSE == ReadFile(hDevHandle, &readBuffer[0], HID_BUFFER_SIZE, 
   &readSize, &overlapped) && ERROR_IO_PENDING == GetLastError())
  {

#define TIMOUT_IN_MILLI_SEC    (2*1000)

   dwRes = WaitForSingleObject(hDevHandle, TIMOUT_IN_MILLI_SEC);

   switch(dwRes)
   {
    case WAIT_OBJECT_0:
     if(FALSE == GetOverlappedResult(hDevHandle, &overlapped, 
      &readSize, TRUE))
     {
      continue;
     }
     break;

    case WAIT_TIMEOUT:
    default:
     continue;
   }/*switch*/

  }/*if ReadFile*/

  
#define IN_DATA_SIZE   (60 + 1)
#define IN_DATA     (0x01)

  if( IN_DATA_SIZE != readSize)
   continue;

  if(IN_DATA != readBuffer[0])
   continue;

 
  for(i = 0; i < readBuffer[1]; i++){
   _tprintf(TEXT("%c"), 
    (int)readBuffer[2 + i]);
  }/*for i*/

 }/*while 1*/

 CloseHandle(hDevHandle);

 ExitThread((DWORD)0);
}/*ReadCustomedHIDThread*/

#define OUT_DATA_SIZE    (60 + 1)

DWORD WINAPI WriteCustomedHIDThread(LPVOID lpParameter)
{
 TCHAR *pDevicePath;
 HANDLE hDevHandle;
 BYTE writeBuffer[HID_BUFFER_SIZE];
 DWORD writtenSize;
 TCHAR strForSending[HID_BUFFER_SIZE];

 pDevicePath = (TCHAR*)lpParameter;

 ZeroMemory(&writeBuffer[0], sizeof(writeBuffer));

 _stprintf_s(&strForSending[0], sizeof(strForSending)/sizeof(TCHAR), 
  TEXT("%s"), TEXT("家常貓肉 Home cooking cat meat\r\n"));

 hDevHandle = CreateFile(pDevicePath, GENERIC_WRITE, 
  FILE_SHARE_READ|FILE_SHARE_WRITE, 
  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

 while(1)
 {
  if(FALSE != gIsLeaveThread)
   break;

#define OUT_DATA     (0x02)
  writeBuffer[0] = OUT_DATA;
  writeBuffer[1] = _tcslen(strForSending)*sizeof(TCHAR);

  memcpy(&writeBuffer[2], &strForSending[0], sizeof(writeBuffer) - 2);

  if(FALSE == WriteFile(hDevHandle, &writeBuffer[0], 
   OUT_DATA_SIZE, &writtenSize, NULL))
  {
   _tprintf(TEXT("GetLastError = %d\r\n"), GetLastError());
  }/*if */
 
  Sleep(2500);
 }/*while*/

 CloseHandle(hDevHandle);

}/*WriteCustomedHIDThread*/


BOOL CtrlHandler( DWORD fdwCtrlType )
{
 switch( fdwCtrlType ) 
 {
 case CTRL_C_EVENT:

  Beep(830, 200);
  Beep(932, 200);
  Beep(988, 200);
  Beep(830, 300);

  Beep(622, 200);
  Beep(494, 300);
  gIsLeaveThread = TRUE;
  Beep(622, 350);
  Beep(392, 700);
  Beep(415, 2000);

  return TRUE;
  break;

 default:
  return FALSE;
 }/*switch*/

 return FALSE;
}/*CtrlHandler*/


int _tmain(int argc, TCHAR *argv[])
{
 int k;
 TCHAR devicePath[MAX_PATH];
 HANDLE hReadThread, hWriteThread;
 unsigned long baudrate;

 baudrate = ULONG_MAX;
 ZeroMemory(&devicePath[0], MAX_PATH*sizeof(TCHAR)); 

 SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE);

 k = 0;
 while(k < argc)
 {
  if(0 == _tcscmp(argv[k], TEXT("-baudrate")) )
  {
   if(k + 1 < argc)
   {
    TCHAR *p_end;
    baudrate = _tcstol(argv[k + 1], &p_end, 10);
   }
   else
   {
    _tprintf(TEXT("-baudrate should be followed baud rate value!\r\n"));
    return 1;
   }/*if -braudrate*/
  }//*if */
  k++;
 }/*while argc*/

 if(ULONG_MAX != baudrate)
  _tprintf(TEXT("baud rate will be set as %u\r\n"), baudrate);

 if(FALSE == SeekCustomedHIdDdvice(DEVICE_VENDOR_ID, DEVICE_PRODUCT_ID, 
  DEVICE_VERSION_NUMER, &devicePath[0], MAX_PATH))
 {
  _tprintf(TEXT("no customed HID device has been found\r\n"));
  _tprintf(TEXT("maybe the pid, vid or vsn is not correct\r\n"));
  return 0;
 }/*if */

 _tprintf(TEXT("device path = %s\r\n"), &devicePath[0]);

 
 if(ULONG_MAX != baudrate)
 {
  HANDLE hDevHandle;
  BYTE writeBuffer[HID_BUFFER_SIZE];
  DWORD writtenSize;
  DWORD dwRes;

  ZeroMemory(&writeBuffer[0], HID_BUFFER_SIZE);

  hDevHandle = CreateFile(&devicePath[0], GENERIC_WRITE, 
   FILE_SHARE_READ|FILE_SHARE_WRITE, 
   NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  
#define OUT_CONTROL     (0xFD)

  writeBuffer[0] = OUT_CONTROL;
  baudrate = _byteswap_ulong(baudrate);
  memcpy(&writeBuffer[1], &baudrate, sizeof(unsigned long));

  if(FALSE == WriteFile(hDevHandle, &writeBuffer[0], OUT_DATA_SIZE,
   &writtenSize, NULL))
  {
   _tprintf(TEXT("GetLastError = %d\r\n"), GetLastError());
  }/*if */

  CloseHandle(hDevHandle);
 }/*set baud rate*/

 hReadThread = hWriteThread = INVALID_HANDLE_VALUE;

 hReadThread = (HANDLE)_beginthreadex(NULL, 0, ReadCustomedHIDThread, 
   (void*)&devicePath[0], 0/*or CREATE_SUSPENDED*/, NULL);

 if(0 >= hReadThread)
 {
  _tprintf(TEXT("Create read thead fail\r\n"));
  return -1;
 }/*if*/


 hWriteThread = (HANDLE)_beginthreadex(NULL, 0, WriteCustomedHIDThread, 
   (void*)&devicePath[0], 0/*or CREATE_SUSPENDED*/, NULL);

 if(0 >= hWriteThread)
 {
  _tprintf(TEXT("Create write thead fail\r\n"));
  gIsLeaveThread = TRUE;
  WaitForSingleObject(hReadThread, INFINITE);
  return -1;
 }/*if*/
 

 WaitForSingleObject(hWriteThread, INFINITE);
 WaitForSingleObject(hReadThread, INFINITE);
 
 CloseHandle(hWriteThread);
 CloseHandle(hReadThread);

 return 0;
}/*main*/ 

二. Build and run the code,

You will find in the C8051's UART terminal, the string "家常貓肉 Home cooking cat meat" keeping output in brautrate 9600:

And if you send some string via the terminal to the C8051, the string will display in the windows console:




 If you want to modify the braudrate of UART, you could input the argument -baudrate baudrate_number:



And the output would be in the baudrate:



If you are interested in how to implement the same work in Linux, please refer to this post.

Reference :

    圈圈教你玩USB, 第二版 (Circle Teach You How to Play USB).




2017年6月8日 星期四

Build Tiny C Compiler by VC in Visual Studio


     Tiny C compiler (TCC) is a profound contribution by Fabrice Bellard. Originally, the predecessor of TCC was for International Obfuscated C Code Contest. Now it has become an authentic C compiler prestiging for its compact, fast in compilation (not the binary built by TCC runs in high speed), complete (TCC could build itself, and It is evidenced the linux kernel built by TCC is workable) and cross platforms (it support the targets could be x86 32/64 and arm).

  For It does not rely on any library besides C standard (like lex or yacc) and dense code, TCC is excellent compiler for the person beginning to study how a compiler works.  It supports C99 standard but C++ (the C++ standards are very long-winded), Thus, TCC is self-consistent but any redundant implementation, it is beneficial for the compiler learner.

    The original code(obfuscated Tiny C Compiler) for International Obfuscated C Code Contest you could download here.  You should notice that : The compiler is not complete, its purpose is to compiling itself only.

    By default, TCC should be built by gcc (or itself). But the code of TCC tallies with C89, hence, it should be able to be compiled by the other compilers; and Visual Studio series are excellent tools for tracking code. It is high efficiency on studying how a compiler works if somebody observe how TCC works on VS. To achieve this purpose,  is this post's goal.

   In here, I adopt TCC version 0.9.26 and Visual Studio 2005, and the native and target platform are the same : 32 bit Windows.  
   You could download TCC source code in here. The Visual Studio version does not be confined to be VS2005, the versions later than it is workable also.

  零. (Optional) Build TCC via MinGW with gcc.

 After you download the TCC bz2 ball, you could use MinGW to build it :

Go into TCC folder, input the command to configurate the Makefile for TCC:

$ configure --prefix=$PWD/built

After that, build and install the TCC :

$ make && make install

While it has done, test the built tcc binary is workable or not  :

$ built/tcc.exe examples/ex1.c -o ex1.exe && ex1.exe
Hello World

The TCC built by gcc on MinGW works.

You could replace the ex1 as ex2, ex3, ex4, ex5, or the test files in folder built\examples.


  The remain steps are for Visual Studio (VS), you could close your MinGW bash now.

  一. Folder for VS working.

    Create a folder inside TCC folder for VS building. For me, I name the folder as VSBuild. The folder will content one VS solution which have two VC projects: one for building the TCC itself, the other for building TCC essential libraries.      


 二. Build TCC compiler.

 I create the first project named as tcc. The VC project settings :
The source files would be built by this VC-project are : i386-asm.c, i386-gen.c, libtcc.c, tcc.c, tccasm.c, tccelf.c, tccgen.c, tccpe.c, tccpp.c and tccrun.c, those are all located in TCC folder root.


  












The preprocessor definitions (which could be set  in project setting -> configuration properties -> C/C++ ->preprocessor) be :


_CRT_SECURE_NO_WARNINGS
_CRT_NONSTDC_NO_WARNINGS
TCC_TARGET_PE
TCC_TARGET_I386


(the first two macro is not necessary for VS2005, but if you use the version later than that, the two definition are required for passing the building)

For the include path, it is very simple, just parent directory (TCC folder root):

..\

Till here, this project setting has been done, press play button to observe if the building is passed or not. theoretically, it should pass.


But if you want to use the built TCC to compile the example code, ex1.c. You will find that the executable file would not be generated by TCC:



../examples/ex1.c:2: error: include file 'tcclib.h' not found


That is, the essential library does not exist for TCC usage.


   二. Build TCC essential library.

   The essential library should be compiled by TCC, in here, I add custom-build setting in VS to built the library. Of course, you could build the library via VC, but it is better to follow the standard.
  The second VC project named as tiny_libmaker, it function is the same as GCC tool ar (library archive), to sort .o file(s) as a single .a file.

  The project content one file, tiny_libmaker.c (located in TCC_RPPT\win32\tools), and the setting for preprocessor definition, include path, external library are empty.







 But there are command lines should be set in build event of project setting:


The command lines be:

$(ConfigurationName)\tcc.exe -c -B../win32 -I../include -c ../lib/libtcc1.c -o $(ConfigurationName)/libtcc1.o -I..  -Wall -g -O2 -fno-strict-aliasing -Wno-pointer-sign -Wno-sign-compare -Wno-unused-result -DTCC_TARGET_I386 -DTCC_TARGET_PE
$(ConfigurationName)\tcc.exe -c -B../win32 -I../include -c ../lib/alloca86.S -o $(ConfigurationName)/alloca86.o -I..  -Wall -g -O2 -fno-strict-aliasing -Wno-pointer-sign -Wno-sign-compare -Wno-unused-result -DTCC_TARGET_I386 -DTCC_TARGET_PE
$(ConfigurationName)\tcc.exe -c -B../win32 -I../include -c ../lib/alloca86-bt.S -o $(ConfigurationName)/alloca86-bt.o -I..  -Wall -g -O2 -fno-strict-aliasing -Wno-pointer-sign -Wno-sign-compare -Wno-unused-result -DTCC_TARGET_I386 -DTCC_TARGET_PE
$(ConfigurationName)\tcc.exe -c -B../win32 -I../include -c ../lib/bcheck.c -o $(ConfigurationName)/bcheck.o -I..  -Wall -g -O2 -fno-strict-aliasing -Wno-pointer-sign -Wno-sign-compare -Wno-unused-result -DTCC_TARGET_I386 -DTCC_TARGET_PE
$(ConfigurationName)\tcc.exe -c -B../win32 -I../include -c ../win32/lib/crt1.c -o $(ConfigurationName)/crt1.o -I..  -Wall -g -O2 -fno-strict-aliasing -Wno-pointer-sign -Wno-sign-compare -Wno-unused-result -DTCC_TARGET_I386 -DTCC_TARGET_PE
$(ConfigurationName)\tcc.exe -c -B../win32 -I../include -c ../win32/lib/wincrt1.c -o $(ConfigurationName)/wincrt1.o -I..  -Wall -g -O2 -fno-strict-aliasing -Wno-pointer-sign -Wno-sign-compare -Wno-unused-result -DTCC_TARGET_I386 -DTCC_TARGET_PE
$(ConfigurationName)\tcc.exe -c -B../win32 -I../include -c ../win32/lib/dllcrt1.c -o $(ConfigurationName)/dllcrt1.o -I..  -Wall -g -O2 -fno-strict-aliasing -Wno-pointer-sign -Wno-sign-compare -Wno-unused-result -DTCC_TARGET_I386 -DTCC_TARGET_PE
$(ConfigurationName)\tcc.exe -c -B../win32 -I../include -c ../win32/lib/dllmain.c -o $(ConfigurationName)/dllmain.o -I..  -Wall -g -O2 -fno-strict-aliasing -Wno-pointer-sign -Wno-sign-compare -Wno-unused-result -DTCC_TARGET_I386 -DTCC_TARGET_PE
$(ConfigurationName)\tcc.exe -c -B../win32 -I../include -c ../win32/lib/chkstk.S -o $(ConfigurationName)/chkstk.o -I..  -Wall -g -O2 -fno-strict-aliasing -Wno-pointer-sign -Wno-sign-compare -Wno-unused-result -DTCC_TARGET_I386 -DTCC_TARGET_PE
mkdir $(ConfigurationName)\lib
$(ConfigurationName)\tiny_libmaker.exe rcs  $(ConfigurationName)//lib/libtcc1.a  $(ConfigurationName)/libtcc1.o $(ConfigurationName)/alloca86.o $(ConfigurationName)/alloca86-bt.o $(ConfigurationName)/bcheck.o $(ConfigurationName)/crt1.o $(ConfigurationName)/wincrt1.o $(ConfigurationName)/dllcrt1.o $(ConfigurationName)/dllmain.o $(ConfigurationName)/chkstk.o
mkdir  $(ConfigurationName)\include
xcopy /y /s /e ..\win32\include  $(ConfigurationName)\include
xcopy /y /s /e ..\include  $(ConfigurationName)\include

You should be prudent that : back-slash(\) and slash (/) for separating folder layers, correspond to Windows shell and TCC arguments, respectively. You could not be regardless of the difference, or the command lines would not run normally.

After the setting done, set the project ( tiny_libmaker.c) depending on tcc project, And build that.



  三. Rebuild and run tiny C compiler again.

Press the play button, you will find there are output on the output dialog, just like below.

:
:
2>..\win32\include\winapi\wingdi.h
2>..\win32\include\winapi\winnetwk.h
2>..\win32\include\winapi\winnls.h
2>..\win32\include\winapi\winnt.h
2>..\win32\include\winapi\winreg.h
2>..\win32\include\winapi\winuser.h
2>..\win32\include\winapi\winver.h
2>已複製 81 個檔案
2>..\include\float.h
2>..\include\stdarg.h
2>..\include\stdbool.h
2>..\include\stddef.h
2>..\include\tcclib.h
2>..\include\varargs.h


 And the ex1.exe will been generated successfully. Run the binary in Windows command line tool, you will find that :


C:\MinGW\msys\1.0\home\Gaiger\tcc-0.9.26\VSBuild>ex1.exe
Hello World


The TCC works without any insufficiency, it is time to study how a self-consistent compiler meets with standard works via Visual Studio environment.

2016年8月10日 星期三

Use Bonjour SDK for mDNS Discovering in Windows



    How to use Bonjour SDK for mDNS is rut, but I found that in the website, there is no simple example WITHOUT window to demonstrate how exploit this library. In the post, I would fill the gap.


   零. Download the Bonjour Library.

       You could download the library in Apple webisite. The apple id is requisite. After you have installed the SDK, check if your Bonjour Service is running or not.


If the service is not running, Download and installed the Bonjour Service .


一.  Create a Visual Studio Project, Copy the Bonjour SDK folders: include and LIB (by default, they locates in C:\Program Files\Bonjour SDK) into the project folder, and add include path and library path refering to them:





 Of course, you should add dnssd.lib as your library reference.

二.

  The discovering code be :

#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment(lib, "IPHLPAPI.lib")

#include <iphlpapi.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "dns_sd.h"

#pragma comment(lib, "Ws2_32.lib")

#define    MAX_STR_LEN    (256)

#ifdef _DEBUG
#define LOG(fmt, ...)    fprintf(stderr, fmt, __VA_ARGS__)
#else
#define LOG(fmt, ...) /* Don't do anything in release builds */
#endif

#define SAFE_FREE(PTR)     if(NULL != (PTR))\
           free(PTR);\
          PTR = NULL



typedef struct ServiceInfo
{
 char *pServiceType;
 char *pServiceName;
 char *pDomainName;
 unsigned int serviceInterface;
 char *pProtocolNumber;
 char *pAddr;
 unsigned short port;
 char *pHostName;
 char *pText;

 DNSServiceRef sdRef;

 struct ServiceInfo *pNext;

}ServiceInfo, ServiceInfoList;

typedef void(__stdcall *mDNSBrowingCallbackFunPtr)
(
char *pServiceType,
char *pServiceName,
char *pDomainName,
char serviceInterface,
char *pProtocolNumber,
char *pAddr,
unsigned short port,
char *pHostName,
char *pText
);

HANDLE m_browingThreadHandle = NULL;
DWORD m_idBrowsingThread = 0;
UINT_PTR m_browsingTimerId;

ServiceInfoList *m_pServiceInfoList = NULL;


const unsigned char unUsedServiceInfoMemHead[] =
{
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
}; //the impossible memory location value


ServiceInfoList *CreateServiceInfoList(void)
{
 ServiceInfo *pServiceInfo;

 pServiceInfo = (ServiceInfo*)malloc(sizeof(ServiceInfo));
 memset(pServiceInfo, (unsigned char)0xff, sizeof(ServiceInfo));

 return pServiceInfo;
}/*CreateServiceInfoList*/


ServiceInfo *CreateServiceInfoNode(ServiceInfoList *pServiceInfoList, DNSServiceRef sdRef)
{
 ServiceInfo *pCurrent;

 if (NULL == pServiceInfoList)
  return NULL;
 
 pCurrent = pServiceInfoList;

 if (0 == memcmp(pServiceInfoList, &unUsedServiceInfoMemHead[0],
  sizeof(unUsedServiceInfoMemHead)) )
 {
  memset(m_pServiceInfoList, 0, sizeof(ServiceInfo));

  pCurrent->sdRef = sdRef;
  return pCurrent;
 }/*if */

 while (NULL != pCurrent->pNext)
  pCurrent = pCurrent->pNext;

 pCurrent->pNext = (ServiceInfo*)malloc(sizeof(ServiceInfo));
 pCurrent = pCurrent->pNext;

 memset(pCurrent, (unsigned char)0x00, sizeof(ServiceInfo));

 pCurrent->sdRef = sdRef;

 return pCurrent;
}/*CreateServiceInfoNode*/


void CleanServiceInfoData(ServiceInfo *pServiceInfo)
{
 if (NULL == pServiceInfo)
  return;

 SAFE_FREE(pServiceInfo->pServiceType); SAFE_FREE(pServiceInfo->pServiceName);
 SAFE_FREE(pServiceInfo->pDomainName);
 //pServiceInfo->serviceInterface;
 SAFE_FREE(pServiceInfo->pProtocolNumber);
 SAFE_FREE(pServiceInfo->pAddr); SAFE_FREE(pServiceInfo->pHostName);

 if (NULL != pServiceInfo->pText)
  free(pServiceInfo->pText);
 pServiceInfo->pText = NULL;

}/*CleanServiceInfoData*/


void DestroyServiceInfo(ServiceInfo **ppServiceInfo)
{
 CleanServiceInfoData(*ppServiceInfo);
 SAFE_FREE(*ppServiceInfo);
}/*DestoryServiceInfo*/


void DestroyServiceInfoList(ServiceInfoList **ppServiceInfoList)
{
 ServiceInfo *pCurrent;

 pCurrent = *ppServiceInfoList;

 if (0 == memcmp(pCurrent, &unUsedServiceInfoMemHead[0],
  sizeof(unUsedServiceInfoMemHead)))
 {
  SAFE_FREE(pCurrent);
  return;
 }/*if unused*/

 while (NULL != pCurrent)
 {
  ServiceInfo *pNext;

  pNext = pCurrent->pNext;
  DestroyServiceInfo(&pCurrent);
  pCurrent = pNext;
 }/*while*/

 *ppServiceInfoList = NULL;
}/*CleanServiceInfoList*/


int GetServiceInfoListLength(ServiceInfoList *pServiceInfoList)
{
 int i;
 ServiceInfo *pCurrent;

 if (NULL == pServiceInfoList)
  return -1;

 if (0 == memcmp(pServiceInfoList, &unUsedServiceInfoMemHead[0],
  sizeof(unUsedServiceInfoMemHead)))
 {
  return 0;
 }/*if */


 pCurrent = pServiceInfoList;
 i = 0;
 while (NULL != pCurrent)
 {
  pCurrent = pCurrent->pNext;
  i++;
 }/*while*/

 return i;
}/*GetServiceInfoListLength*/


void RemoveSpecificServiceInfoNode(
 ServiceInfoList *pServiceInfoList, DNSServiceRef sdRef)
{
 ServiceInfo *pCurrent, *pPrevious;
 int n;

 n = GetServiceInfoListLength(pServiceInfoList);
 pCurrent = pServiceInfoList;

 if (0 == n)
  return;

 if (1 == n)
 {
  if (pCurrent->sdRef == sdRef)
  {
   memset(pCurrent, 0xff, sizeof(ServiceInfo));
   return;
  }/*if */

 }/*if 1 == n*/

 /*if 1 < n  and i == 0*/
 if (pCurrent->sdRef == sdRef)
 {
  ServiceInfo *p2ndNode;

  p2ndNode = pCurrent->pNext;

  memcpy(pCurrent, p2ndNode, sizeof(ServiceInfo));
  DestroyServiceInfo(&p2ndNode); p2ndNode = NULL;
  return ;
 }/*if */

 
 pPrevious = pCurrent;
 pCurrent = pCurrent->pNext;

 while (NULL != pCurrent)
 {
  if (pCurrent->sdRef == sdRef)
  {
   pPrevious->pNext = pCurrent->pNext;
   DestroyServiceInfo(&pCurrent);
   break;
  }
  pPrevious = pCurrent;
  pCurrent = pCurrent->pNext;
 }/*if */

}/*RemoveSpecificServiceInfoNode*/


ServiceInfo *FindSpecificServiceInfoNode(
 ServiceInfoList *pServiceInfoList, DNSServiceRef sdRef)
{
 ServiceInfo *pCurrent;

 pCurrent = pServiceInfoList;

 while (NULL != pCurrent)
 {
  if (pCurrent->sdRef == sdRef)
   break;

  pCurrent = pCurrent->pNext;
 }/*if */

 return pCurrent;
}/*FindSpecificServiceInfoNode*/


void DNSSD_API ServiceGetAddressInfoCallback(DNSServiceRef sdRef,
 DNSServiceFlags flags,
 uint32_t interfaceIndex,
 DNSServiceErrorType errorCode,
 const char *pHostname,
 const struct sockaddr *pAddress,
 uint32_t ttl,
 void *pContext)
{
 if (0 == errorCode) 
 {
  const struct sockaddr_in *in = (const struct sockaddr_in *)pAddress;
  ServiceInfo *pNode;
  pNode = FindSpecificServiceInfoNode(m_pServiceInfoList, sdRef);

  if (NULL != pNode)
  {
   char temp[MAX_STR_LEN];
   memset(&temp[0], 0, MAX_STR_LEN);

   inet_ntop(AF_INET, (PVOID)&in->sin_addr, &temp[0], MAX_STR_LEN);
   pNode->pAddr = _strdup(&temp[0]);
   
   pNode->pProtocolNumber = _strdup("IPv4");
   pNode->pHostName = _strdup(pHostname);
  }

  if (NULL != pContext)
  {
   mDNSBrowingCallbackFunPtr callbackPtr;
   callbackPtr = (mDNSBrowingCallbackFunPtr)pContext;
   callbackPtr(pNode->pServiceType, pNode->pServiceName, pNode->pDomainName,
    pNode->serviceInterface, pNode->pProtocolNumber, pNode->pAddr,
    pNode->port, pNode->pHostName, pNode->pText);
  }

 }/*if 0 == errorCode*/

 //if (0 == (flags & kDNSServiceFlagsMoreComing)) {}

}/*ServiceGetAddressInfoCallback*/


void DNSSD_API ResolveServiceCallback(DNSServiceRef sdRef,
 DNSServiceFlags flags,
 uint32_t interfaceIndex,
 DNSServiceErrorType errorCode,
 const char *pFullname,
 const char *pHosttarget,
 uint16_t port,
 uint16_t txtLen,
 const unsigned char *pTxtRecord,
 void *pContext)
{
 char organizedTxt[MAX_STR_LEN];
 MIB_IFROW IfRow;
 DNSServiceRef client;
 DWORD result;

 DNSServiceErrorType err;
 int i;
 LOG("%s\n", __FUNCTION__);

 if (0 != errorCode)
 {
  LOG("errorCode = %d\n", errorCode);
  return;
 }/*if 0 != errorCode*/

 memset(&organizedTxt[0], 0, MAX_STR_LEN);

 for (i = 0; i < txtLen; i++){
  char c;
  c = pTxtRecord[i];

  if (0x20 > c || '/' == c)
   c = '\n';

  organizedTxt[i] = c;
 }/*for i*/
 organizedTxt[strlen(&organizedTxt[0])] = '\n';


 IfRow.dwIndex = interfaceIndex;
 result = GetIfEntry(&IfRow);

 if (0 != result)
 {
  LOG("Error in GetIfEntry , error code = %d\n", result);
  return ;
 }/*if 0 != result*/

 client = NULL;

 err = DNSServiceGetAddrInfo(&client,
  kDNSServiceFlagsTimeout,
  interfaceIndex,
  kDNSServiceProtocol_IPv4,
  pHosttarget,
  ServiceGetAddressInfoCallback,
  pContext);

 if (0 == err) 
 {
  ServiceInfo *pNode;
  pNode = FindSpecificServiceInfoNode(m_pServiceInfoList, sdRef);


  if (NULL != pNode)
  {
   pNode->serviceInterface = interfaceIndex;
   pNode->port = ((0xff & port) << 8) + (port >> 8);

   if (0 == pTxtRecord || 0 == pTxtRecord[0])
    pNode->pText = NULL;
   else
    pNode->pText = _strdup(&organizedTxt[0]);

   //for next callback, the sdRef has been change, it is nessary to 
   //update the sdRef value for specifying the parameters in correct groups.
   pNode->sdRef = client;
  }/*if NULL != pNode */

  LOG("Looking up %s on %s\n", pHosttarget, IfRow.bDescr);
 }
 else 
 {
  LOG("Error looking up address info for %s\n", pHosttarget);
 }/*if 0 == err*/

}/*ResolveServiceCallback*/


void DNSSD_API IterateServiceNameCallback(DNSServiceRef sdRef,
 DNSServiceFlags flags,
 uint32_t interfaceIndex,
 DNSServiceErrorType errorCode,
 const char *pServiceName,
 const char *pRegtype,
 const char *pReplyDomain,
 void *context)
{
 LOG("%s\n", __FUNCTION__);

 if (0 == errorCode && (flags & kDNSServiceFlagsAdd))
 {
   DNSServiceRef client = NULL;
   DNSServiceErrorType err = DNSServiceResolve(&client,
    0,
    interfaceIndex,
    pServiceName,
    pRegtype,
    pReplyDomain,
    ResolveServiceCallback,
    context);

   if (0 == err) 
   {
    ServiceInfo *pNode;
    pNode = FindSpecificServiceInfoNode(m_pServiceInfoList, sdRef);

    if (NULL != pNode)
    {
     pNode->pServiceName = _strdup(pServiceName);
     pNode->pServiceType = _strdup(pRegtype);
     pNode->pDomainName = _strdup(pReplyDomain);

     //for next callback, the sdRef has been change, it is nessary to 
     //update the sdRef value 
     //for specifying the parameters in correct groups.
     pNode->sdRef = client; 
    }/*if NULL != pNode*/
   }
   else
   {
    LOG("Error trying to browse service instance: %s", pServiceName);
   }
 }
 //if (0 == (kDNSServiceFlagsMoreComing & flags)){}

}/*IterateServiceNameCallback*/


void DNSSD_API IterateServiceTypesCallback(DNSServiceRef sdRef,
 DNSServiceFlags flags,
 uint32_t interfaceIndex,
 DNSServiceErrorType errorCode,
 const char *pServiceName,
 const char *pRegtype,
 const char *pReplyDomain,
 void *pContext)
{
 LOG("%s\n", __FUNCTION__);

 if (0 == errorCode && (kDNSServiceFlagsAdd & flags))
 {
  unsigned int len, copyLen;
  char serviceType[MAX_STR_LEN];
  memset(&serviceType[0], 0, MAX_STR_LEN);
  
  {
   unsigned int i;

   len = strlen(pRegtype);
   copyLen = len;

   if ('.' == pRegtype[len - 1])
    len -= 1;

   for (i = len - 1; i >= 0; i--){
    if ('.' == pRegtype[i]){
     copyLen = i;
     break;
    }
   }/*for i*/
   strncpy_s(&serviceType[0], MAX_STR_LEN, pServiceName, strlen(pServiceName));
   strncat_s(&serviceType[0], MAX_STR_LEN, ".", 1);
   strncat_s(&serviceType[0], MAX_STR_LEN, pRegtype, copyLen);
   strncat_s(&serviceType[0], MAX_STR_LEN, ".", 1);
  }/*local variable*/


  {
   DNSServiceRef client;
   DNSServiceErrorType err;

   err = DNSServiceBrowse(&client, 0, 0,&serviceType[0], "", 
    IterateServiceNameCallback, pContext);

   LOG("Browsing for instances of %s\n", &serviceType[0]);

   if (0 == err)
    CreateServiceInfoNode(m_pServiceInfoList, client);
   else 
    LOG("Error trying to browse service type: %s\n", &serviceType[0]);
  }/*local variable*/

 }/*if 0 == errorCode && (kDNSServiceFlagsAdd & flags) */


 if (0 == (kDNSServiceFlagsMoreComing & flags))
  RemoveSpecificServiceInfoNode(m_pServiceInfoList, sdRef);

 return;
}/*IterateServiceTypesCallback*/


#define BROWERING_INTERVAL     (250)

void CALLBACK BrowingTimerCallback(HWND hwnd, UINT uMsg, UINT timerId, DWORD dwTime)
{
 int count = 0;

 while (1)
 {
  fd_set readfds;
  ServiceInfo *pCurrent;
  struct timeval tv = { 0, 1000 };

  if (0 == GetServiceInfoListLength(m_pServiceInfoList))
  {
   LOG("Done browsing\n");
   KillTimer(NULL, m_browsingTimerId);
   break;
  }
 
  FD_ZERO(&readfds);

  pCurrent = m_pServiceInfoList;

  while (NULL != pCurrent)
  {
   FD_SET(DNSServiceRefSockFD(pCurrent->sdRef), &readfds);
   pCurrent = pCurrent->pNext;
  }

  if (0 < select(0, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv))
  {
   //
   // While iterating through the loop, the callback functions might delete
   // the client pointed to by the current iterator, so I have to increment
   // it BEFORE calling DNSServiceProcessResult
   //
   
   pCurrent = m_pServiceInfoList;

   while (NULL != pCurrent)
   {
    if (FD_ISSET(DNSServiceRefSockFD(pCurrent->sdRef), &readfds))
    {
     DNSServiceErrorType err 
      = DNSServiceProcessResult(pCurrent->sdRef);

     if (++count > 10)
      break;
    }
    pCurrent = pCurrent->pNext;
   }/*while */

  }
  else
   break;
  if (count > 10)
   break;
 }/*while 1*/

}/*BrowingTimerCallback*/


DWORD WINAPI BrowsingThread(LPVOID lpParam)
{
 MSG msg;
 DNSServiceRef client = NULL;

 DNSServiceErrorType err = DNSServiceBrowse(&client, 0, 0,
  "_services._dns-sd._udp", "", IterateServiceTypesCallback, (void*)lpParam);

 if (0 == err) 
 {
  LOG("Browsing for service types using _services._dns-sd._udp\n");
  SetTimer(NULL, 0, BROWERING_INTERVAL,
   (TIMERPROC)&BrowingTimerCallback);

  CreateServiceInfoNode(m_pServiceInfoList, client);
 }
 else 
 {
  LOG("Error starting discovery: %d\n", err);
 }/*if */


 while (FALSE != GetMessage(&msg, NULL, 0, 0))
 {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }/*while*/

 return 0;
}/*BrowsingThread*/


void mDNSBrowsingStop(void)
{
 KillTimer(NULL, m_browsingTimerId);

 if (0 != m_idBrowsingThread)
  PostThreadMessage(m_idBrowsingThread, WM_QUIT, 0, 0);
 m_idBrowsingThread = 0;

 if (NULL != m_browingThreadHandle)
  WaitForSingleObject(m_browingThreadHandle, 5 * 1000);
 m_browingThreadHandle = NULL;

 m_pServiceInfoList = NULL;
}/*mDNSStopmiscover*/


int mDNSBrowsingStart(mDNSBrowingCallbackFunPtr browingResultFunPtr)
{
 mDNSBrowsingStop();

 m_pServiceInfoList = CreateServiceInfoList();
 m_browingThreadHandle = CreateThread(NULL, 0,
  BrowsingThread, browingResultFunPtr, 0, &m_idBrowsingThread);

 if (NULL == m_browingThreadHandle)
  return -1;

 return 0;
}/*mDNSDiscoverStart*/


void __stdcall mDNSBrowingResult(
 char *pServiceType,
 char *pServiceName,
 char *pDomainName,
 char serviceInterface,
 char *pProtocolNumber,
 char *pAddr,
 unsigned short port,
 char *pHostName,
 char *pText
 )
{
 printf("\n");
 printf("\tServiceType : %s\n", pServiceType);
 printf("\tServiceName : %s\n", pServiceName);
 
 printf("\tDomainName : %s\n", pDomainName);

 printf("\tInterface : %d\n", serviceInterface);
 printf("\tProtocol Number : %s\n", pProtocolNumber);
 printf("\tAddress : %s\n", pAddr);
 printf("\tport : %d\n", port);
 printf("\tHostName : %s\n", pHostName);
 printf("\tText : ");

 if (NULL == pText || '\n' == pText[0] && '\n' == pText[1])
 {
  HANDLE  hConsole;

  hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | 
   FOREGROUND_BLUE | FOREGROUND_INTENSITY);
  
  printf("NULL\n");

  SetConsoleTextAttribute(hConsole, FOREGROUND_RED 
   | FOREGROUND_GREEN | FOREGROUND_BLUE);
  printf("\n");
 }
 else
 {
  printf("%s\n", pText);
 }
 printf("\n");

}/*mDNSBrowingResult*/


int main(int argc, char *argv[])
{
#if(0)
 WSADATA wsaData;

 if (0 != WSAStartup(MAKEWORD(2, 0), &wsaData))
  return -1;
#endif

 mDNSBrowsingStart(mDNSBrowingResult);

 getchar();

 mDNSBrowsingStop();
#if(0) 
 WSACleanup();
#endif
 return 0;
}/*main*/


After press Play (build and run) button, the discovering result would be printed in the console.