C언어 > Win32Comm - RS232 connection module for VBA

TODAY1,514 TOTAL2,701,419
사이트 이용안내
Login▼/회원가입
최신글보기 질문게시판 기술자료 동영상강좌

아두이노 센서 ATMEGA128 PWM LED 초음파 AVR 블루투스 LCD UART 모터 적외선


BASIC4MCU | C언어 | C언어 | Win32Comm - RS232 connection module for VBA

페이지 정보

작성자 키트 작성일2017-09-12 12:00 조회1,930회 댓글0건

본문

 

Win32Comm - RS232 connection module for VBA

 

ActiveX 방식의 문제점들

그동안 IO Control ActivX 모듈을 써서 RS232 기기와 연결하는 엑셀 매크로 프로그램들을 여러 개 만들어 쓰고 있었습니다. 그런데 이 방식은 엑셀 외부의 모듈을 사용하기 때문에 매크로를 사용하려면 미리 ActiveX 모듈을 설치해야 하고, 경우에 따라서는(다 그런지는 모르겠지만 제 동료들이 쓰는 일본어 윈도우즈 및 일본어 엑셀에서는) 외부 모듈의 참조순서를 바꿔 줘야 하는 등 여러가지 번잡한 문제가 있어서 다른 사람들과 공유해서 쓰는 것이 쉽지 않았습니다. 일일이 직접 설치해 줘야 하고 문제가 생기면 고쳐줘야 했거든요. 

최근에 만든 엑셀 매크로 프로그램을 여러 나라(일본, 대만, 미국) 공장 및 실험실에 배포해야 할 일이 생겼는데 위에서 말한 문제 때문에 그대로 배포할 수가 없었습니다. 엑티브엑스 모듈이야 그냥 설치화일 주고 미리 깔으라고 하면 된다지만 외부모듈의 참조순서를 바꿔주는 것은 사용자들한테 직접 하라고 하기가 영 껄끄러운 것이었습니다. 저 자신이 해도 일본어 엑셀이나 중국어 엑셀에서는 메뉴 뒤져가며 고치는 것이 꽤 번잡한 일이었거든요. 사용자들마다 깔려있는 프로그램들이 다르기 때문에 기본순서도 다 다릅니다. 그래서 액티브엑스 방식이 아닌 DLL 방식으로 만들어 공개해 놓은 것이 없는지 찾던 중에 EasyComm을 발견하게 된 겁니다. EasyComm은 외부 모듈을 전혀 사용하지 않고, 윈도우즈 운영체제가 기본적으로 갖고 있는 kernel32.dll 내의 Win32 API만을 이용하여 시리얼 포트를 연결하여 읽고 쓸 수 있게 해 줍니다. 저도 지식이 짧아서 VBA로 Win32 API를 직접 호출할 수 있다는 걸 EasyComm 내부를 뜯어보고 나서야 알게 되었습니다.

API 호출방식

API(Application Programming Interface)라는 것이 뭐냐 하면 운영체제의 여러 기능을 일반 프로그램에서 이용할 수 있도록 열어놓은 통로라고 생각하시면 됩니다. 일반 응용프로그램들은 대개 자신이 열고자 하는 파일이 하드디스크의 몇 번째 섹터에 들어있는지 알 필요가 없습니다. 그냥 운영체제한테 파일이름을 주고 열어달라고 하면 운영체제가 다 알아서 해줍니다. 모든 운영체제는(심지어 도스에서도) 이렇게 일반 프로그램들을 위해 파일 입출력, 화면 입출력, 프로세스 제어, 메모리 관리 등의 서비스를 해 주는데 이를 API라 부르기도 하고, 시스템 호출이라고도 하고, 도스에서는 BIOS(Basic Input Output Service)라고 하기도 했습니다. 윈도우즈95 이후의 32비트 윈도우즈 버젼들은 이런 서비스를 Win32 API라고 통칭합니다. 프로그래머는 자신이 이용하고자 하는 기능을 해주는 API함수를 찾아서 이를 호출하기만 하면 됩니다. C나 베이직 등의 고급언어에서 기본으로 제공하는 화일, 화면제어, 메모리 등의 관련 함수와 기능들도 사실 내부적으로는 대부분 API함수들을 이용하고 있습니다.

