Модераторы: feodorv, GremlinProg, xvr, Fixin

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Чтение из pipe 
:(
    Опции темы
TheDestroyer
Дата 31.5.2009, 16:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



В интернете повсюду разбросан пример работы с консольными приложениями: http://www.codenet.ru/progr/bcb/pipes.php
После прочтения справки возник следующий вопрос: обязательно ли использовать  ReadFile вместе с PeekNamedPipe? Ведь в msdn написано:
Цитата

The PeekNamedPipe function is similar to the ReadFile function with the following exceptions:...

Одним из отличий этих функций является то, что ReadFile будет ждать пока не прочитает заданное число байт, а  PeekNamedPipe вернет значение в любом случае и программа продолжит выполняться.
Соответственно будет ли корректным такое чтение из пайпа:
Создание пайпа:
Код

char buf[1024];           //буфер ввода/вывода
  STARTUPINFO si;
  SECURITY_ATTRIBUTES sa;
  SECURITY_DESCRIPTOR sd;        //структура security для пайпов
  PROCESS_INFORMATION pi;
  HANDLE newstdin,newstdout,read_stdout,write_stdin;  //дескрипторы пайпов

if (IsWinNT())        //инициализация security для Windows NT
  {
    InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(&sd, true, NULL, false);
    sa.lpSecurityDescriptor = &sd;
  }

  else sa.lpSecurityDescriptor = NULL;
  sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  sa.bInheritHandle = true;       //разрешаем наследование дескрипторов

  if (!CreatePipe(&newstdin,&write_stdin,&sa,0))   //создаем пайп
                                                   // для stdin
  {
    ErrorMessage("CreatePipe");
    getch();
    return;
  }

  if (!CreatePipe(&read_stdout,&newstdout,&sa,0)) //создаем пайп
                                                  // для stdout
  {
    ErrorMessage("CreatePipe");
    getch();
    CloseHandle(newstdin);
    CloseHandle(write_stdin);
    return;
  }

  GetStartupInfo(&si);      //создаем startupinfo для дочернего процесса

  si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
  si.wShowWindow = SW_HIDE;
  si.hStdOutput = newstdout;
  si.hStdError = newstdout;   //подменяем дескрипторы для
  si.hStdInput = newstdin;    // дочернего процесса

  char app_spawn[] = "d:\\winnt\\system32\\cmd.exe"; 

  //создаем дочерний процесс
  if (!CreateProcess(app_spawn,NULL,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,
                     NULL,NULL,&si,&pi))
  {
    ErrorMessage("CreateProcess");
    getch();
    CloseHandle(newstdin);
    CloseHandle(newstdout);
    CloseHandle(read_stdout);
    CloseHandle(write_stdin);
    return;
  }

Чтение:
Код

unsigned long bread;   //кол-во прочитанных байт

bzero(buf);
while(!eof(read_stdout))
{
ReadFile(read_stdout,buf,1023,&bread,NULL);  //читаем из пайпа stdout
printf("%s",buf);
}

PM MAIL   Вверх
andrew_121
Дата 31.5.2009, 16:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кодофей
****


Профиль
Группа: Завсегдатай
Сообщений: 3448
Регистрация: 3.1.2008

Репутация: 2
Всего: 33



Все уже написано!
Код

#include <iostream>
#include <conio.h>
#include <windows.h>
#include <exception>
#include <string>

using namespace std;

const char* sCreatePipeMsg = "Ошибка при создании пайпа";
const char* sDuplicateHandleMsg = "Ошибка при создании дубликата пайпа";
const char* sCreateChildProcessMsg = "Ошибка при создании дочернего процесса";

class TChildProc
{
private:
   HANDLE FChildStdoutRd;
   HANDLE FChildStdoutWr;
   HANDLE FChildStdinRd;
   HANDLE FChildStdinWr;
   bool CreateChildProcess(char *ExeName, char *CommandLine, HANDLE StdIn, HANDLE StdOut);
public:
   TChildProc(char *ExeNAme, char *CommandLine);
   ~TChildProc();
   char * ReadStrFromChild(char *Buffer, int Timeout = 1000);
   bool WriteStrToChild(const char *Data);
};

TChildProc::TChildProc(char *ExeName, char *CommandLine)
{
 HANDLE StdoutRdTmp, StdinWrTmp;
 SECURITY_ATTRIBUTES saAttr;

 ZeroMemory(&saAttr, sizeof(SECURITY_ATTRIBUTES));
 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
 saAttr.bInheritHandle = true;
 saAttr.lpSecurityDescriptor = NULL;

 if (!CreatePipe(&StdoutRdTmp, &FChildStdoutWr, &saAttr, 0))
    throw exception(sCreatePipeMsg);
 else
   if (!CreatePipe(&FChildStdinRd, &StdinWrTmp, &saAttr, 0))
      throw exception(sCreatePipeMsg);
   else
   {
     if (!DuplicateHandle(GetCurrentProcess(), StdoutRdTmp, GetCurrentProcess(), &FChildStdoutRd, 0, false, DUPLICATE_SAME_ACCESS))
         throw exception(sDuplicateHandleMsg);
     else
       if (!DuplicateHandle(GetCurrentProcess(), StdinWrTmp, GetCurrentProcess(), &FChildStdinWr, 0, false, DUPLICATE_SAME_ACCESS))
           throw exception(sDuplicateHandleMsg);
       else
       {
            CloseHandle(StdoutRdTmp);
            CloseHandle(StdinWrTmp);
            if (!CreateChildProcess(ExeName, CommandLine, FChildStdinRd, FChildStdoutWr))
               throw exception(sCreateChildProcessMsg);
       }
   }
}

TChildProc::~TChildProc()
{
 CloseHandle(FChildStdoutRd);
 CloseHandle(FChildStdoutWr);
 CloseHandle(FChildStdinRd);
 CloseHandle(FChildStdinWr);
}

bool TChildProc::CreateChildProcess(char *ExeName,char * CommandLine,HANDLE StdIn, HANDLE StdOut)
{
 PROCESS_INFORMATION piProcInfo;
 STARTUPINFO siStartInfo;
 char *cmdline = new char[strlen(ExeName)+strlen(CommandLine)+2];

 strcpy(cmdline,ExeName);
 strcat(cmdline," ");
 strcat(cmdline,CommandLine);
 // Set up members of STARTUPINFO structure.
 ZeroMemory(&siStartInfo, sizeof(siStartInfo));
 siStartInfo.cb = sizeof(siStartInfo);

 siStartInfo.hStdInput =StdIn;
 //siStartInfo.hStdInput =GetStdHandle(STD_INPUT_HANDLE); 
 siStartInfo.hStdOutput = StdOut;
 //siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
 
 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
 // Create the child process.
 bool bRes = CreateProcess(NULL,
                      cmdline,       // command line
                      NULL,          // process security attributes
                      NULL,          // primary thread security attributes
                      true,          // handles are inherited
                      0,             // creation flags
                      NULL,          // use parent's environment
                      NULL,          // use parent's current directory
                      &siStartInfo,  // STARTUPINFO pointer
                      &piProcInfo);  // receives PROCESS_INFORMATION
 delete cmdline;
 return bRes;
}

char * TChildProc::ReadStrFromChild(char *Buffer,int Timeout)
{
 const int SleepDevider = 100;
 char *Result = (char*)malloc(1);
 DWORD dwRead, BufSize, DataSize;
 bool Res = false;
 int SleepTime;
 ZeroMemory(Result,1);
 try
 {
     SleepTime = Timeout / SleepDevider;
     do
     {
        for(int i=0;i<SleepDevider;i++)
        {
            Res = PeekNamedPipe(FChildStdoutRd, NULL, 0, NULL, &DataSize, NULL);
            Res = Res && (DataSize > 0);
            if (Res) break;
            Sleep(SleepTime);
        }
        if (Res)
        {
         BufSize = strlen(Result);
         Result = (char*)realloc(Result, BufSize+DataSize+1);
         ZeroMemory(&Result[BufSize],DataSize+1);
         Res = ReadFile(FChildStdoutRd, (void *)&Result[BufSize], DataSize, &dwRead, NULL);         
        }
     }while (Res);
 }
 catch(exception &e)
 {
    Result = (char *)realloc(Result,12);
    strcpy(Result,"Read    Err!");
 }
    Buffer = new char[strlen(Result)];
    strcpy(Buffer,Result);
    delete Result;
    return Buffer;
}

bool TChildProc::WriteStrToChild(const char *Data)
{
 DWORD dwWritten, BufSize;
 char * chBuf = new char[strlen(Data)+5];
 bool Result;
 
 strcpy(chBuf,Data);
 strcat(chBuf,"\n\r");
 BufSize = strlen(chBuf);
 Result = WriteFile(FChildStdinWr, (void *)chBuf, BufSize, &dwWritten, NULL);
 Result = Result && (BufSize == dwWritten);
 delete chBuf;
 return Result;
}

int main(int n, char* a[]) {
   char *MyBuf = "";
   std::string str;
   TChildProc Child("cmd.exe","");
   
   while ( 1 ) {
      cin >> str;
      Child.WriteStrToChild(str.c_str());
      char *MyBuf = "";
      MyBuf = Child.ReadStrFromChild(MyBuf);
      std::cout << "Output of console:" << std::endl << MyBuf;
      free(MyBuf);
   }
   
   return 0;
};




--------------------
Удалил аккаунт. Прощайте!
PM MAIL   Вверх
TheDestroyer
Дата 31.5.2009, 18:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Спасибо, почти то что надо  smile 
Остается следующий вопрос: если использовать данный класс для чтения вывода консольной программы которая может работать достаточно долго, то как понять, что она отработала и закрылась?
Необходимо читать вывод в цикле вне класса TChildProc. Т.е.
Код

char *MyBuf = "";
string str;
TChildProc Child("c:\\test.exe","-commands");
while (!eof(FChildStdoutRd))
{
MyBuf = Child.ReadStrFromChild(MyBuf);
free(MyBuf);
//здесь операции с прочитанным, в т.ч. вывод пользователю
}

Но, видимо, использовать eof неправильно. Вставлять обработку прочитанного в сам класс не выход. Как правильно обеспечить такую структуру чтения вывода консольной программы?

Это сообщение отредактировал(а) TheDestroyer - 31.5.2009, 18:16
PM MAIL   Вверх
andrew_121
Дата 31.5.2009, 18:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кодофей
****


Профиль
Группа: Завсегдатай
Сообщений: 3448
Регистрация: 3.1.2008

Репутация: 2
Всего: 33



TheDestroyer, Попробуйте проверять состояние stdin, stdout

Добавлено через 13 минут и 22 секунды
вот: http://forum.vingrad.ru/forum/topic-261375...1/view-all.html


--------------------
Удалил аккаунт. Прощайте!
PM MAIL   Вверх
TheDestroyer
Дата 31.5.2009, 19:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Цитата(andrew_121 @  31.5.2009,  18:40 Найти цитируемый пост)
проверять состояние stdin, stdout

Вроде как я это и проверяю в коде предыдущего поста.

В приведенной теме поблема немного другая. 
Вроде получилось сделать так:
Объявить хэндл запускаемого процесса PROCESS_INFORMATION piProcInfo глобальным по классу и сделать его public.
Цикл чтения получился такой:
Код

bool end = false;
while (!end)
{
MyBuf = Child.ReadStrFromChild(MyBuf);
A.send_file_data_partial(MyBuf,find_file_type(get_file_ext(fname)), true);
GetExitCodeProcess(Child.piProcInfo.hProcess,&exit); //пока дочерний процесс не закрыт
if (exit != STILL_ACTIVE) {end = true;}
}

PM MAIL   Вверх
TheDestroyer
Дата 1.6.2009, 20:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Обнаружилась проблема: если консольное приложение выдает информацию непрерывно, то все читается нормально, а если оно в течение минуты, например, ничего не выдает (просто работает без вывода, потом продолжает выдавать) то функция PeekNamedPipe(FChildStdoutRd, NULL, 0, NULL, &DataSize, NULL); после этой паузы всегда возвращает false и &DataSize = 0, хотя видно, что консольное приложение работает и должно выводить результат. Вот код чтения из пайпа:
Код

char * TChildProc::ReadStrFromChild(char *Buffer,int Timeout)
{
 const int SleepDevider = 100;
 char *Result = (char*)malloc(1);
 DWORD dwRead, BufSize, DataSize;
 bool Res = false;
 int SleepTime;
 ZeroMemory(Result,1);
 try
 {
     SleepTime = Timeout / SleepDevider;
        for(int i=0;i<SleepDevider;i++)
        {
            Res = PeekNamedPipe(FChildStdoutRd, NULL, 0, NULL, &DataSize, NULL);
            Res = Res && (DataSize > 0);
            if (Res) break;
            Sleep(SleepTime);
        }
        if (Res)
        {
         BufSize = strlen(Result);
         Result = (char*)realloc(Result, BufSize+DataSize+1);
         ZeroMemory(&Result[BufSize],DataSize+1);
         Res = ReadFile(FChildStdoutRd, (void *)&Result[BufSize], DataSize, &dwRead, NULL);    
        }
 }
 catch(exception &e)
 {
    Result = (char *)realloc(Result,12);
    strcpy(Result,"Read    Err!");
 }
    Buffer = new char[strlen(Result)];
    strcpy(Buffer,Result);
    delete Result;
    return Buffer;
}

Вот цикл чтения:
Код

TChildProc Child("c:\\test.exe","-commands");
bool end = false;
while (!end)
{
MyBuf = Child.ReadStrFromChild(MyBuf);
// обработка полученного от консольной программы
GetExitCodeProcess(Child.piProcInfo.hProcess,&exit); //пока дочерний процесс не закрыт
if (exit != STILL_ACTIVE) 
{end = true;}
}
Как проверить что происходит не так? Может что-то не так делаю?
PM MAIL   Вверх
GremlinProg
Дата 2.6.2009, 00:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



TheDestroyer, а не пробовал вместо PeekNamedPipe использовать WaitNamedPipe, она же более логична: подождать, пока не истечет период времени, либо пайп не активирует связь

пример: http://msdn.microsoft.com/en-us/library/aa365592(VS.85).aspx

Добавлено через 4 минуты и 19 секунд
да, там кстати, заодно и причина твоего выхода из цикла, смотри в самом низу, по ссылке:
Код

      if (! fSuccess && GetLastError() != ERROR_MORE_DATA) 
         break; 




--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
TheDestroyer
Дата 2.6.2009, 23:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Да, хорошо бы, но это функция только для именованных пайпов, соответственно не подходит для использования в этом примере.
Нашел в msdn пост по аналогичной проблеме: http://social.msdn.microsoft.com/Forums/en...9-c61c7d8c9f8f/
Видимо, надо использовать именнованные пайпы, как в примере http://msdn.microsoft.com/en-us/library/aa365592(VS.85).aspx
Но я никак не пойму как в этом примере указать, что читать надо из конкретного консольного приложения.
Цитата(GremlinProg @  2.6.2009,  00:08 Найти цитируемый пост)
да, там кстати, заодно и причина твоего выхода из цикла, смотри в самом низу, по ссылке:
код C++
1:
2:
      if (! fSuccess && GetLastError() != ERROR_MORE_DATA) 
         break; 

К сожалению это не то, т.к. мне надо выводить результат не в цикле чтения из пайпа, а в другом, как показано в моем предыдущем посте.
Все еще ищу рабочий пример (а лучше класс) перенаправления вывода консольной программы в свою с помощью пайпов.
PM MAIL   Вверх
TheDestroyer
Дата 3.6.2009, 14:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Написал тестовую консольную программку:
Код

#include <windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
    for (int i=0;i<2;++i)
    {
        printf("test1\r\n");
        Sleep(1000);
    }
    Sleep(10000);
    for (int i=0;i<1000;++i)
    {
        printf("test2\r\n");
        //Sleep(1000);
    }
    return 0;
}