그래도 맘에 들지 않는...

VBA로 Win32API를 호출하여 모든 걸 할 수 있다는 걸 알게 된 저는 엄청 기뻤고, 급히 EasyComm 메뉴얼을 번역해 아래의 포스트에 올려놓았습니다. 그런데 막상 EasyComm을 써서 그동안 짜놓은 프로그램들을 바꾸려니 역시 걸리적거리는 것이 있었습니다. 이건 기능의 문제라기 보다는 사실 취향의 문제인데요... 

1. 대부분의 기능들이 함수가 아닌 프로퍼티로 되어 있다. 
키노시타씨가 왜 함수를 만들지 않고, 프로퍼티만으로 입출력 기능을 구현하려 했는지 잘 모르겠습니다. 프로퍼티는 한번에 단 하나의 입출력만 가능합니다. 필요한 변수들을 한꺼번에 세팅할 수 없기 때문에 귀찮게 여러 줄을 써야 합니다. 

2. 에러처리가 번잡하다. 
프로퍼티(특히 쓰기 작업)로는 입출력 에러 발생여부를 직접 리턴할 수가 없으므로, 각 입출력 코드 뒤에 에러체크 루틴을 일일이 넣을 수 밖에 없는데 그러면 코드가 아주 더~리해 집니다. 물론 에러 캐치 방식을 써도 됩니다만 다른 나라에서 사용할 수 있도록 일본어(또는 제가 번역해 놓은 한글)로 설정해 놓은 에러코드 및 설명을 일일이 영문으로 바꾸려니 그것도 쉬운 작업이 아닙니다.

3. 내겐 필요없는 기능이 너무 많다.
깔끔하고 간결한 기능를 선호하는 저로서는 주렁주렁 덧붙여진 기능들이 사실 번거롭기만 합니다.

위에 나열한 그다지 중요하지 않는 이유들 때문에 저는 EasyComm을 재구성해서, 제 입맛에 맞게 모든 기능을 함수 형태로 바꾸기로 했습니다. EasyComm 내의 중요한 입출력 부분만 가져오고, RS232와 관련없는 것들이나 에러핸들링은 그냥 다 빼버렸습니다. 결과적으로 ecDef와 ec 두 모듈을 합쳐 4,000 라인이 넘던 코드를 350 라인으로 줄여 Win32Comm 모듈 하나로 만들어 버렸습니다. 그래도 실질적인 RS232관련 입출력 기능은 모두 살아 있습니다. 그만큼 EasyComm에 군살이 많았다는 뜻이죠.

Win32Comm 사용법  Win32Comm.bas

위의 텍스트 화일을 다운로드(14 kB)해서 저장하신 후, 엑셀의 VB 에디터(alt-F11)에서 [파일]-[가져오기] 메뉴를 선택해서 다운로드한 파일을 임포트하세요. 그러면 모듈 트리에 Win32Comm이라는 모듈이 나타날 겁니다. 그러면 사용준비가 끝난 겁니다. 간단하죠? 이제 자신의 프로그램 코드를 작성하시면 됩니다. 

다음은 코드 작성예로서 Command1 버튼이 눌려지면 COM1 포트를 열어 그 핸들을 A1 셀에 저장한 후, A2 셀의 내용을 읽어 포트에 전송합니다. Command2 버튼이 눌려지면 시리얼포트에 들어온 값을 A3 셀에 집어넣고 포트를 닫는 작업을 수행합니다.

Private Sub CommandButton1_Click()             ' 포트 열어 문자열 보내기 프로시져
    [a1] = CommOpen("com1", "4800, e, 7, 1")  ' 4800 bps, even parity, 7 data, 1 stop으로 COM1을 오픈. 핸들은 A1셀에 저장
    If [a1] = 0 Then                                        ' 에러일 경우 메세지
        MsgBox "Cannot open COM1.", , "Error"
        Exit Sub
    End If
    CommWrite [a1], CStr([a2])                       ' 문자열 보내기
End Sub


Private Sub CommandButton2_Click()              ' 문자열을 받아들인 후 포트 닫기 프로시져
    If [a1] = 0 Then Exit Sub                            ' 아직 포트가 열려있지 않으면 exit
    [a3] = CommReadStr([a1])                        ' 포트에서 문자열을 읽어 A3셀에 저장.
    CommClose [a1]                                      ' 포트 닫기
    [a1] = 0                                                    ' 이전 핸들값 삭제
End Sub

포트를 열 때 핸드쉐이크(default=none)나 버퍼크기(default=1024 bytes)는 CommSetFlowControl 및 CommSetBuffers 함수를 이용하여 세밀하게 설정할 수 있습니다. 자세한 사용법은 아래의 함수 설명 부분을 참조하세요.

입력 기다리기

CommRead 혹은 CommWrite계열의 입출력 함수를 호출하는 경우, 각 함수에서는 입출력 작업을 운영체제에 의뢰한 후 하드웨어적인 작업이 끝날 때까지 기다리지 않고 바로 리턴합니다. 원하는 문자열이 다 들어올 때까지 기다리는 기능은 갖고 있지 않습니다. 이런 부분은 포트에 문자가 몇 개 들어왔는지는 체크해 볼 수 있는 CommNumRcv 함수 등을 이용해 사용자가 직접 작성해 줘야 합니다. 일반적으로 입력을 기다리는 방법은 루프를 써서 계속 체크하는 방법(폴링이라고 합니다), 주기적으로 입력버퍼에 들어온 것이 있는지 체크하는 방법 그리고 이벤트(혹은 인터럽트) 방식이 있는데 Win32 API는 이벤트 방식을 지원하지 않습니다.

만일 루프를 써서 기다리는 방법을 쓰신다면 반드시 루프 내에 DoEvents 를 넣어 두세요. 안 그러면 문자열이 들어올 때까지 엑셀 전체가 무응답 상태가 되어 버립니다. 별도의 쓰레드를 만들면 이런 문제를 피할 수 있는데 VBA에서 쓰레드를 만드는 방법이 있는지는 모르겠습니다. 어쩌다 어떤 블로그에서 Win32 API로 VBA 쓰레드를 만들어 보려고 시도하는 것을 본 적이 있는데 결국 실패하더군요.  

'예
CommWrite [a1], [a2]         ' 포트로 문자열 전송. [a1]은 포트 핸들, [a2]는 기기명령어를 담고 있다고 가정.
Do While CommNumRcv([a1]) = 0   ' 포트에 들어온 문자가 없으면 기다림
    DoEvents                                  ' 무한루프 때문에 엑셀이 멈춰버리는 것을 방지
Loop
[a3] = CommReadStr([a1])             ' 입력된 문자열을 [a3]에 저장

때로는 루프를 쓰는 것보다 타이머를 이용해 주기적으로 문자열이 들어왔는지 체크하는 것이 더 유용할 수도 있습니다. 타이머를 이용하는 가장 쉬운 방법은 엑셀 내장 함수인 Application.OnTime을 쓰는 것입니다. 이 함수는 지정된 매크로를 지정된 시간에 호출하도록 설정하는데 쓰이는데, 콜백프로시져 끝부분에 자기 자신을 다시 콜백하도록 해놓으면 주기적인 작업을 할 수 있습니다. 콜백프로시져는 반드시 모듈레벨이어야 하고 시트별로 저장된 매크로는 부를 수 없습니다.