Проверяю как происходит захват ее вывода. Результат: в самом начале пусто, т.е. PeekNamedPipe возвращает DataSize=0, но как только тестовая программка отработает или может раньше, выплевывает сразу все захваченное из нее. Похоже на буферизацию. 
Нашел похожую проблему: http://programmersforum.ru/showthread.php?t=20526
там советуют вообще не использовать PeekNamedPipe. Но тут, похоже, надо делать flush или что-то типа него. Подскажите, что можно сделать для опустошения буфера в каждом цикле. чтобы PeekNamedPipe возвращала  DataSize!=0 ?
PM MAIL   Вверх
GremlinProg
Дата 3.6.2009, 14:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



ради эксперимента, попробуй в цикле вызывать:
Код

fflush(stdout);
FlushFileBuffers( GetStdHandle(STD_OUTPUT_HANDLE) );

на каждую итерацию,
а PeekNamedPipe тут и не нужен,
если постоянно (до обрыва) крутить ReadFile, эффект будет такой же (смотри пример, там же все описано)
даже если ReadFile вернется с ошибкой ERROR_MORE_DATA  в GetLastError, это как раз и будет означать, что буфер на данный момент пустой, но труба еще не закрыта, т.е. можно дальше работать


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
TheDestroyer
Дата 3.6.2009, 18:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Так, вообще ReadFile просто висит если задать большой буфер, эта функция не вернется пока не прочитает столько байт, сколько указано.
Вот пример без PeekNamedPipe http://forum.ixbt.com/topic.cgi?id=40:1426:9#9
В первый момент вывода нет, ReadFile стоит (буфер чтения пробовал 10 байт и 255 байт - всеравно). Стоит некоторое время и потом начинается вывод, т.е. буферизация работает. 
Код