'예. Module1 내에 작성
Public Sub CheckInput()
        If [a1] = 0 Then Exit Sub              ' [a1]에 포트 핸들이 저장되어 있다고 가정. 만일 0이면 exit
        If CommNumRcv([a1]) > 0 Then  '  포트에 들어온 문자열이 있으면 
            'your handling routine              ' 필요한 작업을 합니다.
        End If
        Application.OnTime Now + TimeValue("00:00:01"), "CheckInput"    ' 1초 후에 다시 체크하도록 설정
End Sub

프로그램 어디선가 CheckInput을 한번 실행한 이후로는 스스로 계속 돌아갑니다. 그 Application.OnTime 이 편하긴 한데 시간설정의 최소 단위가 1초로 다소 길다는 점, 호출시마다 커서 모양이 화살표로 바뀐다는 점 그리고 어쩌다 시간설정을 잘못 했을 경우 이미 닫아놓았던 파일도 그 시간이 되면 자동으로 다시 열릴 수 있다는 등의 부작용이 있습니다. 보다 빠르고 부작용이 없이 타이머를 쓰는 방법도 있는데 그건 역시 Win32 API 기능을 불러다 쓰는 것입니다. 이것 역시 모듈레벨에서 만들어야 합니다. 먼저 표준모듈(Module1)을 만들고, 입력을 체크하고 필요한 작업을 수행하는 프로시져를 만듭니다. 

Sub CheckInput()
    On Error Resume Next  ' 에러가 발생하면 무시하고 다음 라인으로 넘어갑니다. 고속루틴에서는 필수.

    If [a1] = 0 Then Exit Sub                           ' [a1]에 포트 핸들이 저장되어 있다고 가정. 만일 0이면 exit
    If CommNumRcv([a1]) = 0 Then Exit Sub   '  포트에 들어온 문자열이 없으면 exit
        'your handling routine                           ' 여기에 필요한 작업을 합니다.
End Sub

그 다음에는 모듈의 윗부분에 다음과 같이 Win32 타이머 API를 이용하겠다는 선언을 합니다.

Public Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Public Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long

그리고나면 모듈뿐만 아니라 시트레벨에서도 필요할 때 아무 때나 다음과 같이 타이머를 켜거나, 끌 수 있습니다. SetTimer는 Application.OnTime과 달리, 콜백 프로시져를 한번만 부르는 것이 아니라 KillTimer를 부르기 전까지는 지정된 시간간격으로 계속 콜백한다는 것에 주의하시기 바랍니다. 

SetTimer Application.hWnd, 3100, 200, AddressOf CheckInput  
KillTimer Application.hWnd, 3100  

위의 예에서 3100은 타이머ID입니다. 여러 개의 타이머가 있을 때 구분하기 위해 씁니다. 아무 값이나 자신이 정해서 쓰면 됩니다. 200은 타이머 간격을 밀리초 단위로 설정한 것입니다. 매 0.2초마다 콜백 프로시져가 불려져서 포트에 들어온 데이타가 있는지 체크합니다. 데이타가 있으면 CommReadBytes(바이너리 데이타 읽어오기) 또는 CommReadStr(아스키 문자열 데이타 읽어오기) 함수를 이용해 데이타를 읽어와 처리하면 됩니다. 

Win32Comm_Example.xls

위 엑셀 파일은 위에서 설명한 포트 여닫기, 읽고 쓰기, 타이머 사용법을 실제 화일로 만든 것입니다. 예제일 뿐이므로 실용적이지는 않습니다. 타이머는 포트가 열려 있을 때 버튼을 눌러야 동작하고, 포트를 닫으면 저절로 멈춥니다. 콜백이 일어나고 있음을 확인하기 위하여 A4 셀의 값을 하나씩 증가시킬 뿐 실제로 시리얼 포트 관련 작업은 하지 않습니다. 

Win32Comm 함수 설명

CommOpen 함수

Public Function CommOpen(PortName As String, Optional Setting As String = "9600, n, 8, 1") As Long
PortName: 열고자 하는 포트이름. "COM1", "COM2" 등
Setting: 통신조건. 문법은 DOS의 mode 명령어와 동일합니다. 기본값은 9600 bps, no parity, 8 data bit, 1 stop bit 입니다.
            mode 명령어 문법
            baudrate=1200, 2400, 4800, 9600, 14400, 28800, ... , 119200 중 하나. 저는 9600이랑 4800밖에 안 써봐서..
            parity=no, even, odd, mark, space 중 하나
            data=5,6,7,8 중 하나
            stop=1,2
반환값: 성공=화일핸들, 실패=0. 화일핸들은 다른 함수들을 호출할 때 항상 첫번째 인수로 써야 합니다.
예1) [a1] = CommOpen "COM1", "9600, n, 8, 1"              '약식 표기에서는 순서를 지켜야 합니다.
예2) [a1] = CommOpen "COM1", "parity=n stop=1 data=8 baud=9600"   '변수이름을 지정하면 순서는 관계없습니다.


CommClose 함수

Public Function CommClose(Handle As Long) As Long
Handle: 포트 핸들. CommOpen의 반환값
반환값: 성공<>0, 실패=0
예) 
Dim hPort as Long
hPort = CommOpen("COM1", "9600, n, 8, 1")
...
CommClose hPort

CommSetBuffer 함수
Public Function CommSetBuffers(Handle As Long, InBufferSize As Long, OutBufferSize As Long) As Long
Handle: 포트 핸들. CommOpen의 반환값
InBufferSize/OutBufferSize: 입력 및 출력버퍼의 크기. 바이트 단위. 디폴트=1024
반환값: 성공<>0, 실패=0
예) 
Dim hPort as Long
hPort = CommOpen("COM1", "9600, n, 8, 1") ' 포트 오픈
CommSetBuffers hPort, 4096, 1024    ' 입력버퍼 4KB, 출력버퍼 1KB

CommSetFlowControl 함수

Public Function CommSetFlowControl(Handle As Long, Flow As String, _
        Optional xonChar As Byte = &H11, Optional xoffChar As Byte = &H13, _
        Optional xonlim As Integer = 128, Optional xofflim As Integer = 128) As Long
Handle: 포트 핸들. CommOpen의 반환값
Flow: "No", "XonXoff", "RTS", "DTR" 중 하나. 사실 첫 글자만 구분하므로 "N", "X", "R", "D" 만 써도 됩니다. 디폴트="N"
xonChar/xoffChar: xon/off 방식 흐름제어에 이용할 문자들입니다. 지정하지 않으면 디폴트 문자들로 지정됩니다.
xonLim/xoffLim: xon/off 방식 흐름제어시 xon/xoff를 개시할 쓰기버퍼의 잔량입니다.
반환값: 성공<>0, 실패=0
예) 
Dim hPort as Long
hPort = CommOpen("COM1", "9600, n, 8, 1") ' 포트 오픈
CommSetFlowControl hPort, "X"                  ' Xon/Xoff 흐름제어, 제어문자 및 버퍼임계선은 디폴트 사용

CommWrite 함수

Public Function CommWrite(Handle As Long, ByteData As Variant, Optional length As Long = 0) As Long

Handle: 포트 핸들. CommOpen의 반환값
ByteData: 보낼 데이타. Byte, Integer, Long, Single, Double의 경우 0-255의 범위로 truncation됩니다.
                실수형은 정수형으로 반올림됩니다. 배열은 각 개별항이 truncation 및 반올림되어 전송됩니다.
                엑셀의 문자열(유니코드)는 ANSI코드로 변환된 후 전송됩니다.