fflush(stdout);
FlushFileBuffers( GetStdHandle(STD_OUTPUT_HANDLE) );

не помогают :(
Явно работает буферизация, только вот нигде не могу найти как ей можно управлять. Аналогичная проблема встречается у людей в инете, но решения так и не нашел.
Приветствуются любые предложения по теме.

Это сообщение отредактировал(а) TheDestroyer - 3.6.2009, 18:18
PM MAIL   Вверх
TheDestroyer
Дата 9.6.2009, 12:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Помимо вопроса с буфером появились еще:
1. Возможно ли получать вывод консольной программы в свою программу и при этом оставить вывод этой консольной программы таким какой он без перенаправления вывода в мою программу? Т.к. некоторые консольные программы при перенаправлении вывода в мою программу работают некорректно.
2. Возможно ли подсоединиться к уже запущенной консольной программе и получать ее вывод в свою программу?
PM MAIL   Вверх
xvr
Дата 9.6.2009, 12:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(TheDestroyer @ 9.6.2009,  12:26)
Помимо вопроса с буфером появились еще:
1. Возможно ли получать вывод консольной программы в свою программу и при этом оставить вывод этой консольной программы таким какой он без перенаправления вывода в мою программу? 

Нет (почти нет). Можно перенаправить вывод консольной программы к себе в пайп, а затем при чтении пайпа дублировать то, что прочлось в первоначальный консольный поток (см. утилиту tee в Linux)
Цитата

2. Возможно ли подсоединиться к уже запущенной консольной программе и получать ее вывод в свою программу?
Нет
Уточнение - и п1 и п2 можно сделать внедрившись в консольную программу и перехватив API функции вывода, но это ОЧЕНЬ геморойно  smile 
PM MAIL   Вверх
GremlinProg
Дата 9.6.2009, 13:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(xvr @  9.6.2009,  14:57 Найти цитируемый пост)
Нет (почти нет). Можно перенаправить вывод консольной программы к себе в пайп, а затем при чтении пайпа дублировать то, что прочлось в первоначальный консольный поток (см. утилиту tee в Linux)

а в windows - это конвеерная обработка "|": http://www.citforum.ru/operating_systems/ois/c050.shtml


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
GremlinProg
Дата 9.6.2009, 13:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



пример: type a.txt > b.txt | type b.txt

можно, конечно и PeekNamedPipe использовать,
но тут надо иметь хотя бы самый примитивный протокол обмена информацией,
по крайней мере, команды: "начало передачи" и "конец передачи"
тогда и проблем потери дпанных не будет, независимо от того, какой способ перенаправления используется "снаружи"


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.1042 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.