length: 데이타가 배열/문자열일 경우 보낼 데이타 개수입니다. 0 이면 모든 데이타를 전송합니다(디폴트).
반환값: 출력버퍼로 보낸 데이타 갯수. 전송성공 <> 0,  전송실패=0.
예) 
Dim hPort as Long
hPort = CommOpen("COM1", "9600, n, 8, 1") ' 포트 오픈
CommWrite hPort, "Test"

CommWriteRTS / CommWriteDTR 함수

Public Function CommWriteRTS(Handle As Long, level As Long) As Long
Public Function CommWriteDTR(Handle As Long, level As Long) As Long

Handle: 포트 핸들. CommOpen의 반환값
level:  각 라인의 설정값. 0 이면 reset(마이너스 레벨), 0 이 아니면 set(플러스 레벨)
반환값: 성공 <> 0,  실패=0.
예) 
Dim hPort as Long
hPort = CommOpen("COM1", "9600, n, 8, 1") ' 포트 오픈
CommWriteDTR hPort, 1                             ' set DTR

나중에 CommSetFlowControl함수를 써서 핸드쉐이크를 설정하면 CommWriteRTS/CommWriteDTR로 설정해 놓은 라인상태는 무효화될 수 있으니 주의하세요.
CommNumRcv 함수

Public Function CommNumRcv(Handle As Long) As Long

Handle: 포트 핸들. CommOpen의 반환값
반환값: 입력버퍼에 들어온 문자 갯수. 에러나 no data일 경우 0.
예) 
Dim hPort as Long
hPort = CommOpen("COM1", "9600, n, 8, 1") ' 포트 오픈
CommWrite hPort, Cstr([a2]) + Chr(13)        ' A2 cell의 내용을 기기로 전송
Do While CommNumRcv(hPort)=0                ' 문자가 들어올 때까지 기다림
    DoEvents                                              ' 엑셀 freeze 방지
Loop
[a3] = CommReadStr(hPort)                        ' 들어온 문자열을 A3 셀에 저장
CommReadBytes 함수

Public Function CommReadBytes(Handle As Long, Optional length As Long = 0) As Byte()

Handle: 포트 핸들. CommOpen의 반환값
length: 읽어올 문자의 개수. 0(default)이거나, CommNumRcv보다 클 경우 버퍼내의 모든 문자를 읽어옵니다.
반환값: 에러나 no data일 경우 인덱스가 0 to 0인 배열을, 데이타가 있을 경우 1 부터 시작하는 배열을 반환합니다.
예) 
Dim bdata() as Byte
bdata = CommReadBytes(hPort, 1)     ' 입력버퍼의 데이타 중 1 바이트만 읽어 옵니다.
If Ubound(bdata) Then 
    Msgbox "No data or error"          ' 반환된 배열의 인덱스 상한이 0이면 no data 또는 에러
Else
    [a3] = Cstr(bdata(1))                  ' 아스키 값을 A3 셀에 표시합니다.
End If

CommReadBytes 함수는 바이너리 데이타를 처리하기 위해 만든 것으로 일반적인 아스키 문자열을 읽어올 경우에는 아래의 CommReadStr 함수가 더 편리합니다.
CommReadStr 함수

Public Function CommReadStr(Handle As Long, Optional length As Long = 0) As String

Handle: 포트 핸들. CommOpen의 반환값
length: 읽어올 문자의 개수. 0(default)이거나, CommNumRcv보다 클 경우 버퍼내의 모든 문자를 읽어옵니다.
반환값: 버퍼 내의 데이타를 문자열로서 반환합니다. 에러나 no data일 경우 빈 문자열("")을 반환합니다.
예)  CommNumRcv 예제 참조

CommReadStr 함수는 바이너리 데이타, 특히 아스키값이 0인 데이타를 제대로 처리하지 못합니다. 윈도우즈 및 엑셀 내부에서 0을 문자열 종료로 인식하기 때문입니다. 게다가 CommReadStr함수는 자동으로 ANSI 코드를 유니코드로 변환합니다. 바이너리 데이타를 원형 그대로 입력받기 위해서는 위에서 설명한 CommReadBytes 함수를 이용하세요.
CommReadCTS / CommReadDSR / CommReadDCD / CommReadRI 함수

Public Function CommReadCTS(Handle As Long) As Long
Public Function CommReadDSR(Handle As Long) As Long
Public Function CommReadDCD(Handle As Long) As Long
Public Function CommReadRI(Handle As Long) As Long

Handle: 포트 핸들. CommOpen의 반환값
반환값: 플러스 레벨=+1, 마이너스 레벨=-1, 에러=0.

RS232의 신호라인 각각의 현재 상태를 읽어옵니다. 리턴값 0은 마이너스 레벨이 아니라, 에러를 뜻함에 주의하세요. 너무 단순해서 예는 생략합니다.
CommClear / CommClearRx / CommClearTx 함수

Public Function CommClear(Handle As Long) As Long
Public Function CommClearRx(Handle As Long) As Long
Public Function CommClearTx(Handle As Long) As Long

Handle: 포트 핸들. CommOpen의 반환값
반환값: 성공<>0, 에러=0.

입출력 버퍼를 모두 혹은 각각 초기화합니다. 너무 단순해서 예는 생략합니다.
맺음말

Win32Comm은 EasyComm을 기반으로 수정한 것입니다. 저 역시  EasyComm의 라이센스 정책에 따라 Win32Comm을 프리웨어로 공개합니다. 아무나 용도에 관계없이 자유로이 사용하실 수 있습니다. 단 Win32Comm을 사용하다 발생한 손실에 대해서는 아무런 책임도 지지 않습니다. Win32Comm은 상업용 어플리케이션의 일부로서 포함할 수 있지만, 단독으로 판매해서는 안 됩니다. 

그동안 ActiveX를 사용해 오던 매크로 프로그램을, Win32Comm을 써서 바꿔봤는데 아무 이상없이 잘 작동하고 있습니다. 하지만 제가 사용하는 작동조건이 몇 개 안 되기 때문에 다른 사용조건에서는 에러가 날 수도 있습니다. 그런 부분을 알려주시면 가능한 범위 내에서 업데이트하도록 하겠습니다. 그리고, Generic RS232 Communicator도 조만간 Win32Comm용으로 업데이트하도록 하겠습니다. 아무쪼록 제 작은 노력이 여러분의 작업에 도움이 되었길 바랍니다.

 by stal | 2007/09/29 02:48 | 메카트로닉스 | 트랙백 | 덧글(1)

트랙백 주소 : http://snowxtal.egloos.com/tb/1500127
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
3660040649_KLfTRckQ_ico_comment.gif Commented by stal at 2007/10/01 13:00 3660040649_dNz0QplG_icon_reply.gif
Application.hWnd는 Excel2003 이후에나 지원하는군요. Excel2000에서 타이머함수를 쓰시려면 FindWindow API함수를 이용해 hWnd를 미리 구해야 합니다. 아래와 같이 하시면 됩니다.

hXls = FindWindow("XLMAIN", vbNullString)

FindWindow는 모듈에 다음과 같이 선언한 후에 사용합니다.

Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
http://snowxtal.egloos.com/1500127

댓글 0

조회수 1,930

등록된 댓글이 없습니다.

C언어HOME > C언어 > 전체 목록

게시물 검색

2022년 1월 2월 3월 4월 5월 6월 7월 8월 9월 10월 11월 12월
2021년 1월 2월 3월 4월 5월 6월 7월 8월 9월 10월 11월 12월
2020년 1월 2월 3월 4월 5월 6월 7월 8월 9월 10월 11월 12월
2019년 1월 2월 3월 4월 5월 6월 7월 8월 9월 10월 11월 12월
2018년 1월 2월 3월 4월 5월 6월 7월 8월 9월 10월 11월 12월
Privacy Policy
MCU BASIC ⓒ 2020
모바일버전으로보기